From 7caeaf3a122d6b174f70013157450fc799dfd19d Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 2 Feb 2024 19:10:13 +0300 Subject: [PATCH 01/53] Updated on 2026-08-14 --- .../java/com/tangem/tap/TapApplication.kt | 5 ++++ .../di/domain/WalletManagersFacadeModule.kt | 6 ++-- .../com/tangem/tap/domain/TapWalletManager.kt | 13 ++++----- .../tap/proxy/redux/DaggerGraphState.kt | 2 ++ .../BlockchainWalletManagerFactoryModule.kt | 28 ++++++++++++++++++ .../DefaultBlockchainDataStorage.kt | 29 +++++++++++++++++++ .../DefaultWalletManagersFacade.kt | 6 ++-- .../utils/WalletManagerFactory.kt | 9 ++---- gradle/dependencies.toml | 2 +- 9 files changed, 78 insertions(+), 22 deletions(-) create mode 100644 core/datasource/src/main/java/com/tangem/datasource/di/BlockchainWalletManagerFactoryModule.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/blockchain/DefaultBlockchainDataStorage.kt diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index f892dddee4..7e200d31b9 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -10,6 +10,7 @@ import com.orhanobut.logger.AndroidLogAdapter import com.orhanobut.logger.Logger import com.tangem.Log import com.tangem.LogFormat +import com.tangem.blockchain.common.WalletManagerFactory import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.filter.OneTimeEventFilter @@ -159,6 +160,9 @@ internal class TapApplication : Application(), ImageLoaderFactory { @Inject lateinit var testerFeatureToggles: TesterFeatureToggles + + @Inject + lateinit var blockchainWalletManagerFactory: WalletManagerFactory // endregion Injected override fun onCreate() { @@ -236,6 +240,7 @@ internal class TapApplication : Application(), ImageLoaderFactory { sendFeatureToggles = sendFeatureToggles, derivationsRepository = derivationsRepository, testerFeatureToggles = testerFeatureToggles, + blockchainWalletManagerFactory = blockchainWalletManagerFactory, ), ), ) diff --git a/app/src/main/java/com/tangem/tap/di/domain/WalletManagersFacadeModule.kt b/app/src/main/java/com/tangem/tap/di/domain/WalletManagersFacadeModule.kt index 02a52ce342..65a6dce65c 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/WalletManagersFacadeModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/WalletManagersFacadeModule.kt @@ -1,8 +1,8 @@ package com.tangem.tap.di.domain import com.squareup.moshi.Moshi +import com.tangem.blockchain.common.WalletManagerFactory import com.tangem.datasource.asset.AssetReader -import com.tangem.datasource.config.ConfigManager import com.tangem.datasource.di.SdkMoshi import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.datasource.local.walletmanager.WalletManagersStore @@ -24,7 +24,7 @@ internal object WalletManagersFacadeModule { fun provideWalletManagersFacade( walletManagersStore: WalletManagersStore, userWalletsStore: UserWalletsStore, - configManager: ConfigManager, + blockchainWalletManagerFactory: WalletManagerFactory, mnemonicRepository: MnemonicRepository, assetReader: AssetReader, @SdkMoshi moshi: Moshi, @@ -32,7 +32,7 @@ internal object WalletManagersFacadeModule { return DefaultWalletManagersFacade( walletManagersStore = walletManagersStore, userWalletsStore = userWalletsStore, - configManager = configManager, + blockchainWalletManagerFactory = blockchainWalletManagerFactory, assetReader = assetReader, moshi = moshi, mnemonic = mnemonicRepository.generateDefaultMnemonic(), diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt index 8d9ba42e6a..4379b387ab 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -1,23 +1,23 @@ package com.tangem.tap.domain -import com.tangem.blockchain.common.BlockchainSdkConfig import com.tangem.blockchain.common.Token import com.tangem.blockchain.common.Wallet import com.tangem.blockchain.common.WalletManagerFactory import com.tangem.core.analytics.Analytics +import com.tangem.core.analytics.models.Basic import com.tangem.datasource.config.ConfigManager import com.tangem.domain.common.extensions.withMainContext import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.wallets.models.UserWallet import com.tangem.operations.attestation.Attestation -import com.tangem.core.analytics.models.Basic import com.tangem.tap.common.extensions.setContext import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction import com.tangem.tap.features.disclaimer.createDisclaimer import com.tangem.tap.features.disclaimer.redux.DisclaimerAction import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction +import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.store import com.tangem.tap.tangemSdkManager import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider @@ -30,18 +30,15 @@ class TapWalletManager( private val dispatchers: CoroutineDispatcherProvider = AppCoroutineDispatcherProvider(), ) { - private val blockchainSdkConfig by lazy { - store.state.globalState.configManager?.config?.blockchainSdkConfig ?: BlockchainSdkConfig() - } - private var loadUserWalletDataJob: Job? = null set(value) { field?.cancel() field = value } - val walletManagerFactory: WalletManagerFactory - by lazy { WalletManagerFactory(blockchainSdkConfig) } + val walletManagerFactory: WalletManagerFactory by lazy { + store.state.daggerGraphState.get(DaggerGraphState::blockchainWalletManagerFactory) + } suspend fun onWalletSelected(userWallet: UserWallet, sendAnalyticsEvent: Boolean) { // If a previous job was running, it gets cancelled before the new one starts, diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt index b1839fae93..19875bc1be 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt @@ -1,5 +1,6 @@ package com.tangem.tap.proxy.redux +import com.tangem.blockchain.common.WalletManagerFactory import com.tangem.datasource.connection.NetworkConnectionManager import com.tangem.domain.appcurrency.repository.AppCurrencyRepository import com.tangem.domain.apptheme.repository.AppThemeModeRepository @@ -57,6 +58,7 @@ data class DaggerGraphState( val currenciesRepository: CurrenciesRepository? = null, val derivationsRepository: DerivationsRepository? = null, val testerFeatureToggles: TesterFeatureToggles? = null, + val blockchainWalletManagerFactory: WalletManagerFactory? = null, ) : StateType { inline fun get(getDependency: DaggerGraphState.() -> T?): T { diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/BlockchainWalletManagerFactoryModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/BlockchainWalletManagerFactoryModule.kt new file mode 100644 index 0000000000..aeec8933f9 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/di/BlockchainWalletManagerFactoryModule.kt @@ -0,0 +1,28 @@ +package com.tangem.datasource.di + +import com.tangem.blockchain.common.WalletManagerFactory +import com.tangem.datasource.config.ConfigManager +import com.tangem.datasource.local.blockchain.DefaultBlockchainDataStorage +import com.tangem.datasource.local.preferences.AppPreferencesStore +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal object BlockchainWalletManagerFactoryModule { + + @Provides + @Singleton + fun provideBlockchainWalletManagerFactory( + configManager: ConfigManager, + appPreferencesStore: AppPreferencesStore, + ): WalletManagerFactory { + return WalletManagerFactory( + config = configManager.config.blockchainSdkConfig, + blockchainDataStorage = DefaultBlockchainDataStorage(appPreferencesStore), + ) + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/blockchain/DefaultBlockchainDataStorage.kt b/core/datasource/src/main/java/com/tangem/datasource/local/blockchain/DefaultBlockchainDataStorage.kt new file mode 100644 index 0000000000..3f3d7192f5 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/blockchain/DefaultBlockchainDataStorage.kt @@ -0,0 +1,29 @@ +package com.tangem.datasource.local.blockchain + +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.stringPreferencesKey +import com.tangem.blockchain.common.datastorage.BlockchainDataStorage +import com.tangem.datasource.local.preferences.AppPreferencesStore +import com.tangem.datasource.local.preferences.utils.getSyncOrNull + +/** + * [BlockchainDataStorage] implementation + * + * @property appPreferencesStore app preferences store + * +[REDACTED_AUTHOR] + */ +internal class DefaultBlockchainDataStorage( + private val appPreferencesStore: AppPreferencesStore, +) : BlockchainDataStorage { + + override suspend fun getOrNull(key: String): String? { + return appPreferencesStore.getSyncOrNull(key = stringPreferencesKey(name = key)) + } + + override suspend fun store(key: String, value: String) { + appPreferencesStore.edit { + it[stringPreferencesKey(key)] = value + } + } +} \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt index 213a569e11..f5bbe8c596 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt @@ -19,7 +19,6 @@ import com.tangem.blockchain.extensions.SimpleResult import com.tangem.crypto.bip39.Mnemonic import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.datasource.asset.AssetReader -import com.tangem.datasource.config.ConfigManager import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.datasource.local.walletmanager.WalletManagersStore import com.tangem.domain.common.util.hasDerivation @@ -39,6 +38,7 @@ import kotlinx.coroutines.flow.Flow import timber.log.Timber import java.math.BigDecimal import java.util.EnumSet +import com.tangem.blockchain.common.WalletManagerFactory as BlockchainWalletManagerFactory @Suppress("LargeClass", "TooManyFunctions") // FIXME: Move to its own module and make internal @@ -46,7 +46,7 @@ import java.util.EnumSet class DefaultWalletManagersFacade( private val walletManagersStore: WalletManagersStore, private val userWalletsStore: UserWalletsStore, - configManager: ConfigManager, + blockchainWalletManagerFactory: BlockchainWalletManagerFactory, mnemonic: Mnemonic, assetReader: AssetReader, moshi: Moshi, @@ -54,7 +54,7 @@ class DefaultWalletManagersFacade( private val demoConfig by lazy { DemoConfig() } private val resultFactory by lazy { UpdateWalletManagerResultFactory() } - private val walletManagerFactory by lazy { WalletManagerFactory(configManager) } + private val walletManagerFactory by lazy { WalletManagerFactory(blockchainWalletManagerFactory) } private val sdkTokenConverter by lazy { SdkTokenConverter() } private val txHistoryStateConverter by lazy { SdkTransactionHistoryStateConverter() } private val txHistoryItemConverter by lazy { SdkTransactionHistoryItemConverter(assetReader, moshi) } diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/WalletManagerFactory.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/WalletManagerFactory.kt index 9e83e20e3a..d00e6d64a1 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/WalletManagerFactory.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/WalletManagerFactory.kt @@ -3,23 +3,18 @@ package com.tangem.domain.walletmanager.utils import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.DerivationParams import com.tangem.blockchain.common.WalletManager -import com.tangem.blockchain.common.WalletManagerFactory import com.tangem.crypto.hdWallet.DerivationPath -import com.tangem.datasource.config.ConfigManager import com.tangem.domain.common.DerivationStyleProvider import com.tangem.domain.common.extensions.makeWalletManagerForApp import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.models.scan.ScanResponse import timber.log.Timber +import com.tangem.blockchain.common.WalletManagerFactory as BlockchainWalletManagerFactory internal class WalletManagerFactory( - private val configManager: ConfigManager, + private val sdkWalletManagerFactory: BlockchainWalletManagerFactory, ) { - private val sdkWalletManagerFactory by lazy { - WalletManagerFactory(configManager.config.blockchainSdkConfig) - } - fun createWalletManager( scanResponse: ScanResponse, blockchain: Blockchain, diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index f6b9405dcf..f807b5d3a4 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -85,7 +85,7 @@ spr-client = "3.6.2" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-471" +tangemBlockchainSdk = "develop-473" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-324" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From 1512c4e3172d4f5e9613e1a26e9c198b59bb8b51 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 2 Feb 2024 15:34:06 +0300 Subject: [PATCH 02/53] Updated on 2026-08-14 --- .../impl/presentation/state/StateRouter.kt | 18 +-- .../state/fee/FeeNotificationFactory.kt | 2 +- .../state/fee/SendFeeNotification.kt | 21 +--- .../ui/fee/SendSpeedAndFeeContent.kt | 107 ++++++++++++++---- .../presentation/ui/fee/SendSpeedSelector.kt | 33 ++++++ .../impl/presentation/ui/send/SendContent.kt | 10 +- .../presentation/viewmodel/SendViewModel.kt | 2 +- 7 files changed, 138 insertions(+), 55 deletions(-) diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/StateRouter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/StateRouter.kt index 4edb445226..f29ae7acb7 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/StateRouter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/StateRouter.kt @@ -15,12 +15,16 @@ internal class StateRouter( fragmentManager.get()?.popBackStack() } - fun onBackClick() { - when (currentState.value) { - SendUiStateType.Recipient -> popBackStack() - SendUiStateType.Amount -> showRecipient() - SendUiStateType.Fee -> showAmount() - SendUiStateType.Send -> showFee() + fun onBackClick(isSuccess: Boolean = false) { + if (isSuccess) { + popBackStack() + } else { + when (currentState.value) { + SendUiStateType.Recipient -> popBackStack() + SendUiStateType.Amount -> showRecipient() + SendUiStateType.Fee -> showAmount() + SendUiStateType.Send -> showFee() + } } } @@ -54,7 +58,7 @@ internal class StateRouter( currentState.update { SendUiStateType.Fee } } - private fun showSend() { + fun showSend() { currentState.update { SendUiStateType.Send } } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeNotificationFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeNotificationFactory.kt index 244ed17cdd..d95031bd4e 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeNotificationFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeNotificationFactory.kt @@ -70,7 +70,7 @@ internal class FeeNotificationFactory( val customAmount = customFee.firstOrNull() ?: return val customValue = customAmount.value.parseToBigDecimal(customAmount.decimals) if (selectedFee == FeeType.CUSTOM && minimumValue > customValue) { - add(SendFeeNotification.Informational.TooLow) + add(SendFeeNotification.Warning.TooLow) } } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/SendFeeNotification.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/SendFeeNotification.kt index d58cb3cd98..f895fb4921 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/SendFeeNotification.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/SendFeeNotification.kt @@ -8,22 +8,6 @@ import com.tangem.features.send.impl.R sealed class SendFeeNotification(val config: NotificationConfig) { - sealed class Informational( - val title: TextReference, - val subtitle: TextReference, - ) : SendFeeNotification( - config = NotificationConfig( - title = title, - subtitle = subtitle, - iconResId = R.drawable.ic_alert_circle_24, - ), - ) { - object TooLow : Informational( - title = resourceReference(id = R.string.send_notification_transaction_delay_title), - subtitle = resourceReference(id = R.string.send_notification_transaction_delay_text), - ) - } - sealed class Warning( val title: TextReference, val subtitle: TextReference, @@ -36,6 +20,11 @@ sealed class SendFeeNotification(val config: NotificationConfig) { buttonsState = buttonsState, ), ) { + object TooLow : Warning( + title = resourceReference(id = R.string.send_notification_transaction_delay_title), + subtitle = resourceReference(id = R.string.send_notification_transaction_delay_text), + ) + data class TooHigh( val value: String, ) : Warning( diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedAndFeeContent.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedAndFeeContent.kt index b253efe2e5..0c1316c178 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedAndFeeContent.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedAndFeeContent.kt @@ -15,14 +15,15 @@ import com.tangem.core.ui.components.notifications.Notification import com.tangem.core.ui.res.TangemTheme import com.tangem.features.send.impl.presentation.state.SendStates import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState +import com.tangem.features.send.impl.presentation.state.fee.FeeType import com.tangem.features.send.impl.presentation.state.fee.SendFeeNotification import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList private const val FEE_SELECTOR_KEY = "FEE_SELECTOR_KEY" private const val FEE_CUSTOM_KEY = "FEE_CUSTOM_KEY" -@OptIn(ExperimentalFoundationApi::class) @Composable internal fun SendSpeedAndFeeContent(state: SendStates.FeeState?, clickIntents: SendClickIntents) { if (state == null) return @@ -36,46 +37,92 @@ internal fun SendSpeedAndFeeContent(state: SendStates.FeeState?, clickIntents: S horizontal = TangemTheme.dimens.spacing16, ), ) { - item( - key = FEE_SELECTOR_KEY, - ) { - SendSpeedSelector( - state = state, - clickIntents = clickIntents, - modifier = Modifier.animateItemPlacement(), - ) - } + feeSelector(state, clickIntents) + topNotifications(notifications) customFee(feeSendState) - notifications(notifications) - subtractButton( - receivedAmount = state.receivedAmount, - isSubtract = state.isSubtract, - isSubtractAvailable = state.isSubtractAvailable, - clickIntents = clickIntents, - ) + middleNotifications(notifications) + subtractButton(state, clickIntents) + bottomNotifications(notifications) } } @OptIn(ExperimentalFoundationApi::class) -internal fun LazyListScope.notifications(configs: ImmutableList, modifier: Modifier = Modifier) { +private fun LazyListScope.feeSelector(state: SendStates.FeeState, clickIntents: SendClickIntents) { + item( + key = FEE_SELECTOR_KEY, + ) { + SendSpeedSelector( + state = state, + clickIntents = clickIntents, + modifier = Modifier.animateItemPlacement(), + ) + } +} + +private fun LazyListScope.topNotifications( + configs: ImmutableList, + modifier: Modifier = Modifier, +) { + notifications( + configs = configs.filter { + it is SendFeeNotification.Error.ExceedsBalance || + it is SendFeeNotification.Warning.NetworkFeeUnreachable + }.toImmutableList(), + modifier = modifier, + ) +} + +private fun LazyListScope.middleNotifications( + configs: ImmutableList, + modifier: Modifier = Modifier, +) { + notifications( + configs = configs.filter { + it is SendFeeNotification.Warning.TooLow || + it is SendFeeNotification.Warning.TooHigh + }.toImmutableList(), + modifier = modifier, + ) +} + +private fun LazyListScope.bottomNotifications( + configs: ImmutableList, + modifier: Modifier = Modifier, +) { + notifications( + configs = configs.filterIsInstance().toImmutableList(), + isLast = true, + modifier = modifier, + ) +} + +@OptIn(ExperimentalFoundationApi::class) +private fun LazyListScope.notifications( + configs: ImmutableList, + modifier: Modifier = Modifier, + isLast: Boolean = false, +) { items( items = configs, key = { it::class.java }, contentType = { it::class.java }, itemContent = { + val bottomPadding = if (isLast) TangemTheme.dimens.spacing12 else TangemTheme.dimens.spacing0 Notification( config = it.config, modifier = modifier - .padding(top = TangemTheme.dimens.spacing12) + .padding( + top = TangemTheme.dimens.spacing12, + bottom = bottomPadding, + ) .animateItemPlacement(), containerColor = when (it) { is SendFeeNotification.Error.ExceedsBalance, is SendFeeNotification.Warning.NetworkFeeUnreachable, - -> TangemTheme.colors.background.primary + -> TangemTheme.colors.background.action else -> TangemTheme.colors.button.disabled }, iconTint = when (it) { - is SendFeeNotification.Informational -> TangemTheme.colors.icon.accent is SendFeeNotification.Error.ExceedsBalance -> { if (it.config.buttonsState == null) { TangemTheme.colors.icon.warning @@ -116,20 +163,30 @@ internal fun LazyListScope.customFee(feeSendState: FeeSelectorState, modifier: M @OptIn(ExperimentalFoundationApi::class) internal fun LazyListScope.subtractButton( - receivedAmount: String, - isSubtract: Boolean, - isSubtractAvailable: Boolean, + state: SendStates.FeeState, clickIntents: SendClickIntents, modifier: Modifier = Modifier, ) { + val receivedAmount = state.receivedAmount + val isSubtract = state.isSubtract + val isSubtractAvailable = state.isSubtractAvailable + val feeSendState = state.feeSelectorState if (isSubtractAvailable) { item { + val feeStateContent = feeSendState as? FeeSelectorState.Content + val isCustomAvailable = feeStateContent?.customValues.isNullOrEmpty().not() + val isCustomSelected = feeStateContent?.selectedFee == FeeType.CUSTOM + val topPadding = if (isCustomSelected && isCustomAvailable) { + TangemTheme.dimens.spacing12 + } else { + TangemTheme.dimens.spacing20 + } SendSpeedSubtract( receivingAmount = receivedAmount, isSubtract = isSubtract, onSelectClick = clickIntents::onSubtractSelect, modifier = modifier - .padding(vertical = TangemTheme.dimens.spacing12) + .padding(top = topPadding) .animateItemPlacement(), ) } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelector.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelector.kt index db73ae5c7e..2721c1698e 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelector.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelector.kt @@ -4,6 +4,9 @@ import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* @@ -39,6 +42,7 @@ import com.tangem.features.send.impl.R import com.tangem.features.send.impl.presentation.state.SendStates import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState import com.tangem.features.send.impl.presentation.state.fee.FeeType +import com.tangem.features.send.impl.presentation.state.fee.SendFeeNotification import com.tangem.features.send.impl.presentation.ui.common.FooterContainer import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents import java.math.BigDecimal @@ -112,12 +116,17 @@ internal fun SendSpeedSelector( visible = fees.normal is Fee.Ethereum, label = "Custom fee appearance animation", ) { + val showWarning = state.notifications.any { + it is SendFeeNotification.Warning.TooHigh || + it is SendFeeNotification.Warning.TooLow + } SendSpeedSelectorItem( titleRes = R.string.common_fee_selector_option_custom, iconRes = R.drawable.ic_edit_24, isSelected = isSelected == FeeType.CUSTOM, onSelect = { clickIntents.onFeeSelectorClick(FeeType.CUSTOM) }, showDivider = fees.normal !is Fee.Ethereum, + showWarning = showWarning, ) } } @@ -224,6 +233,7 @@ private fun SendSpeedSelectorItem( symbolLength: Int? = null, isSelected: Boolean = false, showDivider: Boolean = true, + showWarning: Boolean = false, ) { val iconTint by animateColorAsState( targetValue = if (isSelected) { @@ -259,7 +269,10 @@ private fun SendSpeedSelectorItem( symbolLength = symbolLength, textStyle = textStyle, ) + } else { + SpacerWMax() } + WarningIcon(showWarning = showWarning) } if (showDivider) { Box( @@ -340,6 +353,26 @@ private fun RowScope.SelectorValueContent( ) } +@Composable +private fun WarningIcon(showWarning: Boolean = false) { + AnimatedVisibility( + visible = showWarning, + label = "Custom fee warning indicator", + enter = fadeIn(), + exit = fadeOut(), + ) { + Image( + painter = painterResource(R.drawable.ic_alert_triangle_20), + contentDescription = null, + modifier = Modifier + .padding( + vertical = TangemTheme.dimens.spacing12, + horizontal = TangemTheme.dimens.spacing14, + ), + ) + } +} + //region preview @Preview @Composable diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/SendContent.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/SendContent.kt index d014f71caa..694dac7048 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/SendContent.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/SendContent.kt @@ -67,16 +67,16 @@ internal fun SendContent(uiState: SendUiState) { walletBalance = amountState.walletBalance.resolveReference(), ) } - AmountBlock( - amountState = amountState, - isSuccess = isSuccess, - onClick = uiState.clickIntents::showAmount, - ) RecipientBlock( recipientState = recipientState, isSuccess = isSuccess, onClick = uiState.clickIntents::showRecipient, ) + AmountBlock( + amountState = amountState, + isSuccess = isSuccess, + onClick = uiState.clickIntents::showAmount, + ) FeeBlock( feeState = feeState, isSuccess = isSuccess, 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 b02ffb58d7..a675d41dad 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 @@ -352,7 +352,7 @@ internal class SendViewModel @Inject constructor( // region screen state navigation override fun popBackStack() = stateRouter.popBackStack() - override fun onBackClick() = stateRouter.onBackClick() + override fun onBackClick() = stateRouter.onBackClick(uiState.sendState.isSuccess) override fun onNextClick() = stateRouter.onNextClick() override fun onPrevClick() = stateRouter.onPrevClick() From cc1c1ae3c464e10f9d0a32c54144c26b89c3698d Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 2 Feb 2024 23:19:29 +0300 Subject: [PATCH 03/53] Updated on 2026-08-14 --- .../middlewares/TradeCryptoMiddleware.kt | 52 ++++++++++++++++++- .../send/api/navigation/SendRouter.kt | 3 ++ .../send/impl/presentation/SendFragment.kt | 3 ++ .../presentation/state/SendStateFactory.kt | 17 ++++-- .../impl/presentation/state/SendUiState.kt | 5 +- .../impl/presentation/state/StateRouter.kt | 50 ++++++++++++------ .../state/amount/SendAmountStateConverter.kt | 6 +-- .../state/fields/SendAmountFieldConverter.kt | 20 +++++-- .../SendRecipientAddressFieldConverter.kt | 6 +-- .../recipient/SendRecipientStateConverter.kt | 6 +-- .../presentation/ui/SendNavigationButtons.kt | 6 ++- .../send/impl/presentation/ui/SendScreen.kt | 2 + .../impl/presentation/ui/send/SendContent.kt | 34 +++++++++--- .../presentation/viewmodel/SendViewModel.kt | 36 +++++++++---- 14 files changed, 192 insertions(+), 54 deletions(-) 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 36123cf1a9..2432321a48 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 @@ -46,6 +46,11 @@ object TradeCryptoMiddleware { } } + private val isSendRedesignedEnabled: Boolean + get() = store.state.daggerGraphState.get( + getDependency = DaggerGraphState::sendFeatureToggles, + ).isRedesignedSendEnabled + @Suppress("LongMethod", "CyclomaticComplexMethod") private fun handle(state: () -> AppState?, action: TradeCryptoAction) { if (DemoHelper.tryHandle(state, action)) return @@ -57,8 +62,20 @@ object TradeCryptoMiddleware { is TradeCryptoAction.Swap -> openSwap( currency = action.cryptoCurrency, ) - is TradeCryptoAction.SendToken -> handleSendToken(action = action) - is TradeCryptoAction.SendCoin -> handleSendCoin(action = action) + is TradeCryptoAction.SendToken -> { + if (isSendRedesignedEnabled) { + handleNewSendToken(action = action) + } else { + handleSendToken(action = action) + } + } + is TradeCryptoAction.SendCoin -> { + if (isSendRedesignedEnabled) { + handleNewSendCoin(action = action) + } else { + handleSendCoin(action = action) + } + } } } @@ -280,4 +297,35 @@ object TradeCryptoMiddleware { store.dispatchOnMain(NavigationAction.NavigateTo(screen = AppScreen.Send, bundle = bundle)) } } + + private fun handleNewSendToken(action: TradeCryptoAction.SendToken) { + handleNewSend( + userWalletId = action.userWallet.walletId.stringValue, + txInfo = action.transactionInfo, + currency = action.tokenCurrency, + ) + } + + private fun handleNewSendCoin(action: TradeCryptoAction.SendCoin) { + handleNewSend( + userWalletId = action.userWallet.walletId.stringValue, + txInfo = action.transactionInfo, + currency = action.coinStatus.currency, + ) + } + + private fun handleNewSend( + userWalletId: String, + txInfo: TradeCryptoAction.TransactionInfo?, + currency: CryptoCurrency, + ) { + val bundle = bundleOf( + SendRouter.CRYPTO_CURRENCY_KEY to currency, + SendRouter.USER_WALLET_ID_KEY to userWalletId, + SendRouter.TRANSACTION_ID_KEY to txInfo?.transactionId, + SendRouter.DESTINATION_ADDRESS_KEY to txInfo?.destinationAddress, + SendRouter.AMOUNT_KEY to txInfo?.amount, + ) + store.dispatchOnMain(NavigationAction.NavigateTo(screen = AppScreen.Send, bundle = bundle)) + } } \ No newline at end of file diff --git a/features/send/api/src/main/kotlin/com/tangem/features/send/api/navigation/SendRouter.kt b/features/send/api/src/main/kotlin/com/tangem/features/send/api/navigation/SendRouter.kt index 5a44a6614d..fce7f71d36 100644 --- a/features/send/api/src/main/kotlin/com/tangem/features/send/api/navigation/SendRouter.kt +++ b/features/send/api/src/main/kotlin/com/tangem/features/send/api/navigation/SendRouter.kt @@ -9,5 +9,8 @@ interface SendRouter { companion object { const val CRYPTO_CURRENCY_KEY = "send_crypto_currency" const val USER_WALLET_ID_KEY = "send_user_wallet_id" + const val TRANSACTION_ID_KEY = "send_transaction_id" + const val AMOUNT_KEY = "send_amount" + const val DESTINATION_ADDRESS_KEY = "send_destination_address" } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/SendFragment.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/SendFragment.kt index 7cdab91041..c7cbe1a802 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/SendFragment.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/SendFragment.kt @@ -50,10 +50,13 @@ internal class SendFragment : ComposeFragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) lifecycle.addObserver(viewModel) + + val isEditingDisabled = arguments?.getString(SendRouter.TRANSACTION_ID_KEY) != null viewModel.setRouter( innerSendRouter, StateRouter( fragmentManager = WeakReference(parentFragmentManager), + isEditingDisabled = isEditingDisabled, ), ) listenToQrCode() 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 17ca733eee..d53fbaab70 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 @@ -92,18 +92,29 @@ internal class SendStateFactory( // region UI states fun getInitialState(): SendUiState = SendUiState( clickIntents = clickIntents, - currentState = MutableStateFlow(SendUiStateType.Amount), + currentState = MutableStateFlow(SendUiStateType.None), event = consumedEvent(), + isEditingDisabled = false, ) fun getReadyState(): SendUiState { val state = currentStateProvider() return state.copy( - amountState = state.amountState ?: amountStateConverter.convert(Unit), - recipientState = state.recipientState ?: recipientStateConverter.convert(Unit), + amountState = state.amountState ?: amountStateConverter.convert(""), + recipientState = state.recipientState ?: recipientStateConverter.convert(""), feeState = state.feeState ?: feeStateConverter.convert(Unit), ) } + + fun getReadyState(amount: String, destinationAddress: String): SendUiState { + val state = currentStateProvider() + return state.copy( + amountState = state.amountState ?: amountStateConverter.convert(amount), + recipientState = state.recipientState ?: recipientStateConverter.convert(destinationAddress), + feeState = state.feeState ?: feeStateConverter.convert(Unit), + isEditingDisabled = true, + ) + } //endregion //region amount state clicks diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt index 16d0b6190a..2d624f11d8 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt @@ -18,6 +18,7 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import java.math.BigDecimal /** @@ -26,12 +27,13 @@ import java.math.BigDecimal @Immutable internal data class SendUiState( val clickIntents: SendClickIntents, + val isEditingDisabled: Boolean, val amountState: SendStates.AmountState? = null, val recipientState: SendStates.RecipientState? = null, val feeState: SendStates.FeeState? = null, val sendState: SendStates.SendState = SendStates.SendState(), val recipientList: MutableStateFlow> = MutableStateFlow(PagingData.empty()), - val currentState: MutableStateFlow, + val currentState: StateFlow, val event: StateEvent, ) @@ -99,6 +101,7 @@ internal sealed class SendStates { } enum class SendUiStateType { + None, Amount, Recipient, Fee, diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/StateRouter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/StateRouter.kt index f29ae7acb7..ea2c456a23 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/StateRouter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/StateRouter.kt @@ -2,28 +2,40 @@ package com.tangem.features.send.impl.presentation.state import androidx.fragment.app.FragmentManager import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.update import java.lang.ref.WeakReference internal class StateRouter( private val fragmentManager: WeakReference, + private val isEditingDisabled: Boolean, ) { - var currentState: MutableStateFlow = MutableStateFlow(SendUiStateType.Recipient) - private set + private var mutableCurrentState: MutableStateFlow = MutableStateFlow( + if (isEditingDisabled) { + SendUiStateType.None + } else { + SendUiStateType.Recipient + }, + ) + + val currentState: StateFlow = mutableCurrentState fun popBackStack() { fragmentManager.get()?.popBackStack() } fun onBackClick(isSuccess: Boolean = false) { - if (isSuccess) { - popBackStack() - } else { - when (currentState.value) { - SendUiStateType.Recipient -> popBackStack() + when { + isSuccess -> popBackStack() + isEditingDisabled -> when (currentState.value) { + SendUiStateType.Send -> showFee() + else -> popBackStack() + } + else -> when (currentState.value) { SendUiStateType.Amount -> showRecipient() SendUiStateType.Fee -> showAmount() SendUiStateType.Send -> showFee() + else -> popBackStack() } } } @@ -34,31 +46,35 @@ internal class StateRouter( SendUiStateType.Amount -> showFee() SendUiStateType.Fee -> showSend() SendUiStateType.Send -> onBackClick() + else -> popBackStack() } } fun onPrevClick() { - when (currentState.value) { - SendUiStateType.Recipient -> popBackStack() - SendUiStateType.Amount -> showRecipient() - SendUiStateType.Fee -> showAmount() - SendUiStateType.Send -> popBackStack() + if (isEditingDisabled) { + popBackStack() + } else { + when (currentState.value) { + SendUiStateType.Amount -> showRecipient() + SendUiStateType.Fee -> showAmount() + else -> popBackStack() + } } } fun showAmount() { - currentState.update { SendUiStateType.Amount } + mutableCurrentState.update { SendUiStateType.Amount } } fun showRecipient() { - currentState.update { SendUiStateType.Recipient } + mutableCurrentState.update { SendUiStateType.Recipient } } fun showFee() { - currentState.update { SendUiStateType.Fee } + mutableCurrentState.update { SendUiStateType.Fee } } - fun showSend() { - currentState.update { SendUiStateType.Send } + private fun showSend() { + mutableCurrentState.update { SendUiStateType.Send } } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountStateConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountStateConverter.kt index 5bda8cc991..df2a63b153 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountStateConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountStateConverter.kt @@ -22,9 +22,9 @@ internal class SendAmountStateConverter( private val cryptoCurrencyStatusProvider: Provider, private val iconStateConverter: CryptoCurrencyToIconStateConverter, private val sendAmountFieldConverter: SendAmountFieldConverter, -) : Converter { +) : Converter { - override fun convert(value: Unit): SendStates.AmountState { + override fun convert(value: String): SendStates.AmountState { val userWallet = userWalletProvider() val appCurrency = appCurrencyProvider() val status = cryptoCurrencyStatusProvider() @@ -35,7 +35,7 @@ internal class SendAmountStateConverter( walletName = userWallet.name, walletBalance = resourceReference(R.string.send_wallet_balance_format, wrappedList(crypto, fiat)), tokenIconState = iconStateConverter.convert(status), - amountTextField = sendAmountFieldConverter.convert(Unit), + amountTextField = sendAmountFieldConverter.convert(value), isPrimaryButtonEnabled = false, segmentedButtonConfig = persistentListOf( SendAmountSegmentedButtonsConfig( diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldConverter.kt index 25a63f78f8..2fd6c7906b 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldConverter.kt @@ -3,7 +3,9 @@ package com.tangem.features.send.impl.presentation.state.fields import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType +import com.tangem.blockchain.extensions.toBigDecimalOrDefault import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.tokens.model.Amount import com.tangem.domain.tokens.model.AmountType @@ -21,20 +23,28 @@ internal class SendAmountFieldConverter( private val clickIntents: SendClickIntents, private val cryptoCurrencyStatusProvider: Provider, private val appCurrencyProvider: Provider, -) : Converter { +) : Converter { - override fun convert(value: Unit): SendTextField.AmountField { + override fun convert(value: String): SendTextField.AmountField { val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() + val cryptoDecimal = value.toBigDecimalOrDefault() + val cryptoAmount = cryptoDecimal.convertToAmount(cryptoCurrencyStatus.currency) + val fiatValue = if (value.isEmpty()) { + "" + } else { + val fiatDecimal = cryptoCurrencyStatus.value.fiatRate?.multiply(cryptoDecimal) ?: BigDecimal.ZERO + fiatDecimal.parseBigDecimal(FIAT_DECIMALS) + } return SendTextField.AmountField( - value = "", - fiatValue = "", + value = value, + fiatValue = fiatValue, onValueChange = clickIntents::onAmountValueChange, keyboardOptions = KeyboardOptions( imeAction = ImeAction.Next, keyboardType = KeyboardType.Number, ), isFiatValue = false, - cryptoAmount = BigDecimal.ZERO.convertToAmount(cryptoCurrencyStatus.currency), + cryptoAmount = cryptoAmount, fiatAmount = getAppCurrencyAmount(appCurrencyProvider()), isError = false, error = TextReference.Res(R.string.swapping_insufficient_funds), diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientAddressFieldConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientAddressFieldConverter.kt index bc5cf7a463..ab822de809 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientAddressFieldConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientAddressFieldConverter.kt @@ -11,11 +11,11 @@ import com.tangem.utils.converter.Converter internal class SendRecipientAddressFieldConverter( private val clickIntents: SendClickIntents, -) : Converter { +) : Converter { - override fun convert(value: Unit): SendTextField.RecipientAddress { + override fun convert(value: String): SendTextField.RecipientAddress { return SendTextField.RecipientAddress( - value = "", + value = value, onValueChange = clickIntents::onRecipientAddressValueChange, keyboardOptions = KeyboardOptions( imeAction = ImeAction.Next, diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientStateConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientStateConverter.kt index 814d968374..a558d4ded6 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientStateConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientStateConverter.kt @@ -9,7 +9,7 @@ import com.tangem.utils.converter.Converter internal class SendRecipientStateConverter( private val clickIntents: SendClickIntents, private val cryptoCurrencyStatusProvider: Provider, -) : Converter { +) : Converter { private val addressFieldConverter by lazy { SendRecipientAddressFieldConverter(clickIntents) } private val memoFieldConverter by lazy { @@ -19,9 +19,9 @@ internal class SendRecipientStateConverter( ) } - override fun convert(value: Unit): SendStates.RecipientState { + override fun convert(value: String): SendStates.RecipientState { return SendStates.RecipientState( - addressTextField = addressFieldConverter.convert(Unit), + addressTextField = addressFieldConverter.convert(value), memoTextField = memoFieldConverter.convertOrNull(), network = cryptoCurrencyStatusProvider().currency.network.name, isPrimaryButtonEnabled = false, diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt index 525578a157..5ff3dbf5d6 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt @@ -49,9 +49,10 @@ internal fun SendNavigationButtons(uiState: SendUiState) { @Composable private fun SendSecondaryNavigationButton(uiState: SendUiState) { val currentState = uiState.currentState.collectAsState() + val isEditingDisabled = uiState.isEditingDisabled + val isCorrectScreen = currentState.value == SendUiStateType.Amount || currentState.value == SendUiStateType.Fee AnimatedVisibility( - visible = currentState.value == SendUiStateType.Amount || - currentState.value == SendUiStateType.Fee, + visible = !isEditingDisabled && isCorrectScreen, ) { Icon( modifier = Modifier @@ -170,6 +171,7 @@ private fun getButtonData( isSuccess: Boolean, ): Pair Unit> { return when (currentState.value) { + SendUiStateType.None, SendUiStateType.Amount, SendUiStateType.Recipient, SendUiStateType.Fee, diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendScreen.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendScreen.kt index 8fe5c5f53d..04220b3f11 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendScreen.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendScreen.kt @@ -45,6 +45,7 @@ internal fun SendScreen(uiState: SendUiState) { SendUiStateType.Recipient -> R.string.send_recipient_label SendUiStateType.Fee -> R.string.common_fee_selector_title SendUiStateType.Send -> if (!isSuccess) R.string.send_confirm_label else null + else -> null } val iconRes = if (currentState.value == SendUiStateType.Recipient) { R.drawable.ic_qrcode_scan_24 @@ -102,6 +103,7 @@ private fun SendScreenContent( uiState.clickIntents, ) SendUiStateType.Send -> SendContent(uiState) + else -> Unit } } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/SendContent.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/SendContent.kt index 694dac7048..6430b56837 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/SendContent.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/SendContent.kt @@ -70,11 +70,13 @@ internal fun SendContent(uiState: SendUiState) { RecipientBlock( recipientState = recipientState, isSuccess = isSuccess, + isEditingDisabled = uiState.isEditingDisabled, onClick = uiState.clickIntents::showRecipient, ) AmountBlock( amountState = amountState, isSuccess = isSuccess, + isEditingDisabled = uiState.isEditingDisabled, onClick = uiState.clickIntents::showAmount, ) FeeBlock( @@ -121,7 +123,12 @@ private fun FromWallet(walletName: String, walletBalance: String) { } @Composable -private fun AmountBlock(amountState: SendStates.AmountState, isSuccess: Boolean, onClick: () -> Unit) { +private fun AmountBlock( + amountState: SendStates.AmountState, + isSuccess: Boolean, + isEditingDisabled: Boolean, + onClick: () -> Unit, +) { val amount = amountState.amountTextField val cryptoAmount = formatCryptoAmount( @@ -134,6 +141,11 @@ private fun AmountBlock(amountState: SendStates.AmountState, isSuccess: Boolean, fiatCurrencyCode = (amount.fiatAmount.type as AmountType.FiatType).code, fiatCurrencySymbol = amount.fiatAmount.currencySymbol, ) + val backgroundColor = if (isEditingDisabled) { + TangemTheme.colors.button.disabled + } else { + TangemTheme.colors.background.action + } InputRowImage( title = TextReference.Res(R.string.send_amount_label), subtitle = TextReference.Str(cryptoAmount), @@ -142,21 +154,31 @@ private fun AmountBlock(amountState: SendStates.AmountState, isSuccess: Boolean, showNetworkIcon = true, modifier = Modifier .clip(TangemTheme.shapes.roundedCornersXMedium) - .background(TangemTheme.colors.background.action) - .clickable(enabled = !isSuccess) { onClick() }, + .background(backgroundColor) + .clickable(enabled = !isSuccess && !isEditingDisabled) { onClick() }, ) } @Composable -private fun RecipientBlock(recipientState: SendStates.RecipientState, isSuccess: Boolean, onClick: () -> Unit) { +private fun RecipientBlock( + recipientState: SendStates.RecipientState, + isSuccess: Boolean, + isEditingDisabled: Boolean, + onClick: () -> Unit, +) { val address = recipientState.addressTextField val memo = recipientState.memoTextField + val backgroundColor = if (isEditingDisabled) { + TangemTheme.colors.button.disabled + } else { + TangemTheme.colors.background.action + } Column( modifier = Modifier .clip(TangemTheme.shapes.roundedCornersXMedium) - .background(TangemTheme.colors.background.action) - .clickable(enabled = !isSuccess) { onClick() }, + .background(backgroundColor) + .clickable(enabled = !isSuccess && !isEditingDisabled) { onClick() }, ) { val showMemo = memo != null && memo.value.isNotBlank() InputRowRecipientDefault( 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 a675d41dad..f8ec87a05d 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 @@ -88,6 +88,10 @@ internal class SendViewModel @Inject constructor( private val cryptoCurrency: CryptoCurrency = savedStateHandle[SendRouter.CRYPTO_CURRENCY_KEY] ?: error("This screen can't open without `CryptoCurrency`") + private val transactionId: String? = savedStateHandle[SendRouter.TRANSACTION_ID_KEY] + private val amount: String? = savedStateHandle[SendRouter.AMOUNT_KEY] + private val destinationAddress: String? = savedStateHandle[SendRouter.DESTINATION_ADDRESS_KEY] + private val selectedAppCurrencyFlow: StateFlow = createSelectedAppCurrencyFlow() private var innerRouter: InnerSendRouter by Delegates.notNull() @@ -192,11 +196,10 @@ internal class SendViewModel @Inject constructor( .flowWithLifecycle(owner.lifecycle) .onEach { currencyStatus -> currencyStatus.onRight { - cryptoCurrencyStatus = it - coinCryptoCurrencyStatus = it - getWalletsAndRecent() - uiState = stateFactory.getReadyState() - updateNotifications() + onDataLoaded( + currencyStatus = it, + coinCurrencyStatus = it, + ) } } .flowOn(dispatchers.main) @@ -208,10 +211,10 @@ internal class SendViewModel @Inject constructor( flow2 = getCurrencyStatusUpdates(isSingleWallet = isSingleWallet), ) { coinStatus, currencyStatus -> if (coinStatus.isRight() && currencyStatus.isRight()) { - coinStatus.onRight { coinCryptoCurrencyStatus = it } - currencyStatus.onRight { cryptoCurrencyStatus = it } - getWalletsAndRecent() - uiState = stateFactory.getReadyState() + onDataLoaded( + currencyStatus = currencyStatus.getOrElse { error("Currency status is unreachable") }, + coinCurrencyStatus = coinStatus.getOrElse { error("Coin status is unreachable") }, + ) } }.flowWithLifecycle(owner.lifecycle) .flowOn(dispatchers.main) @@ -245,6 +248,21 @@ internal class SendViewModel @Inject constructor( ) } + private fun onDataLoaded(currencyStatus: CryptoCurrencyStatus, coinCurrencyStatus: CryptoCurrencyStatus) { + cryptoCurrencyStatus = currencyStatus + coinCryptoCurrencyStatus = coinCurrencyStatus + + if (transactionId != null && amount != null && destinationAddress != null) { + uiState = stateFactory.getReadyState(amount, destinationAddress) + showFee() + } else { + getWalletsAndRecent() + uiState = stateFactory.getReadyState() + showRecipient() + } + updateNotifications() + } + private fun getWalletsAndRecent() { combine( flow = getUserWallets().conflate(), From 6411d7810a92a3937a96c69109741624ca20bfbf Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 5 Feb 2024 16:34:22 +0300 Subject: [PATCH 04/53] Updated on 2026-08-14 --- .../tap/di/domain/TokensDomainModule.kt | 2 + .../data/tokens/converters/UtxoConverter.kt | 14 ++++ .../tangem/data/tokens/di/TokensDataModule.kt | 6 ++ .../DefaultCurrencyChecksRepository.kt | 70 +++++++++++++++++ .../DefaultWalletManagersFacade.kt | 77 +++---------------- .../walletmanager/WalletManagersFacade.kt | 44 +---------- .../model/blockchains/UtxoAmountLimit.kt | 14 ++++ .../tokens/GetCurrencyWarningsUseCase.kt | 8 +- .../repository/CurrencyChecksRepository.kt | 32 ++++++++ .../state/SendNotificationFactory.kt | 37 +++++++-- .../presentation/viewmodel/SendViewModel.kt | 4 +- .../feature/swap/DefaultSwapRepository.kt | 9 --- .../feature/swap/domain/api/SwapRepository.kt | 5 -- .../feature/swap/domain/SwapInteractorImpl.kt | 6 +- .../swap/domain/di/SwapDomainModule.kt | 3 + 15 files changed, 195 insertions(+), 136 deletions(-) create mode 100644 data/tokens/src/main/kotlin/com/tangem/data/tokens/converters/UtxoConverter.kt create mode 100644 data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt create mode 100644 domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/blockchains/UtxoAmountLimit.kt create mode 100644 domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrencyChecksRepository.kt diff --git a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt index b439d53019..acdcfa699e 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt @@ -99,6 +99,7 @@ internal object TokensDomainModule { networksRepository: NetworksRepository, marketCryptoCurrencyRepository: MarketCryptoCurrencyRepository, swapRepository: SwapRepository, + currencyChecksRepository: CurrencyChecksRepository, showSwapPromoTokenUseCase: ShouldShowSwapPromoTokenUseCase, dispatchers: CoroutineDispatcherProvider, ): GetCurrencyWarningsUseCase { @@ -108,6 +109,7 @@ internal object TokensDomainModule { quotesRepository = quotesRepository, networksRepository = networksRepository, marketCryptoCurrencyRepository = marketCryptoCurrencyRepository, + currencyChecksRepository = currencyChecksRepository, swapRepository = swapRepository, showSwapPromoTokenUseCase = showSwapPromoTokenUseCase, dispatchers = dispatchers, diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/converters/UtxoConverter.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/converters/UtxoConverter.kt new file mode 100644 index 0000000000..2a663558c3 --- /dev/null +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/converters/UtxoConverter.kt @@ -0,0 +1,14 @@ +package com.tangem.data.tokens.converters + +import com.tangem.domain.tokens.model.blockchains.UtxoAmountLimit +import com.tangem.utils.converter.Converter +import com.tangem.blockchain.common.UtxoAmountLimit as BlockchainUtxoAmountLimit + +internal class UtxoConverter : Converter { + override fun convert(value: BlockchainUtxoAmountLimit): UtxoAmountLimit { + return UtxoAmountLimit( + maxLimit = value.maxLimit, + maxAmount = value.maxAmount, + ) + } +} \ No newline at end of file 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 dba2fda4b9..05c56cfaf9 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 @@ -113,4 +113,10 @@ internal object TokensDataModule { ): NetworksCompatibilityRepository { return DefaultNetworksCompatibilityRepository(userWalletsStore = userWalletsStore, dispatchers = dispatchers) } + + @Provides + @Singleton + fun provideCurrencyChecksRepository(walletManagersFacade: WalletManagersFacade): CurrencyChecksRepository { + return DefaultCurrencyChecksRepository(walletManagersFacade = walletManagersFacade) + } } \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt new file mode 100644 index 0000000000..8f21f5d495 --- /dev/null +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrencyChecksRepository.kt @@ -0,0 +1,70 @@ +package com.tangem.data.tokens.repository + +import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider +import com.tangem.blockchain.common.ReserveAmountProvider +import com.tangem.blockchain.common.UtxoAmountLimitProvider +import com.tangem.data.tokens.converters.UtxoConverter +import com.tangem.domain.tokens.model.Network +import com.tangem.domain.tokens.model.blockchains.UtxoAmountLimit +import com.tangem.domain.tokens.repository.CurrencyChecksRepository +import com.tangem.domain.walletmanager.WalletManagersFacade +import com.tangem.domain.wallets.models.UserWalletId +import java.math.BigDecimal + +internal class DefaultCurrencyChecksRepository( + private val walletManagersFacade: WalletManagersFacade, +) : CurrencyChecksRepository { + override suspend fun getExistentialDeposit(userWalletId: UserWalletId, network: Network): BigDecimal? { + val manager = walletManagersFacade.getOrCreateWalletManager( + userWalletId = userWalletId, + network = network, + ) + + return if (manager is ExistentialDepositProvider) manager.getExistentialDeposit() else null + } + + override suspend fun getDustValue(userWalletId: UserWalletId, network: Network): BigDecimal? { + val manager = walletManagersFacade.getOrCreateWalletManager( + userWalletId = userWalletId, + network = network, + ) + + return manager?.dustValue + } + + override suspend fun getReserveAmount(userWalletId: UserWalletId, network: Network): BigDecimal? { + val manager = walletManagersFacade.getOrCreateWalletManager( + userWalletId = userWalletId, + network = network, + ) + + return if (manager is ReserveAmountProvider) manager.getReserveAmount() else null + } + + override suspend fun checkIfAccountFunded(userWalletId: UserWalletId, network: Network, address: String): Boolean { + val manager = walletManagersFacade.getOrCreateWalletManager( + userWalletId = userWalletId, + network = network, + ) + + return if (manager is ReserveAmountProvider) manager.isAccountFunded(address) else true + } + + override suspend fun checkUtxoAmountLimit( + userWalletId: UserWalletId, + network: Network, + amount: BigDecimal, + fee: BigDecimal, + ): UtxoAmountLimit? { + val manager = walletManagersFacade.getOrCreateWalletManager( + userWalletId = userWalletId, + network = network, + ) + val utxoAmount = if (manager is UtxoAmountLimitProvider) { + manager.checkUtxoAmountLimit(amount, fee) + } else { + null + } + return utxoAmount?.let(UtxoConverter()::convert) + } +} \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt index f5bbe8c596..004b40c10b 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt @@ -5,7 +5,6 @@ import arrow.core.raise.either import arrow.core.raise.ensureNotNull import arrow.core.right import com.squareup.moshi.Moshi -import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider import com.tangem.blockchain.blockchains.solana.RentProvider import com.tangem.blockchain.common.* import com.tangem.blockchain.common.address.Address @@ -321,6 +320,16 @@ class DefaultWalletManagersFacade( return walletManager } + @Deprecated("Will be removed in future") + override suspend fun getOrCreateWalletManager(userWalletId: UserWalletId, network: Network): WalletManager? { + val blockchain = Blockchain.fromId(network.id.value) + return getOrCreateWalletManager( + userWalletId = userWalletId, + blockchain = blockchain, + derivationPath = network.derivationPath.value, + ) + } + @Deprecated("Will be removed in future") override suspend fun getStoredWalletManagers(userWalletId: UserWalletId): List { return walletManagersStore.getAllSync(userWalletId) @@ -376,63 +385,6 @@ class DefaultWalletManagersFacade( } } - @Deprecated("Will be removed in future") - override suspend fun getExistentialDeposit(userWalletId: UserWalletId, network: Network): BigDecimal? { - val manager = getOrCreateWalletManager( - userWalletId = userWalletId, - network = network, - ) - - return if (manager is ExistentialDepositProvider) manager.getExistentialDeposit() else null - } - - @Deprecated("Will be removed in future") - override suspend fun getDustValue(userWalletId: UserWalletId, network: Network): BigDecimal? { - val blockchain = Blockchain.fromId(network.id.value) - val manager = getOrCreateWalletManager( - userWalletId = userWalletId, - blockchain = blockchain, - derivationPath = network.derivationPath.value, - ) - - return manager?.dustValue - } - - @Deprecated("Will be removed in future") - override suspend fun getReserveAmount(userWalletId: UserWalletId, network: Network): BigDecimal? { - val manager = getOrCreateWalletManager( - userWalletId = userWalletId, - network = network, - ) - - return if (manager is ReserveAmountProvider) manager.getReserveAmount() else null - } - - @Deprecated("Will be removed in future") - override suspend fun checkIfAccountFunded(userWalletId: UserWalletId, network: Network, address: String): Boolean { - val manager = getOrCreateWalletManager( - userWalletId = userWalletId, - network = network, - ) - - return if (manager is ReserveAmountProvider) manager.isAccountFunded(address) else true - } - - @Deprecated("Will be removed in future") - override suspend fun checkUtxoAmountLimit( - userWalletId: UserWalletId, - network: Network, - amount: BigDecimal, - fee: BigDecimal, - ): UtxoAmountLimit? { - val manager = getOrCreateWalletManager( - userWalletId = userWalletId, - network = network, - ) - - return if (manager is UtxoAmountLimitProvider) manager.checkUtxoAmountLimit(amount, fee) else null - } - @Deprecated("Will be removed in future") override fun getAll(userWalletId: UserWalletId): Flow> { return walletManagersStore.getAll(userWalletId) @@ -605,13 +557,4 @@ class DefaultWalletManagersFacade( walletManager.addTokens(tokensToAdd) } - - private suspend fun getOrCreateWalletManager(userWalletId: UserWalletId, network: Network): WalletManager? { - val blockchain = Blockchain.fromId(network.id.value) - return getOrCreateWalletManager( - userWalletId = userWalletId, - blockchain = blockchain, - derivationPath = network.derivationPath.value, - ) - } } \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt index 4988a715c5..684655b8a2 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt @@ -108,6 +108,9 @@ interface WalletManagersFacade { derivationPath: String?, ): WalletManager? + @Deprecated("Will be removed in future") + suspend fun getOrCreateWalletManager(userWalletId: UserWalletId, network: Network): WalletManager? + @Deprecated("Will be removed in future") suspend fun getStoredWalletManagers(userWalletId: UserWalletId): List @@ -136,47 +139,6 @@ interface WalletManagersFacade { */ suspend fun getRentInfo(userWalletId: UserWalletId, network: Network): CryptoCurrencyWarning.Rent? - /** - * Returns value which indicates if the account balance drops below the existential deposit value, it will be - * deactivated and any remaining funds will be destroyed. - * - * [REDACTED_TODO_COMMENT] - */ - @Deprecated("Will be removed in future") - suspend fun getExistentialDeposit(userWalletId: UserWalletId, network: Network): BigDecimal? - - @Deprecated("Will be removed in future") - suspend fun getDustValue(userWalletId: UserWalletId, network: Network): BigDecimal? - - /** - * Returns reserve amount which is required to create an account - * - * [REDACTED_TODO_COMMENT] - */ - @Deprecated("Will be removed in future") - suspend fun getReserveAmount(userWalletId: UserWalletId, network: Network): BigDecimal? - - /** - * Returns true if account with [address] was reserved with minimum amount - * - * [REDACTED_TODO_COMMENT] - */ - @Deprecated("Will be removed in future") - suspend fun checkIfAccountFunded(userWalletId: UserWalletId, network: Network, address: String): Boolean - - /** - * Checks if transaction amount is within the UTXO limit - * - * [REDACTED_TODO_COMMENT] - */ - @Deprecated("Will be removed in future") - suspend fun checkUtxoAmountLimit( - userWalletId: UserWalletId, - network: Network, - amount: BigDecimal, - fee: BigDecimal, - ): UtxoAmountLimit? - @Deprecated("Will be removed in future") fun getAll(userWalletId: UserWalletId): Flow> diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/blockchains/UtxoAmountLimit.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/blockchains/UtxoAmountLimit.kt new file mode 100644 index 0000000000..ef70be02c4 --- /dev/null +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/blockchains/UtxoAmountLimit.kt @@ -0,0 +1,14 @@ +package com.tangem.domain.tokens.model.blockchains + +import java.math.BigDecimal + +/** + * Model stores utxo limits + * + * @property maxLimit utxo limit + * @property maxAmount max amount + */ +data class UtxoAmountLimit( + val maxLimit: BigDecimal, + val maxAmount: BigDecimal, +) \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt index b43052898e..304c27e7ce 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt @@ -7,10 +7,7 @@ import com.tangem.domain.tokens.model.FeePaidCurrency import com.tangem.domain.tokens.model.Network import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations -import com.tangem.domain.tokens.repository.CurrenciesRepository -import com.tangem.domain.tokens.repository.MarketCryptoCurrencyRepository -import com.tangem.domain.tokens.repository.NetworksRepository -import com.tangem.domain.tokens.repository.QuotesRepository +import com.tangem.domain.tokens.repository.* import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.swap.domain.api.SwapRepository @@ -31,6 +28,7 @@ class GetCurrencyWarningsUseCase( private val marketCryptoCurrencyRepository: MarketCryptoCurrencyRepository, private val showSwapPromoTokenUseCase: ShouldShowSwapPromoTokenUseCase, private val dispatchers: CoroutineDispatcherProvider, + private val currencyChecksRepository: CurrencyChecksRepository, ) { suspend operator fun invoke( @@ -56,7 +54,7 @@ class GetCurrencyWarningsUseCase( isSingleWalletWithTokens = isSingleWalletWithTokens, ), flowOf(walletManagersFacade.getRentInfo(userWalletId, currency.network)), - flowOf(walletManagersFacade.getExistentialDeposit(userWalletId, currency.network)), + flowOf(currencyChecksRepository.getExistentialDeposit(userWalletId, currency.network)), getSwapPromoNotificationWarning( operations = operations, userWalletId = userWalletId, diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrencyChecksRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrencyChecksRepository.kt new file mode 100644 index 0000000000..914007040a --- /dev/null +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrencyChecksRepository.kt @@ -0,0 +1,32 @@ +package com.tangem.domain.tokens.repository + +import com.tangem.domain.tokens.model.Network +import com.tangem.domain.tokens.model.blockchains.UtxoAmountLimit +import com.tangem.domain.wallets.models.UserWalletId +import java.math.BigDecimal + +interface CurrencyChecksRepository { + + /** + * Returns value which indicates if the account balance drops below the existential deposit value, it will be + * deactivated and any remaining funds will be destroyed. + */ + suspend fun getExistentialDeposit(userWalletId: UserWalletId, network: Network): BigDecimal? + + /** Returns dust value */ + suspend fun getDustValue(userWalletId: UserWalletId, network: Network): BigDecimal? + + /** Returns reserve amount which is required to create an account */ + suspend fun getReserveAmount(userWalletId: UserWalletId, network: Network): BigDecimal? + + /** Returns true if account with [address] was reserved with minimum amount */ + suspend fun checkIfAccountFunded(userWalletId: UserWalletId, network: Network, address: String): Boolean + + /** Checks if transaction amount is within the UTXO limit */ + suspend fun checkUtxoAmountLimit( + userWalletId: UserWalletId, + network: Network, + amount: BigDecimal, + fee: BigDecimal, + ): UtxoAmountLimit? +} \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendNotificationFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendNotificationFactory.kt index 18e0b4f34c..8b3051e761 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendNotificationFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendNotificationFactory.kt @@ -4,10 +4,11 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.walletmanager.WalletManagersFacade +import com.tangem.domain.tokens.repository.CurrencyChecksRepository import com.tangem.domain.wallets.models.UserWallet import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents import com.tangem.utils.Provider +import com.tangem.utils.isNullOrZero import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList @@ -21,7 +22,7 @@ internal class SendNotificationFactory( private val coinCryptoCurrencyStatusProvider: Provider, private val currentStateProvider: Provider, private val userWalletProvider: Provider, - private val walletManagersFacade: WalletManagersFacade, + private val currencyChecksRepository: CurrencyChecksRepository, private val clickIntents: SendClickIntents, ) { @@ -39,6 +40,7 @@ internal class SendNotificationFactory( addExceedBalanceNotification(feeAmount, sendAmount) addInvalidAmountNotification(feeState.isSubtract, sendAmount) addMinimumAmountErrorNotification(feeAmount, sendAmount) + addDustWarningNotification(feeAmount, sendAmount) addReserveAmountErrorNotification(recipientState.addressTextField.value) addTransactionLimitErrorNotification(feeAmount, sendAmount) // warnings @@ -117,12 +119,12 @@ internal class SendNotificationFactory( private suspend fun MutableList.addReserveAmountErrorNotification(recipientAddress: String) { val userWalletId = userWalletProvider().walletId val cryptoCurrency = cryptoCurrencyStatusProvider().currency - val isAccountFunded = walletManagersFacade.checkIfAccountFunded( + val isAccountFunded = currencyChecksRepository.checkIfAccountFunded( userWalletId, cryptoCurrency.network, recipientAddress, ) - val minimumAmount = walletManagersFacade.getReserveAmount(userWalletId, cryptoCurrency.network) + val minimumAmount = currencyChecksRepository.getReserveAmount(userWalletId, cryptoCurrency.network) if (!isAccountFunded && minimumAmount != null && minimumAmount > BigDecimal.ZERO) { add( SendNotification.Error.ReserveAmountError( @@ -141,7 +143,7 @@ internal class SendNotificationFactory( ) { val userWalletId = userWalletProvider().walletId val cryptoCurrency = cryptoCurrencyStatusProvider().currency - val utxoLimit = walletManagersFacade.checkUtxoAmountLimit( + val utxoLimit = currencyChecksRepository.checkUtxoAmountLimit( userWalletId = userWalletId, network = cryptoCurrency.network, amount = receivedAmount, @@ -173,7 +175,7 @@ internal class SendNotificationFactory( } else { feeAmount + receivedAmount } - val currencyDeposit = walletManagersFacade.getExistentialDeposit( + val currencyDeposit = currencyChecksRepository.getExistentialDeposit( userWalletId, cryptoCurrency.network, ) @@ -210,6 +212,29 @@ internal class SendNotificationFactory( } } + private suspend fun MutableList.addDustWarningNotification( + feeAmount: BigDecimal, + receivedAmount: BigDecimal, + ) { + val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() + val dustValue = currencyChecksRepository.getDustValue( + userWalletProvider().walletId, + cryptoCurrencyStatus.currency.network, + ) + val balance = cryptoCurrencyStatus.value.amount ?: BigDecimal.ZERO + if (dustValue != null && !balance.isNullOrZero() && receivedAmount < balance) { + val totalAmount = feeAmount + receivedAmount + val change = balance - totalAmount + val isChangeLowerThanDust = change < dustValue && change != BigDecimal.ZERO + val isShowWarning = totalAmount < dustValue || isChangeLowerThanDust + if (isShowWarning) { + add( + SendNotification.Error.MinimumAmountError(dustValue.toPlainString()), + ) + } + } + } + companion object { private const val CARDANO_MINIMUM = "1" private const val DOGECOIN_MINIMUM = "0.01" 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 f8ec87a05d..98e7341fc8 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 @@ -21,6 +21,7 @@ import com.tangem.domain.tokens.* import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.utils.convertToAmount +import com.tangem.domain.tokens.repository.CurrencyChecksRepository import com.tangem.domain.transaction.error.GetFeeError import com.tangem.domain.transaction.usecase.CreateTransactionUseCase import com.tangem.domain.transaction.usecase.GetFeeUseCase @@ -74,6 +75,7 @@ internal class SendViewModel @Inject constructor( private val walletManagersFacade: WalletManagersFacade, private val reduxStateHolder: ReduxStateHolder, private val isAmountSubtractAvailableUseCase: IsAmountSubtractAvailableUseCase, + currencyChecksRepository: CurrencyChecksRepository, isFeeApproximateUseCase: IsFeeApproximateUseCase, getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase, validateWalletMemoUseCase: ValidateWalletMemoUseCase, @@ -136,7 +138,7 @@ internal class SendViewModel @Inject constructor( coinCryptoCurrencyStatusProvider = Provider { coinCryptoCurrencyStatus }, currentStateProvider = Provider { uiState }, userWalletProvider = Provider { userWallet }, - walletManagersFacade = walletManagersFacade, + currencyChecksRepository = currencyChecksRepository, clickIntents = this, ) diff --git a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt index 6bcbabd302..5ce1371a15 100644 --- a/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt +++ b/features/swap/data/src/main/java/com/tangem/feature/swap/DefaultSwapRepository.kt @@ -23,7 +23,6 @@ import com.tangem.datasource.crypto.DataSignatureVerifier import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.tokens.model.Network import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.legacy.WalletsStateHolder import com.tangem.domain.wallets.models.UserWalletId @@ -298,14 +297,6 @@ internal class DefaultSwapRepository @Inject constructor( } } - override suspend fun getExistentialDeposit(userWalletId: UserWalletId, network: Network): BigDecimal? { - return walletManagersFacade.getExistentialDeposit(userWalletId, network) - } - - override suspend fun getDustValue(userWalletId: UserWalletId, network: Network): BigDecimal? { - return walletManagersFacade.getDustValue(userWalletId, network) - } - private fun parseTxDetails(txDetailsJson: String): TxDetails? { return try { txDetailsMoshiAdapter.fromJson(txDetailsJson) diff --git a/features/swap/domain/api/src/main/java/com/tangem/feature/swap/domain/api/SwapRepository.kt b/features/swap/domain/api/src/main/java/com/tangem/feature/swap/domain/api/SwapRepository.kt index 340162c44c..8fcb79ae26 100644 --- a/features/swap/domain/api/src/main/java/com/tangem/feature/swap/domain/api/SwapRepository.kt +++ b/features/swap/domain/api/src/main/java/com/tangem/feature/swap/domain/api/SwapRepository.kt @@ -2,7 +2,6 @@ package com.tangem.feature.swap.domain.api import arrow.core.Either import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.tokens.model.Network import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.swap.domain.models.DataError import com.tangem.feature.swap.domain.models.domain.* @@ -69,8 +68,4 @@ interface SwapRepository { ): Either fun getNativeTokenForNetwork(networkId: String): CryptoCurrency - - suspend fun getExistentialDeposit(userWalletId: UserWalletId, network: Network): BigDecimal? - - suspend fun getDustValue(userWalletId: UserWalletId, network: Network): BigDecimal? } \ 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 0928f9385a..3be4224a4c 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 @@ -16,6 +16,7 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.Quote import com.tangem.domain.tokens.repository.QuotesRepository import com.tangem.domain.tokens.utils.convertToAmount +import com.tangem.domain.tokens.repository.CurrencyChecksRepository import com.tangem.domain.transaction.error.SendTransactionError import com.tangem.domain.transaction.usecase.EstimateFeeUseCase import com.tangem.domain.transaction.usecase.SendTransactionUseCase @@ -58,6 +59,7 @@ internal class SwapInteractorImpl @Inject constructor( private val quotesRepository: QuotesRepository, private val dispatcher: CoroutineDispatcherProvider, private val swapTransactionRepository: SwapTransactionRepository, + private val currencyChecksRepository: CurrencyChecksRepository, private val appCurrencyRepository: AppCurrencyRepository, private val initialToCurrencyResolver: InitialToCurrencyResolver, ) : SwapInteractor { @@ -380,7 +382,7 @@ internal class SwapInteractorImpl @Inject constructor( amount: SwapAmount, fromToken: CryptoCurrency, ) { - val existentialDeposit = repository.getExistentialDeposit(userWalletId, fromToken.network) + val existentialDeposit = currencyChecksRepository.getExistentialDeposit(userWalletId, fromToken.network) if (existentialDeposit != null) { val nativeBalance = userWalletManager.getNativeTokenBalance( fromToken.network.backendId, @@ -404,7 +406,7 @@ internal class SwapInteractorImpl @Inject constructor( is TxFeeState.MultipleFeeState -> feeState.priorityFee.feeValue is TxFeeState.SingleFeeState -> feeState.fee.feeValue } - val dust = repository.getDustValue(userWalletId, fromTokenStatus.currency.network) + val dust = currencyChecksRepository.getDustValue(userWalletId, fromTokenStatus.currency.network) val balance = fromTokenStatus.value.amount ?: BigDecimal.ZERO if (dust != null && !balance.isNullOrZero() && 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 cbf8cd6d81..b1fef42180 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 @@ -7,6 +7,7 @@ import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.tokens.GetCardTokensListUseCase import com.tangem.domain.tokens.GetCryptoCurrencyStatusesSyncUseCase import com.tangem.domain.tokens.repository.CurrenciesRepository +import com.tangem.domain.tokens.repository.CurrencyChecksRepository import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.QuotesRepository import com.tangem.domain.transaction.TransactionRepository @@ -42,6 +43,7 @@ class SwapDomainModule { quotesRepository: QuotesRepository, swapTransactionRepository: SwapTransactionRepository, appCurrencyRepository: AppCurrencyRepository, + currencyChecksRepository: CurrencyChecksRepository, walletManagersFacade: WalletManagersFacade, coroutineDispatcherProvider: CoroutineDispatcherProvider, initialToCurrencyResolver: InitialToCurrencyResolver, @@ -59,6 +61,7 @@ class SwapDomainModule { dispatcher = coroutineDispatcherProvider, swapTransactionRepository = swapTransactionRepository, appCurrencyRepository = appCurrencyRepository, + currencyChecksRepository = currencyChecksRepository, initialToCurrencyResolver = initialToCurrencyResolver, ) } From 9c11182ca623d030c8f66cab611f22ee2ab72924 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 5 Feb 2024 21:00:06 +0300 Subject: [PATCH 05/53] Updated on 2026-08-14 --- core/res/src/main/res/values-ru/strings.xml | 1 + core/res/src/main/res/values/strings.xml | 1 + .../ui/components/fields/SimpleTextField.kt | 16 ++++--- features/send/impl/build.gradle.kts | 3 ++ .../presentation/state/SendStateFactory.kt | 17 +++++++- .../state/fields/SendTextField.kt | 2 + .../SendRecipientMemoFieldConverter.kt | 2 + .../ui/recipient/SendRecipientContent.kt | 4 +- .../ui/recipient/TextFieldWithPaste.kt | 26 +++++++----- .../presentation/viewmodel/SendViewModel.kt | 42 +++++++++---------- libs/crypto/build.gradle.kts | 3 ++ .../com/tangem/lib/crypto/BlockchainUtils.kt | 29 +++++++++++++ .../converter/XrpTaggedAddressConverter.kt | 15 +++++++ .../lib/crypto/models/XrpTaggedAddress.kt | 6 +++ 14 files changed, 127 insertions(+), 40 deletions(-) create mode 100644 libs/crypto/src/main/java/com/tangem/lib/crypto/BlockchainUtils.kt create mode 100644 libs/crypto/src/main/java/com/tangem/lib/crypto/converter/XrpTaggedAddressConverter.kt create mode 100644 libs/crypto/src/main/java/com/tangem/lib/crypto/models/XrpTaggedAddress.kt diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index e257132463..8c7c16f2b5 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -449,6 +449,7 @@ Сканировать Отсканируйте карту, чтобы изменить ее настройки. Изменения затронут только ту карту, которую вы отсканировали, и не повлияют на другие карты, привязанные к вашему кошельку. Приготовьте свою карту + Уже содержится в введенном адресе Сумма Вычесть из суммы отправки Сумма к получению %s diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index dd42088e6b..d384d8f946 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -448,6 +448,7 @@ Scan Card Scan the card to change its settings. The changes will impact only the card you\'ve scanned and will not affect other cards tied to your wallet. Get your card ready! + Already included in the entered address Amount Subtract from send amount The recipient will receive %s diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/fields/SimpleTextField.kt b/core/ui/src/main/java/com/tangem/core/ui/components/fields/SimpleTextField.kt index 1e29006979..db725acbc8 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/fields/SimpleTextField.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/fields/SimpleTextField.kt @@ -1,5 +1,6 @@ package com.tangem.core.ui.components.fields +import androidx.compose.animation.AnimatedContent import androidx.compose.foundation.layout.Box import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardOptions @@ -89,11 +90,16 @@ fun SimpleTextField( decorationBox = decorationBox ?: { textValue -> Box { if (value.isBlank() && placeholder != null) { - Text( - text = placeholder.resolveReference(), - style = textStyle, - color = TangemTheme.colors.text.disabled, - ) + AnimatedContent( + targetState = placeholder, + label = "Placeholder Change Animation", + ) { + Text( + text = it.resolveReference(), + style = textStyle, + color = TangemTheme.colors.text.disabled, + ) + } } textValue() } diff --git a/features/send/impl/build.gradle.kts b/features/send/impl/build.gradle.kts index 46295abadb..95074550ff 100644 --- a/features/send/impl/build.gradle.kts +++ b/features/send/impl/build.gradle.kts @@ -46,6 +46,9 @@ dependencies { implementation(projects.core.utils) implementation(projects.core.navigation) + /** Libs */ + implementation(projects.libs.crypto) + /** Domain modules */ implementation(projects.domain.models) implementation(projects.domain.legacy) 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 d53fbaab70..7f8e97b93e 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 @@ -136,12 +136,13 @@ internal class SendStateFactory( ) } - fun onRecipientAddressValueChange(value: String): SendUiState { + fun onRecipientAddressValueChange(value: String, isXAddress: Boolean = false): SendUiState { val state = currentStateProvider() val recipientState = state.recipientState ?: return state return state.copy( recipientState = recipientState.copy( addressTextField = recipientState.addressTextField.copy(value = value), + memoTextField = recipientState.memoTextField?.copy(isEnabled = !isXAddress), ), ) } @@ -216,6 +217,20 @@ internal class SendStateFactory( isValidating = false, memoTextField = recipientState.memoTextField?.copy( isError = value.isNotEmpty() && !isValidMemo, + isEnabled = true, + ), + ), + ) + } + + fun getOnXAddressMemoState(): SendUiState { + val state = currentStateProvider() + val recipientState = state.recipientState ?: return state + return state.copy( + recipientState = recipientState.copy( + memoTextField = recipientState.memoTextField?.copy( + value = "", + isEnabled = false, ), ), ) diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendTextField.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendTextField.kt index 42b2ac5b0d..d89b75c9b1 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendTextField.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendTextField.kt @@ -47,6 +47,8 @@ internal sealed class SendTextField { val label: TextReference, val isError: Boolean = false, val error: TextReference? = null, + val disabledText: TextReference, + val isEnabled: Boolean, ) : SendTextField() data class CustomFee( diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientMemoFieldConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientMemoFieldConverter.kt index 45e2f47771..91321769f0 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientMemoFieldConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientMemoFieldConverter.kt @@ -44,6 +44,8 @@ internal class SendRecipientMemoFieldConverter( placeholder = resourceReference(R.string.send_optional_field), label = resourceReference(value), error = resourceReference(R.string.send_memo_destination_tag_error), + disabledText = resourceReference(R.string.send_additional_field_already_included), + isEnabled = true, ) } } \ No newline at end of file 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 d2741dffcc..3fa19bdf16 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 @@ -73,16 +73,18 @@ internal fun SendRecipientContent( } uiState.memoTextField?.let { memoField -> item(key = MEMO_FIELD_KEY) { + val placeholder = if (memoField.isEnabled) memoField.placeholder else memoField.disabledText TextFieldWithPaste( value = memoField.value, label = memoField.label, - placeholder = memoField.placeholder, + placeholder = placeholder, footer = stringResource(R.string.send_recipient_memo_footer), onValueChange = memoField.onValueChange, onPasteClick = clickIntents::onRecipientMemoValueChange, modifier = Modifier.padding(top = TangemTheme.dimens.spacing20), isError = memoField.isError, error = memoField.error, + isReadOnly = !memoField.isEnabled, ) } } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/recipient/TextFieldWithPaste.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/recipient/TextFieldWithPaste.kt index 758fef6f57..c3a49b8da6 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/recipient/TextFieldWithPaste.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/recipient/TextFieldWithPaste.kt @@ -27,11 +27,12 @@ internal fun TextFieldWithPaste( footer: String? = null, error: TextReference? = null, isError: Boolean = false, + isReadOnly: Boolean = false, ) { - val (title, color) = if (isError && error != null) { - error to TangemTheme.colors.text.warning - } else { - label to TangemTheme.colors.text.secondary + val (title, color) = when { + isError && error != null -> error to TangemTheme.colors.text.warning + isReadOnly -> label to TangemTheme.colors.text.disabled + else -> label to TangemTheme.colors.text.secondary } FooterContainer(modifier, footer) { Row( @@ -55,18 +56,21 @@ internal fun TextFieldWithPaste( value = value, placeholder = placeholder, onValueChange = onValueChange, + readOnly = isReadOnly, modifier = Modifier .fillMaxWidth() .padding(top = TangemTheme.dimens.spacing6), ) } - PasteButton( - isPasteButtonVisible = value.isBlank(), - onClick = onPasteClick, - modifier = Modifier - .align(CenterVertically) - .padding(end = TangemTheme.dimens.spacing16), - ) + if (!isReadOnly) { + PasteButton( + isPasteButtonVisible = value.isBlank(), + onClick = onPasteClick, + modifier = Modifier + .align(CenterVertically) + .padding(end = TangemTheme.dimens.spacing16), + ) + } } } } \ 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 98e7341fc8..b7bc549c8e 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 @@ -7,8 +7,6 @@ import androidx.lifecycle.* import androidx.paging.PagingData import arrow.core.Either import arrow.core.getOrElse -import com.tangem.blockchain.blockchains.xrp.XrpAddressService -import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.common.extensions.isZero @@ -20,8 +18,8 @@ import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.tokens.* import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.tokens.utils.convertToAmount import com.tangem.domain.tokens.repository.CurrencyChecksRepository +import com.tangem.domain.tokens.utils.convertToAmount import com.tangem.domain.transaction.error.GetFeeError import com.tangem.domain.transaction.usecase.CreateTransactionUseCase import com.tangem.domain.transaction.usecase.GetFeeUseCase @@ -43,6 +41,7 @@ import com.tangem.features.send.impl.presentation.state.fee.FeeNotificationFacto import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState import com.tangem.features.send.impl.presentation.state.fee.FeeStateFactory import com.tangem.features.send.impl.presentation.state.fee.FeeType +import com.tangem.lib.crypto.BlockchainUtils import com.tangem.utils.Provider import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.JobHolder @@ -301,10 +300,12 @@ internal class SendViewModel @Inject constructor( userWalletId = wallet.walletId, network = walletCurrency.network, ) - return@fold AvailableWallet( - name = wallet.name, - address = addresses.first().value, - ) + return@fold addresses.firstOrNull()?.let { + AvailableWallet( + name = wallet.name, + address = it.value, + ) + } }, ifLeft = { null }, ) @@ -426,10 +427,10 @@ internal class SendViewModel @Inject constructor( } override fun onRecipientAddressValueChange(value: String) { - uiState = stateFactory.onRecipientAddressValueChange(value) viewModelScope.launch(dispatchers.main) { - uiState = stateFactory.getOnRecipientAddressValidationStarted() if (!checkIfXrpAddressValue(value)) { + uiState = stateFactory.onRecipientAddressValueChange(value) + uiState = stateFactory.getOnRecipientAddressValidationStarted() val isValidAddress = validateAddress(value) uiState = stateFactory.getOnRecipientAddressValidState(value, isValidAddress) } @@ -437,10 +438,10 @@ internal class SendViewModel @Inject constructor( } override fun onRecipientMemoValueChange(value: String) { - uiState = stateFactory.getOnRecipientMemoValueChange(value) viewModelScope.launch(dispatchers.main) { - uiState = stateFactory.getOnRecipientAddressValidationStarted() if (!checkIfXrpAddressValue(value)) { + uiState = stateFactory.getOnRecipientMemoValueChange(value) + uiState = stateFactory.getOnRecipientAddressValidationStarted() val isValidAddress = validateAddress(uiState.recipientState?.addressTextField?.value.orEmpty()) uiState = stateFactory.getOnRecipientMemoValidState(value, isValidAddress) } @@ -455,16 +456,14 @@ internal class SendViewModel @Inject constructor( ).getOrElse { false } } - private fun checkIfXrpAddressValue(value: String): Boolean { - if (cryptoCurrency.network.id.value == Blockchain.XRP.id && value.firstOrNull() == XRP_X_ADDRESS) { - viewModelScope.launch(dispatchers.io) { - val result = XrpAddressService.decodeXAddress(value) - onRecipientAddressValueChange(result?.address.orEmpty()) - onRecipientMemoValueChange(result?.destinationTag.toString()) - } - return true - } - return false + private suspend fun checkIfXrpAddressValue(value: String): Boolean { + return BlockchainUtils.decodeRippleXAddress(value, cryptoCurrency.network.id.value)?.let { decodedAddress -> + uiState = stateFactory.onRecipientAddressValueChange(value, isXAddress = true) + uiState = stateFactory.getOnXAddressMemoState() + val isValidAddress = validateAddress(decodedAddress.address) + uiState = stateFactory.getOnRecipientAddressValidState(decodedAddress.address, isValidAddress) + true + } ?: false } // endregion @@ -665,7 +664,6 @@ internal class SendViewModel @Inject constructor( // endregion companion object { - private const val XRP_X_ADDRESS = 'X' private const val CHECK_FEE_UPDATE_DELAY = 60_000L private const val BALANCE_UPDATE_DELAY = 10_000L } diff --git a/libs/crypto/build.gradle.kts b/libs/crypto/build.gradle.kts index 60b1f6f10e..917e9c310d 100644 --- a/libs/crypto/build.gradle.kts +++ b/libs/crypto/build.gradle.kts @@ -13,4 +13,7 @@ dependencies { /** SDK */ implementation(deps.tangem.blockchain) + + /** Core */ + implementation(projects.core.utils) } \ No newline at end of file diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/BlockchainUtils.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/BlockchainUtils.kt new file mode 100644 index 0000000000..1e7a2e6a6d --- /dev/null +++ b/libs/crypto/src/main/java/com/tangem/lib/crypto/BlockchainUtils.kt @@ -0,0 +1,29 @@ +package com.tangem.lib.crypto + +import com.tangem.blockchain.blockchains.xrp.XrpAddressService +import com.tangem.blockchain.common.Blockchain +import com.tangem.lib.crypto.converter.XrpTaggedAddressConverter +import com.tangem.lib.crypto.models.XrpTaggedAddress + +/** + * !!!IMPORTANT!!! + * Methods for working with different blockchains + * All methods are depend on specific blockchain or check for specific blockchain + * + * Temporary solution for domain specific logic for Blockchain. + * Instead of creating repositories and unnecessary and overkill use cases + */ +object BlockchainUtils { + + private const val XRP_X_ADDRESS = 'X' + + /** Decodes XRP Blockchain address */ + fun decodeRippleXAddress(xAddress: String, networkId: String): XrpTaggedAddress? { + return if (networkId == Blockchain.XRP.id && xAddress.firstOrNull() == XRP_X_ADDRESS) { + val decodedAddress = XrpAddressService.decodeXAddress(xAddress) + return decodedAddress?.let(XrpTaggedAddressConverter()::convert) + } else { + null + } + } +} \ No newline at end of file diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/converter/XrpTaggedAddressConverter.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/converter/XrpTaggedAddressConverter.kt new file mode 100644 index 0000000000..9d42e0cbc4 --- /dev/null +++ b/libs/crypto/src/main/java/com/tangem/lib/crypto/converter/XrpTaggedAddressConverter.kt @@ -0,0 +1,15 @@ +package com.tangem.lib.crypto.converter + +import com.tangem.lib.crypto.models.XrpTaggedAddress +import com.tangem.utils.converter.Converter +import com.tangem.blockchain.blockchains.xrp.XrpTaggedAddress as BlockchainXrpTaggedAddress + +internal class XrpTaggedAddressConverter : Converter { + + override fun convert(value: BlockchainXrpTaggedAddress): XrpTaggedAddress { + return XrpTaggedAddress( + address = value.address, + destinationTag = value.destinationTag, + ) + } +} \ No newline at end of file diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/XrpTaggedAddress.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/XrpTaggedAddress.kt new file mode 100644 index 0000000000..d9d0946326 --- /dev/null +++ b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/XrpTaggedAddress.kt @@ -0,0 +1,6 @@ +package com.tangem.lib.crypto.models + +data class XrpTaggedAddress( + val address: String, + val destinationTag: Long?, +) \ No newline at end of file From 11ad540d12d5aceefa998042d611d915af1e9f81 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 6 Feb 2024 17:39:50 +0300 Subject: [PATCH 06/53] Updated on 2026-08-14 --- core/datasource/build.gradle.kts | 1 - .../di/AppPreferenceStorageModule.kt | 18 ------ .../datasource/local/AppPreferenceStorage.kt | 13 ---- .../local/AppPreferenceStorageImpl.kt | 24 -------- .../local/preferences/PreferencesKeys.kt | 2 + core/featuretoggles/build.gradle.kts | 10 +++- .../di/FeatureTogglesManagerModule.kt | 7 +-- .../manager/DevFeatureTogglesManager.kt | 24 ++++---- .../manager/MutableFeatureTogglesManager.kt | 2 +- .../featuretoggle/storage/FeatureToggle.kt | 9 ++- .../manager/DevFeatureTogglesManagerTest.kt | 59 ++++++++----------- features/tester/impl/build.gradle.kts | 21 +++---- .../viewmodels/FeatureTogglesViewModel.kt | 11 +++- gradle/dependencies.toml | 2 - 14 files changed, 77 insertions(+), 126 deletions(-) delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/di/AppPreferenceStorageModule.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/AppPreferenceStorage.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/AppPreferenceStorageImpl.kt diff --git a/core/datasource/build.gradle.kts b/core/datasource/build.gradle.kts index 137ccdb922..522cd3257e 100644 --- a/core/datasource/build.gradle.kts +++ b/core/datasource/build.gradle.kts @@ -33,7 +33,6 @@ dependencies { implementation(deps.timber) /** Network */ - implementation(deps.krateSharedPref) implementation(deps.moshi) implementation(deps.moshi.kotlin) implementation(deps.okHttp) diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/AppPreferenceStorageModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/AppPreferenceStorageModule.kt deleted file mode 100644 index 2eefec13ed..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/di/AppPreferenceStorageModule.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.tangem.datasource.di - -import com.tangem.datasource.local.AppPreferenceStorage -import com.tangem.datasource.local.AppPreferenceStorageImpl -import dagger.Binds -import dagger.Module -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -internal interface AppPreferenceStorageModule { - - @Binds - @Singleton - fun bindAppPreferenceStorage(appPreferenceStorageImpl: AppPreferenceStorageImpl): AppPreferenceStorage -} \ 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 deleted file mode 100644 index d56ca29fa0..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/AppPreferenceStorage.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.tangem.datasource.local - -/** - * Application local storage - * -[REDACTED_AUTHOR] - */ -@Deprecated(message = "Use AppPreferencesStore", level = DeprecationLevel.WARNING) -interface AppPreferenceStorage { - - /** Json config with feature toggles 'ToggleName: String - Availability: Boolean' */ - var featureToggles: String -} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/AppPreferenceStorageImpl.kt b/core/datasource/src/main/java/com/tangem/datasource/local/AppPreferenceStorageImpl.kt deleted file mode 100644 index 3ce9a8aca9..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/AppPreferenceStorageImpl.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.tangem.datasource.local - -import android.content.Context -import dagger.hilt.android.qualifiers.ApplicationContext -import hu.autsoft.krate.SimpleKrate -import hu.autsoft.krate.default.withDefault -import hu.autsoft.krate.stringPref -import javax.inject.Inject -import javax.inject.Singleton - -/** - * Implementation of application local storage - * - * @param context application context - * -[REDACTED_AUTHOR] - */ -@Singleton -internal class AppPreferenceStorageImpl @Inject constructor( - @ApplicationContext context: Context, -) : SimpleKrate(context = context), AppPreferenceStorage { - - override var featureToggles: String by stringPref().withDefault("") -} \ 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 02432a3d03..8295d9e9a9 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 @@ -48,6 +48,8 @@ object PreferencesKeys { val IS_WALLET_SWAP_PROMO_SHOW_KEY by lazy { booleanPreferencesKey(name = "isWalletSwapPromoShown") } val IS_TOKEN_SWAP_PROMO_SHOW_KEY by lazy { booleanPreferencesKey(name = "isTokenSwapPromoShown") } + + val FEATURE_TOGGLES_KEY by lazy { stringPreferencesKey(name = "featureToggles") } } /** Preferences keys set that should be migrated from "PreferencesDataSource" to a new DataStore */ diff --git a/core/featuretoggles/build.gradle.kts b/core/featuretoggles/build.gradle.kts index c5f9b07545..5354b55813 100644 --- a/core/featuretoggles/build.gradle.kts +++ b/core/featuretoggles/build.gradle.kts @@ -7,14 +7,22 @@ plugins { } dependencies { - implementation(project(":core:datasource")) + /** DI */ implementation(deps.hilt.android) kapt(deps.hilt.kapt) + + /** Local storages */ + implementation(deps.androidx.datastore) + + /** Other libraries */ implementation(deps.moshi) implementation(deps.moshi.kotlin) implementation(deps.timber) + /** Core modules */ + implementation(projects.core.datasource) + testImplementation(deps.test.coroutine) testImplementation(deps.test.junit) testImplementation(deps.test.mockk) diff --git a/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/di/FeatureTogglesManagerModule.kt b/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/di/FeatureTogglesManagerModule.kt index eadb4bf0fb..e67ca2a738 100644 --- a/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/di/FeatureTogglesManagerModule.kt +++ b/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/di/FeatureTogglesManagerModule.kt @@ -11,7 +11,7 @@ import com.tangem.core.featuretoggle.storage.LocalFeatureTogglesStorage import com.tangem.core.featuretoggle.version.DefaultVersionProvider import com.tangem.core.featuretoggles.BuildConfig import com.tangem.datasource.asset.AssetReader -import com.tangem.datasource.local.AppPreferenceStorage +import com.tangem.datasource.local.preferences.AppPreferencesStore import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -29,7 +29,7 @@ internal object FeatureTogglesManagerModule { fun provideFeatureTogglesManager( @ApplicationContext context: Context, assetReader: AssetReader, - appPreferenceStorage: AppPreferenceStorage, + appPreferencesStore: AppPreferencesStore, ): FeatureTogglesManager { val moshi = Moshi.Builder().addLast(KotlinJsonAdapterFactory()).build() val localFeatureTogglesStorage = LocalFeatureTogglesStorage( @@ -41,8 +41,7 @@ internal object FeatureTogglesManagerModule { return if (BuildConfig.TESTER_MENU_ENABLED) { DevFeatureTogglesManager( localFeatureTogglesStorage = localFeatureTogglesStorage, - appPreferenceStorage = appPreferenceStorage, - jsonAdapter = moshi.adapter(), + appPreferencesStore = appPreferencesStore, versionProvider = versionProvider, ) } else { diff --git a/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/manager/DevFeatureTogglesManager.kt b/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/manager/DevFeatureTogglesManager.kt index 12e0a496b6..9b29a65f66 100644 --- a/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/manager/DevFeatureTogglesManager.kt +++ b/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/manager/DevFeatureTogglesManager.kt @@ -1,25 +1,25 @@ package com.tangem.core.featuretoggle.manager import androidx.annotation.VisibleForTesting -import com.squareup.moshi.JsonAdapter import com.tangem.core.featuretoggle.storage.FeatureTogglesStorage import com.tangem.core.featuretoggle.utils.associateToggles import com.tangem.core.featuretoggle.version.VersionProvider -import com.tangem.datasource.local.AppPreferenceStorage +import com.tangem.datasource.local.preferences.AppPreferencesStore +import com.tangem.datasource.local.preferences.PreferencesKeys +import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull +import com.tangem.datasource.local.preferences.utils.storeObject import kotlin.properties.Delegates /** * Feature toggles manager implementation in DEV build * * @property localFeatureTogglesStorage local feature toggles storage - * @property appPreferenceStorage application local storage - * @property jsonAdapter adapter for parsing json + * @property appPreferencesStore application local store * @property versionProvider application version provider */ internal class DevFeatureTogglesManager( private val localFeatureTogglesStorage: FeatureTogglesStorage, - private val appPreferenceStorage: AppPreferenceStorage, - private val jsonAdapter: JsonAdapter>, + private val appPreferencesStore: AppPreferencesStore, private val versionProvider: VersionProvider, ) : MutableFeatureTogglesManager { @@ -28,11 +28,9 @@ internal class DevFeatureTogglesManager( override suspend fun init() { localFeatureTogglesStorage.init() - val savedFeatureToggles = if (appPreferenceStorage.featureToggles.isNotEmpty()) { - jsonAdapter.fromJson(appPreferenceStorage.featureToggles).orEmpty() - } else { - emptyMap() - } + val savedFeatureToggles = appPreferencesStore.getObjectSyncOrNull>( + key = PreferencesKeys.FEATURE_TOGGLES_KEY, + ) ?: emptyMap() featureTogglesMap = localFeatureTogglesStorage.featureToggles .associateToggles(currentVersion = versionProvider.get().orEmpty()) @@ -46,10 +44,10 @@ internal class DevFeatureTogglesManager( override fun getFeatureToggles(): Map = featureTogglesMap - override fun changeToggle(name: String, isEnabled: Boolean) { + override suspend fun changeToggle(name: String, isEnabled: Boolean) { featureTogglesMap[name] ?: return featureTogglesMap[name] = isEnabled - appPreferenceStorage.featureToggles = jsonAdapter.toJson(featureTogglesMap) + appPreferencesStore.storeObject(PreferencesKeys.FEATURE_TOGGLES_KEY, featureTogglesMap) } @VisibleForTesting(otherwise = VisibleForTesting.NONE) diff --git a/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/manager/MutableFeatureTogglesManager.kt b/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/manager/MutableFeatureTogglesManager.kt index abf6ff5b72..8d7a063c5d 100644 --- a/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/manager/MutableFeatureTogglesManager.kt +++ b/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/manager/MutableFeatureTogglesManager.kt @@ -11,5 +11,5 @@ interface MutableFeatureTogglesManager : FeatureTogglesManager { fun getFeatureToggles(): Map /** Change availability [isEnabled] of toggle with name [name] */ - fun changeToggle(name: String, isEnabled: Boolean) + suspend fun changeToggle(name: String, isEnabled: Boolean) } \ No newline at end of file diff --git a/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/storage/FeatureToggle.kt b/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/storage/FeatureToggle.kt index 69de7b76e7..b019bfc810 100644 --- a/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/storage/FeatureToggle.kt +++ b/core/featuretoggles/src/main/kotlin/com/tangem/core/featuretoggle/storage/FeatureToggle.kt @@ -1,5 +1,8 @@ package com.tangem.core.featuretoggle.storage +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + /** * Data model with information about feature toggle * @@ -10,4 +13,8 @@ package com.tangem.core.featuretoggle.storage * [REDACTED_AUTHOR] */ -internal data class FeatureToggle(val name: String, val version: String) \ No newline at end of file +@JsonClass(generateAdapter = true) +internal data class FeatureToggle( + @Json(name = "name") val name: String, + @Json(name = "version") val version: String, +) \ No newline at end of file diff --git a/core/featuretoggles/src/test/kotlin/com/tangem/core/featuretoggle/manager/DevFeatureTogglesManagerTest.kt b/core/featuretoggles/src/test/kotlin/com/tangem/core/featuretoggle/manager/DevFeatureTogglesManagerTest.kt index 51ac267ee2..e0e8fbef2f 100644 --- a/core/featuretoggles/src/test/kotlin/com/tangem/core/featuretoggle/manager/DevFeatureTogglesManagerTest.kt +++ b/core/featuretoggles/src/test/kotlin/com/tangem/core/featuretoggle/manager/DevFeatureTogglesManagerTest.kt @@ -2,48 +2,42 @@ package com.tangem.core.featuretoggle.manager import android.annotation.SuppressLint import com.google.common.truth.Truth -import com.squareup.moshi.JsonAdapter import com.tangem.core.featuretoggle.storage.FeatureToggle import com.tangem.core.featuretoggle.storage.FeatureTogglesStorage import com.tangem.core.featuretoggle.utils.associateToggles import com.tangem.core.featuretoggle.version.VersionProvider -import com.tangem.datasource.local.AppPreferenceStorage -import io.mockk.Runs -import io.mockk.coEvery -import io.mockk.coVerifyOrder -import io.mockk.just -import io.mockk.mockk -import io.mockk.verifyAll -import io.mockk.verifyOrder -import kotlinx.coroutines.ExperimentalCoroutinesApi +import com.tangem.datasource.local.preferences.AppPreferencesStore +import com.tangem.datasource.local.preferences.PreferencesKeys +import com.tangem.datasource.local.preferences.utils.getObjectSyncOrNull +import com.tangem.datasource.local.preferences.utils.getSyncOrNull +import io.mockk.* import kotlinx.coroutines.test.runTest import org.junit.Test +import kotlin.collections.set /** [REDACTED_AUTHOR] */ -@OptIn(ExperimentalCoroutinesApi::class) @SuppressLint("CheckResult") internal class DevFeatureTogglesManagerTest { private val localFeatureTogglesStorage = mockk() - private val appPreferenceStorage = mockk(relaxed = true) - private val jsonAdapter = mockk>>() + private val appPreferenceStore = mockk(relaxed = true) private val versionProvider = mockk() private val manager = DevFeatureTogglesManager( localFeatureTogglesStorage = localFeatureTogglesStorage, - appPreferenceStorage = appPreferenceStorage, - jsonAdapter = jsonAdapter, + appPreferencesStore = appPreferenceStore, versionProvider = versionProvider, ) @Test fun `successfully initialize storage if shared prefs kept feature toggles`() = runTest { - val currentVersion = "1.0.0" + val currentVersion = "0.1.0" coEvery { localFeatureTogglesStorage.init() } just Runs - coEvery { appPreferenceStorage.featureToggles } returns savedFeatureToggles - coEvery { jsonAdapter.fromJson(savedFeatureToggles) } returns savedFeatureTogglesMap + coEvery { + appPreferenceStore.getObjectSyncOrNull>(PreferencesKeys.FEATURE_TOGGLES_KEY) + } returns savedFeatureTogglesMap coEvery { localFeatureTogglesStorage.featureToggles } returns localFeatureToggles coEvery { versionProvider.get() } returns currentVersion @@ -51,7 +45,6 @@ internal class DevFeatureTogglesManagerTest { coVerifyOrder { localFeatureTogglesStorage.init() - jsonAdapter.fromJson(savedFeatureToggles) versionProvider.get() } @@ -66,12 +59,12 @@ internal class DevFeatureTogglesManagerTest { @Test fun `successfully initialize storage if shared prefs kept empty list`() = runTest { - val currentVersion = "1.0.0" - val sharedPrefFeatureToggles = "[]" + val currentVersion = "0.1.0" coEvery { localFeatureTogglesStorage.init() } just Runs - coEvery { appPreferenceStorage.featureToggles } returns sharedPrefFeatureToggles - coEvery { jsonAdapter.fromJson(sharedPrefFeatureToggles) } returns savedFeatureTogglesMap + coEvery { + appPreferenceStore.getObjectSyncOrNull>(PreferencesKeys.FEATURE_TOGGLES_KEY) + } returns emptyMap() coEvery { localFeatureTogglesStorage.featureToggles } returns localFeatureToggles coEvery { versionProvider.get() } returns currentVersion @@ -79,7 +72,6 @@ internal class DevFeatureTogglesManagerTest { coVerifyOrder { localFeatureTogglesStorage.init() - jsonAdapter.fromJson(sharedPrefFeatureToggles) versionProvider.get() } @@ -94,10 +86,12 @@ internal class DevFeatureTogglesManagerTest { @Test fun `successfully initialize storage if shared prefs didn't keep feature toggles`() = runTest { - val currentVersion = "1.0.0" + val currentVersion = "0.1.0" coEvery { localFeatureTogglesStorage.init() } just Runs - coEvery { appPreferenceStorage.featureToggles } returns "" + coEvery { + appPreferenceStore.getObjectSyncOrNull>(PreferencesKeys.FEATURE_TOGGLES_KEY) + } returns null coEvery { localFeatureTogglesStorage.featureToggles } returns localFeatureToggles coEvery { versionProvider.get() } returns currentVersion @@ -107,7 +101,6 @@ internal class DevFeatureTogglesManagerTest { localFeatureTogglesStorage.init() versionProvider.get() } - verifyAll(inverse = true) { jsonAdapter.fromJson(any()) } val expected = localFeatureToggles .associateToggles(currentVersion) @@ -119,8 +112,7 @@ internal class DevFeatureTogglesManagerTest { @Test fun `successfully initialize storage if versionProvider returns null`() = runTest { coEvery { localFeatureTogglesStorage.init() } just Runs - coEvery { appPreferenceStorage.featureToggles } returns savedFeatureToggles - coEvery { jsonAdapter.fromJson(savedFeatureToggles) } returns savedFeatureTogglesMap + coEvery { appPreferenceStore.getSyncOrNull(PreferencesKeys.FEATURE_TOGGLES_KEY) } returns savedFeatureToggles coEvery { localFeatureTogglesStorage.featureToggles } returns localFeatureToggles coEvery { versionProvider.get() } returns null @@ -128,7 +120,6 @@ internal class DevFeatureTogglesManagerTest { coVerifyOrder { localFeatureTogglesStorage.init() - jsonAdapter.fromJson(savedFeatureToggles) versionProvider.get() } @@ -180,7 +171,7 @@ internal class DevFeatureTogglesManagerTest { } @Test - fun `change toggle that contains in map`() { + fun `change toggle that contains in map`() = runTest { val changeableToggleName = "INACTIVE_TEST_FEATURE_ENABLED" val resultMap = mutableMapOf( changeableToggleName to false, @@ -188,18 +179,16 @@ internal class DevFeatureTogglesManagerTest { ) manager.setFeatureToggles(resultMap) - coEvery { jsonAdapter.toJson(resultMap) } returns "" manager.changeToggle(changeableToggleName, true) resultMap[changeableToggleName] = true - verifyOrder { jsonAdapter.toJson(resultMap) } Truth.assertThat(manager.getFeatureToggles()).containsExactlyEntriesIn(resultMap) } @Test - fun `change toggle that doesn't contains in map`() { + fun `change toggle that doesn't contains in map`() = runTest { val resultMap = mutableMapOf( "INACTIVE_TEST_FEATURE_ENABLED" to false, "ACTIVE2_TEST_FEATURE_ENABLED" to false, @@ -209,8 +198,6 @@ internal class DevFeatureTogglesManagerTest { manager.changeToggle("FEATURE_TOGGLE", true) - verifyAll(inverse = true) { jsonAdapter.toJson(any()) } - Truth.assertThat(manager.getFeatureToggles()).containsExactlyEntriesIn(resultMap) } diff --git a/features/tester/impl/build.gradle.kts b/features/tester/impl/build.gradle.kts index 73011f1a76..9bc0022b59 100644 --- a/features/tester/impl/build.gradle.kts +++ b/features/tester/impl/build.gradle.kts @@ -27,17 +27,18 @@ dependencies { implementation(projects.domain.appTheme) implementation(projects.domain.appTheme.models) - /** Core modules */ - implementation(project(":core:featuretoggles")) - implementation(project(":core:ui")) - - /** Feature Apis */ - implementation(project(":features:tester:api")) - - /** Other modules */ - implementation(project(":libs:crypto")) - /** Other libraries */ implementation(deps.arrow.core) implementation(deps.timber) + + /** Core modules */ + implementation(projects.core.featuretoggles) + implementation(projects.core.ui) + implementation(projects.core.utils) + + /** Feature Apis */ + implementation(projects.features.tester.api) + + /** Other modules */ + implementation(projects.libs.crypto) } \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/featuretoggles/viewmodels/FeatureTogglesViewModel.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/featuretoggles/viewmodels/FeatureTogglesViewModel.kt index 4c10030524..037946a145 100644 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/featuretoggles/viewmodels/FeatureTogglesViewModel.kt +++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/featuretoggles/viewmodels/FeatureTogglesViewModel.kt @@ -4,24 +4,29 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import com.tangem.core.featuretoggle.manager.FeatureTogglesManager import com.tangem.core.featuretoggle.manager.MutableFeatureTogglesManager import com.tangem.feature.tester.presentation.featuretoggles.models.TesterFeatureToggle import com.tangem.feature.tester.presentation.featuretoggles.state.FeatureTogglesContentState import com.tangem.feature.tester.presentation.navigation.InnerTesterRouter +import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch import javax.inject.Inject /** * ViewModel for screen with list of feature toggles * * @property featureTogglesManager manager for getting information about the availability of feature toggles + * @property dispatchers coroutine dispatchers provider * [REDACTED_AUTHOR] */ @HiltViewModel internal class FeatureTogglesViewModel @Inject constructor( private val featureTogglesManager: FeatureTogglesManager, + private val dispatchers: CoroutineDispatcherProvider, ) : ViewModel() { /** Current ui state */ @@ -47,9 +52,11 @@ internal class FeatureTogglesViewModel @Inject constructor( } private fun onToggleValueChange(name: String, isEnabled: Boolean) { - mutableFeatureTogglesManager.changeToggle(name = name, isEnabled = isEnabled) + viewModelScope.launch(dispatchers.main) { + mutableFeatureTogglesManager.changeToggle(name = name, isEnabled = isEnabled) - uiState = uiState.copy(featureToggles = mutableFeatureTogglesManager.getTesterFeatureToggles()) + uiState = uiState.copy(featureToggles = mutableFeatureTogglesManager.getTesterFeatureToggles()) + } } private fun MutableFeatureTogglesManager.getTesterFeatureToggles(): List { diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 7616aa06f7..70d41d892b 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -53,7 +53,6 @@ googlePlayServicesWallet = "19.1.0" hilt = "2.44" hilt-navigation = "1.0.0" jodatime = "2.12.1" -krateSharedPref = "2.0.0" kotlin-immutable-collections = "0.3.5" kotsonGsonExt = "2.5.0" lottie = "3.4.0" @@ -206,7 +205,6 @@ hilt-core = { module = "com.google.dagger:hilt-core", version.ref = "hilt" } hilt-kapt = { module = "com.google.dagger:hilt-compiler", version.ref = "hilt" } hilt-navigation = { module = "com.google.dagger:hilt-compiler", version.ref = "hilt" } jodatime = { module = "joda-time:joda-time", version.ref = "jodatime" } -krateSharedPref = { module = "hu.autsoft:krate", version.ref = "krateSharedPref" } kotsonGson = { module = "com.github.salomonbrys.kotson:kotson", version.ref = "kotsonGsonExt" } lottie = { module = "com.airbnb.android:lottie", version.ref = "lottie" } material = { module = "com.google.android.material:material", version.ref = "googleMaterialComponent" } From 6ffc313b0eb73341f9cf225ff1c39c0a1a593c7a Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 6 Feb 2024 17:27:34 +0300 Subject: [PATCH 07/53] Updated on 2026-08-14 --- features/send/impl/build.gradle.kts | 5 +++ .../presentation/state/SendStateFactory.kt | 5 +++ .../impl/presentation/state/SendUiState.kt | 1 + .../send/impl/presentation/ui/SendScreen.kt | 15 ++++----- .../ui/amount/AmountFieldContainer.kt | 31 +++++++++++++------ .../ui/amount/SendAmountContent.kt | 8 +++-- .../impl/presentation/ui/send/SendContent.kt | 29 +++++++++++------ .../presentation/viewmodel/SendViewModel.kt | 14 +++++++++ 8 files changed, 80 insertions(+), 28 deletions(-) diff --git a/features/send/impl/build.gradle.kts b/features/send/impl/build.gradle.kts index 95074550ff..b35e632634 100644 --- a/features/send/impl/build.gradle.kts +++ b/features/send/impl/build.gradle.kts @@ -46,6 +46,9 @@ dependencies { implementation(projects.core.utils) implementation(projects.core.navigation) + /** Common */ + implementation(projects.common) + /** Libs */ implementation(projects.libs.crypto) @@ -62,6 +65,8 @@ dependencies { implementation(projects.domain.txhistory.models) implementation(projects.domain.transaction) implementation(projects.domain.card) + implementation(projects.domain.balanceHiding) + implementation(projects.domain.balanceHiding.models) /** Feature modules */ implementation(projects.features.send.api) 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 7f8e97b93e..31cfb3ef91 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 @@ -95,6 +95,7 @@ internal class SendStateFactory( currentState = MutableStateFlow(SendUiStateType.None), event = consumedEvent(), isEditingDisabled = false, + isBalanceHidden = false, ) fun getReadyState(): SendUiState { @@ -115,6 +116,10 @@ internal class SendStateFactory( isEditingDisabled = true, ) } + + fun getOnHideBalanceState(isBalanceHidden: Boolean): SendUiState { + return currentStateProvider().copy(isBalanceHidden = isBalanceHidden) + } //endregion //region amount state clicks diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt index 2d624f11d8..1828a6b3ed 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt @@ -34,6 +34,7 @@ internal data class SendUiState( val sendState: SendStates.SendState = SendStates.SendState(), val recipientList: MutableStateFlow> = MutableStateFlow(PagingData.empty()), val currentState: StateFlow, + val isBalanceHidden: Boolean, val event: StateEvent, ) diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendScreen.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendScreen.kt index 04220b3f11..88c36f0d4c 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendScreen.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendScreen.kt @@ -90,17 +90,18 @@ private fun SendScreenContent( ) { state -> when (state) { SendUiStateType.Amount -> SendAmountContent( - uiState.amountState, - uiState.clickIntents, + amountState = uiState.amountState, + isBalanceHiding = uiState.isBalanceHidden, + clickIntents = uiState.clickIntents, ) SendUiStateType.Recipient -> SendRecipientContent( - uiState.recipientState, - uiState.clickIntents, - recipientList, + uiState = uiState.recipientState, + clickIntents = uiState.clickIntents, + recipientList = recipientList, ) SendUiStateType.Fee -> SendSpeedAndFeeContent( - uiState.feeState, - uiState.clickIntents, + state = uiState.feeState, + clickIntents = uiState.clickIntents, ) SendUiStateType.Send -> SendContent(uiState) else -> Unit diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/AmountFieldContainer.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/AmountFieldContainer.kt index afd7b30b6e..ce7ba474c6 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/AmountFieldContainer.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/AmountFieldContainer.kt @@ -1,5 +1,6 @@ package com.tangem.features.send.impl.presentation.ui.amount +import androidx.compose.animation.AnimatedContent import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth @@ -11,13 +12,18 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.text.style.TextAlign +import com.tangem.common.Strings.STARS import com.tangem.core.ui.components.currency.tokenicon.TokenIcon import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme import com.tangem.features.send.impl.presentation.state.SendStates @Composable -internal fun AmountFieldContainer(amountState: SendStates.AmountState, modifier: Modifier = Modifier) { +internal fun AmountFieldContainer( + amountState: SendStates.AmountState, + isBalanceHiding: Boolean, + modifier: Modifier = Modifier, +) { Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier @@ -37,14 +43,21 @@ internal fun AmountFieldContainer(amountState: SendStates.AmountState, modifier: modifier = Modifier .padding(top = TangemTheme.dimens.spacing14), ) - Text( - text = amountState.walletBalance.resolveReference(), - style = TangemTheme.typography.caption2, - color = TangemTheme.colors.text.tertiary, - textAlign = TextAlign.Center, - modifier = Modifier - .padding(top = TangemTheme.dimens.spacing2), - ) + + val balance = if (isBalanceHiding) STARS else amountState.walletBalance.resolveReference() + AnimatedContent( + targetState = balance, + label = "Hide Balance Animation", + ) { + Text( + text = it, + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + textAlign = TextAlign.Center, + modifier = Modifier + .padding(top = TangemTheme.dimens.spacing2), + ) + } TokenIcon( state = amountState.tokenIconState, modifier = Modifier diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/SendAmountContent.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/SendAmountContent.kt index ba14154d01..39e83215dc 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/SendAmountContent.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/SendAmountContent.kt @@ -22,13 +22,17 @@ import com.tangem.features.send.impl.presentation.state.amount.SendAmountSegment import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents @Composable -internal fun SendAmountContent(amountState: SendStates.AmountState?, clickIntents: SendClickIntents) { +internal fun SendAmountContent( + amountState: SendStates.AmountState?, + isBalanceHiding: Boolean, + clickIntents: SendClickIntents, +) { if (amountState == null) return Column( modifier = Modifier .background(TangemTheme.colors.background.tertiary), ) { - AmountFieldContainer(amountState = amountState) + AmountFieldContainer(amountState = amountState, isBalanceHiding = isBalanceHiding) Row( modifier = Modifier .padding( diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/SendContent.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/SendContent.kt index 6430b56837..6d335ee554 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/SendContent.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/SendContent.kt @@ -1,5 +1,6 @@ package com.tangem.features.send.impl.presentation.ui.send +import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background @@ -17,6 +18,7 @@ import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.withStyle +import com.tangem.common.Strings import com.tangem.core.ui.components.inputrow.InputRowDefault import com.tangem.core.ui.components.inputrow.InputRowImage import com.tangem.core.ui.components.inputrow.InputRowRecipientDefault @@ -65,6 +67,7 @@ internal fun SendContent(uiState: SendUiState) { FromWallet( walletName = amountState.walletName, walletBalance = amountState.walletBalance.resolveReference(), + isBalanceHidden = uiState.isBalanceHidden, ) } RecipientBlock( @@ -91,7 +94,7 @@ internal fun SendContent(uiState: SendUiState) { } @Composable -private fun FromWallet(walletName: String, walletBalance: String) { +private fun FromWallet(walletName: String, walletBalance: String, isBalanceHidden: Boolean) { Column( modifier = Modifier .fillMaxWidth() @@ -110,15 +113,21 @@ private fun FromWallet(walletName: String, walletBalance: String) { style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.secondary, ) - Text( - text = walletBalance, - style = TangemTheme.typography.body2, - color = TangemTheme.colors.text.primary1, - modifier = Modifier - .padding( - top = TangemTheme.dimens.spacing8, - ), - ) + val balance = if (isBalanceHidden) Strings.STARS else walletBalance + AnimatedContent( + targetState = balance, + label = "Hide Balance Animation", + ) { + Text( + text = it, + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.primary1, + modifier = Modifier + .padding( + top = TangemTheme.dimens.spacing8, + ), + ) + } } } 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 b7bc549c8e..cb977c38f3 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 @@ -13,6 +13,7 @@ import com.tangem.common.extensions.isZero import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase import com.tangem.domain.redux.LegacyAction import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.tokens.* @@ -74,6 +75,7 @@ internal class SendViewModel @Inject constructor( private val walletManagersFacade: WalletManagersFacade, private val reduxStateHolder: ReduxStateHolder, private val isAmountSubtractAvailableUseCase: IsAmountSubtractAvailableUseCase, + private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, currencyChecksRepository: CurrencyChecksRepository, isFeeApproximateUseCase: IsFeeApproximateUseCase, getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase, @@ -163,6 +165,7 @@ internal class SendViewModel @Inject constructor( override fun onCreate(owner: LifecycleOwner) { subscribeOnCurrencyStatusUpdates(owner) onStateActive() + subscribeOnBalanceHidden(owner) } fun setRouter(router: InnerSendRouter, stateRouter: StateRouter) { @@ -189,6 +192,17 @@ internal class SendViewModel @Inject constructor( } } + private fun subscribeOnBalanceHidden(owner: LifecycleOwner) { + getBalanceHidingSettingsUseCase() + .flowWithLifecycle(owner.lifecycle) + .conflate() + .distinctUntilChanged() + .onEach { + uiState = stateFactory.getOnHideBalanceState(isBalanceHidden = it.isBalanceHidden) + } + .launchIn(viewModelScope) + } + private fun getCurrenciesStatusUpdates(owner: LifecycleOwner, wallet: UserWallet) { val isSingleWallet = wallet.scanResponse.walletData?.token != null && !wallet.isMultiCurrency From edd5efd0c1f78f1f0bf1ff0649ce344b545050ab Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 7 Feb 2024 12:15:51 +0300 Subject: [PATCH 08/53] Updated on 2026-08-14 --- .../com/tangem/core/ui/components/fields/AmountTextField.kt | 3 +++ .../com/tangem/core/ui/components/fields/SimpleTextField.kt | 3 +++ .../tangem/core/ui/components/inputrow/InputRowEnterAmount.kt | 3 +++ .../core/ui/components/inputrow/InputRowEnterInfoAmount.kt | 3 +++ .../state/fee/custom/EthereumCustomFeeConverter.kt | 4 ++++ .../presentation/state/fields/SendAmountFieldConverter.kt | 4 +++- .../send/impl/presentation/state/fields/SendTextField.kt | 3 +++ .../send/impl/presentation/ui/SendNavigationButtons.kt | 4 +++- .../features/send/impl/presentation/ui/amount/AmountField.kt | 1 + .../send/impl/presentation/ui/fee/SendCustomFeeEthereum.kt | 2 ++ 10 files changed, 28 insertions(+), 2 deletions(-) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt b/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt index 59c9b5143d..4bf1b72d84 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt @@ -3,6 +3,7 @@ package com.tangem.core.ui.components.fields import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.Text import androidx.compose.runtime.* @@ -54,6 +55,7 @@ fun AmountTextField( keyboardOptions: KeyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, ), + keyboardActions: KeyboardActions = KeyboardActions.Default, ) { val decimalFormat = rememberDecimalFormat() @@ -75,6 +77,7 @@ fun AmountTextField( textStyle = textStyle, color = color, keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, singleLine = true, visualTransformation = AmountVisualTransformation(decimals, symbol, decimalFormat), decorationBox = { innerTextField -> diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/fields/SimpleTextField.kt b/core/ui/src/main/java/com/tangem/core/ui/components/fields/SimpleTextField.kt index db725acbc8..a85e0eaafc 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/fields/SimpleTextField.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/fields/SimpleTextField.kt @@ -3,6 +3,7 @@ package com.tangem.core.ui.components.fields import androidx.compose.animation.AnimatedContent import androidx.compose.foundation.layout.Box import androidx.compose.foundation.text.BasicTextField +import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.selection.LocalTextSelectionColors import androidx.compose.foundation.text.selection.TextSelectionColors @@ -34,6 +35,7 @@ fun SimpleTextField( singleLine: Boolean = false, visualTransformation: VisualTransformation = VisualTransformation.None, keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default, color: Color = TangemTheme.colors.text.primary1, textStyle: TextStyle = TangemTheme.typography.body2.copy(color = color), readOnly: Boolean = false, @@ -87,6 +89,7 @@ fun SimpleTextField( readOnly = readOnly, visualTransformation = visualTransformation, keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, decorationBox = decorationBox ?: { textValue -> Box { if (value.isBlank() && placeholder != null) { diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowEnterAmount.kt b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowEnterAmount.kt index d25b9e7bc4..dd7c375e77 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowEnterAmount.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowEnterAmount.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.ripple.rememberRipple import androidx.compose.material3.Icon @@ -51,6 +52,7 @@ fun InputRowEnterAmount( titleColor: Color = TangemTheme.colors.text.secondary, textColor: Color = TangemTheme.colors.text.primary1, keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default, iconRes: Int? = null, iconTint: Color = TangemTheme.colors.icon.informative, onIconClick: (() -> Unit)? = null, @@ -79,6 +81,7 @@ fun InputRowEnterAmount( color = textColor, textStyle = TangemTheme.typography.body2, keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, modifier = Modifier .padding(top = TangemTheme.dimens.spacing8), ) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowEnterInfoAmount.kt b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowEnterInfoAmount.kt index 273e0ab9ab..40276abe7a 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowEnterInfoAmount.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowEnterInfoAmount.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -49,6 +50,7 @@ fun InputRowEnterInfoAmount( textColor: Color = TangemTheme.colors.text.primary1, infoColor: Color = TangemTheme.colors.text.tertiary, keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default, showDivider: Boolean = false, ) { DividerContainer( @@ -74,6 +76,7 @@ fun InputRowEnterInfoAmount( color = textColor, textStyle = TangemTheme.typography.body2, keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, modifier = Modifier .padding(top = TangemTheme.dimens.spacing8) .weight(1f), diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/custom/EthereumCustomFeeConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/custom/EthereumCustomFeeConverter.kt index 4997139e4e..4be7b3acfb 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/custom/EthereumCustomFeeConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/custom/EthereumCustomFeeConverter.kt @@ -1,5 +1,6 @@ package com.tangem.features.send.impl.presentation.state.fee.custom +import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType @@ -43,6 +44,7 @@ internal class EthereumCustomFeeConverter( title = resourceReference(R.string.send_max_fee), footer = resourceReference(R.string.send_max_fee_footer), label = getFeeFormatted(value.amount.value), + keyboardActions = KeyboardActions(), ), SendTextField.CustomFee( value = value.gasPrice.toString(), @@ -55,6 +57,7 @@ internal class EthereumCustomFeeConverter( imeAction = ImeAction.Next, keyboardType = KeyboardType.Number, ), + keyboardActions = KeyboardActions(), ), SendTextField.CustomFee( value = value.gasLimit.toString(), @@ -67,6 +70,7 @@ internal class EthereumCustomFeeConverter( imeAction = ImeAction.Done, keyboardType = KeyboardType.Number, ), + keyboardActions = KeyboardActions(onDone = { clickIntents.onNextClick() }), ), ) } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldConverter.kt index 2fd6c7906b..83701146e2 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldConverter.kt @@ -1,5 +1,6 @@ package com.tangem.features.send.impl.presentation.state.fields +import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType @@ -40,9 +41,10 @@ internal class SendAmountFieldConverter( fiatValue = fiatValue, onValueChange = clickIntents::onAmountValueChange, keyboardOptions = KeyboardOptions( - imeAction = ImeAction.Next, + imeAction = ImeAction.Done, keyboardType = KeyboardType.Number, ), + keyboardActions = KeyboardActions(onDone = { clickIntents.onNextClick() }), isFiatValue = false, cryptoAmount = cryptoAmount, fiatAmount = getAppCurrencyAmount(appCurrencyProvider()), diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendTextField.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendTextField.kt index d89b75c9b1..97682a9e21 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendTextField.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendTextField.kt @@ -1,5 +1,6 @@ package com.tangem.features.send.impl.presentation.state.fields +import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.runtime.Immutable import com.tangem.core.ui.extensions.TextReference @@ -21,6 +22,7 @@ internal sealed class SendTextField { override val value: String, override val onValueChange: (String) -> Unit, override val keyboardOptions: KeyboardOptions, + val keyboardActions: KeyboardActions, val cryptoAmount: Amount, val fiatAmount: Amount, val isFiatValue: Boolean, @@ -55,6 +57,7 @@ internal sealed class SendTextField { override val value: String, override val onValueChange: (String) -> Unit, override val keyboardOptions: KeyboardOptions, + val keyboardActions: KeyboardActions, val symbol: String?, val decimals: Int, val title: TextReference, diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt index 5ff3dbf5d6..064ec52822 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt @@ -24,6 +24,7 @@ import androidx.compose.ui.res.stringResource import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tangem.core.ui.R import com.tangem.core.ui.components.* +import com.tangem.core.ui.extensions.rememberHapticFeedback import com.tangem.core.ui.extensions.shareText import com.tangem.core.ui.res.TangemTheme import com.tangem.features.send.impl.presentation.state.SendUiState @@ -95,11 +96,12 @@ private fun SendPrimaryNavigationButton(uiState: SendUiState, modifier: Modifier ) { textId -> when { currentState.value == SendUiStateType.Send && !isSuccess -> { + val hapticFeedback = rememberHapticFeedback(state = currentState, onAction = buttonClick) PrimaryButtonIconEnd( text = stringResource(textId), iconResId = R.drawable.ic_tangem_24, enabled = isButtonEnabled, - onClick = buttonClick, + onClick = hapticFeedback, showProgress = isSending, ) } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/AmountField.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/AmountField.kt index c2ca7a26d8..b18159fd89 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/AmountField.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/AmountField.kt @@ -40,6 +40,7 @@ internal fun AmountField(sendField: SendTextField.AmountField, isFiat: Boolean) symbol = primaryAmount.currencySymbol, onValueChange = sendField.onValueChange, keyboardOptions = sendField.keyboardOptions, + keyboardActions = sendField.keyboardActions, textStyle = TangemTheme.typography.h2.copy( color = TangemTheme.colors.text.primary1, textAlign = TextAlign.Center, diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendCustomFeeEthereum.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendCustomFeeEthereum.kt index a5eba38a01..8dbb522573 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendCustomFeeEthereum.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendCustomFeeEthereum.kt @@ -38,6 +38,7 @@ internal fun SendCustomFeeEthereum( title = value.title, info = value.label, keyboardOptions = value.keyboardOptions, + keyboardActions = value.keyboardActions, onValueChange = value.onValueChange, showDivider = false, modifier = Modifier @@ -54,6 +55,7 @@ internal fun SendCustomFeeEthereum( symbol = value.symbol, onValueChange = value.onValueChange, keyboardOptions = value.keyboardOptions, + keyboardActions = value.keyboardActions, showDivider = false, modifier = Modifier .background( From 1441986cabca08791313dc41474b8252d7a1ef67 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 7 Feb 2024 10:25:35 +0300 Subject: [PATCH 09/53] Updated on 2026-08-14 --- .../java/com/tangem/tap/TapApplication.kt | 6 ++-- .../di/domain/WalletManagersFacadeModule.kt | 9 ++++-- .../com/tangem/tap/domain/TapWalletManager.kt | 10 ++++++- .../tap/proxy/redux/DaggerGraphState.kt | 4 +-- .../di/BlockchainDataStorageModule.kt | 21 ++++++++++++++ .../BlockchainWalletManagerFactoryModule.kt | 28 ------------------- .../DefaultWalletManagersFacade.kt | 10 ++++--- .../utils/WalletManagerFactory.kt | 10 +++++-- 8 files changed, 54 insertions(+), 44 deletions(-) create mode 100644 core/datasource/src/main/java/com/tangem/datasource/di/BlockchainDataStorageModule.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/di/BlockchainWalletManagerFactoryModule.kt diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 7e200d31b9..955106f3dc 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -10,7 +10,7 @@ import com.orhanobut.logger.AndroidLogAdapter import com.orhanobut.logger.Logger import com.tangem.Log import com.tangem.LogFormat -import com.tangem.blockchain.common.WalletManagerFactory +import com.tangem.blockchain.common.datastorage.BlockchainDataStorage import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.filter.OneTimeEventFilter @@ -162,7 +162,7 @@ internal class TapApplication : Application(), ImageLoaderFactory { lateinit var testerFeatureToggles: TesterFeatureToggles @Inject - lateinit var blockchainWalletManagerFactory: WalletManagerFactory + lateinit var blockchainDataStorage: BlockchainDataStorage // endregion Injected override fun onCreate() { @@ -240,7 +240,7 @@ internal class TapApplication : Application(), ImageLoaderFactory { sendFeatureToggles = sendFeatureToggles, derivationsRepository = derivationsRepository, testerFeatureToggles = testerFeatureToggles, - blockchainWalletManagerFactory = blockchainWalletManagerFactory, + blockchainDataStorage = blockchainDataStorage, ), ), ) diff --git a/app/src/main/java/com/tangem/tap/di/domain/WalletManagersFacadeModule.kt b/app/src/main/java/com/tangem/tap/di/domain/WalletManagersFacadeModule.kt index 65a6dce65c..a78ad179cd 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/WalletManagersFacadeModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/WalletManagersFacadeModule.kt @@ -1,8 +1,9 @@ package com.tangem.tap.di.domain import com.squareup.moshi.Moshi -import com.tangem.blockchain.common.WalletManagerFactory +import com.tangem.blockchain.common.datastorage.BlockchainDataStorage import com.tangem.datasource.asset.AssetReader +import com.tangem.datasource.config.ConfigManager import com.tangem.datasource.di.SdkMoshi import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.datasource.local.walletmanager.WalletManagersStore @@ -24,7 +25,8 @@ internal object WalletManagersFacadeModule { fun provideWalletManagersFacade( walletManagersStore: WalletManagersStore, userWalletsStore: UserWalletsStore, - blockchainWalletManagerFactory: WalletManagerFactory, + configManager: ConfigManager, + blockchainDataStorage: BlockchainDataStorage, mnemonicRepository: MnemonicRepository, assetReader: AssetReader, @SdkMoshi moshi: Moshi, @@ -32,7 +34,8 @@ internal object WalletManagersFacadeModule { return DefaultWalletManagersFacade( walletManagersStore = walletManagersStore, userWalletsStore = userWalletsStore, - blockchainWalletManagerFactory = blockchainWalletManagerFactory, + configManager = configManager, + blockchainDataStorage = blockchainDataStorage, assetReader = assetReader, moshi = moshi, mnemonic = mnemonicRepository.generateDefaultMnemonic(), diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt index 4379b387ab..41af6ffed9 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -1,5 +1,6 @@ package com.tangem.tap.domain +import com.tangem.blockchain.common.BlockchainSdkConfig import com.tangem.blockchain.common.Token import com.tangem.blockchain.common.Wallet import com.tangem.blockchain.common.WalletManagerFactory @@ -30,6 +31,10 @@ class TapWalletManager( private val dispatchers: CoroutineDispatcherProvider = AppCoroutineDispatcherProvider(), ) { + private val blockchainSdkConfig by lazy { + store.state.globalState.configManager?.config?.blockchainSdkConfig ?: BlockchainSdkConfig() + } + private var loadUserWalletDataJob: Job? = null set(value) { field?.cancel() @@ -37,7 +42,10 @@ class TapWalletManager( } val walletManagerFactory: WalletManagerFactory by lazy { - store.state.daggerGraphState.get(DaggerGraphState::blockchainWalletManagerFactory) + WalletManagerFactory( + config = blockchainSdkConfig, + blockchainDataStorage = store.state.daggerGraphState.get(DaggerGraphState::blockchainDataStorage), + ) } suspend fun onWalletSelected(userWallet: UserWallet, sendAnalyticsEvent: Boolean) { diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt index 19875bc1be..d10051fa08 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt @@ -1,6 +1,6 @@ package com.tangem.tap.proxy.redux -import com.tangem.blockchain.common.WalletManagerFactory +import com.tangem.blockchain.common.datastorage.BlockchainDataStorage import com.tangem.datasource.connection.NetworkConnectionManager import com.tangem.domain.appcurrency.repository.AppCurrencyRepository import com.tangem.domain.apptheme.repository.AppThemeModeRepository @@ -58,7 +58,7 @@ data class DaggerGraphState( val currenciesRepository: CurrenciesRepository? = null, val derivationsRepository: DerivationsRepository? = null, val testerFeatureToggles: TesterFeatureToggles? = null, - val blockchainWalletManagerFactory: WalletManagerFactory? = null, + val blockchainDataStorage: BlockchainDataStorage? = null, ) : StateType { inline fun get(getDependency: DaggerGraphState.() -> T?): T { diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/BlockchainDataStorageModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/BlockchainDataStorageModule.kt new file mode 100644 index 0000000000..3d285b3fbe --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/di/BlockchainDataStorageModule.kt @@ -0,0 +1,21 @@ +package com.tangem.datasource.di + +import com.tangem.blockchain.common.datastorage.BlockchainDataStorage +import com.tangem.datasource.local.blockchain.DefaultBlockchainDataStorage +import com.tangem.datasource.local.preferences.AppPreferencesStore +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal object BlockchainDataStorageModule { + + @Provides + @Singleton + fun provideBlockchainDataStorage(appPreferencesStore: AppPreferencesStore): BlockchainDataStorage { + return DefaultBlockchainDataStorage(appPreferencesStore) + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/BlockchainWalletManagerFactoryModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/BlockchainWalletManagerFactoryModule.kt deleted file mode 100644 index aeec8933f9..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/di/BlockchainWalletManagerFactoryModule.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.tangem.datasource.di - -import com.tangem.blockchain.common.WalletManagerFactory -import com.tangem.datasource.config.ConfigManager -import com.tangem.datasource.local.blockchain.DefaultBlockchainDataStorage -import com.tangem.datasource.local.preferences.AppPreferencesStore -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -internal object BlockchainWalletManagerFactoryModule { - - @Provides - @Singleton - fun provideBlockchainWalletManagerFactory( - configManager: ConfigManager, - appPreferencesStore: AppPreferencesStore, - ): WalletManagerFactory { - return WalletManagerFactory( - config = configManager.config.blockchainSdkConfig, - blockchainDataStorage = DefaultBlockchainDataStorage(appPreferencesStore), - ) - } -} \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt index 004b40c10b..a040c7db7d 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt @@ -10,6 +10,7 @@ import com.tangem.blockchain.common.* import com.tangem.blockchain.common.address.Address import com.tangem.blockchain.common.address.AddressType import com.tangem.blockchain.common.address.EstimationFeeAddressFactory +import com.tangem.blockchain.common.datastorage.BlockchainDataStorage import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.blockchain.common.txhistory.TransactionHistoryRequest @@ -18,6 +19,7 @@ import com.tangem.blockchain.extensions.SimpleResult import com.tangem.crypto.bip39.Mnemonic import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.datasource.asset.AssetReader +import com.tangem.datasource.config.ConfigManager import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.datasource.local.walletmanager.WalletManagersStore import com.tangem.domain.common.util.hasDerivation @@ -37,23 +39,23 @@ import kotlinx.coroutines.flow.Flow import timber.log.Timber import java.math.BigDecimal import java.util.EnumSet -import com.tangem.blockchain.common.WalletManagerFactory as BlockchainWalletManagerFactory -@Suppress("LargeClass", "TooManyFunctions") +@Suppress("LargeClass", "TooManyFunctions", "LongParameterList") // FIXME: Move to its own module and make internal @Deprecated("Inject the WalletManagerFacade interface using DI instead") class DefaultWalletManagersFacade( private val walletManagersStore: WalletManagersStore, private val userWalletsStore: UserWalletsStore, - blockchainWalletManagerFactory: BlockchainWalletManagerFactory, mnemonic: Mnemonic, assetReader: AssetReader, moshi: Moshi, + configManager: ConfigManager, + blockchainDataStorage: BlockchainDataStorage, ) : WalletManagersFacade { private val demoConfig by lazy { DemoConfig() } private val resultFactory by lazy { UpdateWalletManagerResultFactory() } - private val walletManagerFactory by lazy { WalletManagerFactory(blockchainWalletManagerFactory) } + private val walletManagerFactory by lazy { WalletManagerFactory(configManager, blockchainDataStorage) } private val sdkTokenConverter by lazy { SdkTokenConverter() } private val txHistoryStateConverter by lazy { SdkTransactionHistoryStateConverter() } private val txHistoryItemConverter by lazy { SdkTransactionHistoryItemConverter(assetReader, moshi) } diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/WalletManagerFactory.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/WalletManagerFactory.kt index d00e6d64a1..349d189f2e 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/WalletManagerFactory.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/WalletManagerFactory.kt @@ -3,7 +3,9 @@ package com.tangem.domain.walletmanager.utils import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.DerivationParams import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.common.datastorage.BlockchainDataStorage import com.tangem.crypto.hdWallet.DerivationPath +import com.tangem.datasource.config.ConfigManager import com.tangem.domain.common.DerivationStyleProvider import com.tangem.domain.common.extensions.makeWalletManagerForApp import com.tangem.domain.common.util.derivationStyleProvider @@ -11,9 +13,11 @@ import com.tangem.domain.models.scan.ScanResponse import timber.log.Timber import com.tangem.blockchain.common.WalletManagerFactory as BlockchainWalletManagerFactory -internal class WalletManagerFactory( - private val sdkWalletManagerFactory: BlockchainWalletManagerFactory, -) { +internal class WalletManagerFactory(configManager: ConfigManager, blockchainDataStorage: BlockchainDataStorage) { + + private val sdkWalletManagerFactory by lazy { + BlockchainWalletManagerFactory(configManager.config.blockchainSdkConfig, blockchainDataStorage) + } fun createWalletManager( scanResponse: ScanResponse, From 6d7042f24c1ea0205d10038de77d79040d0f57b2 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 7 Feb 2024 16:37:03 +0400 Subject: [PATCH 10/53] Updated on 2026-08-14 --- app/build.gradle.kts | 3 + gradle/dependencies.toml | 3 +- libs/visa/.gitignore | 1 + libs/visa/build.gradle.kts | 17 + .../main/java/com/tangem/lib/visa/ERC20.java | 270 +++ .../lib/visa/TangemBridgeProcessor.java | 1371 +++++++++++++ .../tangem/lib/visa/TangemPaymentAccount.java | 1781 +++++++++++++++++ .../visa/DefaultVisaContractInfoProvider.kt | 139 ++ .../lib/visa/VisaContractInfoProvider.kt | 90 + .../lib/visa/model/BalancesAndLimits.kt | 43 + .../tangem/lib/visa/utils/BigIntegerExt.kt | 13 + .../com/tangem/lib/visa/utils/ByteArrayExt.kt | 3 + .../com/tangem/lib/visa/utils/VisaConfig.kt | 14 + settings.gradle.kts | 1 + 14 files changed, 3748 insertions(+), 1 deletion(-) create mode 100644 libs/visa/.gitignore create mode 100644 libs/visa/build.gradle.kts create mode 100644 libs/visa/src/main/java/com/tangem/lib/visa/ERC20.java create mode 100644 libs/visa/src/main/java/com/tangem/lib/visa/TangemBridgeProcessor.java create mode 100644 libs/visa/src/main/java/com/tangem/lib/visa/TangemPaymentAccount.java create mode 100644 libs/visa/src/main/kotlin/com/tangem/lib/visa/DefaultVisaContractInfoProvider.kt create mode 100644 libs/visa/src/main/kotlin/com/tangem/lib/visa/VisaContractInfoProvider.kt create mode 100644 libs/visa/src/main/kotlin/com/tangem/lib/visa/model/BalancesAndLimits.kt create mode 100644 libs/visa/src/main/kotlin/com/tangem/lib/visa/utils/BigIntegerExt.kt create mode 100644 libs/visa/src/main/kotlin/com/tangem/lib/visa/utils/ByteArrayExt.kt create mode 100644 libs/visa/src/main/kotlin/com/tangem/lib/visa/utils/VisaConfig.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8c5969e599..4ff3173b40 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -20,6 +20,9 @@ configurations.all { substitute(module("com.facebook.react:hermes-engine")) .using(module("com.facebook.react:hermes-android:0.72.4")) + + substitute(module("org.bouncycastle:bcprov-jdk15on")) + .using(module("org.bouncycastle:bcprov-jdk18on:1.73")) } force( diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 70d41d892b..bdb562b050 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -81,6 +81,7 @@ androidXCamera = "1.3.0" listenableFuture = "1.0" swipeRefreshLayout = "1.1.0" spr-client = "3.6.2" +web3j = "4.10.1" # endregion Other libraries # region Tangem @@ -237,5 +238,5 @@ listenableFuture = { module = "com.google.guava:listenablefuture", version.ref = camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "androidXCamera" } camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "androidXCamera" } camera-view = { module = "androidx.camera:camera-view", version.ref = "androidXCamera" } - +web3j-core = { module = "org.web3j:core", version.ref = "web3j" } # endregion Other diff --git a/libs/visa/.gitignore b/libs/visa/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/libs/visa/.gitignore @@ -0,0 +1 @@ +/build diff --git a/libs/visa/build.gradle.kts b/libs/visa/build.gradle.kts new file mode 100644 index 0000000000..bc0aa12ec9 --- /dev/null +++ b/libs/visa/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + alias(deps.plugins.kotlin.jvm) + id("configuration") +} + +dependencies { + + /** Project */ + implementation(projects.core.utils) + + /** Libs - Other */ + implementation(deps.web3j.core) + implementation(deps.kotlin.coroutines) + implementation(deps.arrow.fx) + implementation(deps.jodatime) + implementation(deps.okHttp.prettyLogging) +} \ No newline at end of file diff --git a/libs/visa/src/main/java/com/tangem/lib/visa/ERC20.java b/libs/visa/src/main/java/com/tangem/lib/visa/ERC20.java new file mode 100644 index 0000000000..7b92736361 --- /dev/null +++ b/libs/visa/src/main/java/com/tangem/lib/visa/ERC20.java @@ -0,0 +1,270 @@ +package com.tangem.lib.visa; + +import org.web3j.abi.EventEncoder; +import org.web3j.abi.TypeReference; +import org.web3j.abi.datatypes.Address; +import org.web3j.abi.datatypes.Event; +import org.web3j.abi.datatypes.Function; +import org.web3j.abi.datatypes.Utf8String; +import org.web3j.abi.datatypes.generated.Uint256; +import org.web3j.abi.datatypes.generated.Uint8; +import org.web3j.crypto.Credentials; +import org.web3j.protocol.Web3j; +import org.web3j.protocol.core.DefaultBlockParameter; +import org.web3j.protocol.core.RemoteFunctionCall; +import org.web3j.protocol.core.methods.request.EthFilter; +import org.web3j.protocol.core.methods.response.BaseEventResponse; +import org.web3j.protocol.core.methods.response.Log; +import org.web3j.protocol.core.methods.response.TransactionReceipt; +import org.web3j.tx.Contract; +import org.web3j.tx.TransactionManager; +import org.web3j.tx.gas.ContractGasProvider; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import io.reactivex.Flowable; + +/** + *

Auto generated code. + *

Do not modify! + *

Please use the web3j command line tools, + * or the org.web3j.codegen.SolidityFunctionWrapperGenerator in the + * codegen module to update. + * + *

Generated with web3j version 1.5.0. + */ +@SuppressWarnings("rawtypes") +class ERC20 extends Contract { + public static final String BINARY = "Bin file was not provided"; + + public static final String FUNC_ALLOWANCE = "allowance"; + + public static final String FUNC_APPROVE = "approve"; + + public static final String FUNC_BALANCEOF = "balanceOf"; + + public static final String FUNC_DECIMALS = "decimals"; + + public static final String FUNC_NAME = "name"; + + public static final String FUNC_SYMBOL = "symbol"; + + public static final String FUNC_TOTALSUPPLY = "totalSupply"; + + public static final String FUNC_TRANSFER = "transfer"; + + public static final String FUNC_TRANSFERFROM = "transferFrom"; + + public static final Event APPROVAL_EVENT = new Event("Approval", + Arrays.asList(new TypeReference

(true) { + }, new TypeReference
(true) { + }, new TypeReference() { + })); + + public static final Event TRANSFER_EVENT = new Event("Transfer", + Arrays.asList(new TypeReference
(true) { + }, new TypeReference
(true) { + }, new TypeReference() { + })); + + @Deprecated + protected ERC20(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) { + super(BINARY, contractAddress, web3j, credentials, gasPrice, gasLimit); + } + + protected ERC20(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) { + super(BINARY, contractAddress, web3j, credentials, contractGasProvider); + } + + @Deprecated + protected ERC20(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) { + super(BINARY, contractAddress, web3j, transactionManager, gasPrice, gasLimit); + } + + protected ERC20(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) { + super(BINARY, contractAddress, web3j, transactionManager, contractGasProvider); + } + + public static List getApprovalEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(APPROVAL_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + ApprovalEventResponse typedResponse = new ApprovalEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.owner = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.spender = (String) eventValues.getIndexedValues().get(1).getValue(); + typedResponse.value = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static ApprovalEventResponse getApprovalEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(APPROVAL_EVENT, log); + ApprovalEventResponse typedResponse = new ApprovalEventResponse(); + typedResponse.log = log; + typedResponse.owner = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.spender = (String) eventValues.getIndexedValues().get(1).getValue(); + typedResponse.value = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + + public static List getTransferEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(TRANSFER_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + TransferEventResponse typedResponse = new TransferEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.from = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.to = (String) eventValues.getIndexedValues().get(1).getValue(); + typedResponse.value = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + @Deprecated + public static ERC20 load(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) { + return new ERC20(contractAddress, web3j, credentials, gasPrice, gasLimit); + } + + @Deprecated + public static ERC20 load(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) { + return new ERC20(contractAddress, web3j, transactionManager, gasPrice, gasLimit); + } + + public static TransferEventResponse getTransferEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(TRANSFER_EVENT, log); + TransferEventResponse typedResponse = new TransferEventResponse(); + typedResponse.log = log; + typedResponse.from = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.to = (String) eventValues.getIndexedValues().get(1).getValue(); + typedResponse.value = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + + public static ERC20 load(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) { + return new ERC20(contractAddress, web3j, credentials, contractGasProvider); + } + + public static ERC20 load(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) { + return new ERC20(contractAddress, web3j, transactionManager, contractGasProvider); + } + + public Flowable transferEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getTransferEventFromLog(log)); + } + + public Flowable transferEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(TRANSFER_EVENT)); + return transferEventFlowable(filter); + } + + public Flowable approvalEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getApprovalEventFromLog(log)); + } + + public Flowable approvalEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(APPROVAL_EVENT)); + return approvalEventFlowable(filter); + } + + public RemoteFunctionCall allowance(String owner, String spender) { + final Function function = new Function(FUNC_ALLOWANCE, + Arrays.asList(new Address(160, owner), + new Address(160, spender)), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, BigInteger.class); + } + + public RemoteFunctionCall approve(String spender, BigInteger value) { + final Function function = new Function( + FUNC_APPROVE, + Arrays.asList(new Address(160, spender), + new Uint256(value)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall balanceOf(String account) { + final Function function = new Function(FUNC_BALANCEOF, + List.of(new Address(160, account)), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, BigInteger.class); + } + + public RemoteFunctionCall decimals() { + final Function function = new Function(FUNC_DECIMALS, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, BigInteger.class); + } + + public RemoteFunctionCall name() { + final Function function = new Function(FUNC_NAME, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, String.class); + } + + public RemoteFunctionCall symbol() { + final Function function = new Function(FUNC_SYMBOL, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, String.class); + } + + public RemoteFunctionCall totalSupply() { + final Function function = new Function(FUNC_TOTALSUPPLY, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, BigInteger.class); + } + + public RemoteFunctionCall transfer(String to, BigInteger value) { + final Function function = new Function( + FUNC_TRANSFER, + Arrays.asList(new Address(160, to), + new Uint256(value)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall transferFrom(String from, String to, BigInteger value) { + final Function function = new Function( + FUNC_TRANSFERFROM, + Arrays.asList(new Address(160, from), + new Address(160, to), + new Uint256(value)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public static class ApprovalEventResponse extends BaseEventResponse { + public String owner; + + public String spender; + + public BigInteger value; + } + + public static class TransferEventResponse extends BaseEventResponse { + public String from; + + public String to; + + public BigInteger value; + } +} diff --git a/libs/visa/src/main/java/com/tangem/lib/visa/TangemBridgeProcessor.java b/libs/visa/src/main/java/com/tangem/lib/visa/TangemBridgeProcessor.java new file mode 100644 index 0000000000..f712f5cb24 --- /dev/null +++ b/libs/visa/src/main/java/com/tangem/lib/visa/TangemBridgeProcessor.java @@ -0,0 +1,1371 @@ +package com.tangem.lib.visa; + +import org.web3j.abi.EventEncoder; +import org.web3j.abi.FunctionEncoder; +import org.web3j.abi.TypeReference; +import org.web3j.abi.datatypes.Address; +import org.web3j.abi.datatypes.Bool; +import org.web3j.abi.datatypes.Event; +import org.web3j.abi.datatypes.Function; +import org.web3j.abi.datatypes.generated.Bytes32; +import org.web3j.abi.datatypes.generated.Bytes4; +import org.web3j.abi.datatypes.generated.Uint256; +import org.web3j.crypto.Credentials; +import org.web3j.protocol.Web3j; +import org.web3j.protocol.core.DefaultBlockParameter; +import org.web3j.protocol.core.RemoteCall; +import org.web3j.protocol.core.RemoteFunctionCall; +import org.web3j.protocol.core.methods.request.EthFilter; +import org.web3j.protocol.core.methods.response.BaseEventResponse; +import org.web3j.protocol.core.methods.response.Log; +import org.web3j.protocol.core.methods.response.TransactionReceipt; +import org.web3j.tx.Contract; +import org.web3j.tx.TransactionManager; +import org.web3j.tx.gas.ContractGasProvider; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import io.reactivex.Flowable; + +/** + *

Auto generated code. + *

Do not modify! + *

Please use the web3j command line tools, + * or the org.web3j.codegen.SolidityFunctionWrapperGenerator in the + * codegen module to update. + * + *

Generated with web3j version 1.5.0. + */ +@SuppressWarnings("rawtypes") +class TangemBridgeProcessor extends Contract { + public static final String BINARY = "608060405262278d0060085534801562000017575f80fd5b5060405162002457380380620024578339810160408190526200003a91620001fa565b600580546001600160a01b038088166001600160a01b031992831617909255600680548784169083161790556002805486841690831617905560038054928516929091169190911790556009819055620000955f33620000a1565b5050505050506200025d565b5f80620000af8484620000dc565b90508015620000d3575f848152600160205260409020620000d1908462000187565b505b90505b92915050565b5f828152602081815260408083206001600160a01b038516845290915281205460ff166200017f575f838152602081815260408083206001600160a01b03861684529091529020805460ff19166001179055620001363390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a4506001620000d6565b505f620000d6565b5f620000d3836001600160a01b0384165f8181526001830160205260408120546200017f57508154600181810184555f848152602080822090930184905584548482528286019093526040902091909155620000d6565b80516001600160a01b0381168114620001f5575f80fd5b919050565b5f805f805f60a086880312156200020f575f80fd5b6200021a86620001de565b94506200022a60208701620001de565b93506200023a60408701620001de565b92506200024a60608701620001de565b9150608086015190509295509295909350565b6121ec806200026b5f395ff3fe608060405234801561000f575f80fd5b506004361061024a575f3560e01c806391792d5b11610140578063c904ea39116100bf578063d6d32b2a11610084578063d6d32b2a14610551578063da90f3a014610564578063e24fd1f114610577578063e452f20d1461058a578063e96ede8e1461059d578063fe2208aa146105b0575f80fd5b8063c904ea39146104fc578063ca15c8731461050f578063cb37f3b214610522578063d47ae89c14610535578063d547741f1461053e575f80fd5b8063a217fddf11610105578063a217fddf14610495578063b6878ac91461049c578063b77c6d35146104af578063be4d1851146104c2578063c6b273e4146104d5575f80fd5b806391792d5b1461041757806391d14854146104205780639353ce4c1461043357806396bc563d1461045a578063978975591461046e575f80fd5b80634413e098116101cc57806373d3467c1161019157806373d3467c146103b85780637f2fadb0146103cb5780638072a022146103de57806383af133d146103f15780639010d07c14610404575f80fd5b80634413e098146103445780635673795114610357578063601c60651461036b57806365ebf99a1461037e57806370e4306e14610391575f80fd5b80632f2ff15d116102125780632f2ff15d146102cc5780633013ce29146102df57806336568abe1461030a57806337de81061461031d5780633d409a8614610330575f80fd5b806301ffc9a71461024e5780630f1071be1461027657806311dce7711461028d578063248a9ca3146102a25780632cc32641146102c4575b5f80fd5b61026161025c366004611c84565b6105c3565b60405190151581526020015b60405180910390f35b61027f60085481565b60405190815260200161026d565b6102a061029b366004611cbf565b6105ed565b005b61027f6102b0366004611cda565b5f9081526020819052604090206001015490565b6102a061065a565b6102a06102da366004611cf1565b6106d2565b6003546102f2906001600160a01b031681565b6040516001600160a01b03909116815260200161026d565b6102a0610318366004611cf1565b6106fc565b6102a061032b366004611cda565b610734565b61027f5f8051602061216e83398151915281565b6102a0610352366004611d1f565b610780565b61027f5f805160206120f983398151915281565b6102a0610379366004611d49565b610847565b6102a061038c366004611cbf565b610a6a565b61027f7f6ac2c8b5322a929d300af17766189693ff6db6939ffe690430ad4720baf47d1c81565b6102a06103c6366004611d88565b610acf565b6102a06103d9366004611d1f565b610bdc565b6102a06103ec366004611cda565b610cb1565b6102a06103ff366004611d1f565b610d3b565b6102f2610412366004611e06565b610df5565b61027f60075481565b61026161042e366004611cf1565b610e13565b61027f7f79af5a85184b45263c1b3721b9c8eb1becd98e55ade5a61da0fed18b90b8325681565b61027f5f8051602061204983398151915281565b61027f7f581be84b4822abe392a3bcac0d4d656e4cd57faf2e321a32108215cdd4a8365681565b61027f5f81565b6102a06104aa366004611e26565b610e3b565b6002546102f2906001600160a01b031681565b6006546102f2906001600160a01b031681565b61027f7f56d36dba8e2d5c88fc1038bd95811d491e67c9a66c6848c9d4c9309b9587e06481565b6102a061050a366004611e5e565b610f3f565b61027f61051d366004611cda565b611030565b6005546102f2906001600160a01b031681565b61027f60095481565b6102a061054c366004611cf1565b611046565b6102a061055f366004611cda565b61106a565b6102a0610572366004611d1f565b6110f3565b6102a0610585366004611ea5565b611357565b6102f2610598366004611cbf565b611495565b6102a06105ab366004611d49565b6114f3565b6102a06105be366004611d49565b6115d6565b5f6001600160e01b03198216635a05180f60e01b14806105e757506105e7826116a1565b92915050565b5f80516020612049833981519152610604816116d5565b600680546001600160a01b0319166001600160a01b0384169081179091556040519081527f68c7b435e0ad7bfb2140fab0735300efbda206f62906049e0b35763cef67cc5e906020015b60405180910390a15050565b600a543390610668826116e2565b5f811160405180606001604052806029815260200161218e60299139906106ab5760405162461bcd60e51b81526004016106a29190611ef3565b60405180910390fd5b505f600a556006546003546106ce916001600160a01b0391821691168484611785565b5050565b5f828152602081905260409020600101546106ec816116d5565b6106f683836117df565b50505050565b6001600160a01b03811633146107255760405163334bd91960e11b815260040160405180910390fd5b61072f8282611812565b505050565b5f8051602061204983398151915261074b816116d5565b60078290556040518281527fb5aa183eb20407e22587bcd13d5c82a85835a4bd60e2a13d7c7efee3c2e9ed489060200161064e565b7f581be84b4822abe392a3bcac0d4d656e4cd57faf2e321a32108215cdd4a836566107aa816116d5565b60405163b9603bdf60e01b8152600481018390526001600160a01b0384169063b9603bdf906024015f604051808303815f87803b1580156107e9575f80fd5b505af11580156107fb573d5f803e3d5ffd5b50505050826001600160a01b03167fc8818db5b3e4e986e2f21e002090b1513e17c05a7a326c3737af624bf6ef78c48360405161083a91815260200190565b60405180910390a2505050565b5f805160206120f983398151915261085e816116d5565b60408051808201909152601b81527f353130307c436f6d6d6f6e3a20616d6f756e74206973207a65726f00000000006020820152826108b05760405162461bcd60e51b81526004016106a29190611ef3565b505f6108bb85611495565b6003546040516370a0823160e01b81526001600160a01b0380841660048301529293505f92909116906370a0823190602401602060405180830381865afa158015610908573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061092c9190611f25565b9050838110156040518060400160405280601f81526020017f353130317c436f6d6d6f6e3a20696e73756666696369656e742066756e647300815250906109865760405162461bcd60e51b81526004016106a29190611ef3565b50600a84905560405163f89db45760e01b8152600481018690526001600160a01b0383169063f89db457906024015f604051808303815f87803b1580156109cb575f80fd5b505af11580156109dd573d5f803e3d5ffd5b50505050600a545f146040518060600160405280602e8152602001612119602e913990610a1d5760405162461bcd60e51b81526004016106a29190611ef3565b5084866001600160a01b03167fa5411d116afcb8415f0019792cccf595f94852ba82886dacbf756d9c3ce85f3f86604051610a5a91815260200190565b60405180910390a3505050505050565b5f80516020612049833981519152610a81816116d5565b600580546001600160a01b0319166001600160a01b0384169081179091556040519081527fccbdeb71dc680b2f0fd85f93e7ef0f70eda32a12b918c9715be07bb646360e599060200161064e565b5f8051602061216e833981519152610ae6816116d5565b5f610af088611495565b90505f60075487610b019190611f50565b6040516334284dfb60e01b8152600481018a9052602481018290526001600160801b03198816604482015261ffff8716606482015285151560848201529091506001600160a01b038316906334284dfb9060a4015f604051808303815f87803b158015610b6c575f80fd5b505af1158015610b7e573d5f803e3d5ffd5b5050505087896001600160a01b03167f92c1dbadbb7b791052341ce7c185ca37861e432438355dc3e1642b7c1287609a89600754604051610bc9929190918252602082015260400190565b60405180910390a3505050505050505050565b7f79af5a85184b45263c1b3721b9c8eb1becd98e55ade5a61da0fed18b90b83256610c06816116d5565b5f610c1084611495565b604051636df6c7d560e11b8152600481018590529091506001600160a01b0382169063dbed8faa906024015f604051808303815f87803b158015610c52575f80fd5b505af1158015610c64573d5f803e3d5ffd5b50505050836001600160a01b03167f466723bb873015d5baf515fce6b0c8df2cb154adfd8badb6bc77a620a6ef676184604051610ca391815260200190565b60405180910390a250505050565b5f80516020612049833981519152610cc8816116d5565b6283d60082106040518060600160405280602a815260200161201f602a913990610d055760405162461bcd60e51b81526004016106a29190611ef3565b5060088290556040518281527fdc10143650bb79cd7a92cdf792545dcc2c3b0a719bebb3d83a8a21bb8fbfc3d69060200161064e565b7f581be84b4822abe392a3bcac0d4d656e4cd57faf2e321a32108215cdd4a83656610d65816116d5565b6040516306b2a38360e41b8152600481018390526001600160a01b03841690636b2a3830906024015f604051808303815f87803b158015610da4575f80fd5b505af1158015610db6573d5f803e3d5ffd5b50505050826001600160a01b03167fcc1685e553848099cad0e11271e780371c44b5ed92a54552fbedf9f1b6b9f2478360405161083a91815260200190565b5f828152600160205260408120610e0c908361183d565b9392505050565b5f918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b7f6ac2c8b5322a929d300af17766189693ff6db6939ffe690430ad4720baf47d1c610e65816116d5565b5f610e6f86611495565b90505f60075484610e809190611f50565b6040516304e7595f60e21b81526004810188905260248101879052604481018290529091506001600160a01b0383169063139d657c906064015f604051808303815f87803b158015610ed0575f80fd5b505af1158015610ee2573d5f803e3d5ffd5b505050508486886001600160a01b03167fcb06dfb173e98cd60a8931644d288167e193c33d81c1019b9f9aab36b13fbc0387600754604051610f2e929190918252602082015260400190565b60405180910390a450505050505050565b5f8051602061216e833981519152610f56816116d5565b5f610f6086611495565b90505f60075485610f719190611f50565b6040516320199e7960e21b8152600481018890526024810182905285151560448201529091506001600160a01b0383169063806679e4906064015f604051808303815f87803b158015610fc2575f80fd5b505af1158015610fd4573d5f803e3d5ffd5b5050505085876001600160a01b03167f92c1dbadbb7b791052341ce7c185ca37861e432438355dc3e1642b7c1287609a8760075460405161101f929190918252602082015260400190565b60405180910390a350505050505050565b5f8181526001602052604081206105e790611848565b5f82815260208190526040902060010154611060816116d5565b6106f68383611812565b5f80516020612049833981519152611081816116d5565b610e10821060405180606001604052806027815260200161214760279139906110bd5760405162461bcd60e51b81526004016106a29190611ef3565b5060098290556040518281527f7f63f876249f25f7856e620802edd09af38a83c9c6040fcdb3717915403083119060200161064e565b5f805160206120f983398151915261110a816116d5565b5f61111484611495565b604051631902cad960e31b8152600481018590529091505f906001600160a01b0383169063c81656c890602401602060405180830381865afa15801561115c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111809190611f25565b6003546040516370a0823160e01b81526001600160a01b0385811660048301529293505f92909116906370a0823190602401602060405180830381865afa1580156111cd573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111f19190611f25565b90505f82116040518060600160405280602781526020016120d2602791399061122d5760405162461bcd60e51b81526004016106a29190611ef3565b5060408051808201909152601f81527f353130317c436f6d6d6f6e3a20696e73756666696369656e742066756e6473006020820152828210156112835760405162461bcd60e51b81526004016106a29190611ef3565b50600a82905560405163e3dbffd560e01b8152600481018690526001600160a01b0384169063e3dbffd5906024015f604051808303815f87803b1580156112c8575f80fd5b505af11580156112da573d5f803e3d5ffd5b50505050600a545f146040518060600160405280602e8152602001612119602e91399061131a5760405162461bcd60e51b81526004016106a29190611ef3565b5084866001600160a01b03167fdfab01fc691e8b69fca482c65142d1bcfeafeb9eb6e9fd5220867d7196d9476e84604051610a5a91815260200190565b7f56d36dba8e2d5c88fc1038bd95811d491e67c9a66c6848c9d4c9309b9587e064611381816116d5565b61138a826116e2565b306001600160a01b0316826001600160a01b031663ce1b1d436040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113d0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113f49190611f63565b6001600160a01b0316146040518060600160405280603281526020016120a060329139906114355760405162461bcd60e51b81526004016106a29190611ef3565b506001600160a01b038381165f8181526004602090815260409182902080546001600160a01b0319169487169485179055905192835290917f0c1464e8c817204497a2076f3b466fe64093e83033c83303b0cb44001cd9b6b5910161083a565b6001600160a01b038082165f90815260046020908152604080832054815160608101909252602f8083529394169283151592611ff090830139906114ec5760405162461bcd60e51b81526004016106a29190611ef3565b5092915050565b5f8051602061216e83398151915261150a816116d5565b5f61151485611495565b90505f831561152557600754611527565b5f5b90505f6115348286611f50565b60405163e4d7ddb960e01b815260048101889052602481018290529091506001600160a01b0384169063e4d7ddb9906044015f604051808303815f87803b15801561157d575f80fd5b505af115801561158f573d5f803e3d5ffd5b505060408051888152602081018690528993506001600160a01b038b1692507f8ba3ad1bd4ff5c3d75861d56df66b7942244d700f4d4cb67df693bac78e805a2910161101f565b5f805160206120f98339815191526115ed816116d5565b5f6115f785611495565b60405163456575e560e01b815260048101869052602481018590529091506001600160a01b0382169063456575e5906044015f604051808303815f87803b158015611640575f80fd5b505af1158015611652573d5f803e3d5ffd5b5050505083856001600160a01b03167f6f60b80b8dcecc3741c03485554a9a35ced96ebba66405fd324caf12a16261da8560405161169291815260200190565b60405180910390a35050505050565b5f6001600160e01b03198216637965db0b60e01b14806105e757506301ffc9a760e01b6001600160e01b03198316146105e7565b6116df8133611851565b50565b6002546040516385bb392360e01b81526001600160a01b038381166004830152909116906385bb392390602401602060405180830381865afa15801561172a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061174e9190611f7e565b60405180606001604052806037815260200161206960379139906106ce5760405162461bcd60e51b81526004016106a29190611ef3565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b1790526106f690859061188a565b5f806117eb84846118eb565b90508015610e0c575f84815260016020526040902061180a908461197a565b509392505050565b5f8061181e848461198e565b90508015610e0c575f84815260016020526040902061180a90846119f7565b5f610e0c8383611a0b565b5f6105e7825490565b61185b8282610e13565b6106ce5760405163e2517d3f60e01b81526001600160a01b0382166004820152602481018390526044016106a2565b5f61189e6001600160a01b03841683611a31565b905080515f141580156118c25750808060200190518101906118c09190611f7e565b155b1561072f57604051635274afe760e01b81526001600160a01b03841660048201526024016106a2565b5f6118f68383610e13565b611973575f838152602081815260408083206001600160a01b03861684529091529020805460ff1916600117905561192b3390565b6001600160a01b0316826001600160a01b0316847f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45060016105e7565b505f6105e7565b5f610e0c836001600160a01b038416611a3e565b5f6119998383610e13565b15611973575f838152602081815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45060016105e7565b5f610e0c836001600160a01b038416611a83565b5f825f018281548110611a2057611a20611f99565b905f5260205f200154905092915050565b6060610e0c83835f611b66565b5f81815260018301602052604081205461197357508154600181810184555f8481526020808220909301849055845484825282860190935260409020919091556105e7565b5f8181526001830160205260408120548015611b5d575f611aa5600183611fad565b85549091505f90611ab890600190611fad565b9050808214611b17575f865f018281548110611ad657611ad6611f99565b905f5260205f200154905080875f018481548110611af657611af6611f99565b5f918252602080832090910192909255918252600188019052604090208390555b8554869080611b2857611b28611fc0565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f9055600193505050506105e7565b5f9150506105e7565b606081471015611b8b5760405163cd78605960e01b81523060048201526024016106a2565b5f80856001600160a01b03168486604051611ba69190611fd4565b5f6040518083038185875af1925050503d805f8114611be0576040519150601f19603f3d011682016040523d82523d5f602084013e611be5565b606091505b5091509150611bf5868383611bff565b9695505050505050565b606082611c1457611c0f82611c5b565b610e0c565b8151158015611c2b57506001600160a01b0384163b155b15611c5457604051639996b31560e01b81526001600160a01b03851660048201526024016106a2565b5080610e0c565b805115611c6b5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b5f60208284031215611c94575f80fd5b81356001600160e01b031981168114610e0c575f80fd5b6001600160a01b03811681146116df575f80fd5b5f60208284031215611ccf575f80fd5b8135610e0c81611cab565b5f60208284031215611cea575f80fd5b5035919050565b5f8060408385031215611d02575f80fd5b823591506020830135611d1481611cab565b809150509250929050565b5f8060408385031215611d30575f80fd5b8235611d3b81611cab565b946020939093013593505050565b5f805f60608486031215611d5b575f80fd5b8335611d6681611cab565b95602085013595506040909401359392505050565b80151581146116df575f80fd5b5f805f805f8060c08789031215611d9d575f80fd5b8635611da881611cab565b9550602087013594506040870135935060608701356001600160801b031981168114611dd2575f80fd5b9250608087013561ffff81168114611de8575f80fd5b915060a0870135611df881611d7b565b809150509295509295509295565b5f8060408385031215611e17575f80fd5b50508035926020909101359150565b5f805f8060808587031215611e39575f80fd5b8435611e4481611cab565b966020860135965060408601359560600135945092505050565b5f805f8060808587031215611e71575f80fd5b8435611e7c81611cab565b935060208501359250604085013591506060850135611e9a81611d7b565b939692955090935050565b5f8060408385031215611eb6575f80fd5b8235611ec181611cab565b91506020830135611d1481611cab565b5f5b83811015611eeb578181015183820152602001611ed3565b50505f910152565b602081525f8251806020840152611f11816040850160208701611ed1565b601f01601f19169190910160400192915050565b5f60208284031215611f35575f80fd5b5051919050565b634e487b7160e01b5f52601160045260245ffd5b808201808211156105e7576105e7611f3c565b5f60208284031215611f73575f80fd5b8151610e0c81611cab565b5f60208284031215611f8e575f80fd5b8151610e0c81611d7b565b634e487b7160e01b5f52603260045260245ffd5b818103818111156105e7576105e7611f3c565b634e487b7160e01b5f52603160045260245ffd5b5f8251611fe5818460208701611ed1565b919091019291505056fe353230327c50726f636573736f723a206e6f207061796d656e74206163636f756e7420666f72207468652063617264353231307c50726f636573736f723a20736574746c656d656e7420706572696f6420746f6f206c6f6e67fa658e47c75a6eb0e27156b2f72e1b295c98b53e8a1d238d7ca6ea6345b34880353230307c50726f636573736f723a207061796d656e74206163636f756e74206e6f74206465706c6f79656420627920666163746f7279353230317c50726f636573736f723a207061796d656e74206163636f756e742070726f636573736f72206d69736d61746368353235307c50726f636573736f723a20726566756e64207265636f7264206e6f7420666f756e64d77cc12a543481a2b3ef8fd055979569715a10db9879cd9e664395eca3a54dff353235327c50726f636573736f723a20726566756e6420746f2070726f63657373207761736e2774207265736574353231317c50726f636573736f723a2073656375726974792064656c617920746f6f206c6f6e67a567244cad934c87b7f7e15b15dd0e75f95a76afd1916f8eab4a391410d6f278353235317c50726f636573736f723a20726566756e6420746f2070726f63657373206973207a65726fa2646970667358221220abcbacdb9048a2744f99cdd5031db80dd0390bf503ccade142542ce178f0668364736f6c63430008160033"; + + public static final String FUNC_AUTHORIZATION_PROCESSOR_ROLE = "AUTHORIZATION_PROCESSOR_ROLE"; + + public static final String FUNC_BALANCE_VERIFIER_ROLE = "BALANCE_VERIFIER_ROLE"; + + public static final String FUNC_DEBT_PROCESSOR_ROLE = "DEBT_PROCESSOR_ROLE"; + + public static final String FUNC_DEFAULT_ADMIN_ROLE = "DEFAULT_ADMIN_ROLE"; + + public static final String FUNC_PAYMENT_ACCOUNT_SETTER = "PAYMENT_ACCOUNT_SETTER"; + + public static final String FUNC_PROPERTY_SETTER_ROLE = "PROPERTY_SETTER_ROLE"; + + public static final String FUNC_REFUND_PROCESSOR_ROLE = "REFUND_PROCESSOR_ROLE"; + + public static final String FUNC_SETTLEMENT_PROCESSOR_ROLE = "SETTLEMENT_PROCESSOR_ROLE"; + + public static final String FUNC_FIXEDFEE = "fixedFee"; + + public static final String FUNC_GETPAYMENTACCOUNT = "getPaymentAccount"; + + public static final String FUNC_GETROLEADMIN = "getRoleAdmin"; + + public static final String FUNC_GETROLEMEMBER = "getRoleMember"; + + public static final String FUNC_GETROLEMEMBERCOUNT = "getRoleMemberCount"; + + public static final String FUNC_GRANTROLE = "grantRole"; + + public static final String FUNC_HASROLE = "hasRole"; + + public static final String FUNC_INCREASEVERIFIEDBALANCEFOR = "increaseVerifiedBalanceFor"; + + public static final String FUNC_PAYMENTACCOUNTFACTORY = "paymentAccountFactory"; + + public static final String FUNC_PAYMENTRECEIVER = "paymentReceiver"; + + public static final String FUNC_PAYMENTTOKEN = "paymentToken"; + + public static final String FUNC_PROCESSAUTHORIZATION = "processAuthorization"; + + public static final String FUNC_PROCESSAUTHORIZATIONCHANGE = "processAuthorizationChange"; + + public static final String FUNC_PROCESSAUTHORIZATIONNOOTP = "processAuthorizationNoOtp"; + + public static final String FUNC_PROCESSPENDINGREFUND = "processPendingRefund"; + + public static final String FUNC_PROCESSREFUND = "processRefund"; + + public static final String FUNC_PROCESSREFUNDCALLBACK = "processRefundCallback"; + + public static final String FUNC_PROCESSSETTLEMENT = "processSettlement"; + + public static final String FUNC_REFUNDACCOUNT = "refundAccount"; + + public static final String FUNC_RENOUNCEROLE = "renounceRole"; + + public static final String FUNC_REVOKEROLE = "revokeRole"; + + public static final String FUNC_SAVEPENDINGREFUND = "savePendingRefund"; + + public static final String FUNC_SECURITYDELAY = "securityDelay"; + + public static final String FUNC_SETFIXEDFEE = "setFixedFee"; + + public static final String FUNC_SETPAYMENTACCOUNT = "setPaymentAccount"; + + public static final String FUNC_SETPAYMENTRECEIVER = "setPaymentReceiver"; + + public static final String FUNC_SETREFUNDACCOUNT = "setRefundAccount"; + + public static final String FUNC_SETSECURITYDELAY = "setSecurityDelay"; + + public static final String FUNC_SETSETTLEMENTPERIOD = "setSettlementPeriod"; + + public static final String FUNC_SETVERIFIEDBALANCEFOR = "setVerifiedBalanceFor"; + + public static final String FUNC_SETTLEMENTPERIOD = "settlementPeriod"; + + public static final String FUNC_SUPPORTSINTERFACE = "supportsInterface"; + + public static final String FUNC_WRITEOFFDEBT = "writeOffDebt"; + + public static final Event AUTHORIZATIONCHANGEPROCESSED_EVENT = new Event("AuthorizationChangeProcessed", + Arrays.asList(new TypeReference

(true) { + }, new TypeReference(true) { + }, new TypeReference() { + }, new TypeReference() { + })); + + public static final Event AUTHORIZATIONPROCESSED_EVENT = new Event("AuthorizationProcessed", + Arrays.asList(new TypeReference
(true) { + }, new TypeReference(true) { + }, new TypeReference() { + }, new TypeReference() { + })); + + public static final Event DEBTWRITEOFFPROCESSED_EVENT = new Event("DebtWriteOffProcessed", + Arrays.asList(new TypeReference
(true) { + }, new TypeReference() { + })); + + public static final Event FIXEDFEESET_EVENT = new Event("FixedFeeSet", + List.of(new TypeReference() { + })); + + public static final Event PAYMENTACCOUNTSET_EVENT = new Event("PaymentAccountSet", + Arrays.asList(new TypeReference
(true) { + }, new TypeReference
() { + })); + + public static final Event PAYMENTRECEIVERSET_EVENT = new Event("PaymentReceiverSet", + List.of(new TypeReference
() { + })); + + public static final Event PENDINGREFUNDPROCESSED_EVENT = new Event("PendingRefundProcessed", + Arrays.asList(new TypeReference
(true) { + }, new TypeReference(true) { + }, new TypeReference() { + })); + + public static final Event REFUNDACCOUNTSET_EVENT = new Event("RefundAccountSet", + List.of(new TypeReference
() { + })); + + public static final Event REFUNDPROCESSED_EVENT = new Event("RefundProcessed", + Arrays.asList(new TypeReference
(true) { + }, new TypeReference(true) { + }, new TypeReference() { + })); + + public static final Event ROLEADMINCHANGED_EVENT = new Event("RoleAdminChanged", + Arrays.asList(new TypeReference(true) { + }, new TypeReference(true) { + }, new TypeReference(true) { + })); + + public static final Event ROLEGRANTED_EVENT = new Event("RoleGranted", + Arrays.asList(new TypeReference(true) { + }, new TypeReference
(true) { + }, new TypeReference
(true) { + })); + + public static final Event ROLEREVOKED_EVENT = new Event("RoleRevoked", + Arrays.asList(new TypeReference(true) { + }, new TypeReference
(true) { + }, new TypeReference
(true) { + })); + + public static final Event SAVEREFUNDPROCESSED_EVENT = new Event("SaveRefundProcessed", + Arrays.asList(new TypeReference
(true) { + }, new TypeReference(true) { + }, new TypeReference() { + })); + + public static final Event SECURITYDELAYSET_EVENT = new Event("SecurityDelaySet", + List.of(new TypeReference() { + })); + + public static final Event SETTLEMENTPERIODSET_EVENT = new Event("SettlementPeriodSet", + List.of(new TypeReference() { + })); + + public static final Event SETTLEMENTPROCESSED_EVENT = new Event("SettlementProcessed", + Arrays.asList(new TypeReference
(true) { + }, new TypeReference(true) { + }, new TypeReference(true) { + }, new TypeReference() { + }, new TypeReference() { + })); + + public static final Event VERIFIEDBALANCEINCREASEDFOR_EVENT = new Event("VerifiedBalanceIncreasedFor", + Arrays.asList(new TypeReference
(true) { + }, new TypeReference() { + })); + + public static final Event VERIFIEDBALANCESETFOR_EVENT = new Event("VerifiedBalanceSetFor", + Arrays.asList(new TypeReference
(true) { + }, new TypeReference() { + })); + + @Deprecated + protected TangemBridgeProcessor(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) { + super(BINARY, contractAddress, web3j, credentials, gasPrice, gasLimit); + } + + protected TangemBridgeProcessor(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) { + super(BINARY, contractAddress, web3j, credentials, contractGasProvider); + } + + @Deprecated + protected TangemBridgeProcessor(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) { + super(BINARY, contractAddress, web3j, transactionManager, gasPrice, gasLimit); + } + + protected TangemBridgeProcessor(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) { + super(BINARY, contractAddress, web3j, transactionManager, contractGasProvider); + } + + public static List getAuthorizationChangeProcessedEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(AUTHORIZATIONCHANGEPROCESSED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + AuthorizationChangeProcessedEventResponse typedResponse = new AuthorizationChangeProcessedEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.card = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(1).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.fee = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static AuthorizationChangeProcessedEventResponse getAuthorizationChangeProcessedEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(AUTHORIZATIONCHANGEPROCESSED_EVENT, log); + AuthorizationChangeProcessedEventResponse typedResponse = new AuthorizationChangeProcessedEventResponse(); + typedResponse.log = log; + typedResponse.card = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(1).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.fee = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue(); + return typedResponse; + } + + public static List getAuthorizationProcessedEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(AUTHORIZATIONPROCESSED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + AuthorizationProcessedEventResponse typedResponse = new AuthorizationProcessedEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.card = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(1).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.fee = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getDebtWriteOffProcessedEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(DEBTWRITEOFFPROCESSED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + DebtWriteOffProcessedEventResponse typedResponse = new DebtWriteOffProcessedEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.card = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getFixedFeeSetEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(FIXEDFEESET_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + FixedFeeSetEventResponse typedResponse = new FixedFeeSetEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.fixedFee = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static AuthorizationProcessedEventResponse getAuthorizationProcessedEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(AUTHORIZATIONPROCESSED_EVENT, log); + AuthorizationProcessedEventResponse typedResponse = new AuthorizationProcessedEventResponse(); + typedResponse.log = log; + typedResponse.card = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(1).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.fee = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue(); + return typedResponse; + } + + public static List getPaymentAccountSetEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(PAYMENTACCOUNTSET_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + PaymentAccountSetEventResponse typedResponse = new PaymentAccountSetEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.card = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.paymentAccount = (String) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getPaymentReceiverSetEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(PAYMENTRECEIVERSET_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + PaymentReceiverSetEventResponse typedResponse = new PaymentReceiverSetEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.paymentReceiver = (String) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getPendingRefundProcessedEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(PENDINGREFUNDPROCESSED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + PendingRefundProcessedEventResponse typedResponse = new PendingRefundProcessedEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.card = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(1).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static DebtWriteOffProcessedEventResponse getDebtWriteOffProcessedEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(DEBTWRITEOFFPROCESSED_EVENT, log); + DebtWriteOffProcessedEventResponse typedResponse = new DebtWriteOffProcessedEventResponse(); + typedResponse.log = log; + typedResponse.card = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + + public static PendingRefundProcessedEventResponse getPendingRefundProcessedEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(PENDINGREFUNDPROCESSED_EVENT, log); + PendingRefundProcessedEventResponse typedResponse = new PendingRefundProcessedEventResponse(); + typedResponse.log = log; + typedResponse.card = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(1).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + + public static List getRefundAccountSetEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(REFUNDACCOUNTSET_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + RefundAccountSetEventResponse typedResponse = new RefundAccountSetEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.refundAccount = (String) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getRefundProcessedEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(REFUNDPROCESSED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + RefundProcessedEventResponse typedResponse = new RefundProcessedEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.card = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(1).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static FixedFeeSetEventResponse getFixedFeeSetEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(FIXEDFEESET_EVENT, log); + FixedFeeSetEventResponse typedResponse = new FixedFeeSetEventResponse(); + typedResponse.log = log; + typedResponse.fixedFee = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + + public static List getRoleAdminChangedEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(ROLEADMINCHANGED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + RoleAdminChangedEventResponse typedResponse = new RoleAdminChangedEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.role = (byte[]) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.previousAdminRole = (byte[]) eventValues.getIndexedValues().get(1).getValue(); + typedResponse.newAdminRole = (byte[]) eventValues.getIndexedValues().get(2).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getRoleGrantedEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(ROLEGRANTED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + RoleGrantedEventResponse typedResponse = new RoleGrantedEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.role = (byte[]) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.account = (String) eventValues.getIndexedValues().get(1).getValue(); + typedResponse.sender = (String) eventValues.getIndexedValues().get(2).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getRoleRevokedEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(ROLEREVOKED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + RoleRevokedEventResponse typedResponse = new RoleRevokedEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.role = (byte[]) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.account = (String) eventValues.getIndexedValues().get(1).getValue(); + typedResponse.sender = (String) eventValues.getIndexedValues().get(2).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static PaymentAccountSetEventResponse getPaymentAccountSetEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(PAYMENTACCOUNTSET_EVENT, log); + PaymentAccountSetEventResponse typedResponse = new PaymentAccountSetEventResponse(); + typedResponse.log = log; + typedResponse.card = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.paymentAccount = (String) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + + public static List getSaveRefundProcessedEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(SAVEREFUNDPROCESSED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + SaveRefundProcessedEventResponse typedResponse = new SaveRefundProcessedEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.card = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(1).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getSecurityDelaySetEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(SECURITYDELAYSET_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + SecurityDelaySetEventResponse typedResponse = new SecurityDelaySetEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.securityDelay = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getSettlementPeriodSetEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(SETTLEMENTPERIODSET_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + SettlementPeriodSetEventResponse typedResponse = new SettlementPeriodSetEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.settlementPeriod = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static PaymentReceiverSetEventResponse getPaymentReceiverSetEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(PAYMENTRECEIVERSET_EVENT, log); + PaymentReceiverSetEventResponse typedResponse = new PaymentReceiverSetEventResponse(); + typedResponse.log = log; + typedResponse.paymentReceiver = (String) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + + public static List getSettlementProcessedEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(SETTLEMENTPROCESSED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + SettlementProcessedEventResponse typedResponse = new SettlementProcessedEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.card = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(1).getValue(); + typedResponse.settlementId = (BigInteger) eventValues.getIndexedValues().get(2).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.fee = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getVerifiedBalanceIncreasedForEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(VERIFIEDBALANCEINCREASEDFOR_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + VerifiedBalanceIncreasedForEventResponse typedResponse = new VerifiedBalanceIncreasedForEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.paymentAccount = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.increase = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getVerifiedBalanceSetForEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(VERIFIEDBALANCESETFOR_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + VerifiedBalanceSetForEventResponse typedResponse = new VerifiedBalanceSetForEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.paymentAccount = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.verifiedBalance = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + @Deprecated + public static TangemBridgeProcessor load(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) { + return new TangemBridgeProcessor(contractAddress, web3j, credentials, gasPrice, gasLimit); + } + + @Deprecated + public static TangemBridgeProcessor load(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) { + return new TangemBridgeProcessor(contractAddress, web3j, transactionManager, gasPrice, gasLimit); + } + + public static TangemBridgeProcessor load(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) { + return new TangemBridgeProcessor(contractAddress, web3j, credentials, contractGasProvider); + } + + public static TangemBridgeProcessor load(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) { + return new TangemBridgeProcessor(contractAddress, web3j, transactionManager, contractGasProvider); + } + + public static RefundAccountSetEventResponse getRefundAccountSetEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(REFUNDACCOUNTSET_EVENT, log); + RefundAccountSetEventResponse typedResponse = new RefundAccountSetEventResponse(); + typedResponse.log = log; + typedResponse.refundAccount = (String) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + + public static RemoteCall deploy(Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider, String paymentReceiver_, String refundAccount_, String paymentAccountFactory_, String paymentToken_, BigInteger securityDelay_) { + String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.asList(new Address(160, paymentReceiver_), + new Address(160, refundAccount_), + new Address(160, paymentAccountFactory_), + new Address(160, paymentToken_), + new Uint256(securityDelay_))); + return deployRemoteCall(TangemBridgeProcessor.class, web3j, credentials, contractGasProvider, BINARY, encodedConstructor); + } + + public static RemoteCall deploy(Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider, String paymentReceiver_, String refundAccount_, String paymentAccountFactory_, String paymentToken_, BigInteger securityDelay_) { + String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.asList(new Address(160, paymentReceiver_), + new Address(160, refundAccount_), + new Address(160, paymentAccountFactory_), + new Address(160, paymentToken_), + new Uint256(securityDelay_))); + return deployRemoteCall(TangemBridgeProcessor.class, web3j, transactionManager, contractGasProvider, BINARY, encodedConstructor); + } + + @Deprecated + public static RemoteCall deploy(Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit, String paymentReceiver_, String refundAccount_, String paymentAccountFactory_, String paymentToken_, BigInteger securityDelay_) { + String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.asList(new Address(160, paymentReceiver_), + new Address(160, refundAccount_), + new Address(160, paymentAccountFactory_), + new Address(160, paymentToken_), + new Uint256(securityDelay_))); + return deployRemoteCall(TangemBridgeProcessor.class, web3j, credentials, gasPrice, gasLimit, BINARY, encodedConstructor); + } + + public static RefundProcessedEventResponse getRefundProcessedEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(REFUNDPROCESSED_EVENT, log); + RefundProcessedEventResponse typedResponse = new RefundProcessedEventResponse(); + typedResponse.log = log; + typedResponse.card = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(1).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + + @Deprecated + public static RemoteCall deploy(Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit, String paymentReceiver_, String refundAccount_, String paymentAccountFactory_, String paymentToken_, BigInteger securityDelay_) { + String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.asList(new Address(160, paymentReceiver_), + new Address(160, refundAccount_), + new Address(160, paymentAccountFactory_), + new Address(160, paymentToken_), + new Uint256(securityDelay_))); + return deployRemoteCall(TangemBridgeProcessor.class, web3j, transactionManager, gasPrice, gasLimit, BINARY, encodedConstructor); + } + + public static RoleAdminChangedEventResponse getRoleAdminChangedEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(ROLEADMINCHANGED_EVENT, log); + RoleAdminChangedEventResponse typedResponse = new RoleAdminChangedEventResponse(); + typedResponse.log = log; + typedResponse.role = (byte[]) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.previousAdminRole = (byte[]) eventValues.getIndexedValues().get(1).getValue(); + typedResponse.newAdminRole = (byte[]) eventValues.getIndexedValues().get(2).getValue(); + return typedResponse; + } + + public static RoleGrantedEventResponse getRoleGrantedEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(ROLEGRANTED_EVENT, log); + RoleGrantedEventResponse typedResponse = new RoleGrantedEventResponse(); + typedResponse.log = log; + typedResponse.role = (byte[]) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.account = (String) eventValues.getIndexedValues().get(1).getValue(); + typedResponse.sender = (String) eventValues.getIndexedValues().get(2).getValue(); + return typedResponse; + } + + public static RoleRevokedEventResponse getRoleRevokedEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(ROLEREVOKED_EVENT, log); + RoleRevokedEventResponse typedResponse = new RoleRevokedEventResponse(); + typedResponse.log = log; + typedResponse.role = (byte[]) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.account = (String) eventValues.getIndexedValues().get(1).getValue(); + typedResponse.sender = (String) eventValues.getIndexedValues().get(2).getValue(); + return typedResponse; + } + + public static SaveRefundProcessedEventResponse getSaveRefundProcessedEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(SAVEREFUNDPROCESSED_EVENT, log); + SaveRefundProcessedEventResponse typedResponse = new SaveRefundProcessedEventResponse(); + typedResponse.log = log; + typedResponse.card = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(1).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + + public static SecurityDelaySetEventResponse getSecurityDelaySetEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(SECURITYDELAYSET_EVENT, log); + SecurityDelaySetEventResponse typedResponse = new SecurityDelaySetEventResponse(); + typedResponse.log = log; + typedResponse.securityDelay = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + + public static SettlementPeriodSetEventResponse getSettlementPeriodSetEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(SETTLEMENTPERIODSET_EVENT, log); + SettlementPeriodSetEventResponse typedResponse = new SettlementPeriodSetEventResponse(); + typedResponse.log = log; + typedResponse.settlementPeriod = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + + public static SettlementProcessedEventResponse getSettlementProcessedEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(SETTLEMENTPROCESSED_EVENT, log); + SettlementProcessedEventResponse typedResponse = new SettlementProcessedEventResponse(); + typedResponse.log = log; + typedResponse.card = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(1).getValue(); + typedResponse.settlementId = (BigInteger) eventValues.getIndexedValues().get(2).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.fee = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue(); + return typedResponse; + } + + public static VerifiedBalanceIncreasedForEventResponse getVerifiedBalanceIncreasedForEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(VERIFIEDBALANCEINCREASEDFOR_EVENT, log); + VerifiedBalanceIncreasedForEventResponse typedResponse = new VerifiedBalanceIncreasedForEventResponse(); + typedResponse.log = log; + typedResponse.paymentAccount = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.increase = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + + public static VerifiedBalanceSetForEventResponse getVerifiedBalanceSetForEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(VERIFIEDBALANCESETFOR_EVENT, log); + VerifiedBalanceSetForEventResponse typedResponse = new VerifiedBalanceSetForEventResponse(); + typedResponse.log = log; + typedResponse.paymentAccount = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.verifiedBalance = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + + public Flowable authorizationChangeProcessedEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getAuthorizationChangeProcessedEventFromLog(log)); + } + + public Flowable authorizationChangeProcessedEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(AUTHORIZATIONCHANGEPROCESSED_EVENT)); + return authorizationChangeProcessedEventFlowable(filter); + } + + public Flowable authorizationProcessedEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getAuthorizationProcessedEventFromLog(log)); + } + + public Flowable authorizationProcessedEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(AUTHORIZATIONPROCESSED_EVENT)); + return authorizationProcessedEventFlowable(filter); + } + + public Flowable debtWriteOffProcessedEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getDebtWriteOffProcessedEventFromLog(log)); + } + + public Flowable debtWriteOffProcessedEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(DEBTWRITEOFFPROCESSED_EVENT)); + return debtWriteOffProcessedEventFlowable(filter); + } + + public Flowable fixedFeeSetEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getFixedFeeSetEventFromLog(log)); + } + + public Flowable fixedFeeSetEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(FIXEDFEESET_EVENT)); + return fixedFeeSetEventFlowable(filter); + } + + public Flowable paymentAccountSetEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getPaymentAccountSetEventFromLog(log)); + } + + public Flowable paymentAccountSetEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(PAYMENTACCOUNTSET_EVENT)); + return paymentAccountSetEventFlowable(filter); + } + + public Flowable paymentReceiverSetEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getPaymentReceiverSetEventFromLog(log)); + } + + public Flowable paymentReceiverSetEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(PAYMENTRECEIVERSET_EVENT)); + return paymentReceiverSetEventFlowable(filter); + } + + public Flowable pendingRefundProcessedEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getPendingRefundProcessedEventFromLog(log)); + } + + public Flowable pendingRefundProcessedEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(PENDINGREFUNDPROCESSED_EVENT)); + return pendingRefundProcessedEventFlowable(filter); + } + + public Flowable refundAccountSetEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getRefundAccountSetEventFromLog(log)); + } + + public Flowable refundAccountSetEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(REFUNDACCOUNTSET_EVENT)); + return refundAccountSetEventFlowable(filter); + } + + public Flowable refundProcessedEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getRefundProcessedEventFromLog(log)); + } + + public Flowable refundProcessedEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(REFUNDPROCESSED_EVENT)); + return refundProcessedEventFlowable(filter); + } + + public Flowable roleAdminChangedEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getRoleAdminChangedEventFromLog(log)); + } + + public Flowable roleAdminChangedEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(ROLEADMINCHANGED_EVENT)); + return roleAdminChangedEventFlowable(filter); + } + + public Flowable roleGrantedEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getRoleGrantedEventFromLog(log)); + } + + public Flowable roleGrantedEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(ROLEGRANTED_EVENT)); + return roleGrantedEventFlowable(filter); + } + + public Flowable roleRevokedEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getRoleRevokedEventFromLog(log)); + } + + public Flowable roleRevokedEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(ROLEREVOKED_EVENT)); + return roleRevokedEventFlowable(filter); + } + + public Flowable saveRefundProcessedEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getSaveRefundProcessedEventFromLog(log)); + } + + public Flowable saveRefundProcessedEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(SAVEREFUNDPROCESSED_EVENT)); + return saveRefundProcessedEventFlowable(filter); + } + + public Flowable verifiedBalanceSetForEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getVerifiedBalanceSetForEventFromLog(log)); + } + + public Flowable verifiedBalanceSetForEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(VERIFIEDBALANCESETFOR_EVENT)); + return verifiedBalanceSetForEventFlowable(filter); + } + + public Flowable securityDelaySetEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getSecurityDelaySetEventFromLog(log)); + } + + public Flowable securityDelaySetEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(SECURITYDELAYSET_EVENT)); + return securityDelaySetEventFlowable(filter); + } + + public Flowable settlementPeriodSetEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getSettlementPeriodSetEventFromLog(log)); + } + + public Flowable settlementPeriodSetEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(SETTLEMENTPERIODSET_EVENT)); + return settlementPeriodSetEventFlowable(filter); + } + + public Flowable settlementProcessedEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getSettlementProcessedEventFromLog(log)); + } + + public Flowable settlementProcessedEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(SETTLEMENTPROCESSED_EVENT)); + return settlementProcessedEventFlowable(filter); + } + + public Flowable verifiedBalanceIncreasedForEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getVerifiedBalanceIncreasedForEventFromLog(log)); + } + + public Flowable verifiedBalanceIncreasedForEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(VERIFIEDBALANCEINCREASEDFOR_EVENT)); + return verifiedBalanceIncreasedForEventFlowable(filter); + } + + public RemoteFunctionCall AUTHORIZATION_PROCESSOR_ROLE() { + final Function function = new Function(FUNC_AUTHORIZATION_PROCESSOR_ROLE, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, byte[].class); + } + + public RemoteFunctionCall BALANCE_VERIFIER_ROLE() { + final Function function = new Function(FUNC_BALANCE_VERIFIER_ROLE, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, byte[].class); + } + + public RemoteFunctionCall DEBT_PROCESSOR_ROLE() { + final Function function = new Function(FUNC_DEBT_PROCESSOR_ROLE, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, byte[].class); + } + + public RemoteFunctionCall DEFAULT_ADMIN_ROLE() { + final Function function = new Function(FUNC_DEFAULT_ADMIN_ROLE, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, byte[].class); + } + + public RemoteFunctionCall PAYMENT_ACCOUNT_SETTER() { + final Function function = new Function(FUNC_PAYMENT_ACCOUNT_SETTER, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, byte[].class); + } + + public RemoteFunctionCall PROPERTY_SETTER_ROLE() { + final Function function = new Function(FUNC_PROPERTY_SETTER_ROLE, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, byte[].class); + } + + public RemoteFunctionCall REFUND_PROCESSOR_ROLE() { + final Function function = new Function(FUNC_REFUND_PROCESSOR_ROLE, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, byte[].class); + } + + public RemoteFunctionCall SETTLEMENT_PROCESSOR_ROLE() { + final Function function = new Function(FUNC_SETTLEMENT_PROCESSOR_ROLE, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, byte[].class); + } + + public RemoteFunctionCall fixedFee() { + final Function function = new Function(FUNC_FIXEDFEE, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, BigInteger.class); + } + + public RemoteFunctionCall getPaymentAccount(String card) { + final Function function = new Function(FUNC_GETPAYMENTACCOUNT, + List.of(new Address(160, card)), + List.of(new TypeReference
() { + })); + return executeRemoteCallSingleValueReturn(function, String.class); + } + + public RemoteFunctionCall getRoleAdmin(byte[] role) { + final Function function = new Function(FUNC_GETROLEADMIN, + List.of(new Bytes32(role)), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, byte[].class); + } + + public RemoteFunctionCall getRoleMember(byte[] role, BigInteger index) { + final Function function = new Function(FUNC_GETROLEMEMBER, + Arrays.asList(new Bytes32(role), + new Uint256(index)), + List.of(new TypeReference
() { + })); + return executeRemoteCallSingleValueReturn(function, String.class); + } + + public RemoteFunctionCall getRoleMemberCount(byte[] role) { + final Function function = new Function(FUNC_GETROLEMEMBERCOUNT, + List.of(new Bytes32(role)), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, BigInteger.class); + } + + public RemoteFunctionCall grantRole(byte[] role, String account) { + final Function function = new Function( + FUNC_GRANTROLE, + Arrays.asList(new Bytes32(role), + new Address(160, account)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall hasRole(byte[] role, String account) { + final Function function = new Function(FUNC_HASROLE, + Arrays.asList(new Bytes32(role), + new Address(160, account)), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, Boolean.class); + } + + public RemoteFunctionCall increaseVerifiedBalanceFor(String paymentAccount, BigInteger increase) { + final Function function = new Function( + FUNC_INCREASEVERIFIEDBALANCEFOR, + Arrays.asList(new Address(160, paymentAccount), + new Uint256(increase)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall paymentAccountFactory() { + final Function function = new Function(FUNC_PAYMENTACCOUNTFACTORY, + List.of(), + List.of(new TypeReference
() { + })); + return executeRemoteCallSingleValueReturn(function, String.class); + } + + public RemoteFunctionCall paymentReceiver() { + final Function function = new Function(FUNC_PAYMENTRECEIVER, + List.of(), + List.of(new TypeReference
() { + })); + return executeRemoteCallSingleValueReturn(function, String.class); + } + + public RemoteFunctionCall paymentToken() { + final Function function = new Function(FUNC_PAYMENTTOKEN, + List.of(), + List.of(new TypeReference
() { + })); + return executeRemoteCallSingleValueReturn(function, String.class); + } + + public RemoteFunctionCall processAuthorization(String card, BigInteger transactionId, BigInteger amount, byte[] otp, BigInteger otpCounter, Boolean forced) { + final Function function = new Function( + FUNC_PROCESSAUTHORIZATION, + Arrays.asList(new Address(160, card), + new Uint256(transactionId), + new Uint256(amount), + new org.web3j.abi.datatypes.generated.Bytes16(otp), + new org.web3j.abi.datatypes.generated.Uint16(otpCounter), + new Bool(forced)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall processAuthorizationChange(String card, BigInteger transactionId, BigInteger amount) { + final Function function = new Function( + FUNC_PROCESSAUTHORIZATIONCHANGE, + Arrays.asList(new Address(160, card), + new Uint256(transactionId), + new Uint256(amount)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall processAuthorizationNoOtp(String card, BigInteger transactionId, BigInteger amount, Boolean forced) { + final Function function = new Function( + FUNC_PROCESSAUTHORIZATIONNOOTP, + Arrays.asList(new Address(160, card), + new Uint256(transactionId), + new Uint256(amount), + new Bool(forced)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall processPendingRefund(String card, BigInteger transactionId) { + final Function function = new Function( + FUNC_PROCESSPENDINGREFUND, + Arrays.asList(new Address(160, card), + new Uint256(transactionId)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall processRefund(String card, BigInteger transactionId, BigInteger refundAmount) { + final Function function = new Function( + FUNC_PROCESSREFUND, + Arrays.asList(new Address(160, card), + new Uint256(transactionId), + new Uint256(refundAmount)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall processRefundCallback() { + final Function function = new Function( + FUNC_PROCESSREFUNDCALLBACK, + List.of(), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall processSettlement(String card, BigInteger transactionId, BigInteger settlementId, BigInteger amount) { + final Function function = new Function( + FUNC_PROCESSSETTLEMENT, + Arrays.asList(new Address(160, card), + new Uint256(transactionId), + new Uint256(settlementId), + new Uint256(amount)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall refundAccount() { + final Function function = new Function(FUNC_REFUNDACCOUNT, + List.of(), + List.of(new TypeReference
() { + })); + return executeRemoteCallSingleValueReturn(function, String.class); + } + + public RemoteFunctionCall renounceRole(byte[] role, String callerConfirmation) { + final Function function = new Function( + FUNC_RENOUNCEROLE, + Arrays.asList(new Bytes32(role), + new Address(160, callerConfirmation)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall revokeRole(byte[] role, String account) { + final Function function = new Function( + FUNC_REVOKEROLE, + Arrays.asList(new Bytes32(role), + new Address(160, account)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall savePendingRefund(String card, BigInteger transactionId, BigInteger amount) { + final Function function = new Function( + FUNC_SAVEPENDINGREFUND, + Arrays.asList(new Address(160, card), + new Uint256(transactionId), + new Uint256(amount)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall securityDelay() { + final Function function = new Function(FUNC_SECURITYDELAY, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, BigInteger.class); + } + + public RemoteFunctionCall setFixedFee(BigInteger fixedFee_) { + final Function function = new Function( + FUNC_SETFIXEDFEE, + List.of(new Uint256(fixedFee_)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall setPaymentAccount(String card, String paymentAccount) { + final Function function = new Function( + FUNC_SETPAYMENTACCOUNT, + Arrays.asList(new Address(160, card), + new Address(160, paymentAccount)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall setPaymentReceiver(String paymentReceiver_) { + final Function function = new Function( + FUNC_SETPAYMENTRECEIVER, + List.of(new Address(160, paymentReceiver_)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall setRefundAccount(String refundAccount_) { + final Function function = new Function( + FUNC_SETREFUNDACCOUNT, + List.of(new Address(160, refundAccount_)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall setSecurityDelay(BigInteger securityDelay_) { + final Function function = new Function( + FUNC_SETSECURITYDELAY, + List.of(new Uint256(securityDelay_)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall setSettlementPeriod(BigInteger settlementPeriod_) { + final Function function = new Function( + FUNC_SETSETTLEMENTPERIOD, + List.of(new Uint256(settlementPeriod_)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall setVerifiedBalanceFor(String paymentAccount, BigInteger verifiedBalance) { + final Function function = new Function( + FUNC_SETVERIFIEDBALANCEFOR, + Arrays.asList(new Address(160, paymentAccount), + new Uint256(verifiedBalance)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall settlementPeriod() { + final Function function = new Function(FUNC_SETTLEMENTPERIOD, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, BigInteger.class); + } + + public RemoteFunctionCall supportsInterface(byte[] interfaceId) { + final Function function = new Function(FUNC_SUPPORTSINTERFACE, + List.of(new Bytes4(interfaceId)), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, Boolean.class); + } + + public RemoteFunctionCall writeOffDebt(String card, BigInteger amount) { + final Function function = new Function( + FUNC_WRITEOFFDEBT, + Arrays.asList(new Address(160, card), + new Uint256(amount)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public static class AuthorizationChangeProcessedEventResponse extends BaseEventResponse { + public String card; + + public BigInteger transactionId; + + public BigInteger amount; + + public BigInteger fee; + } + + public static class AuthorizationProcessedEventResponse extends BaseEventResponse { + public String card; + + public BigInteger transactionId; + + public BigInteger amount; + + public BigInteger fee; + } + + public static class DebtWriteOffProcessedEventResponse extends BaseEventResponse { + public String card; + + public BigInteger amount; + } + + public static class FixedFeeSetEventResponse extends BaseEventResponse { + public BigInteger fixedFee; + } + + public static class PaymentAccountSetEventResponse extends BaseEventResponse { + public String card; + + public String paymentAccount; + } + + public static class PaymentReceiverSetEventResponse extends BaseEventResponse { + public String paymentReceiver; + } + + public static class PendingRefundProcessedEventResponse extends BaseEventResponse { + public String card; + + public BigInteger transactionId; + + public BigInteger amount; + } + + public static class RefundAccountSetEventResponse extends BaseEventResponse { + public String refundAccount; + } + + public static class RefundProcessedEventResponse extends BaseEventResponse { + public String card; + + public BigInteger transactionId; + + public BigInteger amount; + } + + public static class RoleAdminChangedEventResponse extends BaseEventResponse { + public byte[] role; + + public byte[] previousAdminRole; + + public byte[] newAdminRole; + } + + public static class RoleGrantedEventResponse extends BaseEventResponse { + public byte[] role; + + public String account; + + public String sender; + } + + public static class RoleRevokedEventResponse extends BaseEventResponse { + public byte[] role; + + public String account; + + public String sender; + } + + public static class SaveRefundProcessedEventResponse extends BaseEventResponse { + public String card; + + public BigInteger transactionId; + + public BigInteger amount; + } + + public static class SecurityDelaySetEventResponse extends BaseEventResponse { + public BigInteger securityDelay; + } + + public static class SettlementPeriodSetEventResponse extends BaseEventResponse { + public BigInteger settlementPeriod; + } + + public static class SettlementProcessedEventResponse extends BaseEventResponse { + public String card; + + public BigInteger transactionId; + + public BigInteger settlementId; + + public BigInteger amount; + + public BigInteger fee; + } + + public static class VerifiedBalanceIncreasedForEventResponse extends BaseEventResponse { + public String paymentAccount; + + public BigInteger increase; + } + + public static class VerifiedBalanceSetForEventResponse extends BaseEventResponse { + public String paymentAccount; + + public BigInteger verifiedBalance; + } +} diff --git a/libs/visa/src/main/java/com/tangem/lib/visa/TangemPaymentAccount.java b/libs/visa/src/main/java/com/tangem/lib/visa/TangemPaymentAccount.java new file mode 100644 index 0000000000..943509e28e --- /dev/null +++ b/libs/visa/src/main/java/com/tangem/lib/visa/TangemPaymentAccount.java @@ -0,0 +1,1781 @@ +package com.tangem.lib.visa; + +import org.web3j.abi.EventEncoder; +import org.web3j.abi.FunctionEncoder; +import org.web3j.abi.TypeReference; +import org.web3j.abi.datatypes.*; +import org.web3j.abi.datatypes.generated.*; +import org.web3j.crypto.Credentials; +import org.web3j.protocol.Web3j; +import org.web3j.protocol.core.DefaultBlockParameter; +import org.web3j.protocol.core.RemoteCall; +import org.web3j.protocol.core.RemoteFunctionCall; +import org.web3j.protocol.core.methods.request.EthFilter; +import org.web3j.protocol.core.methods.response.BaseEventResponse; +import org.web3j.protocol.core.methods.response.Log; +import org.web3j.protocol.core.methods.response.TransactionReceipt; +import org.web3j.tuples.generated.Tuple3; +import org.web3j.tx.Contract; +import org.web3j.tx.TransactionManager; +import org.web3j.tx.gas.ContractGasProvider; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Callable; + +import io.reactivex.Flowable; + +/** + *

Auto generated code. + *

Do not modify! + *

Please use the web3j command line tools, + * or the org.web3j.codegen.SolidityFunctionWrapperGenerator in the + * codegen module to update. + * + *

Generated with web3j version 1.5.0. + */ +@SuppressWarnings("rawtypes") +class TangemPaymentAccount extends Contract { + public static final String BINARY = "60c06040523060a05234801562000014575f80fd5b5060405162004c6938038062004c69833981016040819052620000379162000109565b6001600160a01b0381166080526200004e62000055565b5062000138565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff1615620000a65760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b0390811614620001065780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b5f602082840312156200011a575f80fd5b81516001600160a01b038116811462000131575f80fd5b9392505050565b60805160a051614af3620001765f395f8181612b9501528181612bbe0152612e1b01525f8181610439015281816104bb01526125500152614af35ff3fe60806040526004361061027f575f3560e01c80638c3b2a9b11610155578063ce1b1d43116100be578063e37b815411610078578063e37b8154146108c2578063e3dbffd51461092e578063e4d7ddb91461094d578063e6e268f41461096c578063f89db45714610980578063f92ad2191461099f575f80fd5b8063ce1b1d4314610831578063d47ae89c14610850578063dbed8faa14610864578063dcab7e5014610883578063df7727b4146102aa578063e1a8eafd146108a3575f80fd5b8063b4d02d0d1161010f578063b4d02d0d14610776578063b9603bdf14610795578063bb542ef0146107b4578063be69191d146107d3578063c45a0155146107e7578063c81656c814610806575f80fd5b80638c3b2a9b146106125780638cf351e7146106315780639335dcb714610650578063a075b7c71461066f578063a789457d1461068e578063ad3cb1cc14610739575f80fd5b8063456575e5116101f75780636b4943a7116101b15780636b4943a7146104985780637da0a877146104ad578063806679e4146104df578063860aefcf146104fe57806386f15d09146105df5780638bbe11af146105f3575f80fd5b8063456575e5146103c357806346236eb9146103e25780634f1ef286146103f657806352d1902d14610409578063572b6c051461041d5780636b2a383014610479575f80fd5b806322611280116102485780632261128014610308578063229865d11461031c5780633013ce291461033b57806334284dfb1461037157806335ba9af8146103905780633c1a5012146103a4575f80fd5b806274f3561461028357806301e948ff146102aa5780630f1071be146102be578063139d657c146102d257806319cdeb6f146102f3575b5f80fd5b34801561028e575f80fd5b506102976109be565b6040519081526020015b60405180910390f35b3480156102b5575f80fd5b506102976109d6565b3480156102c9575f80fd5b506102976109e5565b3480156102dd575f80fd5b506102f16102ec3660046140bc565b610a66565b005b3480156102fe575f80fd5b5061029760055481565b348015610313575f80fd5b506102f1610d5b565b348015610327575f80fd5b506102f16103363660046140e5565b610de8565b348015610346575f80fd5b505f54610359906001600160a01b031681565b6040516001600160a01b0390911681526020016102a1565b34801561037c575f80fd5b506102f161038b366004614136565b610e7f565b34801561039b575f80fd5b50610297610f45565b3480156103af575f80fd5b506102f16103be3660046141a1565b610fd4565b3480156103ce575f80fd5b506102f16103dd3660046141bc565b611060565b3480156103ed575f80fd5b50601b54610297565b6102f16104043660046141f0565b611100565b348015610414575f80fd5b5061029761111f565b348015610428575f80fd5b506104696104373660046141a1565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811691161490565b60405190151581526020016102a1565b348015610484575f80fd5b506102f16104933660046140e5565b61113a565b3480156104a3575f80fd5b5061029760065481565b3480156104b8575f80fd5b507f0000000000000000000000000000000000000000000000000000000000000000610359565b3480156104ea575f80fd5b506102f16104f93660046142ae565b611287565b348015610509575f80fd5b506040805160a08082018352600c54825282518084018452600d548152600e546020828101919091528084019190915283518085018552600f5481526010548183015283850152835180820185526011548152606080850191909152601254608080860191909152855193840186526013548452855180870187526014548152601554818501528484015285518087018752601654815260175481850152848701528551928301909552601854825282015260195492810192909252601a546105d0929083565b6040516102a19392919061432c565b3480156105ea575f80fd5b5061046961132a565b3480156105fe575f80fd5b506102f161060d3660046140e5565b611351565b34801561061d575f80fd5b506102f161062c366004614357565b611468565b34801561063c575f80fd5b506102f161064b366004614388565b611583565b34801561065b575f80fd5b50600354610359906001600160a01b031681565b34801561067a575f80fd5b506102f16106893660046143ca565b611710565b348015610699575f80fd5b506040805180820182526007546001600160a01b039081168252825180840184526008546001600160801b0319608082811b8216845261ffff600160801b938490048116602086810191909152808801959095528751808901895260095490961686528751808901909852600a549182901b9092168752919091041684820152810192909252600b5461072a929083565b6040516102a19392919061442a565b348015610744575f80fd5b50610769604051806040016040528060058152602001640352e302e360dc1b81525081565b6040516102a19190614475565b348015610781575f80fd5b506102f16107903660046140e5565b611801565b3480156107a0575f80fd5b506102f16107af3660046140e5565b611901565b3480156107bf575f80fd5b506102f16107ce3660046141a1565b611a37565b3480156107de575f80fd5b50610469611b08565b3480156107f2575f80fd5b50600154610359906001600160a01b031681565b348015610811575f80fd5b506102976108203660046140e5565b5f908152601c602052604090205490565b34801561083c575f80fd5b50600254610359906001600160a01b031681565b34801561085b575f80fd5b50610297611b2d565b34801561086f575f80fd5b506102f161087e3660046140e5565b611bad565b34801561088e575f80fd5b505f8051602061479083398151915254610297565b3480156108ae575f80fd5b506102f16108bd3660046141a1565b611c0c565b3480156108cd575f80fd5b506109096108dc3660046140e5565b60046020525f9081526040902080546001909101546001600160801b03811690600160801b900460ff1683565b604080519384526001600160801b0390921660208401521515908201526060016102a1565b348015610939575f80fd5b506102f16109483660046140e5565b611da8565b348015610958575f80fd5b506102f16109673660046141bc565b611f82565b348015610977575f80fd5b5061029761212a565b34801561098b575f80fd5b506102f161099a3660046140e5565b61213f565b3480156109aa575f80fd5b506102f16109b93660046144a7565b61230d565b5f805f805160206147708339815191525b5492915050565b5f6109e05f61247c565b905090565b5f6109e060025f9054906101000a90046001600160a01b03166001600160a01b0316630f1071be6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a39573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a5d91906144e7565b6283d600612534565b6002546001600160a01b0316610a7a61254d565b6001600160a01b0316146040518060600160405280603381526020016145b96033913990610ac45760405162461bcd60e51b8152600401610abb9190614475565b60405180910390fd5b50610ace8161259e565b5f8381526004602052604081205490818311610b00575081808203828214610afa57610afa86826125f0565b50610b7f565b50805f610b0d600c612634565b80549091505f81851015610b4157610b30858303610b2b855f61264e565b612534565b9050610b3e81610b2b6109d6565b90505b8015610b67575f610b5482878903612534565b948501949050610b6584825f6126b9565b505b838603868514610b7a57610b7a816126e0565b505050505b816006541015604051806060016040528060298152602001614a186029913990610bbc5760405162461bcd60e51b8152600401610abb9190614475565b506006805483900390555f85815260046020818152604080842093845560019390930180546001600160881b0319169055600254835163659bf9d960e11b81529351610c5d946001600160a01b039092169363cb37f3b29383820193909291908290030181865afa158015610c33573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c5791906144fe565b8261273f565b6040805184815260208101839052859187917f3e565e38537fb4064a62ad5c266c37a9798b673d92066b5bbbd2269c70078fd1910160405180910390a35f546040516370a0823160e01b81523060048201527f273f30c859762d267fd40559825ed2fa0f467908c92843dfd0184b9d577dc082916001600160a01b0316906370a0823190602401602060405180830381865afa158015610cff573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d2391906144e7565b600654610d2e6109be565b601b5460408051948552602085019390935291830152606082015260800160405180910390a15050505050565b6003546001600160a01b0316610d6f61254d565b6001600160a01b03161480610da65750610d89600761284a565b546001600160a01b0316610d9b61254d565b6001600160a01b0316145b6040518060600160405280602f8152602001614a66602f913990610ddd5760405162461bcd60e51b8152600401610abb9190614475565b50610de6612864565b565b6003546001600160a01b0316610dfc61254d565b6001600160a01b03161480610e335750610e16600761284a565b546001600160a01b0316610e2861254d565b6001600160a01b0316145b6040518060600160405280602f8152602001614a66602f913990610e6a5760405162461bcd60e51b8152600401610abb9190614475565b50610e7c81610e77611b2d565b6128b2565b50565b6002546001600160a01b0316610e9361254d565b6001600160a01b0316146040518060600160405280603381526020016145b96033913990610ed45760405162461bcd60e51b8152600401610abb9190614475565b506001610ee2848484612963565b610eee868684846129bf565b604080518681526001600160801b03198616602082015261ffff851681830152905187917faab1db26b8ce0ed14d67fd3d8eab92617f4caf4134148122ad6f2a4e20068df7919081900360600190a2505050505050565b5f80546040516370a0823160e01b815230600482015282916001600160a01b0316906370a0823190602401602060405180830381865afa158015610f8b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610faf91906144e7565b9050600654811015610fc1575f610fce565b600654610fce908261452d565b91505090565b6003546001600160a01b0316610fe861254d565b6001600160a01b0316148061101f5750611002600761284a565b546001600160a01b031661101461254d565b6001600160a01b0316145b6040518060600160405280602f8152602001614a66602f9139906110565760405162461bcd60e51b8152600401610abb9190614475565b50610e7c81612a24565b6002546001600160a01b031661107461254d565b6001600160a01b0316146040518060600160405280603381526020016145b960339139906110b55760405162461bcd60e51b8152600401610abb9190614475565b506110c2601b8383612b0f565b817fea92e836c1b642a333bb58b44d5f13c5a066c5b0f35b8d6da5032af605c9e031826040516110f491815260200190565b60405180910390a25050565b611108612b8a565b61111182612c2e565b61111b8282612d54565b5050565b5f611128612e10565b505f8051602061484383398151915290565b6002546001600160a01b031661114e61254d565b6001600160a01b0316146040518060600160405280603381526020016145b9603391399061118f5760405162461bcd60e51b8152600401610abb9190614475565b505f8160055461119f9190614540565b5f546040516370a0823160e01b81523060048201529192506001600160a01b0316906370a0823190602401602060405180830381865afa1580156111e5573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061120991906144e7565b81111560405180608001604052806048815260200161468d60489139906112435760405162461bcd60e51b8152600401610abb9190614475565b50600581905560408051838152602081018390527fd6004694c2f543b69302481de26731f63c1d41062795b5366d8ca20fd3e3d6d391015b60405180910390a15050565b6002546001600160a01b031661129b61254d565b6001600160a01b0316146040518060600160405280603381526020016145b960339139906112dc5760405162461bcd60e51b8152600401610abb9190614475565b505f6112ea848484846129bf565b837f5e1266400272ee17509f7d7edb190c0fa83572f90677ee6a3bbfac22bf1f679d8460405161131c91815260200190565b60405180910390a250505050565b5f80516020614790833981519152545f905f80516020614730833981519152901515610fce565b5f818152600460205260409020600181015490546001600160801b03909116906113796109e5565b6113839083614540565b42116040518060600160405280602b81526020016145ec602b9139906113bc5760405162461bcd60e51b8152600401610abb9190614475565b50806006541015604051806060016040528060298152602001614a1860299139906113fa5760405162461bcd60e51b8152600401610abb9190614475565b506006805482900390555f8381526004602052604080822091825560019190910180546001600160881b03191690555183907f056bed1bad5d90aa1df671c8d28b1bbb82ff67fd545f804e63cd261140b11f7b9061145b9084815260200190565b60405180910390a2505050565b6003546001600160a01b031661147c61254d565b6001600160a01b031614806114b35750611496600761284a565b546001600160a01b03166114a861254d565b6001600160a01b0316145b6040518060600160405280602f8152602001614a66602f9139906114ea5760405162461bcd60e51b8152600401610abb9190614475565b506040805180820182526009546001600160a01b0316815281518083019092526001600160801b03198416825261ffff831660208381019190915281019190915261154090611537611b2d565b60079190612e59565b604080516001600160801b03198416815261ffff831660208201527fc50d2cc66f62a75104edeabfbcedb2a37ebd1cb8ce0b873ec3afa504e8bb1480910161127b565b6003546001600160a01b031661159761254d565b6001600160a01b031614806115ce57506115b1600761284a565b546001600160a01b03166115c361254d565b6001600160a01b0316145b6040518060600160405280602f8152602001614a66602f9139906116055760405162461bcd60e51b8152600401610abb9190614475565b50600b541561165e576116596040518060400160405280856001600160a01b031681526020016040518060400160405280866001600160801b03191681526020018561ffff16815250815250611537611b2d565b6116b1565b6116b16040518060400160405280856001600160a01b031681526020016040518060400160405280866001600160801b03191681526020018561ffff16815250815250600761300090919063ffffffff16565b604080516001600160a01b03851681526001600160801b03198416602082015261ffff8316918101919091527fae81d31085749debb22edb9a09f263e50cf6047cfc57ec741c07ce62b1121b9a906060015b60405180910390a1505050565b6003546001600160a01b031661172461254d565b6001600160a01b0316148061175b575061173e600761284a565b546001600160a01b031661175061254d565b6001600160a01b0316145b6040518060600160405280602f8152602001614a66602f9139906117925760405162461bcd60e51b8152600401610abb9190614475565b506117b36117a2858585856130a1565b6117aa611b2d565b600c9190613102565b6040805185815260208101859052908101839052606081018290527ffc3ccf3ca5a38fad8786ed39386de19cb574b06205422992c583e4753aa6268f9060800160405180910390a150505050565b6003546001600160a01b031661181561254d565b6001600160a01b0316148061184c575061182f600761284a565b546001600160a01b031661184161254d565b6001600160a01b0316145b6040518060600160405280602f8152602001614a66602f9139906118835760405162461bcd60e51b8152600401610abb9190614475565b50610e7c8160025f9054906101000a90046001600160a01b03166001600160a01b031663cb37f3b26040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118d8573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118fc91906144fe565b6131f0565b6002546001600160a01b031661191561254d565b6001600160a01b0316146040518060600160405280603381526020016145b960339139906119565760405162461bcd60e51b8152600401610abb9190614475565b505f546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa15801561199c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119c091906144e7565b81111560405180608001604052806048815260200161468d60489139906119fa5760405162461bcd60e51b8152600401610abb9190614475565b5060058190556040518181527f321b800465e47508430424e6c089344256e17f9a5b0b43c751dffbdfd3930263906020015b60405180910390a150565b6003546001600160a01b0316611a4b61254d565b6001600160a01b03161480611a825750611a65600761284a565b546001600160a01b0316611a7761254d565b6001600160a01b0316145b6040518060600160405280602f8152602001614a66602f913990611ab95760405162461bcd60e51b8152600401610abb9190614475565b50600380546001600160a01b0319166001600160a01b0383169081179091556040519081527f0435c4db9db56b8cdd5d58a213f47d6fed6c2b35c81bbd6baf87547848f4c88b90602001611a2c565b5f5f80516020614730833981519152610fce5f805160206147908339815191526133a4565b5f6109e060025f9054906101000a90046001600160a01b03166001600160a01b031663d47ae89c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b81573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ba591906144e7565b610e10612534565b6002546001600160a01b0316611bc161254d565b6001600160a01b0316146040518060600160405280603381526020016145b96033913990611c025760405162461bcd60e51b8152600401610abb9190614475565b50610e7c8161340b565b6003546001600160a01b0316611c2061254d565b6001600160a01b03161480611c575750611c3a600761284a565b546001600160a01b0316611c4c61254d565b6001600160a01b0316145b6040518060600160405280602f8152602001614a66602f913990611c8e5760405162461bcd60e51b8152600401610abb9190614475565b50600254604080516060810190915260238082526001600160a01b039092161591614641602083013990611cd55760405162461bcd60e51b8152600401610abb9190614475565b50600280546001600160a01b0319166001600160a01b03831690811790915560408051633013ce2960e01b81529051633013ce29916004808201926020929091908290030181865afa158015611d2d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d5191906144fe565b5f80546001600160a01b0319166001600160a01b0392831690811790915560408051928416835260208301919091527fb84a8bf26086331c9421c7ce6650da3b0ef748bd335c3f00ada56f481e4321939101611a2c565b6002546001600160a01b0316611dbc61254d565b6001600160a01b0316146040518060600160405280603381526020016145b96033913990611dfd5760405162461bcd60e51b8152600401610abb9190614475565b505f80546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015611e44573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611e6891906144e7565b905060025f9054906101000a90046001600160a01b03166001600160a01b0316632cc326416040518163ffffffff1660e01b81526004015f604051808303815f87803b158015611eb6575f80fd5b505af1158015611ec8573d5f803e3d5ffd5b50505f80546040516370a0823160e01b81523060048201529193508492506001600160a01b0316906370a0823190602401602060405180830381865afa158015611f14573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f3891906144e7565b611f42919061452d565b9050611f50601b84836134e9565b827f2c6a6ee02f179bb5f13a4b7fdc9ccd1dd37e524b48ff0957a931348cc9ad75c08260405161145b91815260200190565b6002546001600160a01b0316611f9661254d565b6001600160a01b0316146040518060600160405280603381526020016145b96033913990611fd75760405162461bcd60e51b8152600401610abb9190614475565b505f60045f8481526020019081526020015f205f015490505f811160405180606001604052806023815260200161470d60239139906120295760405162461bcd60e51b8152600401610abb9190614475565b508082106040518060600160405280603b8152602001614929603b9139906120645760405162461bcd60e51b8152600401610abb9190614475565b5081810361207284826125f0565b806006541015604051806060016040528060298152602001614a1860299139906120af5760405162461bcd60e51b8152600401610abb9190614475565b506006805482900390555f8390036120e7575f84815260046020526040812090815560010180546001600160881b03191690556120f8565b5f8481526004602052604090208390555b837f8274e5392b3ab6bba68ee0567f4ede55e361239b8d0122fa5d3b5dc36497b91d8460405161131c91815260200190565b5f805f805160206147308339815191526109cf565b6002546001600160a01b031661215361254d565b6001600160a01b0316146040518060600160405280603381526020016145b960339139906121945760405162461bcd60e51b8152600401610abb9190614475565b505f80546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa1580156121db573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121ff91906144e7565b905060025f9054906101000a90046001600160a01b03166001600160a01b0316632cc326416040518163ffffffff1660e01b81526004015f604051808303815f87803b15801561224d575f80fd5b505af115801561225f573d5f803e3d5ffd5b50505f80546040516370a0823160e01b81523060048201529193508492506001600160a01b0316906370a0823190602401602060405180830381865afa1580156122ab573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906122cf91906144e7565b6122d9919061452d565b9050827f2c6a6ee02f179bb5f13a4b7fdc9ccd1dd37e524b48ff0957a931348cc9ad75c08260405161145b91815260200190565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff165f811580156123525750825b90505f8267ffffffffffffffff16600114801561236e5750303b155b90508115801561237c575080155b1561239a5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff1916600117855583156123c457845460ff60401b1916600160401b1785555b6123cc6135ce565b6123d46135ce565b6123dc6135ce565b6123e461254d565b600180546001600160a01b03199081166001600160a01b039384161790915560038054909116918c1691909117905561242a6124228a8a8a8a6130a1565b600c906135d6565b831561247057845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050565b5f80546040516370a0823160e01b815230600482015282916124f4916001600160a01b03909116906370a0823190602401602060405180830381865afa1580156124c8573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906124ec91906144e7565b600554612534565b600654909150836125145761250761212a565b6125119082614540565b90505b80821015612522575f61252c565b61252c818361452d565b949350505050565b5f8183106125425781612544565b825b90505b92915050565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633148015612587575060143610155b15612599575060131936013560601c90565b503390565b60408051808201909152601b81527f353130307c436f6d6d6f6e3a20616d6f756e74206973207a65726f000000000060208201528161111b5760405162461bcd60e51b8152600401610abb9190614475565b5f828152600460205260409020600101546001600160801b03811690600160801b900460ff1661262e838383612626600c612634565b929190613662565b50505050565b5f4282600e015411156126475781612547565b5060070190565b5f61265b836005016133a4565b1561268c57811561267157506001820154612547565b600183015460038401546126859190612534565b9050612547565b811561269e5761268583600101613690565b6126856126ad84600101613690565b610b2b85600301613690565b6126c2836136b0565b6126cc83836136e0565b806126db576126db83836136ed565b505050565b5f805160206147708339815191528054829082905f90612701908490614540565b909155505080546040805184815260208101929092527ff3a2c404b3362c4238317c3dad1020c2e9e984ba8df38a4a1ff18d57e92a242a910161127b565b5f80546040516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa158015612785573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127a991906144e7565b905080600554146040518060600160405280603381526020016149b760339139906127e75760405162461bcd60e51b8152600401610abb9190614475565b5060408051808201909152601f81525f8051602061475083398151915260208201528282101561282a5760405162461bcd60e51b8152600401610abb9190614475565b506005805483900390555f546126db906001600160a01b031684846136fa565b5f428260040154111561285d5781612547565b5060020190565b5f5f805160206147308339815191528181555f8051602061479083398151915282905560405190917f8892a543ee6d561b73cc067b2cde5a76c251a4e0b978d92390f1301db5d125ab91a150565b6128ba610f45565b8211156040518060400160405280601f81526020015f80516020614750833981519152815250906128fe5760405162461bcd60e51b8152600401610abb9190614475565b505f805160206147308339815191528281556129275f805160206147908339815191528361374c565b60018101546040805185815260208101929092527ffa4870dc2e2f95d11863e7e765a98e12fe92c3d1d2a703b4cf983f9c4b1052e69101611703565b600b54421015806129715750805b6040518060600160405280603581526020016148cd60359139906129a85760405162461bcd60e51b8152600401610abb9190614475565b506126db83836129b8600761284a565b91906137af565b60045f8581526020019081526020015f205f01545f1460405180606001604052806029815260200161498e6029913990612a0c5760405162461bcd60e51b8152600401610abb9190614475565b50612a188383836137ca565b61262e8484848461380b565b5f805160206147308339815191528054612a3c610f45565b8111156040518060400160405280601f81526020015f8051602061475083398151915281525090612a805760405162461bcd60e51b8152600401610abb9190614475565b50612a89611b08565b6040518060600160405280603881526020016146d56038913990612ac05760405162461bcd60e51b8152600401610abb9190614475565b505f8083556001830155612ad483826139a6565b826001600160a01b03167fa578b4c05763eb039caef37b2d7b949c22914c3838384c9c73cefb78c4a8ed0a8260405161145b91815260200190565b826001015f8381526020019081526020015f20545f146040518060600160405280602a8152602001614617602a913990612b5c5760405162461bcd60e51b8152600401610abb9190614475565b505f8281526001840160205260408120829055835482918591612b80908490614540565b9091555050505050565b306001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161480612c1057507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316612c045f80516020614843833981519152546001600160a01b031690565b6001600160a01b031614155b15610de65760405163703e46dd60e11b815260040160405180910390fd5b6003546001600160a01b0316612c4261254d565b6001600160a01b03161480612c795750612c5c600761284a565b546001600160a01b0316612c6e61254d565b6001600160a01b0316145b6040518060600160405280602f8152602001614a66602f913990612cb05760405162461bcd60e51b8152600401610abb9190614475565b506001546040516316da1fb760e11b81526001600160a01b03838116600483015290911690632db43f6e90602401602060405180830381865afa158015612cf9573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d1d9190614553565b604051806060016040528060298152602001614a95602991399061111b5760405162461bcd60e51b8152600401610abb9190614475565b816001600160a01b03166352d1902d6040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015612dae575060408051601f3d908101601f19168201909252612dab918101906144e7565b60015b612dd657604051634c9c8ce360e01b81526001600160a01b0383166004820152602401610abb565b5f805160206148438339815191528114612e0657604051632a87526960e21b815260048101829052602401610abb565b6126db83836139dd565b306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610de65760405163703e46dd60e11b815260040160405180910390fd5b602082015151600384015460801b6001600160801b03199081169116141580612e9c575060208083015101516003840154600160801b900461ffff908116911614155b80612eb95750815160028401546001600160a01b03908116911614155b6040518060600160405280602781526020016149026027913990612ef05760405162461bcd60e51b8152600401610abb9190614475565b506004830154421015612f695781516002840180546001600160a01b039092166001600160a01b03199092169190911790556020808301518051600386018054929093015161ffff16600160801b026001600160901b031990921660809190911c17179055612f5f8142614540565b6004840155505050565b60028301805484546001600160a01b03199081166001600160a01b0380841691909117875560038701805460018901805461ffff600160801b808504821681026001600160901b03199384166001600160801b03871617179093558a51909516959096169490941790955560208088015180519101519092169092029290931660809390931c92909217179055612f5f8142614540565b60048201541515156040518060600160405280602f815260200161458a602f91399061303f5760405162461bcd60e51b8152600401610abb9190614475565b5080516002830180546001600160a01b039092166001600160a01b03199092169190911790556020908101518051600384018054929093015161ffff16600160801b026001600160901b031990921660809190911c1717905542600490910155565b6130a961404a565b506040805160a081018252948552805180820182529384525f6020858101829052808701959095528151808301835293845283850152848101929092528151928301909152600182526060830191909152608082015290565b600e830154421015613166578151600784015560208083015180516008860155810151600985015560408301518051600a8601550151600b840155606082015151600c8401556080820151600d84015561315c8142614540565b600e840155505050565b600783018054845560088401805460018601556009850180546002870155600a860180546003880155600b870180546004890155600c8801805460058a0155600d8901805460068b015588519096556020808901518051909655948501519093556040870151805190925592015190915560608401515190556080830151905561315c8142614540565b5f805160206147708339815191525f6132076109d6565b825460408051808201909152601b81527f353130307c436f6d6d6f6e3a20616d6f756e74206973207a65726f00000000006020820152919250908561325f5760405162461bcd60e51b8152600401610abb9190614475565b5060408051808201909152601881527f353430317c446562743a206e6f206465627420666f756e6400000000000000006020820152816132b25760405162461bcd60e51b8152600401610abb9190614475565b5060408051808201909152601d81527f353430327c446562743a206e6f2066756e647320617661696c61626c650000006020820152826133055760405162461bcd60e51b8152600401610abb9190614475565b50808511156040518060600160405280602581526020016147d560259139906133415760405162461bcd60e51b8152600401610abb9190614475565b505f61334d8387612534565b8454819003855590506133608582613a32565b83546040805183815260208101929092527f75aec94f6ad949cfd9728eb818639f1b0b882089b480409ab73245c06469eef4910160405180910390a1505050505050565b5f6133af8254151590565b6040518060400160405280601d81526020017f353534317c54696d6572733a2074696d6572206e6f7420616374697665000000815250906134035760405162461bcd60e51b8152600401610abb9190614475565b505054421190565b5f80516020614770833981519152805460408051808201909152601b81527f353130307c436f6d6d6f6e3a20616d6f756e74206973207a65726f000000000060208201528361346d5760405162461bcd60e51b8152600401610abb9190614475565b50808311156040518060600160405280602581526020016147d560259139906134a95760405162461bcd60e51b8152600401610abb9190614475565b5081548390038083556040805185815260208101929092527fb940258db5193d85cafbc591d0c29fad88d804ff8b9ab71c8f9545af2bb2c07c9101611703565b5f836001015f8481526020019081526020015f205490505f81116040518060600160405280602581526020016147b0602591399061353a5760405162461bcd60e51b8152600401610abb9190614475565b50808214604051806060016040528060258152602001614a4160259139906135755760405162461bcd60e51b8152600401610abb9190614475565b50835f01548111156040518060600160405280602a8152602001614964602a9139906135b45760405162461bcd60e51b8152600401610abb9190614475565b508354038355505f90815260019091016020526040812055565b610de6613a3c565b600e8201541515156040518060600160405280602f815260200161458a602f9139906136155760405162461bcd60e51b8152600401610abb9190614475565b508051600783015560208082015180516008850155810151600984015560408201518051600a8501550151600b830155606081015151600c83015560800151600d82015542600e90910155565b61366c8483613a85565b1561262e5761367e6001850184613ac0565b8061262e5761262e6003850184613ac0565b805460018201545f9190808210156136a8575f61252c565b900392915050565b6136bc816005016133a4565b15610e7c575f60028201555f60048201556006810154610e7c90600583019061374c565b61111b6001830182613b0f565b61111b6003830182613b0f565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526126db908490613b2b565b60408051808201909152601781527f353534307c54696d6572733a207a65726f2064656c617900000000000000000060208201528161379e5760405162461bcd60e51b8152600401610abb9190614475565b506137a98142614540565b90915550565b82546126db9060018501906001600160a01b03168484613b8c565b601a54421015806137d85750815b156137f3576126db83826137ec600c612634565b9190613d12565b6137ff600c8483613d12565b6126db60138483613d12565b6138148361259e565b5f821561387d575f613826600161247c565b905084811061383757849150613877565b809150857f83067e3478aa8b638f7e34eeecb23875cc3671e615445e892cc22dcd4c8b8df382870360405161386e91815260200190565b60405180910390a25b506138ce565b6138856109d6565b8411156040518060400160405280601f81526020015f80516020614750833981519152815250906138c95760405162461bcd60e51b8152600401610abb9190614475565b508390505b60408051808201909152601f81527f353331337c4163636f756e743a206e6f2066756e647320746f20626c6f636b006020820152816139205760405162461bcd60e51b8152600401610abb9190614475565b50604080516060810182528281526001600160801b0342811660208084019182528615158486019081525f8b815260049092529481209351845590516001909301805494511515600160801b026001600160881b03199095169390921692909217929092179091556006805483929061399a908490614540565b90915550505050505050565b8060055410156139b6575f6139c4565b806005546139c4919061452d565b6005555f5461111b906001600160a01b031683836136fa565b6139e682613d73565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a2805115613a2a576126db8282613dd6565b61111b613e48565b61111b828261273f565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff16610de657604051631afcd79f60e31b815260040160405180910390fd5b5f60058301613a93816133a4565b15613aa1575f915050612547565b600684015481545f91613ab39161452d565b8410159250612547915050565b80826001015410156040518060600160405280602e81526020016149ea602e913990613aff5760405162461bcd60e51b8152600401610abb9190614475565b5060019091018054919091039055565b80826001015f828254613b229190614540565b90915550505050565b5f613b3f6001600160a01b03841683613e67565b905080515f14158015613b63575080806020019051810190613b619190614553565b155b156126db57604051635274afe760e01b81526001600160a01b0384166004820152602401610abb565b83546040805160608101909152602980825261ffff600160801b909304831692841683119190614664602083013990613bd85760405162461bcd60e51b8152600401610abb9190614475565b5082825b8261ffff168161ffff161015613c94576040516bffffffffffffffffffffffff19606088901b1660208201526001600160f01b031960f083901b1660348201526001600160801b03198316603682015260029060460160408051601f1981840301815290829052613c4c9161456e565b602060405180830381855afa158015613c67573d5f803e3d5ffd5b5050506040513d601f19601f82011682018060405250810190613c8a91906144e7565b9150600101613bdc565b5085546040805160608101909152602180825260809290921b6001600160801b03199081169084161491614822602083013990613ce45760405162461bcd60e51b8152600401610abb9190614475565b5050845461ffff909216600160801b026001600160901b031990921660809390931c92909217179092555050565b825f01548211156040518060600160405280603b8152602001614892603b913990613d505760405162461bcd60e51b8152600401610abb9190614475565b50613d5a836136b0565b613d648383613e74565b806126db576126db8383613ec3565b806001600160a01b03163b5f03613da857604051634c9c8ce360e01b81526001600160a01b0382166004820152602401610abb565b5f8051602061484383398151915280546001600160a01b0319166001600160a01b0392909216919091179055565b60605f80846001600160a01b031684604051613df2919061456e565b5f60405180830381855af49150503d805f8114613e2a576040519150601f19603f3d011682016040523d82523d5f602084013e613e2f565b606091505b5091509150613e3f858383613f15565b95945050505050565b3415610de65760405163b398979f60e01b815260040160405180910390fd5b606061254483835f613f74565b613e81600183018261400d565b6040518060600160405280602881526020016147fa6028913990613eb85760405162461bcd60e51b8152600401610abb9190614475565b5061111b82826136e0565b613ed0600383018261400d565b6040518060600160405280602f8152602001614863602f913990613f075760405162461bcd60e51b8152600401610abb9190614475565b5061111b6003830182613b0f565b606082613f2a57613f2582614021565b613f6d565b8151158015613f4157506001600160a01b0384163b155b15613f6a57604051639996b31560e01b81526001600160a01b0385166004820152602401610abb565b50805b9392505050565b606081471015613f995760405163cd78605960e01b8152306004820152602401610abb565b5f80856001600160a01b03168486604051613fb4919061456e565b5f6040518083038185875af1925050503d805f8114613fee576040519150601f19603f3d011682016040523d82523d5f602084013e613ff3565b606091505b5091509150614003868383613f15565b9695505050505050565b5f61401783613690565b9091111592915050565b8051156140315780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6040518060a001604052805f815260200161407660405180604001604052805f81526020015f81525090565b815260200161409660405180604001604052805f81526020015f81525090565b81526020016140b060405180602001604052805f81525090565b81526020015f81525090565b5f805f606084860312156140ce575f80fd5b505081359360208301359350604090920135919050565b5f602082840312156140f5575f80fd5b5035919050565b80356001600160801b031981168114614113575f80fd5b919050565b803561ffff81168114614113575f80fd5b8015158114610e7c575f80fd5b5f805f805f60a0868803121561414a575f80fd5b8535945060208601359350614161604087016140fc565b925061416f60608701614118565b9150608086013561417f81614129565b809150509295509295909350565b6001600160a01b0381168114610e7c575f80fd5b5f602082840312156141b1575f80fd5b8135613f6d8161418d565b5f80604083850312156141cd575f80fd5b50508035926020909101359150565b634e487b7160e01b5f52604160045260245ffd5b5f8060408385031215614201575f80fd5b823561420c8161418d565b9150602083013567ffffffffffffffff80821115614228575f80fd5b818501915085601f83011261423b575f80fd5b81358181111561424d5761424d6141dc565b604051601f8201601f19908116603f01168101908382118183101715614275576142756141dc565b8160405282815288602084870101111561428d575f80fd5b826020860160208301375f6020848301015280955050505050509250929050565b5f805f606084860312156142c0575f80fd5b833592506020840135915060408401356142d981614129565b809150509250925092565b8051825260208082015180518285015290810151604084015250604081015180516060840152602081015160808401525060608101515160a08301526080015160c090910152565b6101e0810161433b82866142e4565b61434860e08301856142e4565b826101c0830152949350505050565b5f8060408385031215614368575f80fd5b614371836140fc565b915061437f60208401614118565b90509250929050565b5f805f6060848603121561439a575f80fd5b83356143a58161418d565b92506143b3602085016140fc565b91506143c160408501614118565b90509250925092565b5f805f80608085870312156143dd575f80fd5b5050823594602084013594506040840135936060013592509050565b80516001600160a01b0316825260209081015180516001600160801b03191682840152015161ffff16604090910152565b60e0810161443882866143f9565b61444560608301856143f9565b8260c0830152949350505050565b5f5b8381101561446d578181015183820152602001614455565b50505f910152565b602081525f8251806020840152614493816040850160208701614453565b601f01601f19169190910160400192915050565b5f805f805f60a086880312156144bb575f80fd5b85356144c68161418d565b97602087013597506040870135966060810135965060800135945092505050565b5f602082840312156144f7575f80fd5b5051919050565b5f6020828403121561450e575f80fd5b8151613f6d8161418d565b634e487b7160e01b5f52601160045260245ffd5b8181038181111561254757612547614519565b8082018082111561254757612547614519565b5f60208284031215614563575f80fd5b8151613f6d81614129565b5f825161457f818460208701614453565b919091019291505056fe353530307c44656c6179656453657474696e67733a2076616c756520616c726561647920696e697469616c697a6564353330327c4163636f756e743a206f6e6c792070726f636573736f722063616e2063616c6c20746869732066756e6374696f6e353334307c4163636f756e743a20736574746c656d656e7420706572696f64206973206e6f74206f766572353432307c526566756e64733a20726566756e64207265636f726420616c726561647920657869737473353336307c4163636f756e743a2070726f636573736f7220616c726561647920736574353531307c4f6e6554696d6550617373776f72643a20696e76616c6964204f545020636f756e746572353335307c4163636f756e743a20726573756c74696e672076657269666965642062616c616e6365206e6f742065717569616c20746f2062616c616e636520616e64206e6f742030353431307c44656c617965645769746864726177616c733a207769746864726177616c2074696d656c6f636b206e6f742065787072696564353332307c4163636f756e743a207472616e73616374696f6e206e6f7420666f756e6476d8021c4b979e99674b80f4e0cc10154af0e5f32df05eb51b29a464e2435500353130317c436f6d6d6f6e3a20696e73756666696369656e742066756e6473003486e95df892f06bd37fd38c44e2fee4c4efb6660a66e6eb20ed6d8a167eae0076d8021c4b979e99674b80f4e0cc10154af0e5f32df05eb51b29a464e2435501353432317c526566756e64733a20726566756e64207265636f7264206e6f7420666f756e64353430337c446562743a20616d6f756e7420697320686967686572207468616e2064656274353532317c4163636f756e744c696d6974733a207370656e64206c696d6974206578636565646564353531317c4f6e6554696d6550617373776f72643a20696e76616c6964204f5450360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc353532337c4163636f756e744c696d6974733a206e6f206f7470207370656e64206c696d6974206578636565646564353532307c4163636f756e744c696d6974733a20616d6f756e7420657863656564732073696e676c65207472616e73616374696f6e206c696d6974353331327c4163636f756e743a2063617264206f72204f5450207374617465206368616e676520697320696e2070726f6772657373353530317c44656c6179656453657474696e67733a2076616c756520616c726561647920736574353332317c4163636f756e743a206e657720616d6f756e7420697320657175616c206f7220686967686572207468616e20617574686f72697a6564353432337c526566756e64733a2070656e64696e6720616d6f756e74206578636565647320746f74616c353331307c4163636f756e743a207472616e73616374696f6e20494420616c72656164792075736564353333307c4163636f756e743a2076657269666965642062616c616e6365206e6f7420657175616c20746f2062616c616e6365353533317c5370656e644c696d6974733a20696e73756666696369656e74207370656e7420746f2063616e63656c353330347c4163636f756e743a20696e73756666696369656e7420626c6f636b656420616d6f756e74353432327c526566756e64733a20696e76616c696420616d6f756e74207265636569766564353330317c4163636f756e743a206f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f6e353337307c4163636f756e743a20756e617574686f72697a656420696d706c656d656e746174696f6ea264697066735822122012a8bf0b550c0636d300948851d60c701346e82b5d29b544481d654d6f3ba1f164736f6c63430008160033"; + + public static final String FUNC_UPGRADE_INTERFACE_VERSION = "UPGRADE_INTERFACE_VERSION"; + + public static final String FUNC_AUTHORIZEDTRANSACTIONS = "authorizedTransactions"; + + public static final String FUNC_AVAILABLEFORDEBTPAYMENT = "availableForDebtPayment"; + + public static final String FUNC_AVAILABLEFORPAYMENT = "availableForPayment"; + + public static final String FUNC_AVAILABLEFORWITHDRAWAL = "availableForWithdrawal"; + + public static final String FUNC_BLOCKEDAMOUNT = "blockedAmount"; + + public static final String FUNC_CANCELWITHDRAWAL = "cancelWithdrawal"; + + public static final String FUNC_CARDWITHOTP = "cardWithOtp"; + + public static final String FUNC_DEBTAMOUNT = "debtAmount"; + + public static final String FUNC_FACTORY = "factory"; + + public static final String FUNC_INCREASEVERIFIEDBALANCE = "increaseVerifiedBalance"; + + public static final String FUNC_INITWITHDRAWAL = "initWithdrawal"; + + public static final String FUNC_INITIALIZE = "initialize"; + + public static final String FUNC_ISTRUSTEDFORWARDER = "isTrustedForwarder"; + + public static final String FUNC_ISWITHDRAWALINPROGRESS = "isWithdrawalInProgress"; + + public static final String FUNC_ISWITHDRAWALREADY = "isWithdrawalReady"; + + public static final String FUNC_LIMITS = "limits"; + + public static final String FUNC_OWNERWALLET = "ownerWallet"; + + public static final String FUNC_PAYMENTTOKEN = "paymentToken"; + + public static final String FUNC_PENDINGREFUNDAMOUNT = "pendingRefundAmount"; + + public static final String FUNC_PENDINGREFUNDTOTAL = "pendingRefundTotal"; + + public static final String FUNC_PROCESSAUTHORIZATION = "processAuthorization"; + + public static final String FUNC_PROCESSAUTHORIZATIONCHANGE = "processAuthorizationChange"; + + public static final String FUNC_PROCESSAUTHORIZATIONNOOTP = "processAuthorizationNoOtp"; + + public static final String FUNC_PROCESSDEBT = "processDebt"; + + public static final String FUNC_PROCESSPENDINGREFUNDPAYMENT = "processPendingRefundPayment"; + + public static final String FUNC_PROCESSREFUNDPAYMENT = "processRefundPayment"; + + public static final String FUNC_PROCESSSETTLEMENT = "processSettlement"; + + public static final String FUNC_PROCESSWITHDRAWAL = "processWithdrawal"; + + public static final String FUNC_PROCESSOR = "processor"; + + public static final String FUNC_PROXIABLEUUID = "proxiableUUID"; + + public static final String FUNC_SAVEPENDINGREFUND = "savePendingRefund"; + + public static final String FUNC_SECURITYDELAY = "securityDelay"; + + public static final String FUNC_SETCARD = "setCard"; + + public static final String FUNC_SETLIMITS = "setLimits"; + + public static final String FUNC_SETOTPSTATE = "setOtpState"; + + public static final String FUNC_SETOWNERWALLET = "setOwnerWallet"; + + public static final String FUNC_SETPROCESSOR = "setProcessor"; + + public static final String FUNC_SETVERIFIEDBALANCE = "setVerifiedBalance"; + + public static final String FUNC_SETTLEMENTPERIOD = "settlementPeriod"; + + public static final String FUNC_TRUSTEDFORWARDER = "trustedForwarder"; + + public static final String FUNC_UNBLOCKUNSETTLEDTRANSACTION = "unblockUnsettledTransaction"; + + public static final String FUNC_UPGRADETOANDCALL = "upgradeToAndCall"; + + public static final String FUNC_VERIFIEDBALANCE = "verifiedBalance"; + + public static final String FUNC_WITHDRAWALAMOUNT = "withdrawalAmount"; + + public static final String FUNC_WITHDRAWALREADYTIMESTAMP = "withdrawalReadyTimestamp"; + + public static final String FUNC_WRITEOFFDEBT = "writeOffDebt"; + + public static final Event ACCOUNTSTATEAFTERSETTLEMENT_EVENT = new Event("AccountStateAfterSettlement", + Arrays.asList(new TypeReference() { + }, new TypeReference() { + }, new TypeReference() { + }, new TypeReference() { + })); + + public static final Event CARDSET_EVENT = new Event("CardSet", + Arrays.asList(new TypeReference

() { + }, new TypeReference() { + }, new TypeReference() { + })); + + public static final Event DEBTINCREASED_EVENT = new Event("DebtIncreased", + Arrays.asList(new TypeReference() { + }, new TypeReference() { + })); + + public static final Event DEBTPAID_EVENT = new Event("DebtPaid", + Arrays.asList(new TypeReference() { + }, new TypeReference() { + })); + + public static final Event DEBTWRITTENOFF_EVENT = new Event("DebtWrittenOff", + Arrays.asList(new TypeReference() { + }, new TypeReference() { + })); + + public static final Event INITIALIZED_EVENT = new Event("Initialized", + List.of(new TypeReference() { + })); + + public static final Event INSUFFICIENTFUNDSONFORCEDAUTH_EVENT = new Event("InsufficientFundsOnForcedAuth", + Arrays.asList(new TypeReference(true) { + }, new TypeReference() { + })); + + public static final Event LIMITSSET_EVENT = new Event("LimitsSet", + Arrays.asList(new TypeReference() { + }, new TypeReference() { + }, new TypeReference() { + }, new TypeReference() { + })); + + public static final Event NOOTPTRANSACTIONAUTHORIZED_EVENT = new Event("NoOtpTransactionAuthorized", + Arrays.asList(new TypeReference(true) { + }, new TypeReference() { + })); + + public static final Event OTPSTATESET_EVENT = new Event("OtpStateSet", + Arrays.asList(new TypeReference() { + }, new TypeReference() { + })); + + public static final Event OWNERWALLETSET_EVENT = new Event("OwnerWalletSet", + List.of(new TypeReference
() { + })); + + public static final Event PENDINGREFUNDSAVED_EVENT = new Event("PendingRefundSaved", + Arrays.asList(new TypeReference(true) { + }, new TypeReference() { + })); + + public static final Event PROCESSORSET_EVENT = new Event("ProcessorSet", + Arrays.asList(new TypeReference
() { + }, new TypeReference
() { + })); + + public static final Event REFUNDPAID_EVENT = new Event("RefundPaid", + Arrays.asList(new TypeReference(true) { + }, new TypeReference() { + })); + + public static final Event TRANSACTIONAMOUNTCHANGED_EVENT = new Event("TransactionAmountChanged", + Arrays.asList(new TypeReference(true) { + }, new TypeReference() { + })); + + public static final Event TRANSACTIONAUTHORIZED_EVENT = new Event("TransactionAuthorized", + Arrays.asList(new TypeReference(true) { + }, new TypeReference() { + }, new TypeReference() { + }, new TypeReference() { + })); + + public static final Event TRANSACTIONSETTLED_EVENT = new Event("TransactionSettled", + Arrays.asList(new TypeReference(true) { + }, new TypeReference(true) { + }, new TypeReference() { + }, new TypeReference() { + })); + + public static final Event UNSETTLEDTRANSACTIONUNBLOCKED_EVENT = new Event("UnsettledTransactionUnblocked", + Arrays.asList(new TypeReference(true) { + }, new TypeReference() { + })); + + public static final Event UPGRADED_EVENT = new Event("Upgraded", + List.of(new TypeReference
(true) { + })); + + public static final Event VERIFIEDBALANCEINCREASED_EVENT = new Event("VerifiedBalanceIncreased", + Arrays.asList(new TypeReference() { + }, new TypeReference() { + })); + + public static final Event VERIFIEDBALANCESET_EVENT = new Event("VerifiedBalanceSet", + List.of(new TypeReference() { + })); + + public static final Event WITHDRAWALCANCELED_EVENT = new Event("WithdrawalCanceled", + List.of()); + + public static final Event WITHDRAWALCOMPLETE_EVENT = new Event("WithdrawalComplete", + Arrays.asList(new TypeReference
(true) { + }, new TypeReference() { + })); + + public static final Event WITHDRAWALINITIATED_EVENT = new Event("WithdrawalInitiated", + Arrays.asList(new TypeReference() { + }, new TypeReference() { + })); + + @Deprecated + protected TangemPaymentAccount(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) { + super(BINARY, contractAddress, web3j, credentials, gasPrice, gasLimit); + } + + protected TangemPaymentAccount(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) { + super(BINARY, contractAddress, web3j, credentials, contractGasProvider); + } + + @Deprecated + protected TangemPaymentAccount(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) { + super(BINARY, contractAddress, web3j, transactionManager, gasPrice, gasLimit); + } + + protected TangemPaymentAccount(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) { + super(BINARY, contractAddress, web3j, transactionManager, contractGasProvider); + } + + public static List getAccountStateAfterSettlementEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(ACCOUNTSTATEAFTERSETTLEMENT_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + AccountStateAfterSettlementEventResponse typedResponse = new AccountStateAfterSettlementEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.balance = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.blockedAmount = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue(); + typedResponse.debtTotal = (BigInteger) eventValues.getNonIndexedValues().get(2).getValue(); + typedResponse.pendingRefundTotal = (BigInteger) eventValues.getNonIndexedValues().get(3).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static AccountStateAfterSettlementEventResponse getAccountStateAfterSettlementEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(ACCOUNTSTATEAFTERSETTLEMENT_EVENT, log); + AccountStateAfterSettlementEventResponse typedResponse = new AccountStateAfterSettlementEventResponse(); + typedResponse.log = log; + typedResponse.balance = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.blockedAmount = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue(); + typedResponse.debtTotal = (BigInteger) eventValues.getNonIndexedValues().get(2).getValue(); + typedResponse.pendingRefundTotal = (BigInteger) eventValues.getNonIndexedValues().get(3).getValue(); + return typedResponse; + } + + public static List getCardSetEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(CARDSET_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + CardSetEventResponse typedResponse = new CardSetEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.card = (String) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.otpRoot = (byte[]) eventValues.getNonIndexedValues().get(1).getValue(); + typedResponse.otpCounter = (BigInteger) eventValues.getNonIndexedValues().get(2).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getDebtIncreasedEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(DEBTINCREASED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + DebtIncreasedEventResponse typedResponse = new DebtIncreasedEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.increase = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.debtAmount = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getDebtPaidEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(DEBTPAID_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + DebtPaidEventResponse typedResponse = new DebtPaidEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.paid = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.debtLeft = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static CardSetEventResponse getCardSetEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(CARDSET_EVENT, log); + CardSetEventResponse typedResponse = new CardSetEventResponse(); + typedResponse.log = log; + typedResponse.card = (String) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.otpRoot = (byte[]) eventValues.getNonIndexedValues().get(1).getValue(); + typedResponse.otpCounter = (BigInteger) eventValues.getNonIndexedValues().get(2).getValue(); + return typedResponse; + } + + public static List getDebtWrittenOffEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(DEBTWRITTENOFF_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + DebtWrittenOffEventResponse typedResponse = new DebtWrittenOffEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.writtenOff = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.debtLeft = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getInitializedEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(INITIALIZED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + InitializedEventResponse typedResponse = new InitializedEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.version = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getInsufficientFundsOnForcedAuthEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(INSUFFICIENTFUNDSONFORCEDAUTH_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + InsufficientFundsOnForcedAuthEventResponse typedResponse = new InsufficientFundsOnForcedAuthEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.insufficientAmount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static DebtIncreasedEventResponse getDebtIncreasedEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(DEBTINCREASED_EVENT, log); + DebtIncreasedEventResponse typedResponse = new DebtIncreasedEventResponse(); + typedResponse.log = log; + typedResponse.increase = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.debtAmount = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue(); + return typedResponse; + } + + public static List getLimitsSetEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(LIMITSSET_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + LimitsSetEventResponse typedResponse = new LimitsSetEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.singleTransactionLimit = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.spendLimit = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue(); + typedResponse.noOtpSpendLimit = (BigInteger) eventValues.getNonIndexedValues().get(2).getValue(); + typedResponse.spendLimitsPeriod = (BigInteger) eventValues.getNonIndexedValues().get(3).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getNoOtpTransactionAuthorizedEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(NOOTPTRANSACTIONAUTHORIZED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + NoOtpTransactionAuthorizedEventResponse typedResponse = new NoOtpTransactionAuthorizedEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getOtpStateSetEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(OTPSTATESET_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + OtpStateSetEventResponse typedResponse = new OtpStateSetEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.otpRoot = (byte[]) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.otpCounter = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static DebtPaidEventResponse getDebtPaidEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(DEBTPAID_EVENT, log); + DebtPaidEventResponse typedResponse = new DebtPaidEventResponse(); + typedResponse.log = log; + typedResponse.paid = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.debtLeft = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue(); + return typedResponse; + } + + public static List getOwnerWalletSetEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(OWNERWALLETSET_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + OwnerWalletSetEventResponse typedResponse = new OwnerWalletSetEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.ownerWallet = (String) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getPendingRefundSavedEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(PENDINGREFUNDSAVED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + PendingRefundSavedEventResponse typedResponse = new PendingRefundSavedEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getProcessorSetEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(PROCESSORSET_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + ProcessorSetEventResponse typedResponse = new ProcessorSetEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.processor = (String) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.paymentToken = (String) eventValues.getNonIndexedValues().get(1).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static DebtWrittenOffEventResponse getDebtWrittenOffEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(DEBTWRITTENOFF_EVENT, log); + DebtWrittenOffEventResponse typedResponse = new DebtWrittenOffEventResponse(); + typedResponse.log = log; + typedResponse.writtenOff = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.debtLeft = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue(); + return typedResponse; + } + + public static List getRefundPaidEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(REFUNDPAID_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + RefundPaidEventResponse typedResponse = new RefundPaidEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getTransactionAmountChangedEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(TRANSACTIONAMOUNTCHANGED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + TransactionAmountChangedEventResponse typedResponse = new TransactionAmountChangedEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.newAmount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getTransactionAuthorizedEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(TRANSACTIONAUTHORIZED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + TransactionAuthorizedEventResponse typedResponse = new TransactionAuthorizedEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.otp = (byte[]) eventValues.getNonIndexedValues().get(1).getValue(); + typedResponse.otpCounter = (BigInteger) eventValues.getNonIndexedValues().get(2).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static InitializedEventResponse getInitializedEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(INITIALIZED_EVENT, log); + InitializedEventResponse typedResponse = new InitializedEventResponse(); + typedResponse.log = log; + typedResponse.version = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + + public static List getTransactionSettledEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(TRANSACTIONSETTLED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + TransactionSettledEventResponse typedResponse = new TransactionSettledEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.settlementId = (BigInteger) eventValues.getIndexedValues().get(1).getValue(); + typedResponse.settlementAmount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.paymentAmount = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getUnsettledTransactionUnblockedEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(UNSETTLEDTRANSACTIONUNBLOCKED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + UnsettledTransactionUnblockedEventResponse typedResponse = new UnsettledTransactionUnblockedEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getUpgradedEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(UPGRADED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + UpgradedEventResponse typedResponse = new UpgradedEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.implementation = (String) eventValues.getIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static InsufficientFundsOnForcedAuthEventResponse getInsufficientFundsOnForcedAuthEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(INSUFFICIENTFUNDSONFORCEDAUTH_EVENT, log); + InsufficientFundsOnForcedAuthEventResponse typedResponse = new InsufficientFundsOnForcedAuthEventResponse(); + typedResponse.log = log; + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.insufficientAmount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + + public static List getVerifiedBalanceIncreasedEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(VERIFIEDBALANCEINCREASED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + VerifiedBalanceIncreasedEventResponse typedResponse = new VerifiedBalanceIncreasedEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.increase = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.verifiedBalance = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getVerifiedBalanceSetEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(VERIFIEDBALANCESET_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + VerifiedBalanceSetEventResponse typedResponse = new VerifiedBalanceSetEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.verifiedBalance = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getWithdrawalCanceledEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(WITHDRAWALCANCELED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + WithdrawalCanceledEventResponse typedResponse = new WithdrawalCanceledEventResponse(); + typedResponse.log = eventValues.getLog(); + responses.add(typedResponse); + } + return responses; + } + + public static LimitsSetEventResponse getLimitsSetEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(LIMITSSET_EVENT, log); + LimitsSetEventResponse typedResponse = new LimitsSetEventResponse(); + typedResponse.log = log; + typedResponse.singleTransactionLimit = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.spendLimit = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue(); + typedResponse.noOtpSpendLimit = (BigInteger) eventValues.getNonIndexedValues().get(2).getValue(); + typedResponse.spendLimitsPeriod = (BigInteger) eventValues.getNonIndexedValues().get(3).getValue(); + return typedResponse; + } + + public static List getWithdrawalCompleteEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(WITHDRAWALCOMPLETE_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + WithdrawalCompleteEventResponse typedResponse = new WithdrawalCompleteEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.to = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public static List getWithdrawalInitiatedEvents(TransactionReceipt transactionReceipt) { + List valueList = staticExtractEventParametersWithLog(WITHDRAWALINITIATED_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (EventValuesWithLog eventValues : valueList) { + WithdrawalInitiatedEventResponse typedResponse = new WithdrawalInitiatedEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.readyToWithdrawTimestamp = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue(); + responses.add(typedResponse); + } + return responses; + } + + @Deprecated + public static TangemPaymentAccount load(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) { + return new TangemPaymentAccount(contractAddress, web3j, credentials, gasPrice, gasLimit); + } + + public static NoOtpTransactionAuthorizedEventResponse getNoOtpTransactionAuthorizedEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(NOOTPTRANSACTIONAUTHORIZED_EVENT, log); + NoOtpTransactionAuthorizedEventResponse typedResponse = new NoOtpTransactionAuthorizedEventResponse(); + typedResponse.log = log; + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + + @Deprecated + public static TangemPaymentAccount load(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) { + return new TangemPaymentAccount(contractAddress, web3j, transactionManager, gasPrice, gasLimit); + } + + public static TangemPaymentAccount load(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) { + return new TangemPaymentAccount(contractAddress, web3j, credentials, contractGasProvider); + } + + public static TangemPaymentAccount load(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) { + return new TangemPaymentAccount(contractAddress, web3j, transactionManager, contractGasProvider); + } + + public static OtpStateSetEventResponse getOtpStateSetEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(OTPSTATESET_EVENT, log); + OtpStateSetEventResponse typedResponse = new OtpStateSetEventResponse(); + typedResponse.log = log; + typedResponse.otpRoot = (byte[]) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.otpCounter = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue(); + return typedResponse; + } + + public static RemoteCall deploy(Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider, String trustedForwarder) { + String encodedConstructor = FunctionEncoder.encodeConstructor(List.of(new Address(160, trustedForwarder))); + return deployRemoteCall(TangemPaymentAccount.class, web3j, credentials, contractGasProvider, BINARY, encodedConstructor); + } + + public static RemoteCall deploy(Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider, String trustedForwarder) { + String encodedConstructor = FunctionEncoder.encodeConstructor(List.of(new Address(160, trustedForwarder))); + return deployRemoteCall(TangemPaymentAccount.class, web3j, transactionManager, contractGasProvider, BINARY, encodedConstructor); + } + + @Deprecated + public static RemoteCall deploy(Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit, String trustedForwarder) { + String encodedConstructor = FunctionEncoder.encodeConstructor(List.of(new Address(160, trustedForwarder))); + return deployRemoteCall(TangemPaymentAccount.class, web3j, credentials, gasPrice, gasLimit, BINARY, encodedConstructor); + } + + public static OwnerWalletSetEventResponse getOwnerWalletSetEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(OWNERWALLETSET_EVENT, log); + OwnerWalletSetEventResponse typedResponse = new OwnerWalletSetEventResponse(); + typedResponse.log = log; + typedResponse.ownerWallet = (String) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + + @Deprecated + public static RemoteCall deploy(Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit, String trustedForwarder) { + String encodedConstructor = FunctionEncoder.encodeConstructor(List.of(new Address(160, trustedForwarder))); + return deployRemoteCall(TangemPaymentAccount.class, web3j, transactionManager, gasPrice, gasLimit, BINARY, encodedConstructor); + } + + public static PendingRefundSavedEventResponse getPendingRefundSavedEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(PENDINGREFUNDSAVED_EVENT, log); + PendingRefundSavedEventResponse typedResponse = new PendingRefundSavedEventResponse(); + typedResponse.log = log; + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + + public static ProcessorSetEventResponse getProcessorSetEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(PROCESSORSET_EVENT, log); + ProcessorSetEventResponse typedResponse = new ProcessorSetEventResponse(); + typedResponse.log = log; + typedResponse.processor = (String) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.paymentToken = (String) eventValues.getNonIndexedValues().get(1).getValue(); + return typedResponse; + } + + public static RefundPaidEventResponse getRefundPaidEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(REFUNDPAID_EVENT, log); + RefundPaidEventResponse typedResponse = new RefundPaidEventResponse(); + typedResponse.log = log; + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + + public static TransactionAmountChangedEventResponse getTransactionAmountChangedEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(TRANSACTIONAMOUNTCHANGED_EVENT, log); + TransactionAmountChangedEventResponse typedResponse = new TransactionAmountChangedEventResponse(); + typedResponse.log = log; + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.newAmount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + + public static TransactionAuthorizedEventResponse getTransactionAuthorizedEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(TRANSACTIONAUTHORIZED_EVENT, log); + TransactionAuthorizedEventResponse typedResponse = new TransactionAuthorizedEventResponse(); + typedResponse.log = log; + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.otp = (byte[]) eventValues.getNonIndexedValues().get(1).getValue(); + typedResponse.otpCounter = (BigInteger) eventValues.getNonIndexedValues().get(2).getValue(); + return typedResponse; + } + + public static TransactionSettledEventResponse getTransactionSettledEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(TRANSACTIONSETTLED_EVENT, log); + TransactionSettledEventResponse typedResponse = new TransactionSettledEventResponse(); + typedResponse.log = log; + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.settlementId = (BigInteger) eventValues.getIndexedValues().get(1).getValue(); + typedResponse.settlementAmount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.paymentAmount = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue(); + return typedResponse; + } + + public static UnsettledTransactionUnblockedEventResponse getUnsettledTransactionUnblockedEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(UNSETTLEDTRANSACTIONUNBLOCKED_EVENT, log); + UnsettledTransactionUnblockedEventResponse typedResponse = new UnsettledTransactionUnblockedEventResponse(); + typedResponse.log = log; + typedResponse.transactionId = (BigInteger) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + + public static UpgradedEventResponse getUpgradedEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(UPGRADED_EVENT, log); + UpgradedEventResponse typedResponse = new UpgradedEventResponse(); + typedResponse.log = log; + typedResponse.implementation = (String) eventValues.getIndexedValues().get(0).getValue(); + return typedResponse; + } + + public static VerifiedBalanceIncreasedEventResponse getVerifiedBalanceIncreasedEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(VERIFIEDBALANCEINCREASED_EVENT, log); + VerifiedBalanceIncreasedEventResponse typedResponse = new VerifiedBalanceIncreasedEventResponse(); + typedResponse.log = log; + typedResponse.increase = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.verifiedBalance = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue(); + return typedResponse; + } + + public static VerifiedBalanceSetEventResponse getVerifiedBalanceSetEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(VERIFIEDBALANCESET_EVENT, log); + VerifiedBalanceSetEventResponse typedResponse = new VerifiedBalanceSetEventResponse(); + typedResponse.log = log; + typedResponse.verifiedBalance = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + + public static WithdrawalCanceledEventResponse getWithdrawalCanceledEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(WITHDRAWALCANCELED_EVENT, log); + WithdrawalCanceledEventResponse typedResponse = new WithdrawalCanceledEventResponse(); + typedResponse.log = log; + return typedResponse; + } + + public static WithdrawalCompleteEventResponse getWithdrawalCompleteEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(WITHDRAWALCOMPLETE_EVENT, log); + WithdrawalCompleteEventResponse typedResponse = new WithdrawalCompleteEventResponse(); + typedResponse.log = log; + typedResponse.to = (String) eventValues.getIndexedValues().get(0).getValue(); + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + return typedResponse; + } + + public static WithdrawalInitiatedEventResponse getWithdrawalInitiatedEventFromLog(Log log) { + EventValuesWithLog eventValues = staticExtractEventParametersWithLog(WITHDRAWALINITIATED_EVENT, log); + WithdrawalInitiatedEventResponse typedResponse = new WithdrawalInitiatedEventResponse(); + typedResponse.log = log; + typedResponse.amount = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.readyToWithdrawTimestamp = (BigInteger) eventValues.getNonIndexedValues().get(1).getValue(); + return typedResponse; + } + + public Flowable accountStateAfterSettlementEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getAccountStateAfterSettlementEventFromLog(log)); + } + + public Flowable accountStateAfterSettlementEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(ACCOUNTSTATEAFTERSETTLEMENT_EVENT)); + return accountStateAfterSettlementEventFlowable(filter); + } + + public Flowable cardSetEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getCardSetEventFromLog(log)); + } + + public Flowable cardSetEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(CARDSET_EVENT)); + return cardSetEventFlowable(filter); + } + + public Flowable debtIncreasedEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getDebtIncreasedEventFromLog(log)); + } + + public Flowable debtIncreasedEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(DEBTINCREASED_EVENT)); + return debtIncreasedEventFlowable(filter); + } + + public Flowable debtPaidEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getDebtPaidEventFromLog(log)); + } + + public Flowable debtPaidEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(DEBTPAID_EVENT)); + return debtPaidEventFlowable(filter); + } + + public Flowable debtWrittenOffEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getDebtWrittenOffEventFromLog(log)); + } + + public Flowable debtWrittenOffEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(DEBTWRITTENOFF_EVENT)); + return debtWrittenOffEventFlowable(filter); + } + + public Flowable initializedEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getInitializedEventFromLog(log)); + } + + public Flowable initializedEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(INITIALIZED_EVENT)); + return initializedEventFlowable(filter); + } + + public Flowable insufficientFundsOnForcedAuthEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getInsufficientFundsOnForcedAuthEventFromLog(log)); + } + + public Flowable insufficientFundsOnForcedAuthEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(INSUFFICIENTFUNDSONFORCEDAUTH_EVENT)); + return insufficientFundsOnForcedAuthEventFlowable(filter); + } + + public Flowable limitsSetEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getLimitsSetEventFromLog(log)); + } + + public Flowable limitsSetEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(LIMITSSET_EVENT)); + return limitsSetEventFlowable(filter); + } + + public Flowable noOtpTransactionAuthorizedEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getNoOtpTransactionAuthorizedEventFromLog(log)); + } + + public Flowable noOtpTransactionAuthorizedEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(NOOTPTRANSACTIONAUTHORIZED_EVENT)); + return noOtpTransactionAuthorizedEventFlowable(filter); + } + + public Flowable otpStateSetEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getOtpStateSetEventFromLog(log)); + } + + public Flowable otpStateSetEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(OTPSTATESET_EVENT)); + return otpStateSetEventFlowable(filter); + } + + public Flowable ownerWalletSetEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getOwnerWalletSetEventFromLog(log)); + } + + public Flowable ownerWalletSetEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(OWNERWALLETSET_EVENT)); + return ownerWalletSetEventFlowable(filter); + } + + public Flowable pendingRefundSavedEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getPendingRefundSavedEventFromLog(log)); + } + + public Flowable pendingRefundSavedEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(PENDINGREFUNDSAVED_EVENT)); + return pendingRefundSavedEventFlowable(filter); + } + + public Flowable processorSetEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getProcessorSetEventFromLog(log)); + } + + public Flowable processorSetEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(PROCESSORSET_EVENT)); + return processorSetEventFlowable(filter); + } + + public Flowable refundPaidEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getRefundPaidEventFromLog(log)); + } + + public Flowable refundPaidEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(REFUNDPAID_EVENT)); + return refundPaidEventFlowable(filter); + } + + public Flowable transactionAmountChangedEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getTransactionAmountChangedEventFromLog(log)); + } + + public Flowable transactionAmountChangedEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(TRANSACTIONAMOUNTCHANGED_EVENT)); + return transactionAmountChangedEventFlowable(filter); + } + + public Flowable transactionAuthorizedEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getTransactionAuthorizedEventFromLog(log)); + } + + public Flowable transactionAuthorizedEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(TRANSACTIONAUTHORIZED_EVENT)); + return transactionAuthorizedEventFlowable(filter); + } + + public Flowable transactionSettledEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getTransactionSettledEventFromLog(log)); + } + + public Flowable transactionSettledEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(TRANSACTIONSETTLED_EVENT)); + return transactionSettledEventFlowable(filter); + } + + public Flowable unsettledTransactionUnblockedEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getUnsettledTransactionUnblockedEventFromLog(log)); + } + + public Flowable unsettledTransactionUnblockedEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(UNSETTLEDTRANSACTIONUNBLOCKED_EVENT)); + return unsettledTransactionUnblockedEventFlowable(filter); + } + + public Flowable upgradedEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getUpgradedEventFromLog(log)); + } + + public Flowable upgradedEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(UPGRADED_EVENT)); + return upgradedEventFlowable(filter); + } + + public Flowable withdrawalInitiatedEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getWithdrawalInitiatedEventFromLog(log)); + } + + public Flowable withdrawalInitiatedEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(WITHDRAWALINITIATED_EVENT)); + return withdrawalInitiatedEventFlowable(filter); + } + + public Flowable verifiedBalanceIncreasedEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getVerifiedBalanceIncreasedEventFromLog(log)); + } + + public Flowable verifiedBalanceIncreasedEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(VERIFIEDBALANCEINCREASED_EVENT)); + return verifiedBalanceIncreasedEventFlowable(filter); + } + + public Flowable verifiedBalanceSetEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getVerifiedBalanceSetEventFromLog(log)); + } + + public Flowable verifiedBalanceSetEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(VERIFIEDBALANCESET_EVENT)); + return verifiedBalanceSetEventFlowable(filter); + } + + public Flowable withdrawalCanceledEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getWithdrawalCanceledEventFromLog(log)); + } + + public Flowable withdrawalCanceledEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(WITHDRAWALCANCELED_EVENT)); + return withdrawalCanceledEventFlowable(filter); + } + + public Flowable withdrawalCompleteEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(log -> getWithdrawalCompleteEventFromLog(log)); + } + + public Flowable withdrawalCompleteEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(WITHDRAWALCOMPLETE_EVENT)); + return withdrawalCompleteEventFlowable(filter); + } + + public RemoteFunctionCall UPGRADE_INTERFACE_VERSION() { + final Function function = new Function(FUNC_UPGRADE_INTERFACE_VERSION, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, String.class); + } + + public RemoteFunctionCall> authorizedTransactions(BigInteger param0) { + final Function function = new Function(FUNC_AUTHORIZEDTRANSACTIONS, + List.of(new Uint256(param0)), + Arrays.asList(new TypeReference() { + }, new TypeReference() { + }, new TypeReference() { + })); + return new RemoteFunctionCall>(function, + new Callable>() { + @Override + public Tuple3 call() throws Exception { + List results = executeCallMultipleValueReturn(function); + return new Tuple3( + (BigInteger) results.get(0).getValue(), + (BigInteger) results.get(1).getValue(), + (Boolean) results.get(2).getValue()); + } + }); + } + + public RemoteFunctionCall availableForDebtPayment() { + final Function function = new Function(FUNC_AVAILABLEFORDEBTPAYMENT, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, BigInteger.class); + } + + public RemoteFunctionCall availableForPayment() { + final Function function = new Function(FUNC_AVAILABLEFORPAYMENT, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, BigInteger.class); + } + + public RemoteFunctionCall availableForWithdrawal() { + final Function function = new Function(FUNC_AVAILABLEFORWITHDRAWAL, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, BigInteger.class); + } + + public RemoteFunctionCall blockedAmount() { + final Function function = new Function(FUNC_BLOCKEDAMOUNT, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, BigInteger.class); + } + + public RemoteFunctionCall cancelWithdrawal() { + final Function function = new Function( + FUNC_CANCELWITHDRAWAL, + List.of(), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall> cardWithOtp() { + final Function function = new Function(FUNC_CARDWITHOTP, + List.of(), + Arrays.asList(new TypeReference() { + }, new TypeReference() { + }, new TypeReference() { + })); + return new RemoteFunctionCall>(function, + new Callable>() { + @Override + public Tuple3 call() throws Exception { + List results = executeCallMultipleValueReturn(function); + return new Tuple3( + (CardWithOtp) results.get(0), + (CardWithOtp) results.get(1), + (BigInteger) results.get(2).getValue()); + } + }); + } + + public RemoteFunctionCall debtAmount() { + final Function function = new Function(FUNC_DEBTAMOUNT, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, BigInteger.class); + } + + public RemoteFunctionCall factory() { + final Function function = new Function(FUNC_FACTORY, + List.of(), + List.of(new TypeReference
() { + })); + return executeRemoteCallSingleValueReturn(function, String.class); + } + + public RemoteFunctionCall increaseVerifiedBalance(BigInteger increase) { + final Function function = new Function( + FUNC_INCREASEVERIFIEDBALANCE, + List.of(new Uint256(increase)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall initWithdrawal(BigInteger amount) { + final Function function = new Function( + FUNC_INITWITHDRAWAL, + List.of(new Uint256(amount)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall initialize(String ownerWallet_, BigInteger singleTransactionLimit, BigInteger spendLimit, BigInteger noOtpSpendLimit, BigInteger spendLimitsPeriod) { + final Function function = new Function( + FUNC_INITIALIZE, + Arrays.asList(new Address(160, ownerWallet_), + new Uint256(singleTransactionLimit), + new Uint256(spendLimit), + new Uint256(noOtpSpendLimit), + new Uint256(spendLimitsPeriod)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall isTrustedForwarder(String forwarder) { + final Function function = new Function(FUNC_ISTRUSTEDFORWARDER, + List.of(new Address(160, forwarder)), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, Boolean.class); + } + + public RemoteFunctionCall isWithdrawalInProgress() { + final Function function = new Function(FUNC_ISWITHDRAWALINPROGRESS, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, Boolean.class); + } + + public RemoteFunctionCall isWithdrawalReady() { + final Function function = new Function(FUNC_ISWITHDRAWALREADY, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, Boolean.class); + } + + public RemoteFunctionCall> limits() { + final Function function = new Function(FUNC_LIMITS, + List.of(), + Arrays.asList(new TypeReference() { + }, new TypeReference() { + }, new TypeReference() { + })); + return new RemoteFunctionCall>(function, + new Callable>() { + @Override + public Tuple3 call() throws Exception { + List results = executeCallMultipleValueReturn(function); + return new Tuple3( + (Limits) results.get(0), + (Limits) results.get(1), + (BigInteger) results.get(2).getValue()); + } + }); + } + + public RemoteFunctionCall ownerWallet() { + final Function function = new Function(FUNC_OWNERWALLET, + List.of(), + List.of(new TypeReference
() { + })); + return executeRemoteCallSingleValueReturn(function, String.class); + } + + public RemoteFunctionCall paymentToken() { + final Function function = new Function(FUNC_PAYMENTTOKEN, + List.of(), + List.of(new TypeReference
() { + })); + return executeRemoteCallSingleValueReturn(function, String.class); + } + + public RemoteFunctionCall pendingRefundAmount(BigInteger transactionId) { + final Function function = new Function(FUNC_PENDINGREFUNDAMOUNT, + List.of(new Uint256(transactionId)), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, BigInteger.class); + } + + public RemoteFunctionCall pendingRefundTotal() { + final Function function = new Function(FUNC_PENDINGREFUNDTOTAL, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, BigInteger.class); + } + + public RemoteFunctionCall processAuthorization(BigInteger transactionId, BigInteger amount, byte[] otp, BigInteger otpCounter, Boolean forced) { + final Function function = new Function( + FUNC_PROCESSAUTHORIZATION, + Arrays.asList(new Uint256(transactionId), + new Uint256(amount), + new Bytes16(otp), + new Uint16(otpCounter), + new Bool(forced)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall processAuthorizationChange(BigInteger transactionId, BigInteger newAmount) { + final Function function = new Function( + FUNC_PROCESSAUTHORIZATIONCHANGE, + Arrays.asList(new Uint256(transactionId), + new Uint256(newAmount)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall processAuthorizationNoOtp(BigInteger transactionId, BigInteger amount, Boolean forced) { + final Function function = new Function( + FUNC_PROCESSAUTHORIZATIONNOOTP, + Arrays.asList(new Uint256(transactionId), + new Uint256(amount), + new Bool(forced)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall processDebt(BigInteger amount) { + final Function function = new Function( + FUNC_PROCESSDEBT, + List.of(new Uint256(amount)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall processPendingRefundPayment(BigInteger transactionId) { + final Function function = new Function( + FUNC_PROCESSPENDINGREFUNDPAYMENT, + List.of(new Uint256(transactionId)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall processRefundPayment(BigInteger transactionId) { + final Function function = new Function( + FUNC_PROCESSREFUNDPAYMENT, + List.of(new Uint256(transactionId)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall processSettlement(BigInteger transactionId, BigInteger settlementId, BigInteger amount) { + final Function function = new Function( + FUNC_PROCESSSETTLEMENT, + Arrays.asList(new Uint256(transactionId), + new Uint256(settlementId), + new Uint256(amount)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall processWithdrawal(String to) { + final Function function = new Function( + FUNC_PROCESSWITHDRAWAL, + List.of(new Address(160, to)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall processor() { + final Function function = new Function(FUNC_PROCESSOR, + List.of(), + List.of(new TypeReference
() { + })); + return executeRemoteCallSingleValueReturn(function, String.class); + } + + public RemoteFunctionCall proxiableUUID() { + final Function function = new Function(FUNC_PROXIABLEUUID, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, byte[].class); + } + + public RemoteFunctionCall savePendingRefund(BigInteger transactionId, BigInteger amount) { + final Function function = new Function( + FUNC_SAVEPENDINGREFUND, + Arrays.asList(new Uint256(transactionId), + new Uint256(amount)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall securityDelay() { + final Function function = new Function(FUNC_SECURITYDELAY, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, BigInteger.class); + } + + public RemoteFunctionCall setCard(String card, byte[] otpRoot, BigInteger otpCounter) { + final Function function = new Function( + FUNC_SETCARD, + Arrays.asList(new Address(160, card), + new Bytes16(otpRoot), + new Uint16(otpCounter)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall setLimits(BigInteger singleTransactionLimit, BigInteger spendLimit, BigInteger noOtpSpendLimit, BigInteger spendLimitsPeriod) { + final Function function = new Function( + FUNC_SETLIMITS, + Arrays.asList(new Uint256(singleTransactionLimit), + new Uint256(spendLimit), + new Uint256(noOtpSpendLimit), + new Uint256(spendLimitsPeriod)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall setOtpState(byte[] otp, BigInteger counter) { + final Function function = new Function( + FUNC_SETOTPSTATE, + Arrays.asList(new Bytes16(otp), + new Uint16(counter)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall setOwnerWallet(String ownerWallet_) { + final Function function = new Function( + FUNC_SETOWNERWALLET, + List.of(new Address(160, ownerWallet_)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall setProcessor(String processor_) { + final Function function = new Function( + FUNC_SETPROCESSOR, + List.of(new Address(160, processor_)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall setVerifiedBalance(BigInteger verifiedBalance_) { + final Function function = new Function( + FUNC_SETVERIFIEDBALANCE, + List.of(new Uint256(verifiedBalance_)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall settlementPeriod() { + final Function function = new Function(FUNC_SETTLEMENTPERIOD, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, BigInteger.class); + } + + public RemoteFunctionCall trustedForwarder() { + final Function function = new Function(FUNC_TRUSTEDFORWARDER, + List.of(), + List.of(new TypeReference
() { + })); + return executeRemoteCallSingleValueReturn(function, String.class); + } + + public RemoteFunctionCall unblockUnsettledTransaction(BigInteger transactionId) { + final Function function = new Function( + FUNC_UNBLOCKUNSETTLEDTRANSACTION, + List.of(new Uint256(transactionId)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall upgradeToAndCall(String newImplementation, byte[] data, BigInteger weiValue) { + final Function function = new Function( + FUNC_UPGRADETOANDCALL, + Arrays.asList(new Address(160, newImplementation), + new org.web3j.abi.datatypes.DynamicBytes(data)), + Collections.emptyList()); + return executeRemoteCallTransaction(function, weiValue); + } + + public RemoteFunctionCall verifiedBalance() { + final Function function = new Function(FUNC_VERIFIEDBALANCE, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, BigInteger.class); + } + + public RemoteFunctionCall withdrawalAmount() { + final Function function = new Function(FUNC_WITHDRAWALAMOUNT, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, BigInteger.class); + } + + public RemoteFunctionCall withdrawalReadyTimestamp() { + final Function function = new Function(FUNC_WITHDRAWALREADYTIMESTAMP, + List.of(), + List.of(new TypeReference() { + })); + return executeRemoteCallSingleValueReturn(function, BigInteger.class); + } + + public RemoteFunctionCall writeOffDebt(BigInteger amount) { + final Function function = new Function( + FUNC_WRITEOFFDEBT, + List.of(new Uint256(amount)), + Collections.emptyList()); + return executeRemoteCallTransaction(function); + } + + public static class OtpState extends StaticStruct { + public byte[] otp; + + public BigInteger counter; + + public OtpState(byte[] otp, BigInteger counter) { + super(new Bytes16(otp), + new Uint16(counter)); + this.otp = otp; + this.counter = counter; + } + + public OtpState(Bytes16 otp, Uint16 counter) { + super(otp, counter); + this.otp = otp.getValue(); + this.counter = counter.getValue(); + } + } + + public static class Limit extends StaticStruct { + public BigInteger _00_limit; + + public BigInteger _01_spent; + + public Limit(BigInteger limit, BigInteger spent) { + super(new Uint256(limit), + new Uint256(spent)); + this._00_limit = limit; + this._01_spent = spent; + } + + public Limit(Uint256 limit, Uint256 spent) { + super(limit, spent); + this._00_limit = limit.getValue(); + this._01_spent = spent.getValue(); + } + } + + public static class Timer extends StaticStruct { + public BigInteger expireTimestamp; + + public Timer(BigInteger expireTimestamp) { + super(new Uint256(expireTimestamp)); + this.expireTimestamp = expireTimestamp; + } + + public Timer(Uint256 expireTimestamp) { + super(expireTimestamp); + this.expireTimestamp = expireTimestamp.getValue(); + } + } + + public static class CardWithOtp extends StaticStruct { + public String card; + + public OtpState otpState; + + public CardWithOtp(String card, OtpState otpState) { + super(new Address(160, card), + otpState); + this.card = card; + this.otpState = otpState; + } + + public CardWithOtp(Address card, OtpState otpState) { + super(card, otpState); + this.card = card.getValue(); + this.otpState = otpState; + } + } + + public static class Limits extends StaticStruct { + public BigInteger _00_singleTransactionLimit; + + public Limit _01_spendLimit; + + public Limit _02_noOtpSpendLimit; + + public Timer _03_spendLimitsTimer; + + public BigInteger _04_spendLimitsPeriod; + + public Limits(BigInteger singleTransactionLimit, Limit spendLimit, Limit noOtpSpendLimit, Timer spendLimitsTimer, BigInteger spendLimitsPeriod) { + super(new Uint256(singleTransactionLimit), + spendLimit, + noOtpSpendLimit, + spendLimitsTimer, + new Uint256(spendLimitsPeriod)); + this._00_singleTransactionLimit = singleTransactionLimit; + this._01_spendLimit = spendLimit; + this._02_noOtpSpendLimit = noOtpSpendLimit; + this._03_spendLimitsTimer = spendLimitsTimer; + this._04_spendLimitsPeriod = spendLimitsPeriod; + } + + public Limits(Uint256 singleTransactionLimit, Limit spendLimit, Limit noOtpSpendLimit, Timer spendLimitsTimer, Uint256 spendLimitsPeriod) { + super(singleTransactionLimit, spendLimit, noOtpSpendLimit, spendLimitsTimer, spendLimitsPeriod); + this._00_singleTransactionLimit = singleTransactionLimit.getValue(); + this._01_spendLimit = spendLimit; + this._02_noOtpSpendLimit = noOtpSpendLimit; + this._03_spendLimitsTimer = spendLimitsTimer; + this._04_spendLimitsPeriod = spendLimitsPeriod.getValue(); + } + } + + public static class AccountStateAfterSettlementEventResponse extends BaseEventResponse { + public BigInteger balance; + + public BigInteger blockedAmount; + + public BigInteger debtTotal; + + public BigInteger pendingRefundTotal; + } + + public static class CardSetEventResponse extends BaseEventResponse { + public String card; + + public byte[] otpRoot; + + public BigInteger otpCounter; + } + + public static class DebtIncreasedEventResponse extends BaseEventResponse { + public BigInteger increase; + + public BigInteger debtAmount; + } + + public static class DebtPaidEventResponse extends BaseEventResponse { + public BigInteger paid; + + public BigInteger debtLeft; + } + + public static class DebtWrittenOffEventResponse extends BaseEventResponse { + public BigInteger writtenOff; + + public BigInteger debtLeft; + } + + public static class InitializedEventResponse extends BaseEventResponse { + public BigInteger version; + } + + public static class InsufficientFundsOnForcedAuthEventResponse extends BaseEventResponse { + public BigInteger transactionId; + + public BigInteger insufficientAmount; + } + + public static class LimitsSetEventResponse extends BaseEventResponse { + public BigInteger singleTransactionLimit; + + public BigInteger spendLimit; + + public BigInteger noOtpSpendLimit; + + public BigInteger spendLimitsPeriod; + } + + public static class NoOtpTransactionAuthorizedEventResponse extends BaseEventResponse { + public BigInteger transactionId; + + public BigInteger amount; + } + + public static class OtpStateSetEventResponse extends BaseEventResponse { + public byte[] otpRoot; + + public BigInteger otpCounter; + } + + public static class OwnerWalletSetEventResponse extends BaseEventResponse { + public String ownerWallet; + } + + public static class PendingRefundSavedEventResponse extends BaseEventResponse { + public BigInteger transactionId; + + public BigInteger amount; + } + + public static class ProcessorSetEventResponse extends BaseEventResponse { + public String processor; + + public String paymentToken; + } + + public static class RefundPaidEventResponse extends BaseEventResponse { + public BigInteger transactionId; + + public BigInteger amount; + } + + public static class TransactionAmountChangedEventResponse extends BaseEventResponse { + public BigInteger transactionId; + + public BigInteger newAmount; + } + + public static class TransactionAuthorizedEventResponse extends BaseEventResponse { + public BigInteger transactionId; + + public BigInteger amount; + + public byte[] otp; + + public BigInteger otpCounter; + } + + public static class TransactionSettledEventResponse extends BaseEventResponse { + public BigInteger transactionId; + + public BigInteger settlementId; + + public BigInteger settlementAmount; + + public BigInteger paymentAmount; + } + + public static class UnsettledTransactionUnblockedEventResponse extends BaseEventResponse { + public BigInteger transactionId; + + public BigInteger amount; + } + + public static class UpgradedEventResponse extends BaseEventResponse { + public String implementation; + } + + public static class VerifiedBalanceIncreasedEventResponse extends BaseEventResponse { + public BigInteger increase; + + public BigInteger verifiedBalance; + } + + public static class VerifiedBalanceSetEventResponse extends BaseEventResponse { + public BigInteger verifiedBalance; + } + + public static class WithdrawalCanceledEventResponse extends BaseEventResponse { + } + + public static class WithdrawalCompleteEventResponse extends BaseEventResponse { + public String to; + + public BigInteger amount; + } + + public static class WithdrawalInitiatedEventResponse extends BaseEventResponse { + public BigInteger amount; + + public BigInteger readyToWithdrawTimestamp; + } +} diff --git a/libs/visa/src/main/kotlin/com/tangem/lib/visa/DefaultVisaContractInfoProvider.kt b/libs/visa/src/main/kotlin/com/tangem/lib/visa/DefaultVisaContractInfoProvider.kt new file mode 100644 index 0000000000..1b038aa5f9 --- /dev/null +++ b/libs/visa/src/main/kotlin/com/tangem/lib/visa/DefaultVisaContractInfoProvider.kt @@ -0,0 +1,139 @@ +package com.tangem.lib.visa + +import arrow.fx.coroutines.parZip +import com.tangem.lib.visa.model.BalancesAndLimits +import com.tangem.lib.visa.model.BalancesAndLimits.Balances +import com.tangem.lib.visa.model.BalancesAndLimits.Limits +import com.tangem.lib.visa.utils.toBigDecimal +import com.tangem.lib.visa.utils.toInstant +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.withContext +import org.joda.time.Instant +import org.web3j.protocol.Web3j +import org.web3j.tx.TransactionManager +import org.web3j.tx.gas.ContractGasProvider + +internal class DefaultVisaContractInfoProvider( + private val web3j: Web3j, + private val transactionManager: TransactionManager, + private val gasProvider: ContractGasProvider, + private val bridgeProcessorAddress: String, + private val dispatchers: CoroutineDispatcherProvider, +) : VisaContractInfoProvider { + + override suspend fun getBalancesAndLimits(walletAddress: String): BalancesAndLimits = withContext(dispatchers.io) { + val tangemBridgeProcessor = TangemBridgeProcessor.load( + /* contractAddress = */ bridgeProcessorAddress, + /* web3j = */ web3j, + /* transactionManager = */ transactionManager, + /* contractGasProvider = */ gasProvider, + ) + + parZip( + dispatchers.io, + { loadPaymentAccount(walletAddress, tangemBridgeProcessor) }, + { loadPaymentTokenInfo(tangemBridgeProcessor) }, + { paymentAccount, paymentToken -> + fetchBalancesAndLimits(paymentAccount, paymentToken) + }, + ) + } + + private fun loadPaymentAccount( + walletAddress: String, + tangemBridgeProcessor: TangemBridgeProcessor, + ): TangemPaymentAccount { + val paymentAccountAddress = tangemBridgeProcessor.getPaymentAccount(walletAddress).send() + + return TangemPaymentAccount.load(paymentAccountAddress, web3j, transactionManager, gasProvider) + } + + private fun loadPaymentTokenInfo(tangemBridgeProcessor: TangemBridgeProcessor): PaymentTokenInfo { + val paymentTokenContractAddress = tangemBridgeProcessor.paymentToken().send() + val paymentTokenContract = ERC20.load(paymentTokenContractAddress, web3j, transactionManager, gasProvider) + val paymentTokenDecimals = paymentTokenContract.decimals().send() + + return PaymentTokenInfo( + decimals = paymentTokenDecimals.toInt(), + contract = paymentTokenContract, + ) + } + + private suspend fun fetchBalancesAndLimits( + paymentAccount: TangemPaymentAccount, + paymentToken: PaymentTokenInfo, + ): BalancesAndLimits = parZip( + dispatchers.io, + { fetchBalances(paymentAccount, paymentToken) }, + { fetchLimits(paymentAccount, paymentToken) }, + { balances, (oldLimit, newLimit, changeDate) -> + BalancesAndLimits(balances, oldLimit, newLimit, changeDate) + }, + ) + + private suspend fun fetchBalances(paymentAccount: TangemPaymentAccount, paymentToken: PaymentTokenInfo): Balances { + return parZip( + dispatchers.io, + { paymentToken.contract.balanceOf(paymentAccount.contractAddress).send() }, + { paymentAccount.verifiedBalance().send() }, + { paymentAccount.availableForPayment().send() }, + { paymentAccount.availableForWithdrawal().send() }, + { paymentAccount.availableForDebtPayment().send() }, + { paymentAccount.blockedAmount().send() }, + { paymentAccount.debtAmount().send() }, + { paymentAccount.pendingRefundTotal().send() }, + ) { total, verified, payment, withdrawal, debtPayment, blocked, debt, refund -> + val decimals = paymentToken.decimals + + Balances( + total = total.toBigDecimal(decimals), + verified = verified.toBigDecimal(decimals), + available = Balances.Available( + forPayment = payment.toBigDecimal(decimals), + forWithdrawal = withdrawal.toBigDecimal(decimals), + forDebtPayment = debtPayment.toBigDecimal(decimals), + ), + blocked = blocked.toBigDecimal(decimals), + debt = debt.toBigDecimal(decimals), + pendingRefund = refund.toBigDecimal(decimals), + ) + } + } + + private fun fetchLimits( + paymentAccount: TangemPaymentAccount, + paymentToken: PaymentTokenInfo, + ): Triple { + val ( + oldLimit, + newLimit, + changeDateSeconds, + ) = paymentAccount.limits().send() + + return Triple( + first = getLimits(oldLimit, paymentToken), + second = getLimits(newLimit, paymentToken), + third = changeDateSeconds.toInstant(), + ) + } + + private fun getLimits(limit: TangemPaymentAccount.Limits, paymentToken: PaymentTokenInfo): Limits = Limits( + spendLimit = limit._01_spendLimit.toLimit(paymentToken.decimals), + noOtpLimit = limit._02_noOtpSpendLimit.toLimit(paymentToken.decimals), + singleTransactionLimit = limit._00_singleTransactionLimit.toBigDecimal(paymentToken.decimals), + expirationDate = limit._03_spendLimitsTimer.expireTimestamp.toInstant(), + spendPeriodSeconds = limit._04_spendLimitsPeriod, + ) + + private fun TangemPaymentAccount.Limit.toLimit(decimals: Int): Limits.Limit { + return Limits.Limit( + limit = _00_limit.toBigDecimal(decimals), + spent = _01_spent.toBigDecimal(decimals), + ) + } + + private data class PaymentTokenInfo( + val decimals: Int, + val contract: ERC20, + ) +} \ No newline at end of file diff --git a/libs/visa/src/main/kotlin/com/tangem/lib/visa/VisaContractInfoProvider.kt b/libs/visa/src/main/kotlin/com/tangem/lib/visa/VisaContractInfoProvider.kt new file mode 100644 index 0000000000..efb5ab487a --- /dev/null +++ b/libs/visa/src/main/kotlin/com/tangem/lib/visa/VisaContractInfoProvider.kt @@ -0,0 +1,90 @@ +package com.tangem.lib.visa + +import com.ihsanbal.logging.Level +import com.ihsanbal.logging.LoggingInterceptor +import com.tangem.lib.visa.model.BalancesAndLimits +import com.tangem.lib.visa.utils.VisaConfig +import com.tangem.lib.visa.utils.VisaConfig.NETWORK_LOGS_TAG +import com.tangem.lib.visa.utils.toHexString +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import okhttp3.OkHttpClient +import org.web3j.crypto.Credentials +import org.web3j.protocol.Web3j +import org.web3j.protocol.http.HttpService +import org.web3j.tx.FastRawTransactionManager +import org.web3j.tx.TransactionManager +import org.web3j.tx.gas.ContractGasProvider +import org.web3j.tx.gas.StaticEIP1559GasProvider +import java.math.BigDecimal +import java.math.BigInteger +import java.util.concurrent.TimeUnit + +interface VisaContractInfoProvider { + + suspend fun getBalancesAndLimits(walletAddress: String): BalancesAndLimits + + class Builder( + private val isNetworkLoggingEnabled: Boolean, + private val dispatchers: CoroutineDispatcherProvider, + private val baseUrl: String = VisaConfig.BASE_RPC_URL, + private val bridgeProcessorAddress: String = VisaConfig.BRIDGE_PROCESSOR_CONTRACT_ADDRESS, + private val chainId: Long = VisaConfig.CHAIN_ID, + private val networkTimeoutSeconds: Long = VisaConfig.NETWORK_TIMEOUT_SECONDS, + private val decimals: Int = VisaConfig.DECIMALS, + private val gasLimit: Long = VisaConfig.GAS_LIMIT, + private val privateKey: String = ByteArray(VisaConfig.PRIVATE_KEY_LENGTH).toHexString(), + ) { + + fun build(): VisaContractInfoProvider { + val web3j = createWeb3J() + val gasProvider = createGasProvider() + val transactionManager = createTransactionManager(web3j) + + return DefaultVisaContractInfoProvider( + web3j = web3j, + transactionManager = transactionManager, + gasProvider = gasProvider, + bridgeProcessorAddress = bridgeProcessorAddress, + dispatchers = dispatchers, + ) + } + + private fun createWeb3J(): Web3j { + val httpClient = OkHttpClient.Builder().apply { + connectTimeout(networkTimeoutSeconds, TimeUnit.SECONDS) + readTimeout(networkTimeoutSeconds, TimeUnit.SECONDS) + writeTimeout(networkTimeoutSeconds, TimeUnit.SECONDS) + + if (isNetworkLoggingEnabled) { + addInterceptor( + LoggingInterceptor.Builder() + .setLevel(Level.BODY) + .tag(NETWORK_LOGS_TAG) + .build(), + ) + } + }.build() + + val web3jService = HttpService( + /* url = */ baseUrl, + /* httpClient = */ httpClient, + /* includeRawResponses = */ false, + ) + + return Web3j.build(web3jService) + } + + private fun createGasProvider(): ContractGasProvider = StaticEIP1559GasProvider( + /* chainId = */ chainId, + /* maxFeePerGas = */ BigDecimal.ONE.movePointLeft(decimals).toBigInteger(), + /* maxPriorityFeePerGas = */ BigDecimal.ONE.movePointLeft(decimals).toBigInteger(), + /* gasLimit = */ BigInteger.valueOf(gasLimit), + ) + + private fun createTransactionManager(web3j: Web3j): TransactionManager = FastRawTransactionManager( + /* web3j = */ web3j, + /* credentials = */ Credentials.create(privateKey), + /* chainId = */ chainId, + ) + } +} \ No newline at end of file diff --git a/libs/visa/src/main/kotlin/com/tangem/lib/visa/model/BalancesAndLimits.kt b/libs/visa/src/main/kotlin/com/tangem/lib/visa/model/BalancesAndLimits.kt new file mode 100644 index 0000000000..5098ee3c43 --- /dev/null +++ b/libs/visa/src/main/kotlin/com/tangem/lib/visa/model/BalancesAndLimits.kt @@ -0,0 +1,43 @@ +package com.tangem.lib.visa.model + +import org.joda.time.Instant +import java.math.BigDecimal +import java.math.BigInteger + +data class BalancesAndLimits( + val balances: Balances, + val oldLimits: Limits, + val newLimits: Limits, + val limitsChangeDate: Instant, +) { + + data class Balances( + val total: BigDecimal, + val verified: BigDecimal, + val available: Available, + val blocked: BigDecimal, + val debt: BigDecimal, + val pendingRefund: BigDecimal, + ) { + + data class Available( + val forPayment: BigDecimal, + val forWithdrawal: BigDecimal, + val forDebtPayment: BigDecimal, + ) + } + + data class Limits( + val spendLimit: Limit, + val noOtpLimit: Limit, + val singleTransactionLimit: BigDecimal, + val expirationDate: Instant, + val spendPeriodSeconds: BigInteger, + ) { + + data class Limit( + val limit: BigDecimal, + val spent: BigDecimal, + ) + } +} \ No newline at end of file diff --git a/libs/visa/src/main/kotlin/com/tangem/lib/visa/utils/BigIntegerExt.kt b/libs/visa/src/main/kotlin/com/tangem/lib/visa/utils/BigIntegerExt.kt new file mode 100644 index 0000000000..4b129ed3c4 --- /dev/null +++ b/libs/visa/src/main/kotlin/com/tangem/lib/visa/utils/BigIntegerExt.kt @@ -0,0 +1,13 @@ +package com.tangem.lib.visa.utils + +import org.joda.time.Instant +import java.math.BigDecimal +import java.math.BigInteger + +internal fun BigInteger.toBigDecimal(decimals: Int): BigDecimal { + return BigDecimal(this).movePointLeft(decimals) +} + +internal fun BigInteger.toInstant(): Instant { + return Instant.ofEpochSecond(toLong()) +} \ No newline at end of file diff --git a/libs/visa/src/main/kotlin/com/tangem/lib/visa/utils/ByteArrayExt.kt b/libs/visa/src/main/kotlin/com/tangem/lib/visa/utils/ByteArrayExt.kt new file mode 100644 index 0000000000..3efd2be245 --- /dev/null +++ b/libs/visa/src/main/kotlin/com/tangem/lib/visa/utils/ByteArrayExt.kt @@ -0,0 +1,3 @@ +package com.tangem.lib.visa.utils + +internal fun ByteArray.toHexString(): String = joinToString("") { "%02X".format(it) } \ No newline at end of file diff --git a/libs/visa/src/main/kotlin/com/tangem/lib/visa/utils/VisaConfig.kt b/libs/visa/src/main/kotlin/com/tangem/lib/visa/utils/VisaConfig.kt new file mode 100644 index 0000000000..6176a369e5 --- /dev/null +++ b/libs/visa/src/main/kotlin/com/tangem/lib/visa/utils/VisaConfig.kt @@ -0,0 +1,14 @@ +package com.tangem.lib.visa.utils + +internal object VisaConfig { + + const val BASE_RPC_URL = "https://rpc-mumbai.maticvigil.com/" + const val BRIDGE_PROCESSOR_CONTRACT_ADDRESS = "0x62119697e78178512bfcc456ae6d1b7dee9fbaa6" + const val CHAIN_ID = 80_001L + const val DECIMALS = 9 + const val GAS_LIMIT = 500_000_000L + const val PRIVATE_KEY_LENGTH = 32 + + const val NETWORK_TIMEOUT_SECONDS = 65L + const val NETWORK_LOGS_TAG = "VisaNetworkLogs" +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 7fc4e85cba..3df3dad60b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -77,6 +77,7 @@ include(":core:deep-links:global") // region Libs modules include(":libs:crypto") include(":libs:auth") +include(":libs:visa") // endregion Libs modules // region Feature modules From 88dbdbc7f6a07ca81638e442f2a6dd383f4b87f6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 7 Feb 2024 12:15:41 +0300 Subject: [PATCH 11/53] Updated on 2026-08-14 --- data/visa/build.gradle.kts | 23 ++++ .../tangem/data/visa/DefaultVisaRepository.kt | 123 ++++++++++++++++++ .../tangem/data/visa/DummyVisaRepository.kt | 12 -- .../com/tangem/data/visa/di/VisaDataModule.kt | 28 +++- .../com/tangem/data/visa/utils/VisaConfig.kt | 18 +++ .../data/visa/utils/VisaCurrencyFactory.kt | 79 +++++++++++ 6 files changed, 268 insertions(+), 15 deletions(-) create mode 100644 data/visa/src/main/kotlin/com/tangem/data/visa/DefaultVisaRepository.kt delete mode 100644 data/visa/src/main/kotlin/com/tangem/data/visa/DummyVisaRepository.kt create mode 100644 data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaConfig.kt create mode 100644 data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaCurrencyFactory.kt diff --git a/data/visa/build.gradle.kts b/data/visa/build.gradle.kts index e57696df79..26f4c9abe7 100644 --- a/data/visa/build.gradle.kts +++ b/data/visa/build.gradle.kts @@ -11,9 +11,32 @@ android { dependencies { + /** Project - Data */ + implementation(projects.core.datasource) + implementation(projects.data.common) + /** Project - Domain */ implementation(projects.domain.visa) + implementation(projects.domain.models) implementation(projects.domain.wallets.models) + implementation(projects.domain.appCurrency.models) + + /** Project - Utils */ + implementation(projects.core.utils) + implementation(projects.domain.legacy) + + /** Project - Libs */ + implementation(projects.libs.visa) + + /** Libs - Other */ + implementation(deps.kotlin.coroutines) + implementation(deps.arrow.core) + implementation(deps.arrow.fx) + implementation(deps.jodatime) + + /** Libs - Tangem */ + implementation(deps.tangem.blockchain) + implementation(deps.tangem.card.core) /** DI */ implementation(deps.hilt.core) diff --git a/data/visa/src/main/kotlin/com/tangem/data/visa/DefaultVisaRepository.kt b/data/visa/src/main/kotlin/com/tangem/data/visa/DefaultVisaRepository.kt new file mode 100644 index 0000000000..9ba7dd179b --- /dev/null +++ b/data/visa/src/main/kotlin/com/tangem/data/visa/DefaultVisaRepository.kt @@ -0,0 +1,123 @@ +package com.tangem.data.visa + +import arrow.fx.coroutines.parZip +import com.tangem.blockchain.common.address.Address +import com.tangem.blockchain.common.address.AddressType +import com.tangem.common.card.EllipticCurve +import com.tangem.data.common.cache.CacheRegistry +import com.tangem.data.visa.utils.VisaConfig +import com.tangem.data.visa.utils.VisaCurrencyFactory +import com.tangem.datasource.api.common.response.getOrThrow +import com.tangem.datasource.api.tangemTech.TangemTechApi +import com.tangem.datasource.local.userwallet.UserWalletsStore +import com.tangem.domain.common.util.cardTypesResolver +import com.tangem.domain.visa.model.VisaCurrency +import com.tangem.domain.visa.repository.VisaRepository +import com.tangem.domain.wallets.models.UserWallet +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.lib.visa.VisaContractInfoProvider +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update +import java.math.BigDecimal + +internal class DefaultVisaRepository( + private val visaContractInfoProvider: VisaContractInfoProvider, + private val tangemTechApi: TangemTechApi, + private val cacheRegistry: CacheRegistry, + private val userWalletsStore: UserWalletsStore, + private val dispatchers: CoroutineDispatcherProvider, +) : VisaRepository { + + private val currencyFactory by lazy(mode = LazyThreadSafetyMode.NONE) { + VisaCurrencyFactory() + } + + private val fetchedCurrencies = MutableStateFlow( + value = hashMapOf(), + ) + + override suspend fun getVisaCurrency(userWalletId: UserWalletId, isRefresh: Boolean): VisaCurrency { + val address = makeAddress(userWalletId) + // val address = "0x143fe062a538176aa0bf162f13d390208f90898f" // for testing + + fetchVisaCurrencyIfExpired(address, isRefresh) + + return requireNotNull(fetchedCurrencies.value[address]) { + "Unable to find VISA currency for $address" + } + } + + private suspend fun fetchVisaCurrencyIfExpired(address: String, isRefresh: Boolean) { + cacheRegistry.invokeOnExpire( + key = getBalancesAndLimitsKey(address), + skipCache = isRefresh, + block = { fetchVisaCurrency(address) }, + ) + } + + private suspend fun fetchVisaCurrency(address: String) { + parZip( + dispatchers.io, + { visaContractInfoProvider.getBalancesAndLimits(address) }, + { getFiatRate() }, + { balancesAndLimits, fiatRate -> + fetchedCurrencies.update { value -> + value.apply { + put(address, currencyFactory.create(balancesAndLimits, fiatRate)) + } + } + }, + ) + } + + private suspend fun getFiatRate(): BigDecimal? { + val fiatCurrencyId = VisaConfig.fiatCurrency.code.lowercase() + val quotes = tangemTechApi.getQuotes( + currencyId = fiatCurrencyId, + coinIds = VisaConfig.TOKEN_ID, + ).getOrThrow() + + return quotes.quotes[VisaConfig.TOKEN_ID]?.price + } + + private suspend fun makeAddress(userWalletId: UserWalletId): String { + val userWallet = findVisaUserWallet(userWalletId) + val walletAddresses = makeWalletAddresses(userWallet) + val walletAddress = walletAddresses.firstOrNull { it.type == AddressType.Default } + + return requireNotNull(walletAddress?.value) { + "Unable to find wallet address" + } + } + + private fun makeWalletAddresses(userWallet: UserWallet): Set
{ + val walletBlockchain = userWallet.scanResponse.cardTypesResolver.getBlockchain() + + return walletBlockchain.makeAddresses(getCardPubKey(userWallet)) + } + + private fun getCardPubKey(userWallet: UserWallet): ByteArray { + val cardWallet = userWallet.scanResponse.card.wallets.firstOrNull { + it.curve == EllipticCurve.Secp256k1 + } + requireNotNull(cardWallet) { "Secp256k1 card wallet not found" } + + return cardWallet.publicKey + } + + private suspend fun findVisaUserWallet(userWalletId: UserWalletId): UserWallet { + val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { + "No user wallet found: $userWalletId" + } + if (!userWallet.scanResponse.cardTypesResolver.isVisaWallet()) { + error("VISA wallet required: $userWalletId") + } + + return userWallet + } + + private fun getBalancesAndLimitsKey(address: String): String { + return "visa_balances_and_limits_$address" + } +} \ No newline at end of file diff --git a/data/visa/src/main/kotlin/com/tangem/data/visa/DummyVisaRepository.kt b/data/visa/src/main/kotlin/com/tangem/data/visa/DummyVisaRepository.kt deleted file mode 100644 index 40815fac3c..0000000000 --- a/data/visa/src/main/kotlin/com/tangem/data/visa/DummyVisaRepository.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.tangem.data.visa - -import com.tangem.domain.visa.model.VisaCurrency -import com.tangem.domain.visa.repository.VisaRepository -import com.tangem.domain.wallets.models.UserWalletId - -internal class DummyVisaRepository : VisaRepository { - - override suspend fun getVisaCurrency(userWalletId: UserWalletId, isRefresh: Boolean): VisaCurrency { - TODO(reason = "Implement in [REDACTED_JIRA]") - } -} \ No newline at end of file diff --git a/data/visa/src/main/kotlin/com/tangem/data/visa/di/VisaDataModule.kt b/data/visa/src/main/kotlin/com/tangem/data/visa/di/VisaDataModule.kt index ab68249dd9..4204ec5430 100644 --- a/data/visa/src/main/kotlin/com/tangem/data/visa/di/VisaDataModule.kt +++ b/data/visa/src/main/kotlin/com/tangem/data/visa/di/VisaDataModule.kt @@ -1,7 +1,13 @@ package com.tangem.data.visa.di -import com.tangem.data.visa.DummyVisaRepository +import com.tangem.data.common.cache.CacheRegistry +import com.tangem.data.visa.BuildConfig +import com.tangem.data.visa.DefaultVisaRepository +import com.tangem.datasource.api.tangemTech.TangemTechApi +import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.visa.repository.VisaRepository +import com.tangem.lib.visa.VisaContractInfoProvider +import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -14,7 +20,23 @@ internal object VisaDataModule { @Provides @Singleton - fun provideVisaRepository(): VisaRepository { - return DummyVisaRepository() + fun provideVisaRepository( + tangemTechApi: TangemTechApi, + cacheRegistry: CacheRegistry, + userWalletsStore: UserWalletsStore, + dispatchers: CoroutineDispatcherProvider, + ): VisaRepository { + val contractInfoProvider = VisaContractInfoProvider.Builder( + isNetworkLoggingEnabled = BuildConfig.LOG_ENABLED, + dispatchers = dispatchers, + ).build() + + return DefaultVisaRepository( + contractInfoProvider, + tangemTechApi, + cacheRegistry, + userWalletsStore, + dispatchers, + ) } } \ No newline at end of file diff --git a/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaConfig.kt b/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaConfig.kt new file mode 100644 index 0000000000..e450a769cc --- /dev/null +++ b/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaConfig.kt @@ -0,0 +1,18 @@ +package com.tangem.data.visa.utils + +import com.tangem.domain.appcurrency.model.AppCurrency + +internal object VisaConfig { + + const val NETWORK_NAME = "Polygon PoS" + + const val TOKEN_SYMBOL = "USDT" + const val TOKEN_ID = "tether" + const val TOKEN_DECIMALS = 8 + + val fiatCurrency = AppCurrency( + code = "EUR", + name = "Euro", + symbol = "€", + ) +} \ No newline at end of file diff --git a/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaCurrencyFactory.kt b/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaCurrencyFactory.kt new file mode 100644 index 0000000000..d2555bdf84 --- /dev/null +++ b/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaCurrencyFactory.kt @@ -0,0 +1,79 @@ +package com.tangem.data.visa.utils + +import com.tangem.domain.visa.model.VisaCurrency +import com.tangem.lib.visa.model.BalancesAndLimits +import org.joda.time.DateTime +import org.joda.time.DateTimeZone +import org.joda.time.Instant +import java.math.BigDecimal +import java.math.BigInteger + +internal class VisaCurrencyFactory { + + fun create(balancesAndLimits: BalancesAndLimits, fiatRate: BigDecimal?): VisaCurrency { + val now = Instant.now() + val currentLimit = if (balancesAndLimits.limitsChangeDate > now) { + balancesAndLimits.oldLimits + } else { + balancesAndLimits.newLimits + } + + return VisaCurrency( + symbol = VisaConfig.TOKEN_SYMBOL, + networkName = VisaConfig.NETWORK_NAME, + decimals = VisaConfig.TOKEN_DECIMALS, + fiatRate = fiatRate, + fiatCurrency = VisaConfig.fiatCurrency, + balances = with(balancesAndLimits) { + VisaCurrency.Balances( + total = balances.total, + verified = balances.verified, + available = balances.available.forPayment, + blocked = balances.blocked, + debt = balances.debt, + pendingRefund = balances.pendingRefund, + ) + }, + limits = VisaCurrency.Limits( + remainingOtp = getRemainingOtp(currentLimit, now), + remainingNoOtp = getRemainingNoOtp(currentLimit, now), + singleTransaction = currentLimit.singleTransactionLimit, + expirationDate = getLimitsExpirationDate(currentLimit, now), + ), + ) + } + + private fun getRemainingOtp(currentLimit: BalancesAndLimits.Limits, now: Instant): BigDecimal { + if (currentLimit.expirationDate >= now) { + return currentLimit.spendLimit.limit - currentLimit.spendLimit.spent + } + + return currentLimit.spendLimit.limit + } + + private fun getRemainingNoOtp(currentLimit: BalancesAndLimits.Limits, now: Instant): BigDecimal { + if (currentLimit.expirationDate >= now) { + return currentLimit.noOtpLimit.limit - currentLimit.noOtpLimit.spent + } + + return currentLimit.noOtpLimit.limit + } + + private fun getLimitsExpirationDate(currentLimits: BalancesAndLimits.Limits, now: Instant): DateTime { + val expirationDate = if (currentLimits.expirationDate >= now) { + currentLimits.expirationDate.toDateTime() + } else { + val spendPeriodDays = currentLimits.spendPeriodSeconds + .div(BigInteger.valueOf(SECONDS_IN_DAY)) + .toInt() + + now.toDateTime().plusDays(spendPeriodDays) + } + + return expirationDate.withZone(DateTimeZone.getDefault()) + } + + private companion object { + const val SECONDS_IN_DAY = 86_400L + } +} \ No newline at end of file From a10c1a850552842c0dbf528d61c440da96cb799c Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 7 Feb 2024 14:44:58 +0300 Subject: [PATCH 12/53] Updated on 2026-08-14 --- .../java/com/tangem/tap/TapApplication.kt | 14 - .../com/tangem/tap/common/redux/AppState.kt | 2 - .../tangem/tap/di/domain/CardDomainModule.kt | 12 +- .../card/DefaultDerivePublicKeysUseCase.kt | 41 --- .../impl/di/CustomTokenInteractorModule.kt | 3 - .../domain/DefaultCustomTokenInteractor.kt | 156 +-------- .../viewmodels/TokensListMigration.kt | 95 ++++-- .../viewmodels/TokensListViewModel.kt | 17 +- .../tokens/legacy/redux/TokensMiddleware.kt | 250 --------------- .../tangem/tap/proxy/DerivationManagerImpl.kt | 298 ------------------ .../com/tangem/tap/proxy/di/ProxyModule.kt | 16 - .../tap/proxy/redux/DaggerGraphState.kt | 6 - .../configs/feature_toggles_config.json | 4 - .../domain/card/DerivePublicKeysUseCase.kt | 22 +- .../com/tangem/domain/tokens/TokensAction.kt | 10 - .../referral/domain/ReferralInteractorImpl.kt | 35 +- .../domain/converter/TokensConverter.kt | 34 -- .../domain/di/ReferralDomainModule.kt | 9 - .../tester/api/TesterFeatureToggles.kt | 11 - .../tester/di/TesterFeatureTogglesModule.kt | 21 -- .../DefaultTesterFeatureToggles.kt | 19 -- .../wallet/viewmodels/WalletViewModel.kt | 138 ++------ .../intents/WalletWarningsClickIntents.kt | 122 +------ .../tangem/lib/crypto/DerivationManager.kt | 12 - 24 files changed, 132 insertions(+), 1215 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/domain/card/DefaultDerivePublicKeysUseCase.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt delete mode 100644 app/src/main/java/com/tangem/tap/proxy/DerivationManagerImpl.kt delete mode 100644 features/referral/domain/src/main/java/com/tangem/feature/referral/domain/converter/TokensConverter.kt delete mode 100644 features/tester/api/src/main/java/com/tangem/features/tester/api/TesterFeatureToggles.kt delete mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/di/TesterFeatureTogglesModule.kt delete mode 100644 features/tester/impl/src/main/java/com/tangem/feature/tester/featuretoggles/DefaultTesterFeatureToggles.kt delete mode 100644 libs/crypto/src/main/java/com/tangem/lib/crypto/DerivationManager.kt diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 955106f3dc..11145469dc 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -29,7 +29,6 @@ import com.tangem.domain.apptheme.GetAppThemeModeUseCase import com.tangem.domain.apptheme.repository.AppThemeModeRepository import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository import com.tangem.domain.card.ScanCardProcessor -import com.tangem.domain.card.repository.DerivationsRepository import com.tangem.domain.common.LogConfig import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository @@ -38,10 +37,8 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.features.managetokens.featuretoggles.ManageTokensFeatureToggles import com.tangem.features.send.api.featuretoggles.SendFeatureToggles -import com.tangem.features.tester.api.TesterFeatureToggles import com.tangem.tap.common.analytics.AnalyticsFactory import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder -import com.tangem.tap.common.analytics.handlers.BlockchainExceptionHandler import com.tangem.tap.common.analytics.handlers.amplitude.AmplitudeAnalyticsHandler import com.tangem.tap.common.analytics.handlers.appsFlyer.AppsFlyerAnalyticsHandler import com.tangem.tap.common.analytics.handlers.firebase.FirebaseAnalyticsHandler @@ -119,9 +116,6 @@ internal class TapApplication : Application(), ImageLoaderFactory { @Inject lateinit var scanCardProcessor: ScanCardProcessor - @Inject - lateinit var blockchainExceptionHandler: BlockchainExceptionHandler - @Inject lateinit var appCurrencyRepository: AppCurrencyRepository @@ -155,12 +149,6 @@ internal class TapApplication : Application(), ImageLoaderFactory { @Inject lateinit var oneTimeEventFilter: OneTimeEventFilter - @Inject - lateinit var derivationsRepository: DerivationsRepository - - @Inject - lateinit var testerFeatureToggles: TesterFeatureToggles - @Inject lateinit var blockchainDataStorage: BlockchainDataStorage // endregion Injected @@ -238,8 +226,6 @@ internal class TapApplication : Application(), ImageLoaderFactory { balanceHidingRepository = balanceHidingRepository, walletsRepository = walletsRepository, sendFeatureToggles = sendFeatureToggles, - derivationsRepository = derivationsRepository, - testerFeatureToggles = testerFeatureToggles, blockchainDataStorage = blockchainDataStorage, ), ), diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt index e312758a52..892271058e 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt @@ -33,7 +33,6 @@ import com.tangem.tap.features.shop.redux.ShopMiddleware import com.tangem.tap.features.shop.redux.ShopState import com.tangem.tap.features.signin.redux.SignInMiddleware import com.tangem.tap.features.signin.redux.SignInState -import com.tangem.tap.features.tokens.legacy.redux.TokensMiddleware import com.tangem.tap.features.tokens.legacy.redux.TokensState import com.tangem.tap.features.wallet.redux.middlewares.TradeCryptoMiddleware import com.tangem.tap.features.welcome.redux.WelcomeMiddleware @@ -91,7 +90,6 @@ data class AppState( SendMiddleware().sendMiddleware, DetailsMiddleware().detailsMiddleware, DisclaimerMiddleware().disclaimerMiddleware, - TokensMiddleware.tokensMiddleware, WalletConnectMiddleware().walletConnectMiddleware, BackupMiddleware().backupMiddleware, ShopMiddleware().shopMiddleware, diff --git a/app/src/main/java/com/tangem/tap/di/domain/CardDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/CardDomainModule.kt index 0d32faca9a..76aa092682 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/CardDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/CardDomainModule.kt @@ -8,8 +8,6 @@ import com.tangem.domain.demo.DemoConfig import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.wallets.legacy.WalletsStateHolder import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase -import com.tangem.tap.domain.TangemSdkManager -import com.tangem.tap.domain.card.DefaultDerivePublicKeysUseCase import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -62,14 +60,8 @@ internal object CardDomainModule { @Provides @ViewModelScoped - fun provideDerivePublicKeysUseCase( - tangemSdkManager: TangemSdkManager, - derivationsRepository: DerivationsRepository, - ): DerivePublicKeysUseCase { - return DefaultDerivePublicKeysUseCase( - tangemSdkManager = tangemSdkManager, - derivationsRepository = derivationsRepository, - ) + fun provideDerivePublicKeysUseCase(derivationsRepository: DerivationsRepository): DerivePublicKeysUseCase { + return DerivePublicKeysUseCase(derivationsRepository = derivationsRepository) } @Provides diff --git a/app/src/main/java/com/tangem/tap/domain/card/DefaultDerivePublicKeysUseCase.kt b/app/src/main/java/com/tangem/tap/domain/card/DefaultDerivePublicKeysUseCase.kt deleted file mode 100644 index a09df2969a..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/card/DefaultDerivePublicKeysUseCase.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.tangem.tap.domain.card - -import arrow.core.Either -import arrow.core.left -import arrow.core.right -import com.tangem.common.doOnFailure -import com.tangem.common.doOnSuccess -import com.tangem.common.extensions.ByteArrayKey -import com.tangem.crypto.hdWallet.DerivationPath -import com.tangem.domain.card.DerivePublicKeysUseCase -import com.tangem.domain.card.repository.DerivationsRepository -import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.operations.derivation.DerivationTaskResponse -import com.tangem.tap.domain.TangemSdkManager - -internal class DefaultDerivePublicKeysUseCase( - private val tangemSdkManager: TangemSdkManager, - private val derivationsRepository: DerivationsRepository, -) : DerivePublicKeysUseCase { - - override suspend fun invoke( - cardId: String?, - derivations: Map>, - ): Either { - tangemSdkManager.derivePublicKeys(cardId = cardId, derivations = derivations) - .doOnSuccess { return it.right() } - .doOnFailure { return Unit.left() } - - return Unit.left() - } - - override suspend fun invoke( - userWalletId: UserWalletId, - currencies: List, - ): Either { - return Either.catch { - derivationsRepository.derivePublicKeys(userWalletId, currencies) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/di/CustomTokenInteractorModule.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/di/CustomTokenInteractorModule.kt index 44d4a98284..61dc90c2dc 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/di/CustomTokenInteractorModule.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/di/CustomTokenInteractorModule.kt @@ -3,7 +3,6 @@ package com.tangem.tap.features.customtoken.impl.di import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.domain.card.DerivePublicKeysUseCase import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase -import com.tangem.features.tester.api.TesterFeatureToggles import com.tangem.tap.features.customtoken.impl.data.DefaultCustomTokenRepository import com.tangem.tap.features.customtoken.impl.domain.CustomTokenInteractor import com.tangem.tap.features.customtoken.impl.domain.DefaultCustomTokenInteractor @@ -30,7 +29,6 @@ internal object CustomTokenInteractorModule { reduxStateHolder: AppStateHolder, getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, derivePublicKeysUseCase: DerivePublicKeysUseCase, - testerFeatureToggles: TesterFeatureToggles, ): CustomTokenInteractor { return DefaultCustomTokenInteractor( featureRepository = DefaultCustomTokenRepository( @@ -40,7 +38,6 @@ internal object CustomTokenInteractorModule { ), getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, derivePublicKeysUseCase = derivePublicKeysUseCase, - testerFeatureToggles = testerFeatureToggles, ) } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/domain/DefaultCustomTokenInteractor.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/domain/DefaultCustomTokenInteractor.kt index 2d6ad2aebf..f3f73a98d3 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/domain/DefaultCustomTokenInteractor.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/domain/DefaultCustomTokenInteractor.kt @@ -1,40 +1,19 @@ package com.tangem.tap.features.customtoken.impl.domain -import com.tangem.blockchain.blockchains.cardano.CardanoUtils import com.tangem.blockchain.common.Blockchain -import com.tangem.common.CompletionResult -import com.tangem.common.card.EllipticCurve -import com.tangem.common.core.TangemError -import com.tangem.common.extensions.ByteArrayKey -import com.tangem.common.extensions.toMapKey -import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.data.tokens.utils.CryptoCurrencyFactory import com.tangem.domain.card.DerivePublicKeysUseCase -import com.tangem.domain.common.configs.CardConfig import com.tangem.domain.common.extensions.toNetworkId import com.tangem.domain.common.util.derivationStyleProvider -import com.tangem.domain.common.util.hasDerivation import com.tangem.domain.features.addCustomToken.CustomCurrency import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase -import com.tangem.features.tester.api.TesterFeatureToggles -import com.tangem.operations.derivation.ExtendedPublicKeysMap -import com.tangem.tap.common.extensions.dispatchDebugErrorNotification -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.domain.TapError import com.tangem.tap.domain.model.Currency import com.tangem.tap.features.customtoken.impl.domain.models.FoundToken -import com.tangem.tap.features.tokens.legacy.redux.TokensMiddleware import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.store -import com.tangem.tap.tangemSdkManager -import com.tangem.tap.userWalletsListManager -import com.tangem.utils.extensions.DELAY_SDK_DIALOG_CLOSE -import kotlinx.coroutines.delay import timber.log.Timber /** @@ -48,7 +27,6 @@ class DefaultCustomTokenInteractor( private val featureRepository: CustomTokenRepository, private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, private val derivePublicKeysUseCase: DerivePublicKeysUseCase, - private val testerFeatureToggles: TesterFeatureToggles, ) : CustomTokenInteractor { // TODO: Move to DI @@ -70,136 +48,12 @@ class DefaultCustomTokenInteractor( val userWallet = getSelectedWalletSyncUseCase().fold(ifLeft = { return }, ifRight = { it }) val currency = Currency.fromCustomCurrency(customCurrency) - if (testerFeatureToggles.isDerivePublicKeysRefactoringEnabled) { - val currencies = listOfNotNull(element = currency.toCryptoCurrency(userWallet.scanResponse)) - derivePublicKeysUseCase(userWalletId = userWallet.walletId, currencies = currencies) - .onRight { - addCryptoCurrenciesUseCase(userWalletId = userWallet.walletId, currencies = currencies) - } - .onLeft { Timber.e("Failed to derive public keys: $it") } - } else { - // TODO: delete [REDACTED_JIRA] - val isNeedToDerive = isNeedToDerive(userWallet, currency) - if (isNeedToDerive) { - deriveMissingBlockchains( - userWallet = userWallet, - currencyList = listOf(currency), - onSuccess = { submitAdd(userWallet = userWallet.copy(scanResponse = it), currency = currency) }, - ) { - throw it - } - } else { - submitAdd(userWallet, currency) + val currencies = listOfNotNull(element = currency.toCryptoCurrency(userWallet.scanResponse)) + derivePublicKeysUseCase(userWalletId = userWallet.walletId, currencies = currencies) + .onRight { + addCryptoCurrenciesUseCase(userWalletId = userWallet.walletId, currencies = currencies) } - } - } - - private fun isNeedToDerive(userWallet: UserWallet, currency: Currency): Boolean { - val scanResponse = userWallet.scanResponse - return currency.derivationPath?.let { !scanResponse.hasDerivation(currency.blockchain, it) } ?: false - } - - private suspend fun deriveMissingBlockchains( - userWallet: UserWallet, - currencyList: List, - onSuccess: suspend (ScanResponse) -> Unit, - onFailure: suspend (TangemError) -> Unit, - ) { - val scanResponse = userWallet.scanResponse - val config = CardConfig.createConfig(scanResponse.card) - val derivationDataList = currencyList.mapNotNull { currency -> - val curve = config.primaryCurve(currency.blockchain) - curve?.let { getDerivations(curve, scanResponse, currency) } - } - - val derivations = buildMap> { - derivationDataList.forEach { - val current = this[it.derivations.first] - if (current != null) { - current.addAll(it.derivations.second) - current.distinct() - } else { - this[it.derivations.first] = it.derivations.second.toMutableList() - } - } - } - if (derivations.isEmpty()) { - onSuccess(scanResponse) - return - } - - when (val result = tangemSdkManager.derivePublicKeys(cardId = null, derivations = derivations)) { - is CompletionResult.Success -> { - val newDerivedKeys = result.data.entries - val oldDerivedKeys = scanResponse.derivedKeys - - val walletKeys = (newDerivedKeys.keys + oldDerivedKeys.keys).toSet() - - val updatedDerivedKeys = walletKeys.associateWith { walletKey -> - val oldDerivations = ExtendedPublicKeysMap(oldDerivedKeys[walletKey] ?: emptyMap()) - val newDerivations = newDerivedKeys[walletKey] ?: ExtendedPublicKeysMap(emptyMap()) - ExtendedPublicKeysMap(oldDerivations + newDerivations) - } - - val updatedScanResponse = scanResponse.copy(derivedKeys = updatedDerivedKeys) - store.dispatchOnMain(GlobalAction.SaveScanResponse(updatedScanResponse)) - delay(DELAY_SDK_DIALOG_CLOSE) - - onSuccess(updatedScanResponse) - } - is CompletionResult.Failure -> { - onFailure.invoke(result.error) - store.dispatchDebugErrorNotification(TapError.CustomError("Error adding tokens")) - } - } - } - - private fun getDerivations( - curve: EllipticCurve, - scanResponse: ScanResponse, - currency: Currency, - ): TokensMiddleware.DerivationData? { - val wallet = scanResponse.card.wallets.firstOrNull { it.curve == curve } ?: return null - - val supportedCurves = currency.blockchain.getSupportedCurves() - val path = currency.blockchain.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle()) - .takeIf { supportedCurves.contains(curve) } - - val customPath = currency.derivationPath?.let { - DerivationPath(it) - }.takeIf { supportedCurves.contains(curve) } - - val bothCandidates = listOfNotNull(path, customPath).distinct().toMutableList() - if (bothCandidates.isEmpty()) return null - - if (currency is Currency.Blockchain && currency.blockchain == Blockchain.Cardano) { - currency.derivationPath?.let { - bothCandidates.add(CardanoUtils.extendedDerivationPath(DerivationPath(it))) - } - } - - val mapKeyOfWalletPublicKey = wallet.publicKey.toMapKey() - val alreadyDerivedKeys: ExtendedPublicKeysMap = - scanResponse.derivedKeys[mapKeyOfWalletPublicKey] ?: ExtendedPublicKeysMap(emptyMap()) - val alreadyDerivedPaths = alreadyDerivedKeys.keys.toList() - - val toDerive = bothCandidates.filterNot { alreadyDerivedPaths.contains(it) } - if (toDerive.isEmpty()) return null - - return TokensMiddleware.DerivationData(derivations = mapKeyOfWalletPublicKey to toDerive) - } - - private suspend fun submitAdd(userWallet: UserWallet, currency: Currency) { - val scanResponse = userWallet.scanResponse - val userWalletId = userWallet.walletId - - val currencyList = listOfNotNull(element = currency.toCryptoCurrency(scanResponse)) - - userWalletsListManager.update(userWalletId) { - it.copy(scanResponse = scanResponse) - } - - addCryptoCurrenciesUseCase(userWalletId, currencyList) + .onLeft { Timber.e("Failed to derive public keys: $it") } } private fun Currency.toCryptoCurrency(scanResponse: ScanResponse): CryptoCurrency? { diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListMigration.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListMigration.kt index 7ccf604f5a..f4f5e5e72d 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListMigration.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListMigration.kt @@ -3,14 +3,20 @@ package com.tangem.tap.features.tokens.impl.presentation.viewmodels import arrow.core.Either import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token +import com.tangem.core.navigation.NavigationAction import com.tangem.data.tokens.utils.CryptoCurrencyFactory +import com.tangem.domain.card.DerivePublicKeysUseCase import com.tangem.domain.common.util.derivationStyleProvider +import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase import com.tangem.domain.tokens.GetCryptoCurrenciesUseCase import com.tangem.domain.tokens.TokenWithBlockchain -import com.tangem.domain.tokens.TokensAction import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UserWallet +import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase +import com.tangem.tap.common.extensions.dispatchDebugErrorNotification +import com.tangem.tap.common.extensions.dispatchOnMain +import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.store import timber.log.Timber import kotlin.properties.Delegates @@ -24,6 +30,8 @@ import kotlin.properties.Delegates internal class TokensListMigration( private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, private val getCurrenciesUseCase: GetCryptoCurrenciesUseCase, + private val derivePublicKeysUseCase: DerivePublicKeysUseCase, + private val addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase, ) { private var currentNewCoins: List by Delegates.notNull() @@ -76,31 +84,72 @@ internal class TokensListMigration( } } - fun onSaveButtonClick( + suspend fun onSaveButtonClick( changedTokensList: MutableList, changedBlockchainList: List, ) { - store.dispatch( - action = TokensAction.SaveChanges( - currentTokens = currentNewTokens, - currentCoins = currentNewCoins, - changedTokens = changedTokensList.mapNotNull { - cryptoCurrencyFactory.createToken( - sdkToken = it.token, - blockchain = it.blockchain, - extraDerivationPath = null, - derivationStyleProvider = currentUserWallet.scanResponse.derivationStyleProvider, - ) - }, - changedCoins = changedBlockchainList.mapNotNull { - cryptoCurrencyFactory.createCoin( - blockchain = it, - extraDerivationPath = null, - derivationStyleProvider = currentUserWallet.scanResponse.derivationStyleProvider, - ) - }, - userWallet = currentUserWallet, - ), + val changedTokens = changedTokensList.mapNotNull { + cryptoCurrencyFactory.createToken( + sdkToken = it.token, + blockchain = it.blockchain, + extraDerivationPath = null, + derivationStyleProvider = currentUserWallet.scanResponse.derivationStyleProvider, + ) + } + + val changedCoins = changedBlockchainList.mapNotNull { + cryptoCurrencyFactory.createCoin( + blockchain = it, + extraDerivationPath = null, + derivationStyleProvider = currentUserWallet.scanResponse.derivationStyleProvider, + ) + } + + val blockchainsToAdd = changedCoins.filterNot(currentNewCoins::contains) + val blockchainsToRemove = currentNewCoins.filterNot(changedCoins::contains) + + val tokensToAdd = changedTokens.filterNot(currentNewTokens::contains) + val tokensToRemove = currentNewTokens.filterNot { token -> changedTokens.any { it == token } } + + removeCurrenciesIfNeeded( + userWalletId = currentUserWallet.walletId, + currencies = blockchainsToRemove + tokensToRemove, + ) + + val isNothingToDoWithTokens = tokensToAdd.isEmpty() && tokensToRemove.isEmpty() + val isNothingToDoWithBlockchain = blockchainsToAdd.isEmpty() && blockchainsToRemove.isEmpty() + if (isNothingToDoWithTokens && isNothingToDoWithBlockchain) { + store.dispatchDebugErrorNotification(message = "Nothing to save") + store.dispatchOnMain(NavigationAction.PopBackTo()) + return + } + + val currencyList = blockchainsToAdd + tokensToAdd + + derivePublicKeysUseCase(userWalletId = currentUserWallet.walletId, currencies = currencyList) + .onRight { + addCryptoCurrenciesUseCase(userWalletId = currentUserWallet.walletId, currencies = currencyList) + store.dispatchOnMain(NavigationAction.PopBackTo()) + } + .onLeft { Timber.e("Failed to derive public keys: $it") } + } + + private suspend fun removeCurrenciesIfNeeded(userWalletId: UserWalletId, currencies: List) { + if (currencies.isEmpty()) return + val currenciesRepository = store.state.daggerGraphState.get(DaggerGraphState::currenciesRepository) + val walletManagersFacade = store.state.daggerGraphState.get(DaggerGraphState::walletManagersFacade) + + currenciesRepository.removeCurrencies(userWalletId = userWalletId, currencies = currencies) + + walletManagersFacade.remove( + userWalletId = userWalletId, + networks = currencies + .filterIsInstance() + .mapTo(hashSetOf(), CryptoCurrency::network), + ) + walletManagersFacade.removeTokens( + userWalletId = userWalletId, + tokens = currencies.filterIsInstance().toSet(), ) } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt index 94c4974fc7..337e8f24ec 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt @@ -13,12 +13,14 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.ui.extensions.getActiveIconRes import com.tangem.core.ui.extensions.getGreyedOutIconRes +import com.tangem.domain.card.DerivePublicKeysUseCase import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation import com.tangem.domain.common.extensions.canHandleBlockchain import com.tangem.domain.common.extensions.canHandleToken import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.domain.common.extensions.supportedTokens import com.tangem.domain.common.util.cardTypesResolver +import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase import com.tangem.domain.tokens.GetCryptoCurrenciesUseCase import com.tangem.domain.tokens.TokenWithBlockchain import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase @@ -67,6 +69,8 @@ internal class TokensListViewModel @Inject constructor( private val router: TokensListRouter, private val dispatchers: AppCoroutineDispatcherProvider, private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, + derivePublicKeysUseCase: DerivePublicKeysUseCase, + addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase, analyticsEventHandler: AnalyticsEventHandler, getCurrenciesUseCase: GetCryptoCurrenciesUseCase, ) : ViewModel(), DefaultLifecycleObserver { @@ -88,6 +92,8 @@ internal class TokensListViewModel @Inject constructor( private val tokensListMigration = TokensListMigration( getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, getCurrenciesUseCase = getCurrenciesUseCase, + derivePublicKeysUseCase = derivePublicKeysUseCase, + addCryptoCurrenciesUseCase = addCryptoCurrenciesUseCase, ) init { @@ -303,10 +309,13 @@ internal class TokensListViewModel @Inject constructor( fun onSaveButtonClick() { analyticsSender.sendWhenSaveButtonClicked() - tokensListMigration.onSaveButtonClick( - changedTokensList = changedTokensList, - changedBlockchainList = changedBlockchainList, - ) + + viewModelScope.launch(dispatchers.main) { + tokensListMigration.onSaveButtonClick( + changedTokensList = changedTokensList, + changedBlockchainList = changedBlockchainList, + ) + } } private fun onSearchValueChange(newValue: String) { diff --git a/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt b/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt deleted file mode 100644 index eb39d0d64b..0000000000 --- a/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt +++ /dev/null @@ -1,250 +0,0 @@ -package com.tangem.tap.features.tokens.legacy.redux - -import com.tangem.blockchain.blockchains.cardano.CardanoUtils -import com.tangem.blockchain.common.Blockchain -import com.tangem.common.CompletionResult -import com.tangem.common.card.EllipticCurve -import com.tangem.common.doOnSuccess -import com.tangem.common.extensions.ByteArrayKey -import com.tangem.common.extensions.toMapKey -import com.tangem.core.navigation.NavigationAction -import com.tangem.crypto.hdWallet.DerivationPath -import com.tangem.domain.common.configs.CardConfig -import com.tangem.domain.common.util.derivationStyleProvider -import com.tangem.domain.common.util.supportsHdWallet -import com.tangem.domain.models.scan.ScanResponse -import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase -import com.tangem.domain.tokens.TokensAction -import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.operations.derivation.ExtendedPublicKeysMap -import com.tangem.tap.common.extensions.dispatchDebugErrorNotification -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.common.redux.AppState -import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.domain.TapError -import com.tangem.tap.domain.card.DefaultDerivePublicKeysUseCase -import com.tangem.tap.proxy.redux.DaggerGraphState -import com.tangem.tap.scope -import com.tangem.tap.store -import com.tangem.tap.tangemSdkManager -import com.tangem.tap.userWalletsListManager -import kotlinx.coroutines.launch -import org.rekotlin.Middleware -import timber.log.Timber - -@Suppress("LargeClass") -object TokensMiddleware { - - // TODO: Move to DI - private val addCryptoCurrenciesUseCase by lazy(LazyThreadSafetyMode.NONE) { - val currenciesRepository = store.state.daggerGraphState.get(DaggerGraphState::currenciesRepository) - val networksRepository = store.state.daggerGraphState.get(DaggerGraphState::networksRepository) - - AddCryptoCurrenciesUseCase(currenciesRepository, networksRepository) - } - - val tokensMiddleware: Middleware = { _, _ -> - { next -> - { action -> - when (action) { - is TokensAction.SaveChanges -> handleSaveChanges(action) - } - next(action) - } - } - } - - private fun handleSaveChanges(action: TokensAction.SaveChanges) { - scope.launch { - val scanResponse = action.userWallet.scanResponse - - val currentTokens = action.currentTokens - val currentBlockchains = action.currentCoins - - val blockchainsToAdd = action.changedCoins.filterNot(currentBlockchains::contains) - val blockchainsToRemove = currentBlockchains.filterNot(action.changedCoins::contains) - - val tokensToAdd = action.changedTokens.filterNot(currentTokens::contains) - val tokensToRemove = currentTokens.filterNot { token -> action.changedTokens.any { it == token } } - - removeCurrenciesIfNeeded( - userWalletId = action.userWallet.walletId, - currencies = blockchainsToRemove + tokensToRemove, - ) - - val isNothingToDoWithTokens = tokensToAdd.isEmpty() && tokensToRemove.isEmpty() - val isNothingToDoWithBlockchain = blockchainsToAdd.isEmpty() && blockchainsToRemove.isEmpty() - if (isNothingToDoWithTokens && isNothingToDoWithBlockchain) { - store.dispatchDebugErrorNotification(message = "Nothing to save") - store.dispatchOnMain(NavigationAction.PopBackTo()) - return@launch - } - - val currencyList = blockchainsToAdd + tokensToAdd - - val featureToggles = store.state.daggerGraphState.get(DaggerGraphState::testerFeatureToggles) - if (featureToggles.isDerivePublicKeysRefactoringEnabled) { - val derivePublicKeys = DefaultDerivePublicKeysUseCase( - tangemSdkManager = tangemSdkManager, - derivationsRepository = store.state.daggerGraphState.get(DaggerGraphState::derivationsRepository), - ) - - derivePublicKeys(userWalletId = action.userWallet.walletId, currencies = currencyList) - .onRight { - addCryptoCurrenciesUseCase( - userWalletId = action.userWallet.walletId, - currencies = currencyList, - ) - store.dispatchOnMain(NavigationAction.PopBackTo()) - } - .onLeft { Timber.e("Failed to derive public keys: $it") } - } else { - // TODO: delete [REDACTED_JIRA] - if (scanResponse.supportsHdWallet()) { - deriveMissingCoins(scanResponse = scanResponse, currencyList = currencyList) { - submitAdd( - userWallet = action.userWallet, - updatedScanResponse = it, - currencyList = currencyList, - ) - } - } else { - submitAdd(action.userWallet, scanResponse, currencyList) - } - } - } - } - - @Deprecated(message = "Use DerivePublicKeysUseCase instead") - private fun deriveMissingCoins( - scanResponse: ScanResponse, - currencyList: List, - onSuccess: (ScanResponse) -> Unit, - ) { - val config = CardConfig.createConfig(scanResponse.card) - val derivationDataList = currencyList.mapNotNull { currency -> - val curve = config.primaryCurve(blockchain = Blockchain.fromId(currency.network.id.value)) - curve?.let { getDerivations(curve, scanResponse, currency) } - } - val derivations = buildMap> { - derivationDataList.forEach { - val current = this[it.derivations.first] - if (current != null) { - current.addAll(it.derivations.second) - current.distinct() - } else { - this[it.derivations.first] = it.derivations.second.toMutableList() - } - } - } - - if (derivations.isEmpty()) { - onSuccess(scanResponse) - return - } - - scope.launch { - val result = tangemSdkManager.derivePublicKeys( - cardId = null, - derivations = derivations, - ) - when (result) { - is CompletionResult.Success -> { - val newDerivedKeys = result.data.entries - val oldDerivedKeys = scanResponse.derivedKeys - - val walletKeys = (newDerivedKeys.keys + oldDerivedKeys.keys).toSet() - - val updatedDerivedKeys = walletKeys.associateWith { walletKey -> - val oldDerivations = ExtendedPublicKeysMap(oldDerivedKeys[walletKey] ?: emptyMap()) - val newDerivations = newDerivedKeys[walletKey] ?: ExtendedPublicKeysMap(emptyMap()) - ExtendedPublicKeysMap(oldDerivations + newDerivations) - } - val updatedScanResponse = scanResponse.copy(derivedKeys = updatedDerivedKeys) - - store.dispatchOnMain(GlobalAction.SaveScanResponse(updatedScanResponse)) - - onSuccess(updatedScanResponse) - } - is CompletionResult.Failure -> { - store.dispatchDebugErrorNotification(TapError.CustomError("Error adding tokens")) - } - } - } - } - - private fun getDerivations( - curve: EllipticCurve, - scanResponse: ScanResponse, - currency: CryptoCurrency, - ): DerivationData? { - val wallet = scanResponse.card.wallets.firstOrNull { it.curve == curve } ?: return null - - val blockchain = Blockchain.fromId(currency.network.id.value) - val supportedCurves = blockchain.getSupportedCurves() - val path = blockchain.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle()) - .takeIf { supportedCurves.contains(curve) } - - val customPath = currency.network.derivationPath.value?.let { - DerivationPath(it) - }.takeIf { supportedCurves.contains(curve) } - - val bothCandidates = listOfNotNull(path, customPath).distinct().toMutableList() - if (bothCandidates.isEmpty()) return null - - if (currency is CryptoCurrency.Coin && blockchain == Blockchain.Cardano) { - currency.network.derivationPath.value?.let { - bothCandidates.add(CardanoUtils.extendedDerivationPath(DerivationPath(it))) - } - } - - val mapKeyOfWalletPublicKey = wallet.publicKey.toMapKey() - val alreadyDerivedKeys: ExtendedPublicKeysMap = - scanResponse.derivedKeys[mapKeyOfWalletPublicKey] ?: ExtendedPublicKeysMap(emptyMap()) - val alreadyDerivedPaths = alreadyDerivedKeys.keys.toList() - - val toDerive = bothCandidates.filterNot { alreadyDerivedPaths.contains(it) } - if (toDerive.isEmpty()) return null - - return DerivationData(derivations = mapKeyOfWalletPublicKey to toDerive) - } - - class DerivationData(val derivations: Pair>) - - private fun submitAdd( - userWallet: UserWallet, - updatedScanResponse: ScanResponse, - currencyList: List, - ) { - scope.launch { - userWalletsListManager.update( - userWalletId = userWallet.walletId, - update = { it.copy(scanResponse = updatedScanResponse) }, - ).doOnSuccess { - addCryptoCurrenciesUseCase(userWallet.walletId, currencyList) - } - } - store.dispatchOnMain(NavigationAction.PopBackTo()) - } - - private suspend fun removeCurrenciesIfNeeded(userWalletId: UserWalletId, currencies: List) { - if (currencies.isEmpty()) return - val currenciesRepository = store.state.daggerGraphState.get(DaggerGraphState::currenciesRepository) - val walletManagersFacade = store.state.daggerGraphState.get(DaggerGraphState::walletManagersFacade) - - currenciesRepository.removeCurrencies(userWalletId = userWalletId, currencies = currencies) - - walletManagersFacade.remove( - userWalletId = userWalletId, - networks = currencies - .filterIsInstance() - .mapTo(hashSetOf(), CryptoCurrency::network), - ) - walletManagersFacade.removeTokens( - userWalletId = userWalletId, - tokens = currencies.filterIsInstance().toSet(), - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/proxy/DerivationManagerImpl.kt b/app/src/main/java/com/tangem/tap/proxy/DerivationManagerImpl.kt deleted file mode 100644 index 63b07213d1..0000000000 --- a/app/src/main/java/com/tangem/tap/proxy/DerivationManagerImpl.kt +++ /dev/null @@ -1,298 +0,0 @@ -package com.tangem.tap.proxy - -import com.tangem.blockchain.blockchains.cardano.CardanoUtils -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.Token -import com.tangem.common.CompletionResult -import com.tangem.common.card.EllipticCurve -import com.tangem.common.core.TangemError -import com.tangem.common.core.TangemSdkError -import com.tangem.common.extensions.ByteArrayKey -import com.tangem.common.extensions.toMapKey -import com.tangem.crypto.hdWallet.DerivationPath -import com.tangem.data.tokens.utils.CryptoCurrencyFactory -import com.tangem.domain.common.BlockchainNetwork -import com.tangem.domain.common.DerivationStyleProvider -import com.tangem.domain.common.configs.CardConfig -import com.tangem.domain.common.extensions.fromNetworkId -import com.tangem.domain.common.util.derivationStyleProvider -import com.tangem.domain.common.util.hasDerivation -import com.tangem.domain.models.scan.ScanResponse -import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase -import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.tokens.repository.CurrenciesRepository -import com.tangem.domain.tokens.repository.NetworksRepository -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.lib.crypto.DerivationManager -import com.tangem.lib.crypto.models.Currency -import com.tangem.lib.crypto.models.Currency.NonNativeToken -import com.tangem.lib.crypto.models.errors.UserCancelledException -import com.tangem.operations.derivation.ExtendedPublicKeysMap -import com.tangem.tap.common.extensions.dispatchDebugErrorNotification -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.domain.TapError -import com.tangem.tap.features.tokens.legacy.redux.TokensMiddleware -import com.tangem.tap.scope -import com.tangem.tap.userWalletsListManager -import com.tangem.utils.extensions.DELAY_SDK_DIALOG_CLOSE -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import kotlin.coroutines.suspendCoroutine -import com.tangem.tap.domain.model.Currency as WalletModelCurrency - -class DerivationManagerImpl( - private val appStateHolder: AppStateHolder, - private val currenciesRepository: CurrenciesRepository, - private val networksRepository: NetworksRepository, -) : DerivationManager { - - // TODO: Move to DI - private val addCryptoCurrenciesUseCase by lazy(LazyThreadSafetyMode.NONE) { - AddCryptoCurrenciesUseCase(currenciesRepository, networksRepository) - } - - override suspend fun deriveAndAddTokens(currency: Currency) = suspendCoroutine { continuation -> - val selectedUserWallet = requireNotNull( - userWalletsListManager.selectedUserWalletSync, - ) { "selectedUserWallet shouldn't be null" } - val scanResponse = selectedUserWallet.scanResponse - val blockchain = requireNotNull( - Blockchain.fromNetworkId(currency.networkId), - ) { "unsupported blockchain" } - val derivationStyleProvider = scanResponse.derivationStyleProvider - val derivationPath = requireNotNull( - blockchain.derivationPath(derivationStyleProvider.getDerivationStyle())?.rawPath, - ) { "derivationPath shouldn't be null" } - val hasDerivation = scanResponse.hasDerivation( - blockchain, - derivationPath, - ) - if (hasDerivation) { - scope.launch { - addToken( - userWalletId = selectedUserWallet.walletId, - blockchain = blockchain, - currency = currency, - derivationPath = derivationPath, - derivationStyleProvider = derivationStyleProvider, - ) - continuation.resumeWith(Result.success(derivationPath)) - } - } else { - val blockchainNetwork = BlockchainNetwork(blockchain, scanResponse.derivationStyleProvider) - val appCurrency = com.tangem.tap.domain.model.Currency.fromBlockchainNetwork( - blockchainNetwork, - getAppToken(currency), - ) - deriveMissingBlockchains( - scanResponse = scanResponse, - currencyList = listOf(appCurrency), - onSuccess = { updatedScanResponse -> - scope.launch { - userWalletsListManager.update( - userWalletId = selectedUserWallet.walletId, - update = { it.copy(scanResponse = updatedScanResponse) }, - ) - addToken( - userWalletId = selectedUserWallet.walletId, - blockchain = blockchain, - currency = currency, - derivationPath = derivationPath, - derivationStyleProvider = derivationStyleProvider, - ) - continuation.resumeWith(Result.success(derivationPath)) - } - }, - onFailure = { - continuation.resumeWith(Result.failure(it)) - }, - ) - } - } - - private suspend fun addToken( - userWalletId: UserWalletId, - blockchain: Blockchain, - currency: Currency, - derivationPath: String, - derivationStyleProvider: DerivationStyleProvider, - ) { - val cryptoCurrency = convertCurrency( - blockchain = blockchain, - currency = currency, - derivationPath = derivationPath, - derivationStyleProvider = derivationStyleProvider, - ) - - addCryptoCurrenciesUseCase(userWalletId, cryptoCurrency) - } - - private fun convertCurrency( - blockchain: Blockchain, - currency: Currency, - derivationPath: String, - derivationStyleProvider: DerivationStyleProvider, - ): CryptoCurrency { - val cryptoCurrencyFactory = CryptoCurrencyFactory() - return when (currency) { - is Currency.NativeToken -> { - cryptoCurrencyFactory.createCoin( - blockchain = blockchain, - extraDerivationPath = derivationPath, - derivationStyleProvider = derivationStyleProvider, - ) - } - is NonNativeToken -> { - val sdkToken = Token( - name = currency.name, - symbol = currency.symbol, - contractAddress = currency.contractAddress, - decimals = currency.decimalCount, - id = currency.id, - ) - cryptoCurrencyFactory.createToken( - sdkToken = sdkToken, - blockchain = blockchain, - extraDerivationPath = derivationPath, - derivationStyleProvider = derivationStyleProvider, - ) - } - } as CryptoCurrency - } - - private fun deriveMissingBlockchains( - scanResponse: ScanResponse, - currencyList: List, - onSuccess: (ScanResponse) -> Unit, - onFailure: (Exception) -> Unit, - ) { - val config = CardConfig.createConfig(scanResponse.card) - val derivationDataList = currencyList.mapNotNull { currency -> - val curve = config.primaryCurve(currency.blockchain) - curve?.let { getDerivations(curve, scanResponse, currency) } - } - val derivations = buildMap> { - derivationDataList.forEach { - val current = this[it.derivations.first] - if (current != null) { - current.addAll(it.derivations.second) - current.distinct() - } else { - this[it.derivations.first] = it.derivations.second.toMutableList() - } - } - } - if (derivations.isEmpty()) { - onSuccess(scanResponse) - return - } - - scope.launch { - val selectedUserWallet = userWalletsListManager.selectedUserWalletSync - - val result = appStateHolder.tangemSdkManager?.derivePublicKeys( - cardId = null, // always ignore cardId in derive task - derivations = derivations, - ) - when (result) { - is CompletionResult.Success -> { - val newDerivedKeys = result.data.entries - val oldDerivedKeys = scanResponse.derivedKeys - - val walletKeys = (newDerivedKeys.keys + oldDerivedKeys.keys).toSet() - - val updatedDerivedKeys = walletKeys.associateWith { walletKey -> - val oldDerivations = ExtendedPublicKeysMap(oldDerivedKeys[walletKey] ?: emptyMap()) - val newDerivations = newDerivedKeys[walletKey] ?: ExtendedPublicKeysMap(emptyMap()) - ExtendedPublicKeysMap(oldDerivations + newDerivations) - } - val updatedScanResponse = scanResponse.copy( - derivedKeys = updatedDerivedKeys, - ) - if (selectedUserWallet != null) { - userWalletsListManager.update( - userWalletId = selectedUserWallet.walletId, - update = { it.copy(scanResponse = updatedScanResponse) }, - ) - } - appStateHolder.mainStore?.dispatchOnMain(GlobalAction.SaveScanResponse(updatedScanResponse)) - delay(DELAY_SDK_DIALOG_CLOSE) - onSuccess(updatedScanResponse) - } - is CompletionResult.Failure -> { - appStateHolder.mainStore?.dispatchDebugErrorNotification( - TapError.CustomError( - "Error derivation", - ), - ) - onFailure.invoke(handleTangemError(result.error)) - } - else -> { - error("result result is null") - } - } - } - } - - private fun getDerivations( - curve: EllipticCurve, - scanResponse: ScanResponse, - currency: com.tangem.tap.domain.model.Currency, - ): TokensMiddleware.DerivationData? { - val wallet = scanResponse.card.wallets.firstOrNull { it.curve == curve } ?: return null - - val supportedCurves = currency.blockchain.getSupportedCurves() - val path = currency.blockchain.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle()) - .takeIf { supportedCurves.contains(curve) } - - val customPath = currency.derivationPath?.let { - DerivationPath(it) - }.takeIf { supportedCurves.contains(curve) } - - val bothCandidates = listOfNotNull(path, customPath).distinct().toMutableList() - if (bothCandidates.isEmpty()) return null - - if (currency is WalletModelCurrency.Blockchain && - currency.blockchain == Blockchain.Cardano - ) { - currency.derivationPath?.let { - bothCandidates.add(CardanoUtils.extendedDerivationPath(DerivationPath(it))) - } - } - - val mapKeyOfWalletPublicKey = wallet.publicKey.toMapKey() - val alreadyDerivedKeys: ExtendedPublicKeysMap = - scanResponse.derivedKeys[mapKeyOfWalletPublicKey] ?: ExtendedPublicKeysMap(emptyMap()) - val alreadyDerivedPaths = alreadyDerivedKeys.keys.toList() - - val toDerive = bothCandidates.filterNot { alreadyDerivedPaths.contains(it) } - if (toDerive.isEmpty()) return null - - return TokensMiddleware.DerivationData(derivations = mapKeyOfWalletPublicKey to toDerive) - } - - private fun getAppToken(currency: Currency): Token? { - return if (currency is NonNativeToken) { - Token( - symbol = currency.symbol, - contractAddress = currency.contractAddress, - decimals = currency.decimalCount, - ) - } else { - null - } - } - /** - * Simple error handler - * for now specifically handle only UserCancelled - * - * @param error [TangemError] - */ - private fun handleTangemError(error: TangemError): Exception { - if (error is TangemSdkError.UserCancelled) { - return UserCancelledException() - } - return IllegalStateException(error.customMessage) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/proxy/di/ProxyModule.kt b/app/src/main/java/com/tangem/tap/proxy/di/ProxyModule.kt index 8c0c4eda47..d72bae0238 100644 --- a/app/src/main/java/com/tangem/tap/proxy/di/ProxyModule.kt +++ b/app/src/main/java/com/tangem/tap/proxy/di/ProxyModule.kt @@ -4,9 +4,7 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.tokens.repository.CurrenciesRepository -import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.walletmanager.WalletManagersFacade -import com.tangem.lib.crypto.DerivationManager import com.tangem.lib.crypto.TransactionManager import com.tangem.lib.crypto.UserWalletManager import com.tangem.tap.proxy.* @@ -56,18 +54,4 @@ internal object ProxyModule { walletManagersFacade = walletManagersFacade, ) } - - @Provides - @Singleton - fun provideDerivationManager( - appStateHolder: AppStateHolder, - currenciesRepository: CurrenciesRepository, - networksRepository: NetworksRepository, - ): DerivationManager { - return DerivationManagerImpl( - appStateHolder = appStateHolder, - currenciesRepository = currenciesRepository, - networksRepository = networksRepository, - ) - } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt index d10051fa08..3bfa07a2ce 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt @@ -8,7 +8,6 @@ import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository import com.tangem.domain.card.ScanCardProcessor import com.tangem.domain.card.ScanCardUseCase import com.tangem.domain.card.repository.CardSdkConfigRepository -import com.tangem.domain.card.repository.DerivationsRepository import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.walletmanager.WalletManagersFacade @@ -18,7 +17,6 @@ import com.tangem.features.managetokens.featuretoggles.ManageTokensFeatureToggle import com.tangem.features.managetokens.navigation.ManageTokensRouter import com.tangem.features.send.api.featuretoggles.SendFeatureToggles import com.tangem.features.send.api.navigation.SendRouter -import com.tangem.features.tester.api.TesterFeatureToggles import com.tangem.features.tester.api.TesterRouter import com.tangem.features.tokendetails.navigation.TokenDetailsRouter import com.tangem.features.wallet.navigation.WalletRouter @@ -53,11 +51,7 @@ data class DaggerGraphState( val sendFeatureToggles: SendFeatureToggles? = null, val sendRouter: SendRouter? = null, val qrScanningRouter: QrScanningRouter? = null, - - // FIXME: It is used only for TokensList screen. Remove after refactoring of TokensList val currenciesRepository: CurrenciesRepository? = null, - val derivationsRepository: DerivationsRepository? = null, - val testerFeatureToggles: TesterFeatureToggles? = null, val blockchainDataStorage: BlockchainDataStorage? = null, ) : StateType { diff --git a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json index e91e7f03e8..13b20fb797 100644 --- a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json +++ b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json @@ -18,9 +18,5 @@ { "name": "WALLETS_SCROLLING_PREVIEW_ENABLED", "version": "5.5.0" - }, - { - "name": "DERIVE_PUBLIC_KEYS_REFACTORING_ENABLED", - "version": "5.5.0" } ] diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/DerivePublicKeysUseCase.kt b/domain/card/src/main/kotlin/com/tangem/domain/card/DerivePublicKeysUseCase.kt index 8aad6ceb32..3061e99534 100644 --- a/domain/card/src/main/kotlin/com/tangem/domain/card/DerivePublicKeysUseCase.kt +++ b/domain/card/src/main/kotlin/com/tangem/domain/card/DerivePublicKeysUseCase.kt @@ -1,21 +1,17 @@ package com.tangem.domain.card import arrow.core.Either -import com.tangem.common.extensions.ByteArrayKey -import com.tangem.crypto.hdWallet.DerivationPath +import com.tangem.domain.card.repository.DerivationsRepository import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.operations.derivation.DerivationTaskResponse -// TODO: Convert to class [REDACTED_JIRA] -interface DerivePublicKeysUseCase { +class DerivePublicKeysUseCase( + private val derivationsRepository: DerivationsRepository, +) { - // TODO: delete [REDACTED_JIRA] - @Deprecated(message = "Use invoke(cardId: String?, derivations: Map>) instead") - suspend operator fun invoke( - cardId: String? = null, - derivations: Map>, - ): Either - - suspend operator fun invoke(userWalletId: UserWalletId, currencies: List): Either + suspend operator fun invoke(userWalletId: UserWalletId, currencies: List): Either { + return Either.catch { + derivationsRepository.derivePublicKeys(userWalletId, currencies) + } + } } \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/tokens/TokensAction.kt b/domain/legacy/src/main/java/com/tangem/domain/tokens/TokensAction.kt index d5a0d8be8e..90378e19c2 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/tokens/TokensAction.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/tokens/TokensAction.kt @@ -2,8 +2,6 @@ package com.tangem.domain.tokens import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token -import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.wallets.models.UserWallet import org.rekotlin.Action sealed interface TokensAction : Action { @@ -13,14 +11,6 @@ sealed interface TokensAction : Action { object ManageAccess : SetArgs object ReadAccess : SetArgs } - - data class SaveChanges( - val currentTokens: List, - val currentCoins: List, - val changedTokens: List, - val changedCoins: List, - val userWallet: UserWallet, - ) : TokensAction } data class TokenWithBlockchain(val token: Token, val blockchain: Blockchain) \ No newline at end of file diff --git a/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/ReferralInteractorImpl.kt b/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/ReferralInteractorImpl.kt index 379d153a48..154764b162 100644 --- a/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/ReferralInteractorImpl.kt +++ b/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/ReferralInteractorImpl.kt @@ -4,30 +4,23 @@ import arrow.core.getOrElse import com.tangem.domain.card.DerivePublicKeysUseCase import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase -import com.tangem.feature.referral.domain.converter.TokensConverter import com.tangem.feature.referral.domain.models.ReferralData import com.tangem.feature.referral.domain.models.TokenData -import com.tangem.features.tester.api.TesterFeatureToggles -import com.tangem.lib.crypto.DerivationManager import com.tangem.lib.crypto.UserWalletManager import timber.log.Timber @Suppress("LongParameterList") internal class ReferralInteractorImpl( private val repository: ReferralRepository, - private val derivationManager: DerivationManager, private val userWalletManager: UserWalletManager, - private val tokensConverter: TokensConverter, private val derivePublicKeysUseCase: DerivePublicKeysUseCase, private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, private val addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase, - private val testerFeatureToggles: TesterFeatureToggles, ) : ReferralInteractor { private val tokensForReferral = mutableListOf() - override val isDemoMode: Boolean - get() = repository.isDemoMode + override val isDemoMode: Boolean get() = repository.isDemoMode override suspend fun getReferralStatus(): ReferralData { val referralData = repository.getReferralData(userWalletManager.getWalletId()) @@ -38,19 +31,9 @@ internal class ReferralInteractorImpl( } override suspend fun startReferral(): ReferralData { - return if (tokensForReferral.isNotEmpty()) { - if (testerFeatureToggles.isDerivePublicKeysRefactoringEnabled) { - startReferralNew(tokenData = tokensForReferral.first()) - } else { - // TODO: delete [REDACTED_JIRA] - startReferralLegacy(tokenData = tokensForReferral.first()) - } - } else { - error("Tokens for ref is empty") - } - } + if (tokensForReferral.isEmpty()) error("Tokens for ref is empty") - private suspend fun startReferralNew(tokenData: TokenData): ReferralData { + val tokenData = tokensForReferral.first() val userWallet = getSelectedWalletSyncUseCase().getOrElse { error("Failed to get selected wallet: $it") } @@ -79,18 +62,6 @@ internal class ReferralInteractorImpl( ) } - private suspend fun startReferralLegacy(tokenData: TokenData): ReferralData { - val currency = tokensConverter.convert(tokenData) - val derivationPath = derivationManager.deriveAndAddTokens(currency) - val publicAddress = userWalletManager.getWalletAddress(currency.networkId, derivationPath) - return repository.startReferral( - walletId = userWalletManager.getWalletId(), - networkId = currency.networkId, - tokenId = currency.id, - address = publicAddress, - ) - } - private fun saveReferralTokens(tokens: List) { tokensForReferral.clear() tokensForReferral.addAll(tokens) diff --git a/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/converter/TokensConverter.kt b/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/converter/TokensConverter.kt deleted file mode 100644 index a477782d9f..0000000000 --- a/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/converter/TokensConverter.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.tangem.feature.referral.domain.converter - -import com.tangem.feature.referral.domain.models.TokenData -import com.tangem.lib.crypto.models.Currency -import com.tangem.lib.crypto.models.Currency.NativeToken -import com.tangem.lib.crypto.models.Currency.NonNativeToken -import com.tangem.utils.converter.Converter - -import javax.inject.Inject - -class TokensConverter @Inject constructor() : Converter { - - override fun convert(value: TokenData): Currency { - return if (value.decimalCount != null && - value.contractAddress != null - ) { - NonNativeToken( - id = value.id, - name = value.name, - symbol = value.symbol, - networkId = value.networkId, - contractAddress = value.contractAddress, - decimalCount = value.decimalCount, - ) - } else { - NativeToken( - id = value.id, - name = value.name, - symbol = value.symbol, - networkId = value.networkId, - ) - } - } -} \ No newline at end of file diff --git a/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/di/ReferralDomainModule.kt b/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/di/ReferralDomainModule.kt index 9b90eb5b0a..0cd466fed7 100644 --- a/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/di/ReferralDomainModule.kt +++ b/features/referral/domain/src/main/java/com/tangem/feature/referral/domain/di/ReferralDomainModule.kt @@ -6,9 +6,6 @@ import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.feature.referral.domain.ReferralInteractor import com.tangem.feature.referral.domain.ReferralInteractorImpl import com.tangem.feature.referral.domain.ReferralRepository -import com.tangem.feature.referral.domain.converter.TokensConverter -import com.tangem.features.tester.api.TesterFeatureToggles -import com.tangem.lib.crypto.DerivationManager import com.tangem.lib.crypto.UserWalletManager import dagger.Module import dagger.Provides @@ -24,23 +21,17 @@ class ReferralDomainModule { @ViewModelScoped fun provideReferralInteractor( referralRepository: ReferralRepository, - derivationManager: DerivationManager, userWalletManager: UserWalletManager, - tokensConverter: TokensConverter, derivePublicKeysUseCase: DerivePublicKeysUseCase, getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase, - testerFeatureToggles: TesterFeatureToggles, ): ReferralInteractor { return ReferralInteractorImpl( repository = referralRepository, - derivationManager = derivationManager, userWalletManager = userWalletManager, - tokensConverter = tokensConverter, derivePublicKeysUseCase = derivePublicKeysUseCase, getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, addCryptoCurrenciesUseCase = addCryptoCurrenciesUseCase, - testerFeatureToggles = testerFeatureToggles, ) } } \ No newline at end of file diff --git a/features/tester/api/src/main/java/com/tangem/features/tester/api/TesterFeatureToggles.kt b/features/tester/api/src/main/java/com/tangem/features/tester/api/TesterFeatureToggles.kt deleted file mode 100644 index 84a02490cc..0000000000 --- a/features/tester/api/src/main/java/com/tangem/features/tester/api/TesterFeatureToggles.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.tangem.features.tester.api - -/** - * Tester feature toggles - * -[REDACTED_AUTHOR] - */ -interface TesterFeatureToggles { - - val isDerivePublicKeysRefactoringEnabled: Boolean -} \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/di/TesterFeatureTogglesModule.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/di/TesterFeatureTogglesModule.kt deleted file mode 100644 index ee49bf39b7..0000000000 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/di/TesterFeatureTogglesModule.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.tangem.feature.tester.di - -import com.tangem.core.featuretoggle.manager.FeatureTogglesManager -import com.tangem.feature.tester.featuretoggles.DefaultTesterFeatureToggles -import com.tangem.features.tester.api.TesterFeatureToggles -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -internal object TesterFeatureTogglesModule { - - @Provides - @Singleton - fun provideTesterFeatureToggles(featureTogglesManager: FeatureTogglesManager): TesterFeatureToggles { - return DefaultTesterFeatureToggles(featureTogglesManager = featureTogglesManager) - } -} \ No newline at end of file diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/featuretoggles/DefaultTesterFeatureToggles.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/featuretoggles/DefaultTesterFeatureToggles.kt deleted file mode 100644 index 77e2de389a..0000000000 --- a/features/tester/impl/src/main/java/com/tangem/feature/tester/featuretoggles/DefaultTesterFeatureToggles.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.tangem.feature.tester.featuretoggles - -import com.tangem.core.featuretoggle.manager.FeatureTogglesManager -import com.tangem.features.tester.api.TesterFeatureToggles - -/** - * Default implementation of Tester feature toggles - * - * @property featureTogglesManager manager for getting information about the availability of feature toggles - * -[REDACTED_AUTHOR] - */ -internal class DefaultTesterFeatureToggles( - private val featureTogglesManager: FeatureTogglesManager, -) : TesterFeatureToggles { - - override val isDerivePublicKeysRefactoringEnabled: Boolean - get() = featureTogglesManager.isFeatureEnabled(name = "DERIVE_PUBLIC_KEYS_REFACTORING_ENABLED") -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index 8adfe05590..71d9d95d97 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -5,13 +5,8 @@ import androidx.paging.cachedIn import arrow.core.Either import arrow.core.getOrElse import arrow.core.right -import com.tangem.blockchain.blockchains.cardano.CardanoUtils -import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.address.AddressType -import com.tangem.common.card.EllipticCurve -import com.tangem.common.extensions.ByteArrayKey import com.tangem.common.extensions.isZero -import com.tangem.common.extensions.toMapKey import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.navigation.AppScreen @@ -23,19 +18,14 @@ import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.WrappedList import com.tangem.core.ui.extensions.resourceReference -import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.domain.analytics.ChangeCardAnalyticsContextUseCase import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase -import com.tangem.domain.card.DerivePublicKeysUseCase import com.tangem.domain.card.SetCardWasScannedUseCase import com.tangem.domain.common.CardTypesResolver -import com.tangem.domain.common.configs.CardConfig import com.tangem.domain.common.util.cardTypesResolver -import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.demo.IsDemoCardUseCase -import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.redux.LegacyAction import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.settings.* @@ -66,7 +56,6 @@ import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCard import com.tangem.feature.wallet.presentation.wallet.state.factory.TokenListWithWallet import com.tangem.feature.wallet.presentation.wallet.state.factory.WalletStateFactory import com.tangem.feature.wallet.presentation.wallet.subscribers.MaybeTokenListFlow -import com.tangem.operations.derivation.ExtendedPublicKeysMap import com.tangem.utils.Provider import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.JobHolder @@ -103,7 +92,6 @@ internal class WalletViewModel @Inject constructor( private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase, private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase, private val getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase, - private val derivePublicKeysUseCase: DerivePublicKeysUseCase, private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase, private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase, private val getExploreUrlUseCase: GetExploreUrlUseCase, @@ -295,112 +283,30 @@ internal class WalletViewModel @Inject constructor( } override fun onGenerateMissedAddressesClick(missedAddressCurrencies: List) { - val state = uiState as? WalletState.ContentState ?: return - - analyticsEventsHandler.send(WalletScreenAnalyticsEvent.Basic.CardWasScanned(AnalyticsParam.ScannedFrom.Main)) - analyticsEventsHandler.send(WalletScreenAnalyticsEvent.MainScreen.NoticeScanYourCardTapped) - - viewModelScope.launch(dispatchers.io) { - val userWallet = getWallet(index = state.walletsListConfig.selectedWalletIndex) - - deriveMissingCurrencies( - scanResponse = userWallet.scanResponse, - currencyList = missedAddressCurrencies, - ) { scannedCardResponse -> - updateWalletUseCase( - userWalletId = userWallet.walletId, - update = { it.copy(scanResponse = scannedCardResponse) }, - ) - .onRight { - fetchTokenListUseCase(userWalletId = it.walletId) - } - } - } + // TODO: delete [REDACTED_JIRA] + // val state = uiState as? WalletState.ContentState ?: return + // + // analyticsEventsHandler.send(WalletScreenAnalyticsEvent.Basic.CardWasScanned(AnalyticsParam.ScannedFrom.Main)) + // analyticsEventsHandler.send(WalletScreenAnalyticsEvent.MainScreen.NoticeScanYourCardTapped) + // + // viewModelScope.launch(dispatchers.io) { + // val userWallet = getWallet(index = state.walletsListConfig.selectedWalletIndex) + // + // deriveMissingCurrencies( + // scanResponse = userWallet.scanResponse, + // currencyList = missedAddressCurrencies, + // ) { scannedCardResponse -> + // updateWalletUseCase( + // userWalletId = userWallet.walletId, + // update = { it.copy(scanResponse = scannedCardResponse) }, + // ) + // .onRight { + // fetchTokenListUseCase(userWalletId = it.walletId) + // } + // } + // } } - @Deprecated("Use DerivePublicKeysUseCase instead") - private fun deriveMissingCurrencies( - scanResponse: ScanResponse, - currencyList: List, - onSuccess: suspend (ScanResponse) -> Unit, - ) { - val config = CardConfig.createConfig(scanResponse.card) - val derivationDataList = currencyList.mapNotNull { - config.primaryCurve(blockchain = Blockchain.fromId(it.network.id.value))?.let { curve -> - getNewDerivations(curve, scanResponse, it) - } - } - - val derivations = buildMap> { - derivationDataList.forEach { - val current = this[it.derivations.first] - if (current != null) { - current.addAll(it.derivations.second) - current.distinct() - } else { - this[it.derivations.first] = it.derivations.second.toMutableList() - } - } - }.ifEmpty { return } - - viewModelScope.launch(dispatchers.io) { - derivePublicKeysUseCase(cardId = null, derivations = derivations) - .onRight { - val newDerivedKeys = it.entries - val oldDerivedKeys = scanResponse.derivedKeys - - val walletKeys = (newDerivedKeys.keys + oldDerivedKeys.keys).toSet() - - val updatedDerivedKeys = walletKeys.associateWith { walletKey -> - val oldDerivations = ExtendedPublicKeysMap(oldDerivedKeys[walletKey] ?: emptyMap()) - val newDerivations = newDerivedKeys[walletKey] ?: ExtendedPublicKeysMap(emptyMap()) - ExtendedPublicKeysMap(oldDerivations + newDerivations) - } - val updatedScanResponse = scanResponse.copy(derivedKeys = updatedDerivedKeys) - - onSuccess(updatedScanResponse) - } - } - } - - private fun getNewDerivations( - curve: EllipticCurve, - scanResponse: ScanResponse, - currency: CryptoCurrency, - ): DerivationData? { - val wallet = scanResponse.card.wallets.firstOrNull { it.curve == curve } ?: return null - - val blockchain = Blockchain.fromId(currency.network.id.value) - val supportedCurves = blockchain.getSupportedCurves() - val path = blockchain.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle()) - .takeIf { supportedCurves.contains(curve) } - - val customPath = currency.network.derivationPath.value?.let { - DerivationPath(it) - }.takeIf { supportedCurves.contains(curve) } - - val bothCandidates = listOfNotNull(path, customPath).distinct().toMutableList() - if (bothCandidates.isEmpty()) return null - - if (currency is CryptoCurrency.Coin && blockchain == Blockchain.Cardano) { - currency.network.derivationPath.value?.let { - bothCandidates.add(CardanoUtils.extendedDerivationPath(DerivationPath(it))) - } - } - - val mapKeyOfWalletPublicKey = wallet.publicKey.toMapKey() - val alreadyDerivedKeys: ExtendedPublicKeysMap = - scanResponse.derivedKeys[mapKeyOfWalletPublicKey] ?: ExtendedPublicKeysMap(emptyMap()) - val alreadyDerivedPaths = alreadyDerivedKeys.keys.toList() - - val toDerive = bothCandidates.filterNot { alreadyDerivedPaths.contains(it) } - if (toDerive.isEmpty()) return null - - return DerivationData(derivations = mapKeyOfWalletPublicKey to toDerive) - } - - class DerivationData(val derivations: Pair>) - override fun onScanToUnlockWalletClick() { val state = uiState as? WalletState.ContentState ?: return diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt index ae87db6d02..0a09fcf2c4 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt @@ -1,19 +1,10 @@ package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents -import com.tangem.blockchain.blockchains.cardano.CardanoUtils -import com.tangem.blockchain.common.Blockchain -import com.tangem.common.card.EllipticCurve -import com.tangem.common.extensions.ByteArrayKey -import com.tangem.common.extensions.toMapKey import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.ui.extensions.resourceReference -import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.domain.card.DerivePublicKeysUseCase import com.tangem.domain.card.SetCardWasScannedUseCase -import com.tangem.domain.common.configs.CardConfig -import com.tangem.domain.common.util.derivationStyleProvider -import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.redux.LegacyAction import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.settings.NeverToSuggestRateAppUseCase @@ -23,7 +14,6 @@ import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UnlockWalletsError import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.domain.wallets.usecase.UnlockWalletsUseCase -import com.tangem.domain.wallets.usecase.UpdateWalletUseCase import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.Basic import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen @@ -35,8 +25,6 @@ import com.tangem.feature.wallet.presentation.wallet.state.WalletEvent import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController import com.tangem.feature.wallet.presentation.wallet.state2.transformers.CloseBottomSheetTransformer import com.tangem.feature.wallet.presentation.wallet.state2.utils.WalletEventSender -import com.tangem.features.tester.api.TesterFeatureToggles -import com.tangem.operations.derivation.ExtendedPublicKeysMap import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.hilt.android.scopes.ViewModelScoped import kotlinx.coroutines.launch @@ -70,7 +58,6 @@ internal class WalletWarningsClickIntentsImplementer @Inject constructor( private val stateHolder: WalletStateController, private val walletEventSender: WalletEventSender, private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, - private val updateWalletUseCase: UpdateWalletUseCase, private val unlockWalletsUseCase: UnlockWalletsUseCase, private val derivePublicKeysUseCase: DerivePublicKeysUseCase, private val scanCardToUnlockWalletClickHandler: ScanCardToUnlockWalletClickHandler, @@ -80,7 +67,6 @@ internal class WalletWarningsClickIntentsImplementer @Inject constructor( private val remindToRateAppLaterUseCase: RemindToRateAppLaterUseCase, private val analyticsEventHandler: AnalyticsEventHandler, private val reduxStateHolder: ReduxStateHolder, - private val testerFeatureToggles: TesterFeatureToggles, private val dispatchers: CoroutineDispatcherProvider, ) : BaseWalletClickIntents(), WalletWarningsClickIntents { @@ -117,111 +103,15 @@ internal class WalletWarningsClickIntentsImplementer @Inject constructor( analyticsEventHandler.send(MainScreen.NoticeScanYourCardTapped) viewModelScope.launch(dispatchers.main) { - if (testerFeatureToggles.isDerivePublicKeysRefactoringEnabled) { - derivePublicKeysUseCase( - userWalletId = userWallet.walletId, - currencies = missedAddressCurrencies, - ) - .onRight { fetchTokenListUseCase(userWalletId = userWallet.walletId) } - .onLeft { Timber.e("Failed to derive public keys: $it") } - } else { - // TODO: delete [REDACTED_JIRA] - deriveMissingCurrencies( - scanResponse = userWallet.scanResponse, - currencyList = missedAddressCurrencies, - ) { scannedCardResponse -> - updateWalletUseCase( - userWalletId = userWallet.walletId, - update = { it.copy(scanResponse = scannedCardResponse) }, - ) - .onRight { fetchTokenListUseCase(userWalletId = it.walletId) } - } - } + derivePublicKeysUseCase( + userWalletId = userWallet.walletId, + currencies = missedAddressCurrencies, + ) + .onRight { fetchTokenListUseCase(userWalletId = userWallet.walletId) } + .onLeft { Timber.e("Failed to derive public keys: $it") } } } - private fun deriveMissingCurrencies( - scanResponse: ScanResponse, - currencyList: List, - onSuccess: suspend (ScanResponse) -> Unit, - ) { - val config = CardConfig.createConfig(scanResponse.card) - val derivationDataList = currencyList.mapNotNull { - config.primaryCurve(blockchain = Blockchain.fromId(it.network.id.value))?.let { curve -> - getNewDerivations(curve, scanResponse, it) - } - } - - val derivations = buildMap> { - derivationDataList.forEach { - val current = this[it.derivations.first] - if (current != null) { - current.addAll(it.derivations.second) - current.distinct() - } else { - this[it.derivations.first] = it.derivations.second.toMutableList() - } - } - }.ifEmpty { return } - - viewModelScope.launch(dispatchers.io) { - derivePublicKeysUseCase(cardId = null, derivations = derivations) - .onRight { - val newDerivedKeys = it.entries - val oldDerivedKeys = scanResponse.derivedKeys - - val walletKeys = (newDerivedKeys.keys + oldDerivedKeys.keys).toSet() - - val updatedDerivedKeys = walletKeys.associateWith { walletKey -> - val oldDerivations = ExtendedPublicKeysMap(oldDerivedKeys[walletKey] ?: emptyMap()) - val newDerivations = newDerivedKeys[walletKey] ?: ExtendedPublicKeysMap(emptyMap()) - ExtendedPublicKeysMap(oldDerivations + newDerivations) - } - val updatedScanResponse = scanResponse.copy(derivedKeys = updatedDerivedKeys) - - onSuccess(updatedScanResponse) - } - } - } - - private fun getNewDerivations( - curve: EllipticCurve, - scanResponse: ScanResponse, - currency: CryptoCurrency, - ): DerivationData? { - val wallet = scanResponse.card.wallets.firstOrNull { it.curve == curve } ?: return null - - val blockchain = Blockchain.fromId(currency.network.id.value) - val supportedCurves = blockchain.getSupportedCurves() - val path = blockchain.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle()) - .takeIf { supportedCurves.contains(curve) } - - val customPath = currency.network.derivationPath.value?.let { - DerivationPath(it) - }.takeIf { supportedCurves.contains(curve) } - - val bothCandidates = listOfNotNull(path, customPath).distinct().toMutableList() - if (bothCandidates.isEmpty()) return null - - if (currency is CryptoCurrency.Coin && blockchain == Blockchain.Cardano) { - currency.network.derivationPath.value?.let { - bothCandidates.add(CardanoUtils.extendedDerivationPath(DerivationPath(it))) - } - } - - val mapKeyOfWalletPublicKey = wallet.publicKey.toMapKey() - val alreadyDerivedKeys: ExtendedPublicKeysMap = - scanResponse.derivedKeys[mapKeyOfWalletPublicKey] ?: ExtendedPublicKeysMap(emptyMap()) - val alreadyDerivedPaths = alreadyDerivedKeys.keys.toList() - - val toDerive = bothCandidates.filterNot { alreadyDerivedPaths.contains(it) } - if (toDerive.isEmpty()) return null - - return DerivationData(derivations = mapKeyOfWalletPublicKey to toDerive) - } - - class DerivationData(val derivations: Pair>) - override fun onOpenUnlockWalletsBottomSheetClick() { val config = requireNotNull(stateHolder.getSelectedWallet().bottomSheetConfig) { "Impossible to open unlock wallet bottom sheet if it's null" diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/DerivationManager.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/DerivationManager.kt deleted file mode 100644 index e3f12c83f6..0000000000 --- a/libs/crypto/src/main/java/com/tangem/lib/crypto/DerivationManager.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.tangem.lib.crypto - -import com.tangem.lib.crypto.models.Currency - -@Deprecated(message = "Use DerivePublicKeysUseCase instead") -interface DerivationManager { - - /** - * Makes derivation for [Currency] if it is missing and adds token to wallet - */ - suspend fun deriveAndAddTokens(currency: Currency): String -} \ No newline at end of file From 710e944eae03374f840e8c5d4b9dc0afacd96ec0 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 7 Feb 2024 14:14:11 +0300 Subject: [PATCH 13/53] Updated on 2026-08-14 --- .../api/common/BigDecimalAdapter.kt | 2 +- .../datasource/api/common/DateTimeAdapter.kt | 23 +++++ .../datasource/api/common/LocalDateAdapter.kt | 8 +- .../response/ApiResponseCallAdapterFactory.kt | 2 +- .../com/tangem/datasource/di/MoshiModule.kt | 2 + .../data/visa/utils/VisaCurrencyFactory.kt | 10 +-- libs/visa/build.gradle.kts | 17 +++- .../visa/DefaultVisaContractInfoProvider.kt | 42 +++++----- .../lib/visa/VisaContractInfoProvider.kt | 4 +- .../kotlin/com/tangem/lib/visa/api/VisaApi.kt | 16 ++++ .../com/tangem/lib/visa/api/VisaApiBuilder.kt | 61 ++++++++++++++ ...sAndLimits.kt => VisaBalancesAndLimits.kt} | 2 +- .../lib/visa/model/VisaTxHistoryResponse.kt | 84 +++++++++++++++++++ .../com/tangem/lib/visa/utils/VisaConfig.kt | 3 + 14 files changed, 238 insertions(+), 38 deletions(-) create mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/common/DateTimeAdapter.kt create mode 100644 libs/visa/src/main/kotlin/com/tangem/lib/visa/api/VisaApi.kt create mode 100644 libs/visa/src/main/kotlin/com/tangem/lib/visa/api/VisaApiBuilder.kt rename libs/visa/src/main/kotlin/com/tangem/lib/visa/model/{BalancesAndLimits.kt => VisaBalancesAndLimits.kt} (96%) create mode 100644 libs/visa/src/main/kotlin/com/tangem/lib/visa/model/VisaTxHistoryResponse.kt diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/common/BigDecimalAdapter.kt b/core/datasource/src/main/java/com/tangem/datasource/api/common/BigDecimalAdapter.kt index 568b042179..e81aa15e06 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/common/BigDecimalAdapter.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/common/BigDecimalAdapter.kt @@ -7,7 +7,7 @@ import java.math.BigDecimal /** [REDACTED_AUTHOR] */ -class BigDecimalAdapter { +internal class BigDecimalAdapter { @FromJson fun fromJson(value: String) = BigDecimal(value) diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/common/DateTimeAdapter.kt b/core/datasource/src/main/java/com/tangem/datasource/api/common/DateTimeAdapter.kt new file mode 100644 index 0000000000..38f4b81532 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/api/common/DateTimeAdapter.kt @@ -0,0 +1,23 @@ +package com.tangem.datasource.api.common + +import com.squareup.moshi.* +import org.joda.time.DateTime + +internal class DateTimeAdapter : JsonAdapter() { + + @FromJson + override fun fromJson(reader: JsonReader): DateTime? { + val dateString = reader.nextString() ?: return null + + return DateTime.parse(dateString) + } + + @ToJson + override fun toJson(writer: JsonWriter, value: DateTime?) { + if (value != null) { + writer.value(value.toString()) + } else { + writer.nullValue() + } + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/common/LocalDateAdapter.kt b/core/datasource/src/main/java/com/tangem/datasource/api/common/LocalDateAdapter.kt index b11c76fccd..95978206f2 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/common/LocalDateAdapter.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/common/LocalDateAdapter.kt @@ -1,14 +1,10 @@ package com.tangem.datasource.api.common -import com.squareup.moshi.FromJson -import com.squareup.moshi.JsonAdapter -import com.squareup.moshi.JsonReader -import com.squareup.moshi.JsonWriter -import com.squareup.moshi.ToJson +import com.squareup.moshi.* import org.joda.time.LocalDate import org.joda.time.format.DateTimeFormat -class LocalDateAdapter : JsonAdapter() { +internal class LocalDateAdapter : JsonAdapter() { private val formatter = DateTimeFormat.forPattern("yyyy-MM-dd") diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/common/response/ApiResponseCallAdapterFactory.kt b/core/datasource/src/main/java/com/tangem/datasource/api/common/response/ApiResponseCallAdapterFactory.kt index 11eca2e0d6..8f8c65e92d 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/common/response/ApiResponseCallAdapterFactory.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/common/response/ApiResponseCallAdapterFactory.kt @@ -6,7 +6,7 @@ import retrofit2.Retrofit import java.lang.reflect.ParameterizedType import java.lang.reflect.Type -internal class ApiResponseCallAdapterFactory private constructor() : CallAdapter.Factory() { +class ApiResponseCallAdapterFactory private constructor() : CallAdapter.Factory() { override fun get(returnType: Type, annotations: Array, retrofit: Retrofit): CallAdapter<*, *>? { if (getRawType(returnType) != Call::class.java) { diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/MoshiModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/MoshiModule.kt index bf3d94095f..c22d4433bf 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/MoshiModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/MoshiModule.kt @@ -4,6 +4,7 @@ import com.squareup.moshi.Moshi import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory import com.tangem.common.json.MoshiJsonConverter import com.tangem.datasource.api.common.BigDecimalAdapter +import com.tangem.datasource.api.common.DateTimeAdapter import com.tangem.datasource.api.common.LocalDateAdapter import dagger.Module import dagger.Provides @@ -22,6 +23,7 @@ class MoshiModule { return Moshi.Builder() .add(BigDecimalAdapter()) .add(LocalDateAdapter()) + .add(DateTimeAdapter()) .add(KotlinJsonAdapterFactory()) .build() } diff --git a/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaCurrencyFactory.kt b/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaCurrencyFactory.kt index d2555bdf84..92bb35d307 100644 --- a/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaCurrencyFactory.kt +++ b/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaCurrencyFactory.kt @@ -1,7 +1,7 @@ package com.tangem.data.visa.utils import com.tangem.domain.visa.model.VisaCurrency -import com.tangem.lib.visa.model.BalancesAndLimits +import com.tangem.lib.visa.model.VisaBalancesAndLimits import org.joda.time.DateTime import org.joda.time.DateTimeZone import org.joda.time.Instant @@ -10,7 +10,7 @@ import java.math.BigInteger internal class VisaCurrencyFactory { - fun create(balancesAndLimits: BalancesAndLimits, fiatRate: BigDecimal?): VisaCurrency { + fun create(balancesAndLimits: VisaBalancesAndLimits, fiatRate: BigDecimal?): VisaCurrency { val now = Instant.now() val currentLimit = if (balancesAndLimits.limitsChangeDate > now) { balancesAndLimits.oldLimits @@ -43,7 +43,7 @@ internal class VisaCurrencyFactory { ) } - private fun getRemainingOtp(currentLimit: BalancesAndLimits.Limits, now: Instant): BigDecimal { + private fun getRemainingOtp(currentLimit: VisaBalancesAndLimits.Limits, now: Instant): BigDecimal { if (currentLimit.expirationDate >= now) { return currentLimit.spendLimit.limit - currentLimit.spendLimit.spent } @@ -51,7 +51,7 @@ internal class VisaCurrencyFactory { return currentLimit.spendLimit.limit } - private fun getRemainingNoOtp(currentLimit: BalancesAndLimits.Limits, now: Instant): BigDecimal { + private fun getRemainingNoOtp(currentLimit: VisaBalancesAndLimits.Limits, now: Instant): BigDecimal { if (currentLimit.expirationDate >= now) { return currentLimit.noOtpLimit.limit - currentLimit.noOtpLimit.spent } @@ -59,7 +59,7 @@ internal class VisaCurrencyFactory { return currentLimit.noOtpLimit.limit } - private fun getLimitsExpirationDate(currentLimits: BalancesAndLimits.Limits, now: Instant): DateTime { + private fun getLimitsExpirationDate(currentLimits: VisaBalancesAndLimits.Limits, now: Instant): DateTime { val expirationDate = if (currentLimits.expirationDate >= now) { currentLimits.expirationDate.toDateTime() } else { diff --git a/libs/visa/build.gradle.kts b/libs/visa/build.gradle.kts index bc0aa12ec9..8ef254a4e4 100644 --- a/libs/visa/build.gradle.kts +++ b/libs/visa/build.gradle.kts @@ -1,17 +1,30 @@ plugins { - alias(deps.plugins.kotlin.jvm) + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) id("configuration") } +android { + namespace = "com.tangem.libs.visa" +} + dependencies { /** Project */ implementation(projects.core.utils) + implementation(projects.core.datasource) + implementation(projects.data.common) + + /** Libs - Network */ + implementation(deps.moshi.kotlin) + implementation(deps.okHttp) + implementation(deps.okHttp.prettyLogging) + implementation(deps.retrofit) + implementation(deps.retrofit.moshi) /** Libs - Other */ implementation(deps.web3j.core) implementation(deps.kotlin.coroutines) implementation(deps.arrow.fx) implementation(deps.jodatime) - implementation(deps.okHttp.prettyLogging) } \ No newline at end of file diff --git a/libs/visa/src/main/kotlin/com/tangem/lib/visa/DefaultVisaContractInfoProvider.kt b/libs/visa/src/main/kotlin/com/tangem/lib/visa/DefaultVisaContractInfoProvider.kt index 1b038aa5f9..c2ac96c707 100644 --- a/libs/visa/src/main/kotlin/com/tangem/lib/visa/DefaultVisaContractInfoProvider.kt +++ b/libs/visa/src/main/kotlin/com/tangem/lib/visa/DefaultVisaContractInfoProvider.kt @@ -1,9 +1,9 @@ package com.tangem.lib.visa import arrow.fx.coroutines.parZip -import com.tangem.lib.visa.model.BalancesAndLimits -import com.tangem.lib.visa.model.BalancesAndLimits.Balances -import com.tangem.lib.visa.model.BalancesAndLimits.Limits +import com.tangem.lib.visa.model.VisaBalancesAndLimits +import com.tangem.lib.visa.model.VisaBalancesAndLimits.Balances +import com.tangem.lib.visa.model.VisaBalancesAndLimits.Limits import com.tangem.lib.visa.utils.toBigDecimal import com.tangem.lib.visa.utils.toInstant import com.tangem.utils.coroutines.CoroutineDispatcherProvider @@ -21,22 +21,24 @@ internal class DefaultVisaContractInfoProvider( private val dispatchers: CoroutineDispatcherProvider, ) : VisaContractInfoProvider { - override suspend fun getBalancesAndLimits(walletAddress: String): BalancesAndLimits = withContext(dispatchers.io) { - val tangemBridgeProcessor = TangemBridgeProcessor.load( - /* contractAddress = */ bridgeProcessorAddress, - /* web3j = */ web3j, - /* transactionManager = */ transactionManager, - /* contractGasProvider = */ gasProvider, - ) + override suspend fun getBalancesAndLimits(walletAddress: String): VisaBalancesAndLimits { + return withContext(dispatchers.io) { + val tangemBridgeProcessor = TangemBridgeProcessor.load( + /* contractAddress = */ bridgeProcessorAddress, + /* web3j = */ web3j, + /* transactionManager = */ transactionManager, + /* contractGasProvider = */ gasProvider, + ) - parZip( - dispatchers.io, - { loadPaymentAccount(walletAddress, tangemBridgeProcessor) }, - { loadPaymentTokenInfo(tangemBridgeProcessor) }, - { paymentAccount, paymentToken -> - fetchBalancesAndLimits(paymentAccount, paymentToken) - }, - ) + parZip( + dispatchers.io, + { loadPaymentAccount(walletAddress, tangemBridgeProcessor) }, + { loadPaymentTokenInfo(tangemBridgeProcessor) }, + { paymentAccount, paymentToken -> + fetchBalancesAndLimits(paymentAccount, paymentToken) + }, + ) + } } private fun loadPaymentAccount( @@ -62,12 +64,12 @@ internal class DefaultVisaContractInfoProvider( private suspend fun fetchBalancesAndLimits( paymentAccount: TangemPaymentAccount, paymentToken: PaymentTokenInfo, - ): BalancesAndLimits = parZip( + ): VisaBalancesAndLimits = parZip( dispatchers.io, { fetchBalances(paymentAccount, paymentToken) }, { fetchLimits(paymentAccount, paymentToken) }, { balances, (oldLimit, newLimit, changeDate) -> - BalancesAndLimits(balances, oldLimit, newLimit, changeDate) + VisaBalancesAndLimits(balances, oldLimit, newLimit, changeDate) }, ) diff --git a/libs/visa/src/main/kotlin/com/tangem/lib/visa/VisaContractInfoProvider.kt b/libs/visa/src/main/kotlin/com/tangem/lib/visa/VisaContractInfoProvider.kt index efb5ab487a..35a8a3c4ad 100644 --- a/libs/visa/src/main/kotlin/com/tangem/lib/visa/VisaContractInfoProvider.kt +++ b/libs/visa/src/main/kotlin/com/tangem/lib/visa/VisaContractInfoProvider.kt @@ -2,7 +2,7 @@ package com.tangem.lib.visa import com.ihsanbal.logging.Level import com.ihsanbal.logging.LoggingInterceptor -import com.tangem.lib.visa.model.BalancesAndLimits +import com.tangem.lib.visa.model.VisaBalancesAndLimits import com.tangem.lib.visa.utils.VisaConfig import com.tangem.lib.visa.utils.VisaConfig.NETWORK_LOGS_TAG import com.tangem.lib.visa.utils.toHexString @@ -21,7 +21,7 @@ import java.util.concurrent.TimeUnit interface VisaContractInfoProvider { - suspend fun getBalancesAndLimits(walletAddress: String): BalancesAndLimits + suspend fun getBalancesAndLimits(walletAddress: String): VisaBalancesAndLimits class Builder( private val isNetworkLoggingEnabled: Boolean, diff --git a/libs/visa/src/main/kotlin/com/tangem/lib/visa/api/VisaApi.kt b/libs/visa/src/main/kotlin/com/tangem/lib/visa/api/VisaApi.kt new file mode 100644 index 0000000000..b05b3096a2 --- /dev/null +++ b/libs/visa/src/main/kotlin/com/tangem/lib/visa/api/VisaApi.kt @@ -0,0 +1,16 @@ +package com.tangem.lib.visa.api + +import com.tangem.datasource.api.common.response.ApiResponse +import com.tangem.lib.visa.model.VisaTxHistoryResponse +import retrofit2.http.GET +import retrofit2.http.Query + +interface VisaApi { + + @GET("transaction") + suspend fun getTxHistory( + @Query("card_public_key") cardPublicKey: String, + @Query("limit") limit: Int, + @Query("offset") offset: Int, + ): ApiResponse +} \ No newline at end of file diff --git a/libs/visa/src/main/kotlin/com/tangem/lib/visa/api/VisaApiBuilder.kt b/libs/visa/src/main/kotlin/com/tangem/lib/visa/api/VisaApiBuilder.kt new file mode 100644 index 0000000000..50f200249b --- /dev/null +++ b/libs/visa/src/main/kotlin/com/tangem/lib/visa/api/VisaApiBuilder.kt @@ -0,0 +1,61 @@ +package com.tangem.lib.visa.api + +import android.util.Log +import com.ihsanbal.logging.Level +import com.ihsanbal.logging.LoggingInterceptor +import com.squareup.moshi.Moshi +import com.tangem.datasource.api.common.response.ApiResponseCallAdapterFactory +import com.tangem.lib.visa.utils.VisaConfig +import com.tangem.lib.visa.utils.VisaConfig.NETWORK_LOGS_TAG +import okhttp3.Interceptor +import okhttp3.OkHttpClient +import retrofit2.Retrofit +import retrofit2.converter.moshi.MoshiConverterFactory +import java.util.concurrent.TimeUnit + +class VisaApiBuilder( + private val useDevApi: Boolean, + private val isNetworkLoggingEnabled: Boolean, + private val moshi: Moshi, + private val networkTimeoutSeconds: Long = VisaConfig.NETWORK_TIMEOUT_SECONDS, +) { + + fun build(): VisaApi { + val okHttpClient = createOkHttpClient() + val retrofit = createRetrofit(okHttpClient) + return retrofit.create(VisaApi::class.java) + } + + private fun createOkHttpClient(): OkHttpClient { + val builder = OkHttpClient.Builder().apply { + connectTimeout(networkTimeoutSeconds, TimeUnit.SECONDS) + readTimeout(networkTimeoutSeconds, TimeUnit.SECONDS) + writeTimeout(networkTimeoutSeconds, TimeUnit.SECONDS) + + if (isNetworkLoggingEnabled) { + addInterceptor(createNetworkLoggingInterceptor()) + } + } + + return builder.build() + } + + private fun createRetrofit(okHttpClient: OkHttpClient): Retrofit { + val baseUrl = if (useDevApi) VisaConfig.VISA_API_DEV_URL else VisaConfig.VISA_API_PROD_URL + + return Retrofit.Builder() + .addConverterFactory(MoshiConverterFactory.create(moshi)) + .addCallAdapterFactory(ApiResponseCallAdapterFactory.create()) + .baseUrl(baseUrl) + .client(okHttpClient) + .build() + } +} + +private fun createNetworkLoggingInterceptor(): Interceptor { + return LoggingInterceptor.Builder() + .setLevel(Level.BODY) + .log(Log.VERBOSE) + .tag(NETWORK_LOGS_TAG) + .build() +} \ No newline at end of file diff --git a/libs/visa/src/main/kotlin/com/tangem/lib/visa/model/BalancesAndLimits.kt b/libs/visa/src/main/kotlin/com/tangem/lib/visa/model/VisaBalancesAndLimits.kt similarity index 96% rename from libs/visa/src/main/kotlin/com/tangem/lib/visa/model/BalancesAndLimits.kt rename to libs/visa/src/main/kotlin/com/tangem/lib/visa/model/VisaBalancesAndLimits.kt index 5098ee3c43..50145e05da 100644 --- a/libs/visa/src/main/kotlin/com/tangem/lib/visa/model/BalancesAndLimits.kt +++ b/libs/visa/src/main/kotlin/com/tangem/lib/visa/model/VisaBalancesAndLimits.kt @@ -4,7 +4,7 @@ import org.joda.time.Instant import java.math.BigDecimal import java.math.BigInteger -data class BalancesAndLimits( +data class VisaBalancesAndLimits( val balances: Balances, val oldLimits: Limits, val newLimits: Limits, diff --git a/libs/visa/src/main/kotlin/com/tangem/lib/visa/model/VisaTxHistoryResponse.kt b/libs/visa/src/main/kotlin/com/tangem/lib/visa/model/VisaTxHistoryResponse.kt new file mode 100644 index 0000000000..dcec060570 --- /dev/null +++ b/libs/visa/src/main/kotlin/com/tangem/lib/visa/model/VisaTxHistoryResponse.kt @@ -0,0 +1,84 @@ +package com.tangem.lib.visa.model + +import com.squareup.moshi.Json +import org.joda.time.DateTime +import java.math.BigDecimal + +data class VisaTxHistoryResponse( + @Json(name = "card_wallet_address") + val cardWalletAddress: String, + @Json(name = "transactions") + val transactions: List, +) { + + data class Transaction( + @Json(name = "auth_code") + val authCode: String?, + @Json(name = "billing_amount") + val billingAmount: BigDecimal, + @Json(name = "billing_currency_code") + val billingCurrencyCode: Int, + @Json(name = "blockchain_amount") + val blockchainAmount: BigDecimal, + @Json(name = "blockchain_coin_name") + val blockchainCoinName: String, + @Json(name = "blockchain_fee") + val blockchainFee: BigDecimal, + // @Json(name = "local_dt") + // val localDate: DateTime?, + @Json(name = "merchant_category_code") + val merchantCategoryCode: String?, + @Json(name = "merchant_city") + val merchantCity: String?, + @Json(name = "merchant_country_code") + val merchantCountryCode: String?, + @Json(name = "merchant_name") + val merchantName: String?, + @Json(name = "requests") + val requests: List, + @Json(name = "rrn") + val rrn: String?, + @Json(name = "transaction_amount") + val transactionAmount: BigDecimal, + @Json(name = "transaction_currency_code") + val transactionCurrencyCode: Int, + @Json(name = "transaction_dt") + val transactionDt: DateTime, + @Json(name = "transaction_id") + val transactionId: Long, + @Json(name = "transaction_status") + val transactionStatus: String, + @Json(name = "transaction_type") + val transactionType: String, + ) { + + data class Request( + @Json(name = "billing_amount") + val billingAmount: BigDecimal, + @Json(name = "billing_currency_code") + val billingCurrencyCode: Int, + @Json(name = "blockchain_amount") + val blockchainAmount: BigDecimal, + @Json(name = "blockchain_fee") + val blockchainFee: BigDecimal, + @Json(name = "error_code") + val errorCode: Int, + @Json(name = "request_dt") + val requestDt: DateTime, + @Json(name = "request_status") + val requestStatus: String, + @Json(name = "request_type") + val requestType: String, + @Json(name = "transaction_amount") + val transactionAmount: BigDecimal, + @Json(name = "transaction_currency_code") + val transactionCurrencyCode: Int, + @Json(name = "transaction_request_id") + val transactionRequestId: Long, + @Json(name = "tx_hash") + val txHash: String?, + @Json(name = "tx_status") + val txStatus: String?, + ) + } +} \ No newline at end of file diff --git a/libs/visa/src/main/kotlin/com/tangem/lib/visa/utils/VisaConfig.kt b/libs/visa/src/main/kotlin/com/tangem/lib/visa/utils/VisaConfig.kt index 6176a369e5..7819bd734e 100644 --- a/libs/visa/src/main/kotlin/com/tangem/lib/visa/utils/VisaConfig.kt +++ b/libs/visa/src/main/kotlin/com/tangem/lib/visa/utils/VisaConfig.kt @@ -9,6 +9,9 @@ internal object VisaConfig { const val GAS_LIMIT = 500_000_000L const val PRIVATE_KEY_LENGTH = 32 + const val VISA_API_PROD_URL = "https://payapi.tangem-tech.com/api/v1/" + const val VISA_API_DEV_URL = "[REDACTED_ENV_URL]" + const val NETWORK_TIMEOUT_SECONDS = 65L const val NETWORK_LOGS_TAG = "VisaNetworkLogs" } \ No newline at end of file From c22dc2df3bd043ddb41c58d1fb3fe0d7abcc0294 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 7 Feb 2024 19:27:16 +0300 Subject: [PATCH 14/53] Updated on 2026-08-14 --- .../tangem/tap/di/domain/VisaDomainModule.kt | 9 ++ data/visa/build.gradle.kts | 3 + .../tangem/data/visa/DefaultVisaRepository.kt | 53 ++++++++-- .../com/tangem/data/visa/di/VisaDataModule.kt | 10 ++ .../tangem/data/visa/utils/CurrencyUtils.kt | 15 +++ .../visa/utils/VisaTxHistoryItemFactory.kt | 19 ++++ .../visa/utils/VisaTxHistoryPagingSource.kt | 100 ++++++++++++++++++ domain/visa/build.gradle.kts | 1 + .../domain/visa/GetVisaTxHistoryUseCase.kt | 32 ++++++ .../domain/visa/model/VisaTxHistoryItem.kt | 15 +++ .../domain/visa/repository/VisaRepository.kt | 9 ++ 11 files changed, 259 insertions(+), 7 deletions(-) create mode 100644 data/visa/src/main/kotlin/com/tangem/data/visa/utils/CurrencyUtils.kt create mode 100644 data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxHistoryItemFactory.kt create mode 100644 data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxHistoryPagingSource.kt create mode 100644 domain/visa/src/main/kotlin/com/tangem/domain/visa/GetVisaTxHistoryUseCase.kt create mode 100644 domain/visa/src/main/kotlin/com/tangem/domain/visa/model/VisaTxHistoryItem.kt diff --git a/app/src/main/java/com/tangem/tap/di/domain/VisaDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/VisaDomainModule.kt index 61170463f0..2795fe2a8c 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/VisaDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/VisaDomainModule.kt @@ -1,18 +1,27 @@ package com.tangem.tap.di.domain import com.tangem.domain.visa.GetVisaCurrencyUseCase +import com.tangem.domain.visa.GetVisaTxHistoryUseCase import com.tangem.domain.visa.repository.VisaRepository import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.components.ViewModelComponent +import dagger.hilt.android.scopes.ViewModelScoped @Module @InstallIn(ViewModelComponent::class) internal object VisaDomainModule { @Provides + @ViewModelScoped fun provideVisaCurrencyUseCase(visaRepository: VisaRepository): GetVisaCurrencyUseCase { return GetVisaCurrencyUseCase(visaRepository) } + + @Provides + @ViewModelScoped + fun provideGetVisaTxHistoryUseCase(visaRepository: VisaRepository): GetVisaTxHistoryUseCase { + return GetVisaTxHistoryUseCase(visaRepository) + } } \ No newline at end of file diff --git a/data/visa/build.gradle.kts b/data/visa/build.gradle.kts index 26f4c9abe7..d41cf5f9c7 100644 --- a/data/visa/build.gradle.kts +++ b/data/visa/build.gradle.kts @@ -33,6 +33,9 @@ dependencies { implementation(deps.arrow.core) implementation(deps.arrow.fx) implementation(deps.jodatime) + implementation(deps.timber) + implementation(deps.androidx.paging.runtime) + implementation(deps.moshi.kotlin) /** Libs - Tangem */ implementation(deps.tangem.blockchain) diff --git a/data/visa/src/main/kotlin/com/tangem/data/visa/DefaultVisaRepository.kt b/data/visa/src/main/kotlin/com/tangem/data/visa/DefaultVisaRepository.kt index 9ba7dd179b..e2e0bc5567 100644 --- a/data/visa/src/main/kotlin/com/tangem/data/visa/DefaultVisaRepository.kt +++ b/data/visa/src/main/kotlin/com/tangem/data/visa/DefaultVisaRepository.kt @@ -1,22 +1,30 @@ package com.tangem.data.visa +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData import arrow.fx.coroutines.parZip import com.tangem.blockchain.common.address.Address import com.tangem.blockchain.common.address.AddressType import com.tangem.common.card.EllipticCurve +import com.tangem.common.extensions.toHexString import com.tangem.data.common.cache.CacheRegistry import com.tangem.data.visa.utils.VisaConfig import com.tangem.data.visa.utils.VisaCurrencyFactory +import com.tangem.data.visa.utils.VisaTxHistoryPagingSource import com.tangem.datasource.api.common.response.getOrThrow import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.visa.model.VisaCurrency +import com.tangem.domain.visa.model.VisaTxHistoryItem import com.tangem.domain.visa.repository.VisaRepository import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import com.tangem.lib.visa.VisaContractInfoProvider +import com.tangem.lib.visa.api.VisaApi import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update import java.math.BigDecimal @@ -24,6 +32,7 @@ import java.math.BigDecimal internal class DefaultVisaRepository( private val visaContractInfoProvider: VisaContractInfoProvider, private val tangemTechApi: TangemTechApi, + private val visaApi: VisaApi, private val cacheRegistry: CacheRegistry, private val userWalletsStore: UserWalletsStore, private val dispatchers: CoroutineDispatcherProvider, @@ -71,14 +80,34 @@ internal class DefaultVisaRepository( ) } - private suspend fun getFiatRate(): BigDecimal? { - val fiatCurrencyId = VisaConfig.fiatCurrency.code.lowercase() - val quotes = tangemTechApi.getQuotes( - currencyId = fiatCurrencyId, - coinIds = VisaConfig.TOKEN_ID, - ).getOrThrow() + override suspend fun getTxHistory( + userWalletId: UserWalletId, + pageSize: Int, + isRefresh: Boolean, + ): Flow> { + val userWallet = findVisaUserWallet(userWalletId) + val cardPubKey = getCardPubKey(userWallet).toHexString() + // val cardPubKey = "03DEF02B1FECC8BD3CFD52CE93235194479E1DE931EF0F55DC194967E7CCC3D12C" // for testing + val pager = Pager( + config = PagingConfig( + pageSize = pageSize, + initialLoadSize = pageSize, + ), + pagingSourceFactory = { + VisaTxHistoryPagingSource( + params = VisaTxHistoryPagingSource.Params( + cardPublicKey = cardPubKey, + pageSize = pageSize, + isRefresh = isRefresh, + ), + cacheRegistry = cacheRegistry, + visaApi = visaApi, + dispatchers = dispatchers, + ) + }, + ) - return quotes.quotes[VisaConfig.TOKEN_ID]?.price + return pager.flow } private suspend fun makeAddress(userWalletId: UserWalletId): String { @@ -91,6 +120,16 @@ internal class DefaultVisaRepository( } } + private suspend fun getFiatRate(): BigDecimal? { + val fiatCurrencyId = VisaConfig.fiatCurrency.code.lowercase() + val quotes = tangemTechApi.getQuotes( + currencyId = fiatCurrencyId, + coinIds = VisaConfig.TOKEN_ID, + ).getOrThrow() + + return quotes.quotes[VisaConfig.TOKEN_ID]?.price + } + private fun makeWalletAddresses(userWallet: UserWallet): Set
{ val walletBlockchain = userWallet.scanResponse.cardTypesResolver.getBlockchain() diff --git a/data/visa/src/main/kotlin/com/tangem/data/visa/di/VisaDataModule.kt b/data/visa/src/main/kotlin/com/tangem/data/visa/di/VisaDataModule.kt index 4204ec5430..a0227da794 100644 --- a/data/visa/src/main/kotlin/com/tangem/data/visa/di/VisaDataModule.kt +++ b/data/visa/src/main/kotlin/com/tangem/data/visa/di/VisaDataModule.kt @@ -1,12 +1,15 @@ package com.tangem.data.visa.di +import com.squareup.moshi.Moshi import com.tangem.data.common.cache.CacheRegistry import com.tangem.data.visa.BuildConfig import com.tangem.data.visa.DefaultVisaRepository import com.tangem.datasource.api.tangemTech.TangemTechApi +import com.tangem.datasource.di.NetworkMoshi import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.visa.repository.VisaRepository import com.tangem.lib.visa.VisaContractInfoProvider +import com.tangem.lib.visa.api.VisaApiBuilder import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module import dagger.Provides @@ -21,6 +24,7 @@ internal object VisaDataModule { @Provides @Singleton fun provideVisaRepository( + @NetworkMoshi moshi: Moshi, tangemTechApi: TangemTechApi, cacheRegistry: CacheRegistry, userWalletsStore: UserWalletsStore, @@ -30,10 +34,16 @@ internal object VisaDataModule { isNetworkLoggingEnabled = BuildConfig.LOG_ENABLED, dispatchers = dispatchers, ).build() + val visaApi = VisaApiBuilder( + useDevApi = true, + isNetworkLoggingEnabled = BuildConfig.LOG_ENABLED, + moshi = moshi, + ).build() return DefaultVisaRepository( contractInfoProvider, tangemTechApi, + visaApi, cacheRegistry, userWalletsStore, dispatchers, diff --git a/data/visa/src/main/kotlin/com/tangem/data/visa/utils/CurrencyUtils.kt b/data/visa/src/main/kotlin/com/tangem/data/visa/utils/CurrencyUtils.kt new file mode 100644 index 0000000000..b664ad4320 --- /dev/null +++ b/data/visa/src/main/kotlin/com/tangem/data/visa/utils/CurrencyUtils.kt @@ -0,0 +1,15 @@ +package com.tangem.data.visa.utils + +import android.os.Build +import timber.log.Timber +import java.util.Currency + +internal fun findCurrencyByNumericCode(code: Int): Currency { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + Currency.getAvailableCurrencies().firstOrNull { it.numericCode == code } + ?: Currency.getInstance(VisaConfig.fiatCurrency.code) + } else { + Timber.w("Unable to get currency by numeric code on API level ${Build.VERSION.SDK_INT}") + Currency.getInstance(VisaConfig.fiatCurrency.code) + } +} \ No newline at end of file diff --git a/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxHistoryItemFactory.kt b/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxHistoryItemFactory.kt new file mode 100644 index 0000000000..86d1439068 --- /dev/null +++ b/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxHistoryItemFactory.kt @@ -0,0 +1,19 @@ +package com.tangem.data.visa.utils + +import com.tangem.domain.visa.model.VisaTxHistoryItem +import com.tangem.lib.visa.model.VisaTxHistoryResponse + +internal class VisaTxHistoryItemFactory { + + fun create(transaction: VisaTxHistoryResponse.Transaction): VisaTxHistoryItem { + return VisaTxHistoryItem( + id = transaction.transactionId.toString(), + date = transaction.transactionDt, + amount = transaction.blockchainAmount, + fiatAmount = transaction.transactionAmount, + merchantName = transaction.merchantName, + status = transaction.transactionStatus, + fiatCurrency = findCurrencyByNumericCode(transaction.transactionCurrencyCode), + ) + } +} \ No newline at end of file diff --git a/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxHistoryPagingSource.kt b/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxHistoryPagingSource.kt new file mode 100644 index 0000000000..1c54f97d8d --- /dev/null +++ b/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxHistoryPagingSource.kt @@ -0,0 +1,100 @@ +package com.tangem.data.visa.utils + +import androidx.paging.PagingSource +import androidx.paging.PagingState +import com.tangem.data.common.cache.CacheRegistry +import com.tangem.datasource.api.common.response.getOrThrow +import com.tangem.domain.visa.model.VisaTxHistoryItem +import com.tangem.lib.visa.api.VisaApi +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.withContext +import timber.log.Timber + +internal class VisaTxHistoryPagingSource( + params: Params, + private val cacheRegistry: CacheRegistry, + private val visaApi: VisaApi, + private val dispatchers: CoroutineDispatcherProvider, +) : PagingSource() { + + private val itemsFactory = VisaTxHistoryItemFactory() + + private val cardPublicKey = params.cardPublicKey + private val pageSize = params.pageSize + private val isRefresh = params.isRefresh + + private val pagedItems = MutableStateFlow>>( + value = emptyMap(), + ) + + override fun getRefreshKey(state: PagingState): Int? { + return state.anchorPosition?.let { anchorPosition -> + val anchorPage = state.closestPageToPosition(anchorPosition) + + anchorPage?.prevKey?.plus(pageSize) ?: anchorPage?.nextKey?.minus(pageSize) + } + } + + override suspend fun load(params: LoadParams): LoadResult { + val offsetToLoad = params.key ?: INITIAL_OFFSET + + return try { + fetchItemsIfExpired(offsetToLoad, pageSize, isRefresh = isRefresh && params is LoadParams.Refresh) + + val items = pagedItems.value[offsetToLoad].orEmpty() + val prevOffset = when { + items.isEmpty() -> null + offsetToLoad > INITIAL_OFFSET -> offsetToLoad - pageSize + else -> null + } + val nextOffset = when { + items.isEmpty() -> INITIAL_OFFSET + items.size % pageSize == 0 -> offsetToLoad + pageSize + else -> null + } + + LoadResult.Page(items, prevOffset, nextOffset) + } catch (e: Throwable) { + Timber.e(e, "Unable to load the transaction history for the requested offset: $offsetToLoad") + LoadResult.Error(e) + } + } + + private suspend fun fetchItemsIfExpired(offset: Int, pageSize: Int, isRefresh: Boolean) { + cacheRegistry.invokeOnExpire( + key = getCacheKey(offset), + skipCache = isRefresh, + block = { fetchItems(offset, pageSize) }, + ) + } + + private suspend fun fetchItems(offset: Int, pageSize: Int) = withContext(dispatchers.io) { + val response = visaApi.getTxHistory( + cardPublicKey = cardPublicKey, + limit = pageSize, + offset = offset, + ).getOrThrow() + + pagedItems.update { + it.toMutableMap().apply { + this[offset] = response.transactions.map(itemsFactory::create) + } + } + } + + private fun getCacheKey(offset: Int): String { + return "visa_tx_history_${cardPublicKey}_$offset" + } + + class Params( + val cardPublicKey: String, + val pageSize: Int, + val isRefresh: Boolean, + ) + + private companion object { + const val INITIAL_OFFSET = 0 + } +} \ No newline at end of file diff --git a/domain/visa/build.gradle.kts b/domain/visa/build.gradle.kts index 64262e878a..8032c2e995 100644 --- a/domain/visa/build.gradle.kts +++ b/domain/visa/build.gradle.kts @@ -19,4 +19,5 @@ dependencies { /** Libs - Other */ implementation(deps.jodatime) + implementation(deps.androidx.paging.runtime) } \ No newline at end of file diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/visa/GetVisaTxHistoryUseCase.kt b/domain/visa/src/main/kotlin/com/tangem/domain/visa/GetVisaTxHistoryUseCase.kt new file mode 100644 index 0000000000..c14447313e --- /dev/null +++ b/domain/visa/src/main/kotlin/com/tangem/domain/visa/GetVisaTxHistoryUseCase.kt @@ -0,0 +1,32 @@ +package com.tangem.domain.visa + +import androidx.paging.PagingData +import arrow.core.Either +import arrow.core.left +import arrow.core.right +import com.tangem.domain.visa.model.VisaTxHistoryItem +import com.tangem.domain.visa.repository.VisaRepository +import com.tangem.domain.wallets.models.UserWalletId +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.map + +class GetVisaTxHistoryUseCase( + private val visaRepository: VisaRepository, +) { + + suspend operator fun invoke( + userWalletId: UserWalletId, + pageSize: Int = DEFAULT_PAGE_SIZE, + isRefresh: Boolean = false, + ): Flow>> { + return visaRepository + .getTxHistory(userWalletId, pageSize, isRefresh) + .map, Either>> { it.right() } + .catch { emit(it.left()) } + } + + companion object { + const val DEFAULT_PAGE_SIZE = 10 + } +} \ No newline at end of file diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/visa/model/VisaTxHistoryItem.kt b/domain/visa/src/main/kotlin/com/tangem/domain/visa/model/VisaTxHistoryItem.kt new file mode 100644 index 0000000000..6d3bcc876a --- /dev/null +++ b/domain/visa/src/main/kotlin/com/tangem/domain/visa/model/VisaTxHistoryItem.kt @@ -0,0 +1,15 @@ +package com.tangem.domain.visa.model + +import org.joda.time.DateTime +import java.math.BigDecimal +import java.util.Currency + +data class VisaTxHistoryItem( + val id: String, + val date: DateTime, + val amount: BigDecimal, + val fiatAmount: BigDecimal, + val merchantName: String?, + val status: String, + val fiatCurrency: Currency, +) \ No newline at end of file diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/visa/repository/VisaRepository.kt b/domain/visa/src/main/kotlin/com/tangem/domain/visa/repository/VisaRepository.kt index 96d6efd7fd..bd3860aa2b 100644 --- a/domain/visa/src/main/kotlin/com/tangem/domain/visa/repository/VisaRepository.kt +++ b/domain/visa/src/main/kotlin/com/tangem/domain/visa/repository/VisaRepository.kt @@ -1,9 +1,18 @@ package com.tangem.domain.visa.repository +import androidx.paging.PagingData import com.tangem.domain.visa.model.VisaCurrency +import com.tangem.domain.visa.model.VisaTxHistoryItem import com.tangem.domain.wallets.models.UserWalletId +import kotlinx.coroutines.flow.Flow interface VisaRepository { suspend fun getVisaCurrency(userWalletId: UserWalletId, isRefresh: Boolean = false): VisaCurrency + + suspend fun getTxHistory( + userWalletId: UserWalletId, + pageSize: Int, + isRefresh: Boolean = false, + ): Flow> } \ No newline at end of file From 657e7cd3d07392a728930274b9ceabcbeacb6c2c Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 8 Feb 2024 17:01:46 +0300 Subject: [PATCH 15/53] Updated on 2026-08-14 --- .../presentation/wallet/state2/WalletStateController.kt | 6 +++++- .../wallet/subscribers/BasicTokenListSubscriber.kt | 2 +- .../wallet/subscribers/MultiWalletWarningsSubscriber.kt | 2 +- .../subscribers/SingleWalletNotificationsSubscriber.kt | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/WalletStateController.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/WalletStateController.kt index d2e9c06cb8..0ef36a7f4a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/WalletStateController.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/WalletStateController.kt @@ -42,7 +42,11 @@ internal class WalletStateController @Inject constructor() { mutableUiState.update { getInitialState() } } - fun getWalletIfSelected(walletId: UserWalletId): WalletState? { + fun getWalletState(userWalletId: UserWalletId): WalletState? { + return value.wallets.firstOrNull { it.walletCardState.id == userWalletId } + } + + fun getWalletStateIfSelected(walletId: UserWalletId): WalletState? { val selectedWalletId = getSelectedWalletId() return value.wallets.firstOrNull { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt index 379657ed45..ddc8f7fb6a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt @@ -37,7 +37,7 @@ internal abstract class BasicTokenListSubscriber( return combine( flow = tokenListFlow() .onEach { - val displayedState = stateHolder.getWalletIfSelected(userWallet.walletId) + val displayedState = stateHolder.getWalletStateIfSelected(userWallet.walletId) tokenListAnalyticsSender.send(displayedState, userWallet, it.getOrElse { return@onEach }) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletWarningsSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletWarningsSubscriber.kt index 09a4cc324d..152aa30cae 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletWarningsSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletWarningsSubscriber.kt @@ -27,7 +27,7 @@ internal class MultiWalletWarningsSubscriber( .conflate() .distinctUntilChanged() .onEach { warnings -> - val displayedState = stateHolder.getWalletIfSelected(userWalletId) + val displayedState = stateHolder.getWalletState(userWalletId) stateHolder.update(SetWarningsTransformer(userWalletId, warnings)) walletWarningsAnalyticsSender.send(displayedState, warnings) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletNotificationsSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletNotificationsSubscriber.kt index e4b6e2f5e9..edb19f0254 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletNotificationsSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletNotificationsSubscriber.kt @@ -30,7 +30,7 @@ internal class SingleWalletNotificationsSubscriber( .conflate() .distinctUntilChanged() .onEach { warnings -> - val displayedState = stateHolder.getWalletIfSelected(userWalletId) + val displayedState = stateHolder.getWalletState(userWalletId) stateHolder.update(SetWarningsTransformer(userWalletId, warnings)) walletWarningsAnalyticsSender.send(displayedState, warnings) From 4eead599f344f7bbc97ca2281dda33a081677f04 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 7 Feb 2024 17:45:33 +0300 Subject: [PATCH 16/53] Updated on 2026-08-14 --- .../ui/components/fields/SimpleTextField.kt | 77 ++++++++++++------- .../state/SendEventStateFactory.kt | 4 +- .../presentation/state/SendStateFactory.kt | 32 ++------ .../state/amount/AmountStateFactory.kt | 44 +++++++++++ .../presentation/state/fee/FeeStateFactory.kt | 4 +- .../SendAmountFieldMaxAmountConverter.kt | 44 +++++++++++ .../presentation/ui/amount/AmountField.kt | 6 +- .../presentation/viewmodel/SendViewModel.kt | 25 +++--- 8 files changed, 163 insertions(+), 73 deletions(-) create mode 100644 features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountStateFactory.kt create mode 100644 features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldMaxAmountConverter.kt diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/fields/SimpleTextField.kt b/core/ui/src/main/java/com/tangem/core/ui/components/fields/SimpleTextField.kt index a85e0eaafc..4a3fdbff20 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/fields/SimpleTextField.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/fields/SimpleTextField.kt @@ -45,26 +45,31 @@ fun SimpleTextField( mutableStateOf( TextFieldValue( text = value, - selection = when { - value.isEmpty() -> TextRange.Zero - else -> TextRange(value.length, value.length) - }, + selection = getValueRange(value), ), ) } val focusRequester = remember { FocusRequester.Default } val customTextSelectionColors = TextSelectionColors( - handleColor = TangemTheme.colors.text.secondary, - backgroundColor = TangemTheme.colors.text.secondary.copy(alpha = 0.4f), + handleColor = TangemTheme.colors.text.accent, + backgroundColor = TangemTheme.colors.text.accent.copy(alpha = 0.3f), ) val textFieldValue = textFieldValueState.copy(text = value) - SideEffect { - if (textFieldValue.selection != textFieldValueState.selection || - textFieldValue.composition != textFieldValueState.composition - ) { - textFieldValueState = textFieldValue + val isSelectionChanged by remember { + derivedStateOf { + textFieldValue.selection != textFieldValueState.selection || + textFieldValue.composition != textFieldValueState.composition || + textFieldValue.text != textFieldValueState.text + } + } + + LaunchedEffect(key1 = isSelectionChanged) { + if (isSelectionChanged) { + textFieldValueState = textFieldValue.copy( + selection = getValueRange(value), + ) } } @@ -91,24 +96,44 @@ fun SimpleTextField( keyboardOptions = keyboardOptions, keyboardActions = keyboardActions, decorationBox = decorationBox ?: { textValue -> - Box { - if (value.isBlank() && placeholder != null) { - AnimatedContent( - targetState = placeholder, - label = "Placeholder Change Animation", - ) { - Text( - text = it.resolveReference(), - style = textStyle, - color = TangemTheme.colors.text.disabled, - ) - } - } - textValue() - } + SimpleTextPlaceholder( + placeholder = placeholder, + value = value, + textStyle = textStyle, + textValue = textValue, + ) }, modifier = modifier .focusRequester(focusRequester), ) } +} + +private fun getValueRange(value: String) = when { + value.isEmpty() -> TextRange.Zero + else -> TextRange(value.length, value.length) +} + +@Composable +private fun SimpleTextPlaceholder( + placeholder: TextReference?, + value: String, + textStyle: TextStyle, + textValue: @Composable () -> Unit, +) { + Box { + if (value.isBlank() && placeholder != null) { + AnimatedContent( + targetState = placeholder, + label = "Placeholder Change Animation", + ) { + Text( + text = it.resolveReference(), + style = textStyle, + color = TangemTheme.colors.text.disabled, + ) + } + } + textValue() + } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendEventStateFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendEventStateFactory.kt index a175478e41..2faa56655f 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendEventStateFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendEventStateFactory.kt @@ -23,7 +23,9 @@ internal class SendEventStateFactory( private val clickIntents: SendClickIntents, private val feeStateFactory: FeeStateFactory, ) { - private val sendTransactionErrorConverter by lazy { SendTransactionAlertConverter(clickIntents) } + private val sendTransactionErrorConverter by lazy(LazyThreadSafetyMode.NONE) { + SendTransactionAlertConverter(clickIntents) + } fun onConsumeEventState(): SendUiState { return currentStateProvider().copy(event = consumedEvent()) 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 31cfb3ef91..57a1a698ae 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 @@ -14,10 +14,8 @@ import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.usecase.ValidateWalletMemoUseCase import com.tangem.features.send.impl.R import com.tangem.features.send.impl.presentation.domain.AvailableWallet -import com.tangem.features.send.impl.presentation.state.amount.SendAmountCurrencyConverter import com.tangem.features.send.impl.presentation.state.amount.SendAmountStateConverter import com.tangem.features.send.impl.presentation.state.fee.SendFeeStateConverter -import com.tangem.features.send.impl.presentation.state.fields.SendAmountFieldChangeConverter import com.tangem.features.send.impl.presentation.state.fields.SendAmountFieldConverter import com.tangem.features.send.impl.presentation.state.recipient.SendRecipientListConverter import com.tangem.features.send.impl.presentation.state.recipient.SendRecipientStateConverter @@ -38,29 +36,17 @@ internal class SendStateFactory( private val validateWalletMemoUseCase: ValidateWalletMemoUseCase, private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase, ) { - private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter) - private val amountFieldConverter by lazy { + + private val amountFieldConverter by lazy(LazyThreadSafetyMode.NONE) { SendAmountFieldConverter( clickIntents = clickIntents, cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, appCurrencyProvider = appCurrencyProvider, ) } - private val amountFieldChangeConverter by lazy { - SendAmountFieldChangeConverter( - currentStateProvider = currentStateProvider, - cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, - ) - } - private val amountCurrencyConverter by lazy { - SendAmountCurrencyConverter( - currentStateProvider = currentStateProvider, - cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, - ) - } - private val amountStateConverter by lazy { + private val amountStateConverter by lazy(LazyThreadSafetyMode.NONE) { SendAmountStateConverter( appCurrencyProvider = appCurrencyProvider, iconStateConverter = iconStateConverter, @@ -69,20 +55,20 @@ internal class SendStateFactory( cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, ) } - private val recipientStateConverter by lazy { + private val recipientStateConverter by lazy(LazyThreadSafetyMode.NONE) { SendRecipientStateConverter( clickIntents = clickIntents, cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, ) } - private val feeStateConverter by lazy { + private val feeStateConverter by lazy(LazyThreadSafetyMode.NONE) { SendFeeStateConverter( appCurrencyProvider = appCurrencyProvider, cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, ) } - private val recipientListStateConverter by lazy { + private val recipientListStateConverter by lazy(LazyThreadSafetyMode.NONE) { SendRecipientListConverter( currentStateProvider = currentStateProvider, cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, @@ -122,12 +108,6 @@ internal class SendStateFactory( } //endregion - //region amount state clicks - fun getOnAmountValueChange(value: String) = amountFieldChangeConverter.convert(value) - - fun getOnCurrencyChangedState(isFiat: Boolean) = amountCurrencyConverter.convert(isFiat) - //endregion - //region recipient fun onLoadedRecipientList( wallets: List, diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountStateFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountStateFactory.kt new file mode 100644 index 0000000000..6f56b6aff3 --- /dev/null +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/AmountStateFactory.kt @@ -0,0 +1,44 @@ +package com.tangem.features.send.impl.presentation.state.amount + +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.features.send.impl.presentation.state.SendUiState +import com.tangem.features.send.impl.presentation.state.fields.SendAmountFieldChangeConverter +import com.tangem.features.send.impl.presentation.state.fields.SendAmountFieldMaxAmountConverter +import com.tangem.utils.Provider + +/** + * Factory to produce amount state for [SendUiState] + */ +internal class AmountStateFactory( + private val currentStateProvider: Provider, + private val cryptoCurrencyStatusProvider: Provider, +) { + + private val amountFieldChangeConverter by lazy(LazyThreadSafetyMode.NONE) { + SendAmountFieldChangeConverter( + currentStateProvider = currentStateProvider, + cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, + ) + } + private val amountFieldMaxAmountConverter by lazy(LazyThreadSafetyMode.NONE) { + SendAmountFieldMaxAmountConverter( + currentStateProvider = currentStateProvider, + cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, + ) + } + + private val amountCurrencyConverter by lazy(LazyThreadSafetyMode.NONE) { + SendAmountCurrencyConverter( + currentStateProvider = currentStateProvider, + cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, + ) + } + + fun getOnAmountValueChange(value: String) = amountFieldChangeConverter.convert(value) + + fun getOnMaxAmountClick(): SendUiState { + return amountFieldMaxAmountConverter.convert(Unit) + } + + fun getOnCurrencyChangedState(isFiat: Boolean) = amountCurrencyConverter.convert(isFiat) +} \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeStateFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeStateFactory.kt index fcbfd66705..91ad0f8eba 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeStateFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeStateFactory.kt @@ -27,7 +27,7 @@ internal class FeeStateFactory( private val appCurrencyProvider: Provider, private val isFeeApproximateUseCase: IsFeeApproximateUseCase, ) { - private val customFeeFieldConverter by lazy { + private val customFeeFieldConverter by lazy(LazyThreadSafetyMode.NONE) { SendFeeCustomFieldConverter( clickIntents = clickIntents, appCurrencyProvider = appCurrencyProvider, @@ -35,7 +35,7 @@ internal class FeeStateFactory( ) } - val feeConverter by lazy { + val feeConverter by lazy(LazyThreadSafetyMode.NONE) { FeeConverter( clickIntents = clickIntents, appCurrencyProvider = appCurrencyProvider, diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldMaxAmountConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldMaxAmountConverter.kt new file mode 100644 index 0000000000..6ca3cb3d93 --- /dev/null +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldMaxAmountConverter.kt @@ -0,0 +1,44 @@ +package com.tangem.features.send.impl.presentation.state.fields + +import com.tangem.core.ui.utils.parseBigDecimal +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.features.send.impl.presentation.state.SendUiState +import com.tangem.utils.Provider +import com.tangem.utils.converter.Converter + +internal class SendAmountFieldMaxAmountConverter( + private val currentStateProvider: Provider, + private val cryptoCurrencyStatusProvider: Provider, +) : Converter { + + override fun convert(value: Unit): SendUiState { + val state = currentStateProvider() + val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() + val amountState = state.amountState ?: return state + val amountTextField = amountState.amountTextField + val feeState = state.feeState ?: return state + + val cryptoDecimals = amountTextField.cryptoAmount.decimals + val fiatDecimals = amountTextField.fiatAmount.decimals + val decimalCryptoValue = cryptoCurrencyStatus.value.amount + val decimalFiatValue = cryptoCurrencyStatus.value.fiatAmount + + val cryptoValue = decimalCryptoValue?.parseBigDecimal(cryptoDecimals).orEmpty() + val fiatValue = decimalFiatValue?.parseBigDecimal(fiatDecimals).orEmpty() + return state.copy( + amountState = amountState.copy( + isPrimaryButtonEnabled = true, + amountTextField = amountTextField.copy( + value = cryptoValue, + fiatValue = fiatValue, + isError = false, + cryptoAmount = amountTextField.cryptoAmount.copy(value = decimalCryptoValue), + fiatAmount = amountTextField.fiatAmount.copy(value = decimalFiatValue), + ), + ), + feeState = feeState.copy( + isSubtract = true, + ), + ) + } +} \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/AmountField.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/AmountField.kt index b18159fd89..bd6b8b54e1 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/AmountField.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/AmountField.kt @@ -28,10 +28,10 @@ internal fun AmountField(sendField: SendTextField.AmountField, isFiat: Boolean) sendField.value to sendField.fiatValue } - val (primaryAmount, secondaryAmount) = if (!isFiat) { - sendField.cryptoAmount to sendField.fiatAmount - } else { + val (primaryAmount, secondaryAmount) = if (isFiat) { sendField.fiatAmount to sendField.cryptoAmount + } else { + sendField.cryptoAmount to sendField.fiatAmount } AmountTextField( 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 cb977c38f3..d6e32da750 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 @@ -9,8 +9,6 @@ import arrow.core.Either import arrow.core.getOrElse import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.transaction.TransactionFee -import com.tangem.common.extensions.isZero -import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase @@ -38,6 +36,7 @@ import com.tangem.features.send.api.navigation.SendRouter import com.tangem.features.send.impl.navigation.InnerSendRouter import com.tangem.features.send.impl.presentation.domain.AvailableWallet import com.tangem.features.send.impl.presentation.state.* +import com.tangem.features.send.impl.presentation.state.amount.AmountStateFactory import com.tangem.features.send.impl.presentation.state.fee.FeeNotificationFactory import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState import com.tangem.features.send.impl.presentation.state.fee.FeeStateFactory @@ -110,6 +109,11 @@ internal class SendViewModel @Inject constructor( getExplorerTransactionUrlUseCase = getExplorerTransactionUrlUseCase, ) + private val amountStateFactory = AmountStateFactory( + currentStateProvider = Provider { uiState }, + cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus }, + ) + private val feeStateFactory = FeeStateFactory( clickIntents = this, currentStateProvider = Provider { uiState }, @@ -403,24 +407,15 @@ internal class SendViewModel @Inject constructor( // region amount state clicks override fun onCurrencyChangeClick(isFiat: Boolean) { - uiState = stateFactory.getOnCurrencyChangedState(isFiat) + uiState = amountStateFactory.getOnCurrencyChangedState(isFiat) } override fun onAmountValueChange(value: String) { - uiState = stateFactory.getOnAmountValueChange(value) + uiState = amountStateFactory.getOnAmountValueChange(value) } override fun onMaxValueClick() { - val amountState = uiState.amountState ?: return - val amountTextField = amountState.amountTextField - val (amount, decimals) = if (amountTextField.isFiatValue) { - cryptoCurrencyStatus.value.fiatAmount to amountTextField.fiatAmount.decimals - } else { - cryptoCurrencyStatus.value.amount to amountTextField.cryptoAmount.decimals - } - if (amount != null && !amount.isZero()) { - onAmountValueChange(amount.parseBigDecimal(decimals)) - } + uiState = amountStateFactory.getOnMaxAmountClick() } // endregion @@ -558,7 +553,7 @@ internal class SendViewModel @Inject constructor( override fun onExploreClick(txUrl: String) = innerRouter.openUrl(txUrl) override fun onAmountReduceClick(reducedAmount: String) { - uiState = stateFactory.getOnAmountValueChange(reducedAmount) + uiState = amountStateFactory.getOnAmountValueChange(reducedAmount) uiState = sendNotificationFactory.dismissHighFeeWarningState() loadFee() } From f65dcebf65d048c89612ec4798951ed08f443260 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 8 Feb 2024 11:38:02 +0300 Subject: [PATCH 17/53] Updated on 2026-08-14 --- .../ui/components/transactions/Transaction.kt | 55 +++++++-- .../transactions/state/TransactionState.kt | 3 +- .../TokenDetailsLoadedBalanceConverter.kt | 4 +- ...ilsPendingTxToTransactionStateConverter.kt | 109 ------------------ .../TokenDetailsTxHistoryItemFlowConverter.kt | 24 +--- ...tailsTxHistoryTransactionStateConverter.kt | 14 +-- .../presentation/common/WalletPreviewData.kt | 6 +- .../implementors/VisaWalletContentLoader.kt | 28 ++--- .../VisaWalletContentLoaderFactory.kt | 15 +-- .../WalletLoadingTxHistoryConverter.kt | 2 +- ...letPendingTxToTransactionStateConverter.kt | 109 ------------------ .../WalletTxHistoryItemFlowConverter.kt | 27 +---- ...alletTxHistoryTransactionStateConverter.kt | 14 +-- .../SetBalancesAndLimitsTransformer.kt | 2 + .../SetTxHistoryItemsTransformer.kt | 7 +- .../converter/TxHistoryItemFlowConverter.kt | 51 ++------ .../converter/TxHistoryItemStateConverter.kt | 14 +-- .../VisaTxHistoryItemStateConverter.kt | 46 ++++++++ .../wallet/subscribers/TxHistorySubscriber.kt | 41 +++---- .../VisaWalletBalancesAndLimitsSubscriber.kt | 32 ----- .../subscribers/VisaWalletSubscriber.kt | 105 +++++++++++++++++ .../presentation/wallet/ui/WalletScreenV2.kt | 3 +- .../viewmodels/intents/VisaWalletIntents.kt | 6 + 23 files changed, 267 insertions(+), 450 deletions(-) delete mode 100644 features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsPendingTxToTransactionStateConverter.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletPendingTxToTransactionStateConverter.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/VisaTxHistoryItemStateConverter.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/VisaWalletBalancesAndLimitsSubscriber.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/VisaWalletSubscriber.kt diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt index 3e863a35f6..180acd8a31 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider @@ -30,6 +31,7 @@ import com.tangem.core.ui.components.transactions.state.TransactionState.Content import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.res.TangemTheme import java.util.UUID @@ -46,6 +48,7 @@ import java.util.UUID [REDACTED_AUTHOR] */ @Composable +@Suppress("LongMethod") fun Transaction(state: TransactionState, isBalanceHidden: Boolean, modifier: Modifier = Modifier) { Surface( modifier = modifier @@ -76,10 +79,15 @@ fun Transaction(state: TransactionState, isBalanceHidden: Boolean, modifier: Mod Title( state = state, modifier = Modifier - .padding(horizontal = TangemTheme.dimens.spacing12) + .padding( + start = TangemTheme.dimens.spacing12, + end = TangemTheme.dimens.spacing6, + ) .constrainAs(titleItem) { start.linkTo(iconItem.end) + end.linkTo(amountItem.start) top.linkTo(iconItem.top) + width = Dimension.fillToConstraints }, ) @@ -173,11 +181,16 @@ private fun Icon(state: TransactionState, modifier: Modifier = Modifier) { private fun Title(state: TransactionState, modifier: Modifier = Modifier) { when (state) { is TransactionState.Content -> { - Row(modifier = modifier, horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing6)) { + Row( + modifier = modifier, + horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing6), + ) { Text( text = state.title.resolveReference(), color = TangemTheme.colors.text.primary1, style = TangemTheme.typography.subtitle2, + overflow = TextOverflow.Ellipsis, + maxLines = 1, ) if (state.status is Status.Unconfirmed) { @@ -266,7 +279,7 @@ private fun Timestamp(state: TransactionState, modifier: Modifier = Modifier) { when (state) { is TransactionState.Content -> { Text( - text = state.timestamp, + text = state.time, modifier = modifier, textAlign = TextAlign.End, color = TangemTheme.colors.text.tertiary, @@ -321,89 +334,109 @@ private class TransactionItemStateProvider : CollectionPreviewParameterProvider< TransactionState.Content( txHash = UUID.randomUUID().toString(), amount = "-0.500913 BTC", - timestamp = "8:41", + time = "8:41", status = Status.Confirmed, direction = Direction.OUTGOING, iconRes = R.drawable.ic_arrow_up_24, title = resourceReference(R.string.common_transfer), subtitle = TextReference.Str("33BddS...ga2B"), + timestamp = 0L, onClick = {}, ), TransactionState.Content( txHash = UUID.randomUUID().toString(), amount = "+0.500913 BTC", - timestamp = "8:41", + time = "8:41", status = Status.Unconfirmed, direction = Direction.INCOMING, iconRes = R.drawable.ic_arrow_down_24, title = resourceReference(R.string.common_transfer), subtitle = TextReference.Str("33BddS...ga2B"), + timestamp = 0L, onClick = {}, ), TransactionState.Content( txHash = UUID.randomUUID().toString(), amount = "+0.500913 BTC", - timestamp = "8:41", + time = "8:41", status = Status.Unconfirmed, direction = Direction.OUTGOING, iconRes = R.drawable.ic_doc_24, title = resourceReference(R.string.common_approval), subtitle = TextReference.Str("33BddS...ga2B"), + timestamp = 0L, onClick = {}, ), TransactionState.Content( txHash = UUID.randomUUID().toString(), amount = "+0.500913 BTC", - timestamp = "8:41", + time = "8:41", status = Status.Failed, direction = Direction.OUTGOING, iconRes = R.drawable.ic_doc_24, title = resourceReference(R.string.common_approval), subtitle = TextReference.Str("33BddS...ga2B"), + timestamp = 0L, onClick = {}, ), TransactionState.Content( txHash = UUID.randomUUID().toString(), amount = "+0.500913 BTC", - timestamp = "8:41", + time = "8:41", status = Status.Confirmed, direction = Direction.OUTGOING, iconRes = R.drawable.ic_doc_24, title = resourceReference(R.string.common_approval), subtitle = TextReference.Str("33BddS...ga2B"), + timestamp = 0L, onClick = {}, ), TransactionState.Content( txHash = UUID.randomUUID().toString(), amount = "+0.500913 BTC", - timestamp = "8:41", + time = "8:41", status = Status.Unconfirmed, direction = Direction.INCOMING, iconRes = R.drawable.ic_arrow_down_24, title = resourceReference(R.string.common_swap), subtitle = TextReference.Str("33BddS...ga2B"), + timestamp = 0L, onClick = {}, ), TransactionState.Content( txHash = UUID.randomUUID().toString(), amount = "+0.500913 BTC", - timestamp = "8:41", + time = "8:41", status = Status.Confirmed, direction = Direction.INCOMING, iconRes = R.drawable.ic_doc_24, title = TextReference.Str("Submit"), subtitle = TextReference.Str("33BddS...ga2B"), + timestamp = 0L, onClick = {}, ), TransactionState.Content( txHash = UUID.randomUUID().toString(), amount = "+0.500913 BTC", - timestamp = "8:41", + time = "8:41", status = Status.Confirmed, direction = Direction.OUTGOING, iconRes = R.drawable.ic_arrow_up_24, title = TextReference.Str("Submit"), subtitle = TextReference.Str("33BddS...ga2B"), + timestamp = 0L, + onClick = {}, + ), + TransactionState.Content( + txHash = UUID.randomUUID().toString(), + amount = "0.625 USDT", + time = "€0.50", + status = Status.Confirmed, + direction = Direction.OUTGOING, + iconRes = R.drawable.ic_arrow_up_24, + title = TextReference.Str("Unlimint Banking Cards Sandbox London"), + subtitle = stringReference(value = "8:41 • authorized"), + timestamp = 0L, onClick = {}, ), TransactionState.Loading(txHash = UUID.randomUUID().toString()), diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/state/TransactionState.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/state/TransactionState.kt index c18e43f28e..7df1982a29 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/state/TransactionState.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/state/TransactionState.kt @@ -25,13 +25,14 @@ sealed interface TransactionState { data class Content( override val txHash: String, val amount: String, - val timestamp: String, + val time: String, val status: Status, val direction: Direction, val onClick: () -> Unit, @DrawableRes val iconRes: Int, val title: TextReference, val subtitle: TextReference, + val timestamp: Long, ) : TransactionState { sealed class Status { diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt index 142947657c..e8c09263eb 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsLoadedBalanceConverter.kt @@ -12,7 +12,7 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsBalanceBlockState import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsNotification -import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsPendingTxToTransactionStateConverter +import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsTxHistoryTransactionStateConverter import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents import com.tangem.utils.Provider import com.tangem.utils.converter.Converter @@ -29,7 +29,7 @@ internal class TokenDetailsLoadedBalanceConverter( ) : Converter, TokenDetailsState> { private val txHistoryItemConverter by lazy { - TokenDetailsPendingTxToTransactionStateConverter(symbol, decimals, clickIntents) + TokenDetailsTxHistoryTransactionStateConverter(symbol, decimals, clickIntents) } override fun convert(value: Either): TokenDetailsState { diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsPendingTxToTransactionStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsPendingTxToTransactionStateConverter.kt deleted file mode 100644 index 6ba156961c..0000000000 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsPendingTxToTransactionStateConverter.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory - -import com.tangem.core.ui.components.transactions.state.TransactionState -import com.tangem.core.ui.extensions.TextReference -import com.tangem.core.ui.extensions.resourceReference -import com.tangem.core.ui.extensions.stringReference -import com.tangem.core.ui.extensions.wrappedList -import com.tangem.core.ui.utils.DateTimeFormatters -import com.tangem.domain.txhistory.models.TxHistoryItem -import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents -import com.tangem.features.tokendetails.impl.R -import com.tangem.utils.converter.Converter -import com.tangem.utils.toBriefAddressFormat -import com.tangem.utils.toFormattedCurrencyString -import org.joda.time.DateTime -import org.joda.time.DateTimeZone - -// FIXME: Refactoring needed -/** Same as [TokenDetailsTxHistoryTransactionStateConverter] but with other timestamp format */ -internal class TokenDetailsPendingTxToTransactionStateConverter( - private val symbol: String, - private val decimals: Int, - private val clickIntents: TokenDetailsClickIntents, -) : Converter { - - override fun convert(value: TxHistoryItem): TransactionState { - return createTransactionStateItem(item = value) - } - - private fun createTransactionStateItem(item: TxHistoryItem): TransactionState { - return TransactionState.Content( - txHash = item.txHash, - amount = item.getAmount(), - timestamp = item.timestampInMillis.toTimeFormat(), - status = item.status.tiUiStatus(), - direction = item.extractDirection(), - iconRes = item.extractIcon(), - title = item.extractTitle(), - subtitle = item.extractSubtitle(), - onClick = { clickIntents.onTransactionClick(item.txHash) }, - ) - } - - private fun TxHistoryItem.extractIcon(): Int = if (status == TxHistoryItem.TransactionStatus.Failed) { - R.drawable.ic_close_24 - } else { - when (type) { - is TxHistoryItem.TransactionType.Approve -> R.drawable.ic_doc_24 - is TxHistoryItem.TransactionType.Operation, - is TxHistoryItem.TransactionType.Swap, - is TxHistoryItem.TransactionType.Transfer, - is TxHistoryItem.TransactionType.UnknownOperation, - -> if (isOutgoing) R.drawable.ic_arrow_up_24 else R.drawable.ic_arrow_down_24 - } - } - - private fun TxHistoryItem.extractTitle(): TextReference = when (val type = type) { - is TxHistoryItem.TransactionType.Approve -> resourceReference(R.string.common_approval) - is TxHistoryItem.TransactionType.Operation -> stringReference(type.name) - is TxHistoryItem.TransactionType.Swap -> resourceReference(R.string.common_swap) - is TxHistoryItem.TransactionType.Transfer -> resourceReference(R.string.common_transfer) - is TxHistoryItem.TransactionType.UnknownOperation -> resourceReference(R.string.transaction_history_operation) - } - - private fun TxHistoryItem.extractSubtitle(): TextReference = - when (val interactionAddress = interactionAddressType) { - is TxHistoryItem.InteractionAddressType.Contract -> resourceReference( - id = R.string.transaction_history_contract_address, - formatArgs = wrappedList(interactionAddress.address.toBriefAddressFormat()), - ) - is TxHistoryItem.InteractionAddressType.Multiple -> resourceReference( - id = if (isOutgoing) { - R.string.transaction_history_transaction_to_address - } else { - R.string.transaction_history_transaction_from_address - }, - formatArgs = wrappedList(resourceReference(R.string.transaction_history_multiple_addresses)), - ) - is TxHistoryItem.InteractionAddressType.User -> resourceReference( - id = if (isOutgoing) { - R.string.transaction_history_transaction_to_address - } else { - R.string.transaction_history_transaction_from_address - }, - formatArgs = wrappedList(interactionAddress.address.toBriefAddressFormat()), - ) - } - - private fun TxHistoryItem.TransactionStatus.tiUiStatus() = when (this) { - TxHistoryItem.TransactionStatus.Confirmed -> TransactionState.Content.Status.Confirmed - TxHistoryItem.TransactionStatus.Failed -> TransactionState.Content.Status.Failed - TxHistoryItem.TransactionStatus.Unconfirmed -> TransactionState.Content.Status.Unconfirmed - } - - private fun Long.toTimeFormat(): String { - return DateTimeFormatters.formatTime(time = DateTime(this, DateTimeZone.getDefault())) - } - - private fun TxHistoryItem.extractDirection() = - if (isOutgoing) TransactionState.Content.Direction.OUTGOING else TransactionState.Content.Direction.INCOMING - - private fun TxHistoryItem.getAmount(): String { - val prefix = when (status) { - TxHistoryItem.TransactionStatus.Failed -> "" - else -> if (isOutgoing) "-" else "+" - } - return prefix + amount.toFormattedCurrencyString(currency = symbol, decimals = decimals) - } -} \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryItemFlowConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryItemFlowConverter.kt index 56ae213609..9fb6df70b1 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryItemFlowConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryItemFlowConverter.kt @@ -5,7 +5,6 @@ import com.tangem.core.ui.components.transactions.state.TransactionState import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.components.transactions.state.TxHistoryState.TxHistoryItemState import com.tangem.core.ui.utils.toDateFormat -import com.tangem.core.ui.utils.toTimeFormat import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents @@ -52,8 +51,7 @@ internal class TokenDetailsTxHistoryItemFlowConverter( terminalSeparatorType = TerminalSeparatorType.SOURCE_COMPLETE, item = TxHistoryItemState.Title(clickIntents::onExploreClick), ) - .insertGroupTitle() // method uses the raw timestamp - .formatTransactionsTimestamp() // method formats the timestamp + .insertGroupTitle() } } .launchIn(CoroutineScope(Dispatchers.IO)) @@ -94,28 +92,10 @@ internal class TokenDetailsTxHistoryItemFlowConverter( } } - /** - * Map the [PagingData] to format the [TxHistoryItemState] timestamp - */ - private fun PagingData.formatTransactionsTimestamp(): PagingData { - return map { txHistoryItemState -> - if (txHistoryItemState is TxHistoryItemState.Transaction && - txHistoryItemState.state is TransactionState.Content - ) { - val txContent = txHistoryItemState.state as TransactionState.Content - txHistoryItemState.copy( - state = txContent.copy(timestamp = txContent.timestamp.toLong().toTimeFormat()), - ) - } else { - txHistoryItemState - } - } - } - private fun TxHistoryItemState?.getTimestamp(): Long? { return if (this is TxHistoryItemState.Transaction && this.state is TransactionState.Content) { val txContent = this.state as TransactionState.Content - requireNotNull(txContent.timestamp.toLongOrNull()) { "Timestamp must be Long type" } + txContent.timestamp } else { null } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryTransactionStateConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryTransactionStateConverter.kt index 22d68befc8..1e2ab2341d 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryTransactionStateConverter.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/txhistory/TokenDetailsTxHistoryTransactionStateConverter.kt @@ -6,6 +6,7 @@ import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.wrappedList +import com.tangem.core.ui.utils.toTimeFormat import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents import com.tangem.features.tokendetails.impl.R @@ -13,8 +14,6 @@ import com.tangem.utils.converter.Converter import com.tangem.utils.toBriefAddressFormat import com.tangem.utils.toFormattedCurrencyString -// FIXME: Refactoring needed -/** Same as [TokenDetailsPendingTxToTransactionStateConverter] but with other timestamp format */ internal class TokenDetailsTxHistoryTransactionStateConverter( private val symbol: String, private val decimals: Int, @@ -30,12 +29,13 @@ internal class TokenDetailsTxHistoryTransactionStateConverter( return TransactionState.Content( txHash = item.txHash, amount = item.getAmount(), - timestamp = item.getRawTimestamp(), + time = item.timestampInMillis.toTimeFormat(), status = item.status.tiUiStatus(), direction = item.extractDirection(), iconRes = item.extractIcon(), title = item.extractTitle(), subtitle = item.extractSubtitle(), + timestamp = item.timestampInMillis, onClick = { clickIntents.onTransactionClick(item.txHash) }, ) } @@ -87,14 +87,6 @@ internal class TokenDetailsTxHistoryTransactionStateConverter( private fun TxHistoryItem.extractDirection() = if (isOutgoing) Direction.OUTGOING else Direction.INCOMING - /** - * Get timestamp without formatting. - * It's life hack that help us to add transaction's group title to flow. - * - * @see [convert] - */ - private fun TxHistoryItem.getRawTimestamp() = this.timestampInMillis.toString() - private fun TxHistoryItem.TransactionStatus.tiUiStatus() = when (this) { TxHistoryItem.TransactionStatus.Confirmed -> TransactionState.Content.Status.Confirmed TxHistoryItem.TransactionStatus.Failed -> TransactionState.Content.Status.Failed diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt index 9ffd482c85..727545a58c 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt @@ -426,12 +426,13 @@ internal object WalletPreviewData { TransactionState.Content( txHash = UUID.randomUUID().toString(), amount = "-0.500913 BTC", - timestamp = "8:41", + time = "8:41", status = TransactionState.Content.Status.Unconfirmed, direction = TransactionState.Content.Direction.OUTGOING, iconRes = com.tangem.core.ui.R.drawable.ic_arrow_up_24, title = resourceReference(com.tangem.core.ui.R.string.common_transfer), subtitle = TextReference.Str("33BddS...ga2B"), + timestamp = 0L, onClick = {}, ), ), @@ -443,12 +444,13 @@ internal object WalletPreviewData { TransactionState.Content( txHash = UUID.randomUUID().toString(), amount = "-0.500913 BTC", - timestamp = "8:41", + time = "8:41", status = TransactionState.Content.Status.Confirmed, direction = TransactionState.Content.Direction.OUTGOING, iconRes = com.tangem.core.ui.R.drawable.ic_arrow_up_24, title = resourceReference(com.tangem.core.ui.R.string.common_transfer), subtitle = TextReference.Str("33BddS...ga2B"), + timestamp = 0L, onClick = {}, ), ), diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/VisaWalletContentLoader.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/VisaWalletContentLoader.kt index 7d41dcb257..990533203a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/VisaWalletContentLoader.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/VisaWalletContentLoader.kt @@ -1,46 +1,32 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors -import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase -import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase -import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase import com.tangem.domain.visa.GetVisaCurrencyUseCase +import com.tangem.domain.visa.GetVisaTxHistoryUseCase import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.subscribers.TxHistorySubscriber -import com.tangem.feature.wallet.presentation.wallet.subscribers.VisaWalletBalancesAndLimitsSubscriber +import com.tangem.feature.wallet.presentation.wallet.subscribers.VisaWalletSubscriber import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletSubscriber import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 -@Suppress("LongParameterList") internal class VisaWalletContentLoader( private val userWallet: UserWallet, private val clickIntents: WalletClickIntentsV2, private val isRefresh: Boolean, - private val stateHolder: WalletStateController, - private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase, - private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase, - private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase, + private val stateController: WalletStateController, + private val getVisaTxHistoryUseCase: GetVisaTxHistoryUseCase, private val getVisaCurrencyUseCase: GetVisaCurrencyUseCase, ) : WalletContentLoader(id = userWallet.walletId) { override fun create(): List { return listOf( - VisaWalletBalancesAndLimitsSubscriber( + VisaWalletSubscriber( userWallet = userWallet, - stateHolder = stateHolder, + stateController = stateController, isRefresh = isRefresh, getVisaCurrencyUseCase = getVisaCurrencyUseCase, + getVisaTxHistoryUseCase = getVisaTxHistoryUseCase, clickIntents = clickIntents, ), - TxHistorySubscriber( - userWallet = userWallet, - isRefresh = isRefresh, - stateHolder = stateHolder, - clickIntents = clickIntents, - getPrimaryCurrencyStatusUpdatesUseCase = getPrimaryCurrencyStatusUpdatesUseCase, - txHistoryItemsCountUseCase = txHistoryItemsCountUseCase, - txHistoryItemsUseCase = txHistoryItemsUseCase, - ), ) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/VisaWalletContentLoaderFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/VisaWalletContentLoaderFactory.kt index e81f30afff..1d00369116 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/VisaWalletContentLoaderFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/VisaWalletContentLoaderFactory.kt @@ -1,9 +1,7 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors -import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase -import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase -import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase import com.tangem.domain.visa.GetVisaCurrencyUseCase +import com.tangem.domain.visa.GetVisaTxHistoryUseCase import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 @@ -11,13 +9,10 @@ import dagger.hilt.android.scopes.ViewModelScoped import javax.inject.Inject @ViewModelScoped -@Suppress("LongParameterList") internal class VisaWalletContentLoaderFactory @Inject constructor( private val stateHolder: WalletStateController, - private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase, - private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase, - private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase, private val getVisaCurrencyUseCase: GetVisaCurrencyUseCase, + private val getVisaTxHistoryUseCase: GetVisaTxHistoryUseCase, ) { fun create(userWallet: UserWallet, clickIntents: WalletClickIntentsV2, isRefresh: Boolean): WalletContentLoader { @@ -25,11 +20,9 @@ internal class VisaWalletContentLoaderFactory @Inject constructor( userWallet = userWallet, clickIntents = clickIntents, isRefresh = isRefresh, - stateHolder = stateHolder, - getPrimaryCurrencyStatusUpdatesUseCase = getPrimaryCurrencyStatusUpdatesUseCase, - txHistoryItemsCountUseCase = txHistoryItemsCountUseCase, - txHistoryItemsUseCase = txHistoryItemsUseCase, + stateController = stateHolder, getVisaCurrencyUseCase = getVisaCurrencyUseCase, + getVisaTxHistoryUseCase = getVisaTxHistoryUseCase, ) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt index ff688613a4..3f4b843c86 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt @@ -32,7 +32,7 @@ internal class WalletLoadingTxHistoryConverter( private val txHistoryItemConverter by lazy { val blockchain = currentCardTypeResolverProvider().getBlockchain() - WalletPendingTxToTransactionStateConverter( + WalletTxHistoryTransactionStateConverter( symbol = blockchain.currency, decimals = blockchain.decimals(), clickIntents = clickIntents, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletPendingTxToTransactionStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletPendingTxToTransactionStateConverter.kt deleted file mode 100644 index a3d2cfa92e..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletPendingTxToTransactionStateConverter.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory - -import com.tangem.core.ui.components.transactions.state.TransactionState -import com.tangem.core.ui.extensions.TextReference -import com.tangem.core.ui.extensions.resourceReference -import com.tangem.core.ui.extensions.stringReference -import com.tangem.core.ui.extensions.wrappedList -import com.tangem.core.ui.utils.DateTimeFormatters -import com.tangem.domain.txhistory.models.TxHistoryItem -import com.tangem.feature.wallet.impl.R -import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents -import com.tangem.utils.converter.Converter -import com.tangem.utils.toBriefAddressFormat -import com.tangem.utils.toFormattedCurrencyString -import org.joda.time.DateTime -import org.joda.time.DateTimeZone - -// FIXME: Refactoring needed -/** Same as [WalletTxHistoryTransactionStateConverter] but with other timestamp format */ -internal class WalletPendingTxToTransactionStateConverter( - private val symbol: String, - private val decimals: Int, - private val clickIntents: WalletClickIntents, -) : Converter { - - override fun convert(value: TxHistoryItem): TransactionState { - return createTransactionStateItem(item = value) - } - - private fun createTransactionStateItem(item: TxHistoryItem): TransactionState { - return TransactionState.Content( - txHash = item.txHash, - amount = item.getAmount(), - timestamp = item.timestampInMillis.toTimeFormat(), - status = item.status.tiUiStatus(), - direction = item.extractDirection(), - iconRes = item.extractIcon(), - title = item.extractTitle(), - subtitle = item.extractSubtitle(), - onClick = { clickIntents.onTransactionClick(item.txHash) }, - ) - } - - private fun TxHistoryItem.extractIcon(): Int = if (status == TxHistoryItem.TransactionStatus.Failed) { - R.drawable.ic_close_24 - } else { - when (type) { - is TxHistoryItem.TransactionType.Approve -> R.drawable.ic_doc_24 - is TxHistoryItem.TransactionType.Operation, - is TxHistoryItem.TransactionType.Swap, - is TxHistoryItem.TransactionType.Transfer, - is TxHistoryItem.TransactionType.UnknownOperation, - -> if (isOutgoing) R.drawable.ic_arrow_up_24 else R.drawable.ic_arrow_down_24 - } - } - - private fun TxHistoryItem.extractTitle(): TextReference = when (val type = type) { - is TxHistoryItem.TransactionType.Approve -> resourceReference(R.string.common_approval) - is TxHistoryItem.TransactionType.Operation -> stringReference(type.name) - is TxHistoryItem.TransactionType.Swap -> resourceReference(R.string.common_swap) - is TxHistoryItem.TransactionType.Transfer -> resourceReference(R.string.common_transfer) - is TxHistoryItem.TransactionType.UnknownOperation -> resourceReference(R.string.transaction_history_operation) - } - - private fun TxHistoryItem.extractSubtitle(): TextReference = - when (val interactionAddress = interactionAddressType) { - is TxHistoryItem.InteractionAddressType.Contract -> resourceReference( - id = R.string.transaction_history_contract_address, - formatArgs = wrappedList(interactionAddress.address.toBriefAddressFormat()), - ) - is TxHistoryItem.InteractionAddressType.Multiple -> resourceReference( - id = if (isOutgoing) { - R.string.transaction_history_transaction_to_address - } else { - R.string.transaction_history_transaction_from_address - }, - formatArgs = wrappedList(resourceReference(R.string.transaction_history_multiple_addresses)), - ) - is TxHistoryItem.InteractionAddressType.User -> resourceReference( - id = if (isOutgoing) { - R.string.transaction_history_transaction_to_address - } else { - R.string.transaction_history_transaction_from_address - }, - formatArgs = wrappedList(interactionAddress.address.toBriefAddressFormat()), - ) - } - - private fun TxHistoryItem.TransactionStatus.tiUiStatus() = when (this) { - TxHistoryItem.TransactionStatus.Confirmed -> TransactionState.Content.Status.Confirmed - TxHistoryItem.TransactionStatus.Failed -> TransactionState.Content.Status.Failed - TxHistoryItem.TransactionStatus.Unconfirmed -> TransactionState.Content.Status.Unconfirmed - } - - private fun Long.toTimeFormat(): String { - return DateTimeFormatters.formatTime(time = DateTime(this, DateTimeZone.getDefault())) - } - - private fun TxHistoryItem.extractDirection() = - if (isOutgoing) TransactionState.Content.Direction.OUTGOING else TransactionState.Content.Direction.INCOMING - - private fun TxHistoryItem.getAmount(): String { - val prefix = when (status) { - TxHistoryItem.TransactionStatus.Failed -> "" - else -> if (isOutgoing) "-" else "+" - } - return prefix + amount.toFormattedCurrencyString(currency = symbol, decimals = decimals) - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt index 91864cc3ae..f0ab700230 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt @@ -64,8 +64,7 @@ internal class WalletTxHistoryItemFlowConverter( terminalSeparatorType = TerminalSeparatorType.SOURCE_COMPLETE, item = TxHistoryItemState.Title(clickIntents::onExploreClick), ) - .insertGroupTitle() // method uses the raw timestamp - .formatTransactionsTimestamp() // method formats the timestamp + .insertGroupTitle() } } .launchIn(CoroutineScope(Dispatchers.IO)) @@ -103,28 +102,10 @@ internal class WalletTxHistoryItemFlowConverter( } } - /** - * Map the [PagingData] to format the [TxHistoryItemState] timestamp - */ - private fun PagingData.formatTransactionsTimestamp(): PagingData { - return map { txHistoryItemState -> - if (txHistoryItemState is TxHistoryItemState.Transaction && - txHistoryItemState.state is TransactionState.Content - ) { - val txContent = txHistoryItemState.state as TransactionState.Content - txHistoryItemState.copy( - state = txContent.copy(timestamp = txContent.timestamp.toTimeFormat()), - ) - } else { - txHistoryItemState - } - } - } - private fun TxHistoryItemState?.getTimestamp(): Long? { return if (this is TxHistoryItemState.Transaction && this.state is TransactionState.Content) { val txContent = this.state as TransactionState.Content - requireNotNull(txContent.timestamp.toLongOrNull()) { "Timestamp must be Long type" } + txContent.timestamp } else { null } @@ -147,8 +128,4 @@ internal class WalletTxHistoryItemFlowConverter( DateTimeFormatters.formatDate(date = localDate) } } - - private fun String.toTimeFormat(): String { - return DateTimeFormatters.formatTime(time = DateTime(this.toLong(), DateTimeZone.getDefault())) - } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryTransactionStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryTransactionStateConverter.kt index a7b4c8efa5..720addb22a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryTransactionStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryTransactionStateConverter.kt @@ -5,6 +5,7 @@ import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.wrappedList +import com.tangem.core.ui.utils.toTimeFormat import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents @@ -12,8 +13,6 @@ import com.tangem.utils.converter.Converter import com.tangem.utils.toBriefAddressFormat import com.tangem.utils.toFormattedCurrencyString -// FIXME: Refactoring needed -/** Same as [WalletPendingTxToTransactionStateConverter] but with other timestamp format */ internal class WalletTxHistoryTransactionStateConverter( private val symbol: String, private val decimals: Int, @@ -29,12 +28,13 @@ internal class WalletTxHistoryTransactionStateConverter( return TransactionState.Content( txHash = item.txHash, amount = item.getAmount(), - timestamp = item.getRawTimestamp(), + time = item.timestampInMillis.toTimeFormat(), status = item.status.tiUiStatus(), direction = item.extractDirection(), iconRes = item.extractIcon(), title = item.extractTitle(), subtitle = item.extractSubtitle(), + timestamp = item.timestampInMillis, onClick = { clickIntents.onTransactionClick(item.txHash) }, ) } @@ -87,14 +87,6 @@ internal class WalletTxHistoryTransactionStateConverter( private fun TxHistoryItem.extractDirection() = if (isOutgoing) TransactionState.Content.Direction.OUTGOING else TransactionState.Content.Direction.INCOMING - /** - * Get timestamp without formatting. - * It's life hack that help us to add transaction's group title to flow. - * - * @see [convert] - */ - private fun TxHistoryItem.getRawTimestamp() = this.timestampInMillis.toString() - private fun TxHistoryItem.TransactionStatus.tiUiStatus() = when (this) { TxHistoryItem.TransactionStatus.Confirmed -> TransactionState.Content.Status.Confirmed TxHistoryItem.TransactionStatus.Failed -> TransactionState.Content.Status.Failed diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetBalancesAndLimitsTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetBalancesAndLimitsTransformer.kt index 9af5df2c08..959a6527ac 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetBalancesAndLimitsTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetBalancesAndLimitsTransformer.kt @@ -26,12 +26,14 @@ internal class SetBalancesAndLimitsTransformer( val visaCurrency = maybeVisaCurrency.getOrElse { return state.copy( walletCardState = getErrorWalletCardState(state.walletCardState), + depositButtonState = state.depositButtonState.copy(isEnabled = false), balancesAndLimitBlockState = BalancesAndLimitsBlockState.Error, ) } state.copy( walletCardState = getContentWalletCardState(state.walletCardState, visaCurrency), + depositButtonState = state.depositButtonState.copy(isEnabled = true), balancesAndLimitBlockState = getContentBlockState(visaCurrency), ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetTxHistoryItemsTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetTxHistoryItemsTransformer.kt index 9a47b46770..f1001f3495 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetTxHistoryItemsTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetTxHistoryItemsTransformer.kt @@ -1,8 +1,8 @@ package com.tangem.feature.wallet.presentation.wallet.state2.transformers import androidx.paging.PagingData +import com.tangem.core.ui.components.transactions.state.TransactionState import com.tangem.core.ui.components.transactions.state.TxHistoryState -import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState import com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter.TxHistoryItemFlowConverter @@ -11,8 +11,8 @@ import kotlinx.coroutines.flow.Flow import timber.log.Timber internal class SetTxHistoryItemsTransformer( - private val userWallet: UserWallet, - private val flow: Flow>, + userWallet: UserWallet, + private val flow: Flow>, private val clickIntents: WalletClickIntentsV2, ) : WalletStateTransformer(userWallet.walletId) { @@ -39,7 +39,6 @@ internal class SetTxHistoryItemsTransformer( private fun TxHistoryState.toContentState(): TxHistoryState { val converter = TxHistoryItemFlowConverter( - userWallet = userWallet, currentState = this, clickIntents = clickIntents, ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/TxHistoryItemFlowConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/TxHistoryItemFlowConverter.kt index 6c642647c3..6372e43581 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/TxHistoryItemFlowConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/TxHistoryItemFlowConverter.kt @@ -5,10 +5,6 @@ import com.tangem.core.ui.components.transactions.state.TransactionState import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.components.transactions.state.TxHistoryState.TxHistoryItemState import com.tangem.core.ui.utils.toDateFormat -import com.tangem.core.ui.utils.toTimeFormat -import com.tangem.domain.common.util.cardTypesResolver -import com.tangem.domain.txhistory.models.TxHistoryItem -import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 import com.tangem.utils.converter.Converter import kotlinx.coroutines.CoroutineScope @@ -19,21 +15,11 @@ import java.util.UUID private val scope = CoroutineScope(Dispatchers.IO) internal class TxHistoryItemFlowConverter( - private val userWallet: UserWallet, private val currentState: TxHistoryState, private val clickIntents: WalletClickIntentsV2, -) : Converter>, TxHistoryState?> { +) : Converter>, TxHistoryState?> { - private val txHistoryItemConverter by lazy { - val blockchain = userWallet.scanResponse.cardTypesResolver.getBlockchain() - TxHistoryItemStateConverter( - symbol = blockchain.currency, - decimals = blockchain.decimals(), - clickIntents = clickIntents, - ) - } - - override fun convert(value: Flow>): TxHistoryState { + override fun convert(value: Flow>): TxHistoryState { val txHistoryContent = currentState as? TxHistoryState.Content ?: TxHistoryState.Content(contentItems = MutableStateFlow(PagingData.empty())) @@ -43,16 +29,14 @@ internal class TxHistoryItemFlowConverter( .onEach { txHistoryStatePagingData -> txHistoryContent.contentItems.update { txHistoryStatePagingData - .map { item -> - // [createTransactionState] returns timestamp without formatting - TxHistoryItemState.Transaction(state = createTransactionState(item)) + .map { item -> + TxHistoryItemState.Transaction(item) } .insertHeaderItem( terminalSeparatorType = TerminalSeparatorType.SOURCE_COMPLETE, item = TxHistoryItemState.Title(clickIntents::onExploreClick), ) - .insertGroupTitle() // method uses the raw timestamp - .formatTransactionsTimestamp() // method formats the timestamp + .insertGroupTitle() } } .cachedIn(scope) @@ -61,10 +45,6 @@ internal class TxHistoryItemFlowConverter( return txHistoryContent } - private fun createTransactionState(item: TxHistoryItem): TransactionState { - return txHistoryItemConverter.convert(value = item) - } - private fun PagingData.insertGroupTitle(): PagingData { return insertSeparators(terminalSeparatorType = TerminalSeparatorType.SOURCE_COMPLETE) { before, after -> // Use raw timestamp to get date @@ -88,28 +68,11 @@ internal class TxHistoryItemFlowConverter( } } - /** - * Map the [PagingData] to format the [TxHistoryItemState] timestamp - */ - private fun PagingData.formatTransactionsTimestamp(): PagingData { - return map { txHistoryItemState -> - if (txHistoryItemState is TxHistoryItemState.Transaction && - txHistoryItemState.state is TransactionState.Content - ) { - val txContent = txHistoryItemState.state as TransactionState.Content - txHistoryItemState.copy( - state = txContent.copy(timestamp = txContent.timestamp.toLong().toTimeFormat()), - ) - } else { - txHistoryItemState - } - } - } - private fun TxHistoryItemState?.getTimestamp(): Long? { return if (this is TxHistoryItemState.Transaction && this.state is TransactionState.Content) { val txContent = this.state as TransactionState.Content - requireNotNull(txContent.timestamp.toLongOrNull()) { "Timestamp must be Long type" } + + txContent.timestamp } else { null } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/TxHistoryItemStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/TxHistoryItemStateConverter.kt index e9c78803f3..888144c3eb 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/TxHistoryItemStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/TxHistoryItemStateConverter.kt @@ -5,6 +5,7 @@ import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.wrappedList +import com.tangem.core.ui.utils.toTimeFormat import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 @@ -27,13 +28,14 @@ internal class TxHistoryItemStateConverter( return TransactionState.Content( txHash = item.txHash, amount = item.getAmount(), - timestamp = item.getRawTimestamp(), + time = item.timestampInMillis.toTimeFormat(), status = item.status.tiUiStatus(), direction = item.extractDirection(), iconRes = item.extractIcon(), title = item.extractTitle(), subtitle = item.extractSubtitle(), - onClick = { clickIntents.onTransactionClick(item.txHash) }, + timestamp = item.timestampInMillis, + onClick = { clickIntents.onVisaTransactionClick(item.txHash) }, ) } @@ -85,14 +87,6 @@ internal class TxHistoryItemStateConverter( private fun TxHistoryItem.extractDirection() = if (isOutgoing) TransactionState.Content.Direction.OUTGOING else TransactionState.Content.Direction.INCOMING - /** - * Get timestamp without formatting. - * It's life hack that help us to add transaction's group title to flow. - * - * @see [convert] - */ - private fun TxHistoryItem.getRawTimestamp() = this.timestampInMillis.toString() - private fun TxHistoryItem.TransactionStatus.tiUiStatus() = when (this) { TxHistoryItem.TransactionStatus.Confirmed -> TransactionState.Content.Status.Confirmed TxHistoryItem.TransactionStatus.Failed -> TransactionState.Content.Status.Failed diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/VisaTxHistoryItemStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/VisaTxHistoryItemStateConverter.kt new file mode 100644 index 0000000000..c1f801a339 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/VisaTxHistoryItemStateConverter.kt @@ -0,0 +1,46 @@ +package com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter + +import com.tangem.core.ui.components.transactions.state.TransactionState +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.utils.BigDecimalFormatter +import com.tangem.core.ui.utils.DateTimeFormatters +import com.tangem.domain.visa.model.VisaCurrency +import com.tangem.domain.visa.model.VisaTxHistoryItem +import com.tangem.feature.wallet.impl.R +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.VisaWalletIntents +import com.tangem.utils.converter.Converter +import org.joda.time.DateTimeZone + +internal class VisaTxHistoryItemStateConverter( + private val visaCurrency: VisaCurrency, + private val clickIntents: VisaWalletIntents, +) : Converter { + + override fun convert(value: VisaTxHistoryItem): TransactionState { + val localDate = value.date.withZone(DateTimeZone.getDefault()) + val time = DateTimeFormatters.formatTime(time = localDate) + val subtitle = "$time • ${value.status}" + + return TransactionState.Content( + txHash = value.id, + amount = BigDecimalFormatter.formatCryptoAmount( + cryptoAmount = value.amount, + cryptoCurrency = visaCurrency.symbol, + decimals = visaCurrency.decimals, + ), + // Show tx fiat amount instead of tx time + time = BigDecimalFormatter.formatFiatAmount( + fiatAmount = value.fiatAmount, + fiatCurrencyCode = value.fiatCurrency.currencyCode, + fiatCurrencySymbol = value.fiatCurrency.symbol, + ), + status = TransactionState.Content.Status.Confirmed, + direction = TransactionState.Content.Direction.INCOMING, + iconRes = R.drawable.ic_arrow_up_24, + title = stringReference(value = value.merchantName ?: "Unknown merchant"), + subtitle = stringReference(subtitle), + timestamp = localDate.millis, + onClick = { clickIntents.onVisaTransactionClick(value.id) }, + ) + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/TxHistorySubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/TxHistorySubscriber.kt index d9d435e6f0..c30daff4df 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/TxHistorySubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/TxHistorySubscriber.kt @@ -2,8 +2,8 @@ package com.tangem.feature.wallet.presentation.wallet.subscribers import androidx.paging.PagingData import androidx.paging.cachedIn +import androidx.paging.map import arrow.core.Either -import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase import com.tangem.domain.tokens.model.CryptoCurrencyStatus @@ -15,12 +15,16 @@ import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.domain.collectLatest import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.* +import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetTxHistoryCountErrorTransformer +import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetTxHistoryCountTransformer +import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetTxHistoryItemsErrorTransformer +import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetTxHistoryItemsTransformer +import com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter.TxHistoryItemStateConverter import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map typealias MaybeTxHistoryCount = Either typealias MaybeTxHistoryItems = Either>> @@ -37,24 +41,6 @@ internal class TxHistorySubscriber( ) : WalletSubscriber() { override fun create(coroutineScope: CoroutineScope): Flow> { - // TODO: [REDACTED_JIRA] - if (userWallet.scanResponse.cardTypesResolver.isVisaWallet()) { - return flow { - stateHolder.update( - object : WalletStateTransformer(userWallet.walletId) { - override fun transform(prevState: WalletState): WalletState { - return when (prevState) { - is WalletState.Visa.Content -> prevState.copy( - txHistoryState = TxHistoryState.Empty(onExploreClick = {}), - ) - else -> prevState - } - } - }, - ) - } - } - return flow { getPrimaryCurrencyStatusUpdatesUseCase.collectLatest(userWalletId = userWallet.walletId) { status -> val maybeTxHistoryItemCount = txHistoryItemsCountUseCase( @@ -109,10 +95,19 @@ internal class TxHistorySubscriber( clickIntents = clickIntents, ) }, - ifRight = { + ifRight = { itemsFlow -> + val blockchain = userWallet.scanResponse.cardTypesResolver.getBlockchain() + val itemConverter = TxHistoryItemStateConverter( + symbol = blockchain.currency, + decimals = blockchain.decimals(), + clickIntents = clickIntents, + ) + SetTxHistoryItemsTransformer( userWallet = userWallet, - flow = it, + flow = itemsFlow.map { items -> + items.map(itemConverter::convert) + }, clickIntents = clickIntents, ) }, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/VisaWalletBalancesAndLimitsSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/VisaWalletBalancesAndLimitsSubscriber.kt deleted file mode 100644 index a25af15cdc..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/VisaWalletBalancesAndLimitsSubscriber.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.subscribers - -import com.tangem.domain.visa.GetVisaCurrencyUseCase -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetBalancesAndLimitsTransformer -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flow - -@Suppress("LongParameterList") -internal class VisaWalletBalancesAndLimitsSubscriber( - private val userWallet: UserWallet, - private val stateHolder: WalletStateController, - private val isRefresh: Boolean, - private val getVisaCurrencyUseCase: GetVisaCurrencyUseCase, - private val clickIntents: WalletClickIntentsV2, -) : WalletSubscriber() { - - override fun create(coroutineScope: CoroutineScope): Flow<*> { - return flow { - stateHolder.update( - SetBalancesAndLimitsTransformer( - userWallet = userWallet, - maybeVisaCurrency = getVisaCurrencyUseCase(userWallet.walletId, isRefresh), - clickIntents = clickIntents, - ), - ) - } - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/VisaWalletSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/VisaWalletSubscriber.kt new file mode 100644 index 0000000000..ea540ec981 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/VisaWalletSubscriber.kt @@ -0,0 +1,105 @@ +package com.tangem.feature.wallet.presentation.wallet.subscribers + +import androidx.paging.PagingData +import androidx.paging.cachedIn +import androidx.paging.map +import arrow.core.Either +import arrow.core.getOrElse +import com.tangem.domain.txhistory.models.TxHistoryListError +import com.tangem.domain.visa.GetVisaCurrencyUseCase +import com.tangem.domain.visa.GetVisaTxHistoryUseCase +import com.tangem.domain.visa.model.VisaCurrency +import com.tangem.domain.visa.model.VisaTxHistoryItem +import com.tangem.domain.wallets.models.UserWallet +import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetBalancesAndLimitsTransformer +import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetTxHistoryCountTransformer +import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetTxHistoryItemsErrorTransformer +import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetTxHistoryItemsTransformer +import com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter.VisaTxHistoryItemStateConverter +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map +import timber.log.Timber + +internal class VisaWalletSubscriber( + private val userWallet: UserWallet, + private val stateController: WalletStateController, + private val isRefresh: Boolean, + private val getVisaCurrencyUseCase: GetVisaCurrencyUseCase, + private val getVisaTxHistoryUseCase: GetVisaTxHistoryUseCase, + private val clickIntents: WalletClickIntentsV2, +) : WalletSubscriber() { + + override fun create(coroutineScope: CoroutineScope): Flow<*> { + return flow { + setLoadingTxHistoryState() + + val maybeCurrency = getVisaCurrencyUseCase(userWallet.walletId, isRefresh) + setLoadedCurrencyState(maybeCurrency) + + val currency = maybeCurrency.getOrElse { + setFailedTxHistoryState(it) + return@flow + } + val txHistoryItemsFlow = getVisaTxHistoryUseCase(userWallet.walletId, isRefresh = isRefresh) + .map { maybeTxHistoryItems -> + maybeTxHistoryItems.getOrElse { + Timber.e(it, "Failed to load tx history for wallet ${userWallet.walletId}") + throw it + } + } + .catch { setFailedTxHistoryState(it) } + .cachedIn(coroutineScope) + + setLoadedTxHistoryState(txHistoryItemsFlow, currency) + } + } + + private fun setLoadedCurrencyState(maybeCurrency: Either) { + stateController.update( + SetBalancesAndLimitsTransformer( + userWallet = userWallet, + maybeVisaCurrency = maybeCurrency, + clickIntents = clickIntents, + ), + ) + } + + private fun setLoadingTxHistoryState() { + stateController.update( + SetTxHistoryCountTransformer( + userWalletId = userWallet.walletId, + transactionsCount = 10, + clickIntents = clickIntents, + ), + ) + } + + private fun setFailedTxHistoryState(it: Throwable) { + stateController.update( + SetTxHistoryItemsErrorTransformer( + userWalletId = userWallet.walletId, + error = TxHistoryListError.DataError(it), + clickIntents = clickIntents, + ), + ) + } + + private fun setLoadedTxHistoryState(itemsFlow: Flow>, currency: VisaCurrency) { + val itemConverter = VisaTxHistoryItemStateConverter(currency, clickIntents) + + stateController.update( + SetTxHistoryItemsTransformer( + userWallet = userWallet, + flow = itemsFlow.map { items -> + items.map(itemConverter::convert) + }, + clickIntents = clickIntents, + ), + ) + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreenV2.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreenV2.kt index 46380e8b44..15e0698e0d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreenV2.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreenV2.kt @@ -30,6 +30,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.ActionsBottomSheetCon import com.tangem.feature.wallet.presentation.wallet.state.WalletAlertState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletBottomSheetConfig import com.tangem.feature.wallet.presentation.wallet.state2.model.* +import com.tangem.feature.wallet.presentation.wallet.state2.model.holder.TxHistoryStateHolder import com.tangem.feature.wallet.presentation.wallet.ui.components.TokenActionsBottomSheet import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletsList import com.tangem.feature.wallet.presentation.wallet.ui.components.common.* @@ -92,7 +93,7 @@ private fun WalletContent( BaseScaffold(state = state, selectedWallet = selectedWallet, snackbarHostState = snackbarHostState) { val movableItemModifier = Modifier.changeWalletAnimator(walletsListState) - val lazyTxHistoryItems = (selectedWallet as? WalletState.SingleCurrency)?.let { walletState -> + val lazyTxHistoryItems = (selectedWallet as? TxHistoryStateHolder)?.let { walletState -> (walletState.txHistoryState as? TxHistoryState.Content)?.contentItems?.collectAsLazyPagingItems() } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/VisaWalletIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/VisaWalletIntents.kt index 5dad048ccd..00c4927cde 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/VisaWalletIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/VisaWalletIntents.kt @@ -23,6 +23,8 @@ internal interface VisaWalletIntents { fun onDepositClick() fun onBalancesAndLimitsClick() + + fun onVisaTransactionClick(id: String) } @ViewModelScoped @@ -93,4 +95,8 @@ internal class VisaWalletIntentsImplementor @Inject constructor( null } } + + override fun onVisaTransactionClick(id: String) { + // TODO: Implement [REDACTED_JIRA] + } } \ No newline at end of file From 1771ce7847b4fc5ae0dc2fe3a5079a71f682e8df Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 9 Feb 2024 11:43:29 +0300 Subject: [PATCH 18/53] Updated on 2026-08-14 --- .../tangem/tap/di/domain/VisaDomainModule.kt | 7 +++ .../tangem/data/visa/DefaultVisaRepository.kt | 28 +++++++++++ .../data/visa/utils/VisaTxDetailsFactory.kt | 49 +++++++++++++++++++ .../visa/utils/VisaTxHistoryPagingSource.kt | 8 +++ .../domain/visa/GetVisaTxDetailsUseCase.kt | 15 ++++++ .../tangem/domain/visa/model/VisaTxDetails.kt | 40 +++++++++++++++ .../domain/visa/repository/VisaRepository.kt | 3 ++ 7 files changed, 150 insertions(+) create mode 100644 data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxDetailsFactory.kt create mode 100644 domain/visa/src/main/kotlin/com/tangem/domain/visa/GetVisaTxDetailsUseCase.kt create mode 100644 domain/visa/src/main/kotlin/com/tangem/domain/visa/model/VisaTxDetails.kt diff --git a/app/src/main/java/com/tangem/tap/di/domain/VisaDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/VisaDomainModule.kt index 2795fe2a8c..479be1cc3c 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/VisaDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/VisaDomainModule.kt @@ -1,6 +1,7 @@ package com.tangem.tap.di.domain import com.tangem.domain.visa.GetVisaCurrencyUseCase +import com.tangem.domain.visa.GetVisaTxDetailsUseCase import com.tangem.domain.visa.GetVisaTxHistoryUseCase import com.tangem.domain.visa.repository.VisaRepository import dagger.Module @@ -24,4 +25,10 @@ internal object VisaDomainModule { fun provideGetVisaTxHistoryUseCase(visaRepository: VisaRepository): GetVisaTxHistoryUseCase { return GetVisaTxHistoryUseCase(visaRepository) } + + @Provides + @ViewModelScoped + fun provideGetVisaTxDetailsUseCase(visaRepository: VisaRepository): GetVisaTxDetailsUseCase { + return GetVisaTxDetailsUseCase(visaRepository) + } } \ No newline at end of file diff --git a/data/visa/src/main/kotlin/com/tangem/data/visa/DefaultVisaRepository.kt b/data/visa/src/main/kotlin/com/tangem/data/visa/DefaultVisaRepository.kt index e2e0bc5567..d14a201a2b 100644 --- a/data/visa/src/main/kotlin/com/tangem/data/visa/DefaultVisaRepository.kt +++ b/data/visa/src/main/kotlin/com/tangem/data/visa/DefaultVisaRepository.kt @@ -11,22 +11,26 @@ import com.tangem.common.extensions.toHexString import com.tangem.data.common.cache.CacheRegistry import com.tangem.data.visa.utils.VisaConfig import com.tangem.data.visa.utils.VisaCurrencyFactory +import com.tangem.data.visa.utils.VisaTxDetailsFactory import com.tangem.data.visa.utils.VisaTxHistoryPagingSource import com.tangem.datasource.api.common.response.getOrThrow import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.visa.model.VisaCurrency +import com.tangem.domain.visa.model.VisaTxDetails import com.tangem.domain.visa.model.VisaTxHistoryItem import com.tangem.domain.visa.repository.VisaRepository import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import com.tangem.lib.visa.VisaContractInfoProvider import com.tangem.lib.visa.api.VisaApi +import com.tangem.lib.visa.model.VisaTxHistoryResponse import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update +import kotlinx.coroutines.withContext import java.math.BigDecimal internal class DefaultVisaRepository( @@ -41,10 +45,16 @@ internal class DefaultVisaRepository( private val currencyFactory by lazy(mode = LazyThreadSafetyMode.NONE) { VisaCurrencyFactory() } + private val txDetailsFactory by lazy(mode = LazyThreadSafetyMode.NONE) { + VisaTxDetailsFactory() + } private val fetchedCurrencies = MutableStateFlow( value = hashMapOf(), ) + private val fetchedHistoryItems = MutableStateFlow( + value = emptyMap>(), + ) override suspend fun getVisaCurrency(userWalletId: UserWalletId, isRefresh: Boolean): VisaCurrency { val address = makeAddress(userWalletId) @@ -102,6 +112,7 @@ internal class DefaultVisaRepository( ), cacheRegistry = cacheRegistry, visaApi = visaApi, + fetchedItems = fetchedHistoryItems, dispatchers = dispatchers, ) }, @@ -110,6 +121,23 @@ internal class DefaultVisaRepository( return pager.flow } + override suspend fun getTxDetails(userWalletId: UserWalletId, txId: String): VisaTxDetails { + return withContext(dispatchers.io) { + val userWallet = findVisaUserWallet(userWalletId) + val cardPubKey = getCardPubKey(userWallet).toHexString() + // val cardPubKey = "03DEF02B1FECC8BD3CFD52CE93235194479E1DE931EF0F55DC194967E7CCC3D12C" // for testing + val transaction = fetchedHistoryItems.value[cardPubKey]?.firstOrNull { + it.transactionId.toString() == txId + } + requireNotNull(transaction) { "Transaction not found: $txId" } + + txDetailsFactory.create( + transaction = transaction, + walletBlockchain = userWallet.scanResponse.cardTypesResolver.getBlockchain(), + ) + } + } + private suspend fun makeAddress(userWalletId: UserWalletId): String { val userWallet = findVisaUserWallet(userWalletId) val walletAddresses = makeWalletAddresses(userWallet) diff --git a/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxDetailsFactory.kt b/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxDetailsFactory.kt new file mode 100644 index 0000000000..ad0bbf4fb3 --- /dev/null +++ b/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxDetailsFactory.kt @@ -0,0 +1,49 @@ +package com.tangem.data.visa.utils + +import com.tangem.blockchain.common.Blockchain +import com.tangem.domain.visa.model.VisaTxDetails +import com.tangem.lib.visa.model.VisaTxHistoryResponse + +internal class VisaTxDetailsFactory { + + fun create(transaction: VisaTxHistoryResponse.Transaction, walletBlockchain: Blockchain): VisaTxDetails { + return VisaTxDetails( + id = transaction.transactionId.toString(), + type = transaction.transactionType, + status = transaction.transactionStatus, + blockchainAmount = transaction.blockchainAmount, + blockchainFee = transaction.blockchainFee, + transactionAmount = transaction.transactionAmount, + transactionCurrencyCode = transaction.transactionCurrencyCode, + merchantName = transaction.merchantName, + merchantCity = transaction.merchantCity, + merchantCountryCode = transaction.merchantCountryCode, + merchantCategoryCode = transaction.merchantCategoryCode, + fiatCurrency = findCurrencyByNumericCode(transaction.transactionCurrencyCode), + requests = transaction.requests.map { createRequest(it, walletBlockchain) }, + ) + } + + private fun createRequest( + request: VisaTxHistoryResponse.Transaction.Request, + walletBlockchain: Blockchain, + ): VisaTxDetails.Request { + return VisaTxDetails.Request( + billingAmount = request.billingAmount, + billingCurrencyCode = request.billingCurrencyCode, + blockchainAmount = request.blockchainAmount, + blockchainFee = request.blockchainFee, + errorCode = request.errorCode, + requestDate = request.requestDt, + requestStatus = request.requestStatus, + requestType = request.requestType, + transactionAmount = request.transactionAmount, + txCurrencyCode = request.transactionCurrencyCode, + id = request.transactionRequestId.toString(), + txHash = request.txHash, + txStatus = request.txStatus, + fiatCurrency = findCurrencyByNumericCode(request.transactionCurrencyCode), + exploreUrl = request.txHash?.let(walletBlockchain::getExploreTxUrl), + ) + } +} \ No newline at end of file diff --git a/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxHistoryPagingSource.kt b/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxHistoryPagingSource.kt index 1c54f97d8d..4af82f3014 100644 --- a/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxHistoryPagingSource.kt +++ b/data/visa/src/main/kotlin/com/tangem/data/visa/utils/VisaTxHistoryPagingSource.kt @@ -6,6 +6,7 @@ import com.tangem.data.common.cache.CacheRegistry import com.tangem.datasource.api.common.response.getOrThrow import com.tangem.domain.visa.model.VisaTxHistoryItem import com.tangem.lib.visa.api.VisaApi +import com.tangem.lib.visa.model.VisaTxHistoryResponse import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update @@ -16,6 +17,7 @@ internal class VisaTxHistoryPagingSource( params: Params, private val cacheRegistry: CacheRegistry, private val visaApi: VisaApi, + private val fetchedItems: MutableStateFlow>>, private val dispatchers: CoroutineDispatcherProvider, ) : PagingSource() { @@ -77,6 +79,12 @@ internal class VisaTxHistoryPagingSource( offset = offset, ).getOrThrow() + fetchedItems.update { + it.toMutableMap().apply { + this[cardPublicKey] = this[cardPublicKey].orEmpty() + response.transactions + } + } + pagedItems.update { it.toMutableMap().apply { this[offset] = response.transactions.map(itemsFactory::create) diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/visa/GetVisaTxDetailsUseCase.kt b/domain/visa/src/main/kotlin/com/tangem/domain/visa/GetVisaTxDetailsUseCase.kt new file mode 100644 index 0000000000..3ff945017f --- /dev/null +++ b/domain/visa/src/main/kotlin/com/tangem/domain/visa/GetVisaTxDetailsUseCase.kt @@ -0,0 +1,15 @@ +package com.tangem.domain.visa + +import arrow.core.Either +import com.tangem.domain.visa.model.VisaTxDetails +import com.tangem.domain.visa.repository.VisaRepository +import com.tangem.domain.wallets.models.UserWalletId + +class GetVisaTxDetailsUseCase( + private val visaRepository: VisaRepository, +) { + + suspend operator fun invoke(userWalletId: UserWalletId, transactionId: String): Either { + return Either.catch { visaRepository.getTxDetails(userWalletId, transactionId) } + } +} \ No newline at end of file diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/visa/model/VisaTxDetails.kt b/domain/visa/src/main/kotlin/com/tangem/domain/visa/model/VisaTxDetails.kt new file mode 100644 index 0000000000..1e833b7c16 --- /dev/null +++ b/domain/visa/src/main/kotlin/com/tangem/domain/visa/model/VisaTxDetails.kt @@ -0,0 +1,40 @@ +package com.tangem.domain.visa.model + +import org.joda.time.DateTime +import java.math.BigDecimal +import java.util.Currency + +data class VisaTxDetails( + val id: String, + val type: String, + val status: String, + val blockchainAmount: BigDecimal, + val blockchainFee: BigDecimal, + val transactionAmount: BigDecimal, + val transactionCurrencyCode: Int, + val merchantName: String?, + val merchantCity: String?, + val merchantCountryCode: String?, + val merchantCategoryCode: String?, + val fiatCurrency: Currency, + val requests: List, +) { + + data class Request( + val id: String, + val billingAmount: BigDecimal, + val billingCurrencyCode: Int, + val blockchainAmount: BigDecimal, + val blockchainFee: BigDecimal, + val errorCode: Int, + val requestDate: DateTime, + val requestStatus: String, + val requestType: String, + val transactionAmount: BigDecimal, + val txCurrencyCode: Int, + val txHash: String?, + val txStatus: String?, + val exploreUrl: String?, + val fiatCurrency: Currency, + ) +} \ No newline at end of file diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/visa/repository/VisaRepository.kt b/domain/visa/src/main/kotlin/com/tangem/domain/visa/repository/VisaRepository.kt index bd3860aa2b..dac5f4cf72 100644 --- a/domain/visa/src/main/kotlin/com/tangem/domain/visa/repository/VisaRepository.kt +++ b/domain/visa/src/main/kotlin/com/tangem/domain/visa/repository/VisaRepository.kt @@ -2,6 +2,7 @@ package com.tangem.domain.visa.repository import androidx.paging.PagingData import com.tangem.domain.visa.model.VisaCurrency +import com.tangem.domain.visa.model.VisaTxDetails import com.tangem.domain.visa.model.VisaTxHistoryItem import com.tangem.domain.wallets.models.UserWalletId import kotlinx.coroutines.flow.Flow @@ -15,4 +16,6 @@ interface VisaRepository { pageSize: Int, isRefresh: Boolean = false, ): Flow> + + suspend fun getTxDetails(userWalletId: UserWalletId, txId: String): VisaTxDetails } \ No newline at end of file From 3bd03920182f16f3a8ccd1bd5317de3874f255c2 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 9 Feb 2024 10:31:06 +0100 Subject: [PATCH 19/53] Updated on 2026-08-14 --- ...lidator.kt => ContractAddressValidator.kt} | 2 +- .../viewmodels/AddCustomTokenViewModel.kt | 8 +- core/res/src/main/res/values/strings.xml | 1 - .../tokens/ValidateContractAddressUseCase.kt | 6 +- features/manage-tokens/impl/build.gradle.kts | 2 + .../common/analytics/ManageTokens.kt | 102 ++++++++++++++ .../common/state/NetworkItemState.kt | 4 +- .../customtokens/state/TextFieldState.kt | 1 + ...stomTokenStateToCryptoCurrencyConverter.kt | 2 +- ...ntractAddressToCustomTokenDataConverter.kt | 4 + .../state/factory/CustomTokensStateFactory.kt | 6 + .../FoundTokenToCustomTokenDataConverter.kt | 4 + .../previewdata/AddCustomTokenPreviewData.kt | 1 + .../customtokens/ui/TokenTextField.kt | 16 ++- .../viewmodels/CustomTokensClickIntents.kt | 9 ++ .../viewmodels/CustomTokensViewModel.kt | 125 +++++++++++++++++- .../managetokens/state/ManageTokensState.kt | 1 + .../state/factory/ManageTokensStateFactory.kt | 5 +- .../ChooseNetworkStatePreviewData.kt | 4 +- .../ManageTokensStatePreviewData.kt | 1 + .../managetokens/ui/ManageTokensScreen.kt | 31 +++++ .../viewmodels/ManageTokensClickIntents.kt | 7 +- .../viewmodels/ManageTokensUiEvents.kt | 6 + .../viewmodels/ManageTokensViewModel.kt | 66 ++++++--- gradle/dependencies.toml | 2 +- 25 files changed, 371 insertions(+), 45 deletions(-) rename app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/validators/{ContactAddressValidator.kt => ContractAddressValidator.kt} (97%) create mode 100644 features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/common/analytics/ManageTokens.kt create mode 100644 features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensUiEvents.kt diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/validators/ContactAddressValidator.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/validators/ContractAddressValidator.kt similarity index 97% rename from app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/validators/ContactAddressValidator.kt rename to app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/validators/ContractAddressValidator.kt index b522a922b1..5a8c8bf385 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/validators/ContactAddressValidator.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/validators/ContractAddressValidator.kt @@ -10,7 +10,7 @@ import com.tangem.domain.AddCustomTokenError * [REDACTED_AUTHOR] */ -object ContactAddressValidator { +object ContractAddressValidator { /** Validate a [address] using [blockchain] */ fun validate(address: String, blockchain: Blockchain): ContractAddressValidatorResult { diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt index fae60b48c3..0faab87b40 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt @@ -33,7 +33,7 @@ import com.tangem.tap.features.customtoken.impl.presentation.models.AddCustomTok import com.tangem.tap.features.customtoken.impl.presentation.models.AddCustomTokenSelectorField.SelectorItem import com.tangem.tap.features.customtoken.impl.presentation.routers.CustomTokenRouter import com.tangem.tap.features.customtoken.impl.presentation.states.AddCustomTokenStateHolder -import com.tangem.tap.features.customtoken.impl.presentation.validators.ContactAddressValidator +import com.tangem.tap.features.customtoken.impl.presentation.validators.ContractAddressValidator import com.tangem.tap.features.customtoken.impl.presentation.validators.ContractAddressValidatorResult import com.tangem.tap.features.details.ui.cardsettings.TextReference import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider @@ -461,7 +461,7 @@ internal class AddCustomTokenViewModel @Inject constructor( private fun getTokenWarningSet(): Set { val networkSelectorValue = uiState.form.networkSelectorField.selectedItem.blockchain - val isContractAddressFieldEmpty = ContactAddressValidator.validate( + val isContractAddressFieldEmpty = ContractAddressValidator.validate( address = uiState.form.contractAddressInputField.value, blockchain = networkSelectorValue, ).let { @@ -513,7 +513,7 @@ internal class AddCustomTokenViewModel @Inject constructor( val state = when { isAllTokenFieldsFilled() && isNetworkSelected() -> { val networkSelectorValue = uiState.form.networkSelectorField.selectedItem.blockchain - val error = ContactAddressValidator.validate( + val error = ContractAddressValidator.validate( address = uiState.form.contractAddressInputField.value, blockchain = networkSelectorValue, ) @@ -727,7 +727,7 @@ internal class AddCustomTokenViewModel @Inject constructor( ) val selectedNetwork = uiState.form.networkSelectorField.selectedItem.blockchain - val validatorResult = ContactAddressValidator.validate( + val validatorResult = ContractAddressValidator.validate( address = enteredValue, blockchain = selectedNetwork, ) diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index d384d8f946..636726e3ca 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -285,7 +285,6 @@ Using non-native networks for tokens enables cross-blockchain interoperability, allowing assets to be utilized in diverse decentralized applications and smart contracts across platforms. However, this often involves a custodian or smart contract to hold the original asset securely, introducing centralization and counterparty risk. Not original or primary blockchain the token is hosted Non-native networks - Blockchain the cryptocurrency was initially created Networks Choose networks Wallet diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ValidateContractAddressUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ValidateContractAddressUseCase.kt index 361a79c45c..5ae4755564 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ValidateContractAddressUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ValidateContractAddressUseCase.kt @@ -13,7 +13,11 @@ class ValidateContractAddressUseCase(private val tokensListRepository: TokensLis block = { if (address.isEmpty()) raise(AddCustomTokenError.FieldIsEmpty) - if (!tokensListRepository.validateAddress(networkId, address)) { + if (!tokensListRepository.validateAddress( + contractAddress = address, + networkId = networkId, + ) + ) { raise(AddCustomTokenError.InvalidContractAddress) } }, diff --git a/features/manage-tokens/impl/build.gradle.kts b/features/manage-tokens/impl/build.gradle.kts index 692109ece2..e388043baa 100644 --- a/features/manage-tokens/impl/build.gradle.kts +++ b/features/manage-tokens/impl/build.gradle.kts @@ -41,6 +41,8 @@ dependencies { kapt(deps.hilt.kapt) /** Core modules */ + implementation(projects.core.analytics) + implementation(projects.core.analytics.models) implementation(projects.core.featuretoggles) implementation(projects.core.navigation) implementation(projects.core.ui) diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/common/analytics/ManageTokens.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/common/analytics/ManageTokens.kt new file mode 100644 index 0000000000..3a527de7f2 --- /dev/null +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/common/analytics/ManageTokens.kt @@ -0,0 +1,102 @@ +package com.tangem.managetokens.presentation.common.analytics + +import com.tangem.core.analytics.models.AnalyticsEvent +import com.tangem.core.analytics.models.AnalyticsParam + +sealed class ManageTokens( + event: String, + params: Map = emptyMap(), +) : AnalyticsEvent("Manage Tokens", event, params) { + + class ScreenOpened : ManageTokens("Manage Tokens Screen Opened") + + class TokenIsNotFound(userInput: String) : ManageTokens( + event = "Token Is Not Found", + params = mapOf("Input" to userInput), + ) + + class TokenSwitcherChanged( + token: String, + state: AnalyticsParam.OnOffState, + ) : ManageTokens( + event = "Token Switcher Changed", + params = mapOf( + "Token" to token, + "State" to state.value, + ), + ) + + class ButtonAdd(token: String) : ManageTokens( + event = "Button - Add", + params = mapOf("Token" to token), + ) + + class ButtonEdit(token: String) : ManageTokens( + event = "Button - Edit", + params = mapOf("Token" to token), + ) + + object ButtonChooseWallet : ManageTokens(event = "Button - Choose Wallet") + + class WalletSelected(source: Source) : ManageTokens( + event = "Wallet Selected", + params = mapOf("Source" to source.name), + ) { + + enum class Source(name: String) { + MainToken("Main Token"), + CustomToken("Custom Token"), + } + } + + object NoticeNonNativeNetworkClicked : ManageTokens(event = "Notice - Non Native Network Clicked") + + class ButtonGenerateAddresses(cardCount: Int) : ManageTokens( + event = "Button - Get Addresses", + params = mapOf("CardCount" to cardCount.toString()), + ) + + object ButtonCustomToken : ManageTokens("Button - Custom Token") + + class CustomTokenWasAdded( + val derivation: String, + val networkId: String, + val token: String? = null, + val contractAddress: String? = null, + ) : ManageTokens( + event = "Custom Token Was Added", + params = mutableMapOf( + "Derivation" to derivation, + "Network Id" to networkId, + ).apply { + token?.let { put("Token", it) } + contractAddress?.let { put("Contract Address", it) } + }, + ) + + class CustomTokenNetworkSelected(blockchain: String) : ManageTokens( + event = "Custom Token Network Selected", + params = mapOf("blockchain" to blockchain), + ) + + class CustomTokenDerivationSelected(derivation: String) : ManageTokens( + event = "Custom Token Derivation Selected", + params = mapOf("Derivation" to derivation), + ) + + class CustomTokenAddress(validated: Boolean) : ManageTokens( + "Custom Token Address", + params = mapOf("Validation" to if (validated) "Ok" else "Error"), + ) + + object CustomTokenName : ManageTokens("Custom Token Name") + + object CustomTokenSymbol : ManageTokens("Custom Token Symbol") + + object CustomTokenDecimals : ManageTokens("Custom Token Decimals") + + enum class Derivation(val value: String) { + DEFAULT("Default"), + CUSTOM("Custom"), + } +} \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/common/state/NetworkItemState.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/common/state/NetworkItemState.kt index 7f57f95a84..6255c750f9 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/common/state/NetworkItemState.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/common/state/NetworkItemState.kt @@ -38,7 +38,7 @@ internal sealed interface NetworkItemState { * @property onToggleClick lambda be invoked when switch is been toggled */ @Suppress("LongParameterList") - class Toggleable( + data class Toggleable( override val name: String, override val protocolName: String, override val id: String, @@ -72,7 +72,7 @@ internal sealed interface NetworkItemState { * @property onNetworkClick lambda be invoked when network item is been clicked * */ - class Selectable( + data class Selectable( override val name: String, override val protocolName: String, val iconResId: Int, diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/TextFieldState.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/TextFieldState.kt index 090d4a2a22..3014818228 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/TextFieldState.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/TextFieldState.kt @@ -8,6 +8,7 @@ internal sealed class TextFieldState { val isEnabled: Boolean, val error: AddCustomTokenWarning? = null, val onValueChange: (String) -> Unit, + val onFocusExit: () -> Unit, ) : TextFieldState() fun isInputValid(): Boolean = this is Editable && value.isNotBlank() && error == null diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/AddCustomTokenStateToCryptoCurrencyConverter.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/AddCustomTokenStateToCryptoCurrencyConverter.kt index 94f5828c6c..15e17fc927 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/AddCustomTokenStateToCryptoCurrencyConverter.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/AddCustomTokenStateToCryptoCurrencyConverter.kt @@ -39,7 +39,7 @@ internal class AddCustomTokenStateToCryptoCurrencyConverter( private fun parseTokenOrNull(tokenData: CustomTokenData?): CryptoCurrencyFactory.Token? { val contractAddress = (tokenData?.contractAddressTextField as? TextFieldState.Editable)?.value val symbol = (tokenData?.symbolTextField as? TextFieldState.Editable)?.value - val name = (tokenData?.contractAddressTextField as? TextFieldState.Editable)?.value + val name = (tokenData?.nameTextField as? TextFieldState.Editable)?.value val decimals = (tokenData?.decimalsTextField as? TextFieldState.Editable)?.value?.toIntOrNull() return if ( !contractAddress.isNullOrBlank() && !symbol.isNullOrBlank() && !name.isNullOrBlank() && decimals != null diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/ContractAddressToCustomTokenDataConverter.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/ContractAddressToCustomTokenDataConverter.kt index b6ab812200..422f3c4f4b 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/ContractAddressToCustomTokenDataConverter.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/ContractAddressToCustomTokenDataConverter.kt @@ -14,21 +14,25 @@ internal class ContractAddressToCustomTokenDataConverter( value = value, isEnabled = true, onValueChange = clickIntents::onContractAddressChange, + onFocusExit = clickIntents::onContractAddressFocusExit, ), nameTextField = TextFieldState.Editable( value = "", isEnabled = true, onValueChange = clickIntents::onTokenNameChange, + onFocusExit = clickIntents::onTokenNameFocusExit, ), symbolTextField = TextFieldState.Editable( value = "", isEnabled = true, onValueChange = clickIntents::onSymbolChange, + onFocusExit = clickIntents::onSymbolFocusExit, ), decimalsTextField = TextFieldState.Editable( value = "", isEnabled = true, onValueChange = clickIntents::onDecimalsChange, + onFocusExit = clickIntents::onDecimalsFocusExit, ), ) } diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/CustomTokensStateFactory.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/CustomTokensStateFactory.kt index 8a72ab97bf..f32d3f3e88 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/CustomTokensStateFactory.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/CustomTokensStateFactory.kt @@ -201,21 +201,25 @@ internal class CustomTokensStateFactory( value = "", isEnabled = true, onValueChange = clickIntents::onContractAddressChange, + onFocusExit = clickIntents::onContractAddressFocusExit, ), nameTextField = TextFieldState.Editable( value = "", isEnabled = false, onValueChange = clickIntents::onTokenNameChange, + onFocusExit = clickIntents::onTokenNameFocusExit, ), symbolTextField = TextFieldState.Editable( value = "", isEnabled = false, onValueChange = clickIntents::onSymbolChange, + onFocusExit = clickIntents::onSymbolFocusExit, ), decimalsTextField = TextFieldState.Editable( value = "", isEnabled = false, onValueChange = clickIntents::onDecimalsChange, + onFocusExit = clickIntents::onDecimalsFocusExit, ), ) } else { @@ -268,6 +272,8 @@ internal class CustomTokensStateFactory( value = contractAddress, isEnabled = true, onValueChange = clickIntents::onContractAddressChange, + onFocusExit = clickIntents::onContractAddressFocusExit, + ), nameTextField = TextFieldState.Loading, symbolTextField = TextFieldState.Loading, diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/FoundTokenToCustomTokenDataConverter.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/FoundTokenToCustomTokenDataConverter.kt index ebe6b1bee2..3aa06bdd9f 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/FoundTokenToCustomTokenDataConverter.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/FoundTokenToCustomTokenDataConverter.kt @@ -15,21 +15,25 @@ internal class FoundTokenToCustomTokenDataConverter( value = value.contractAddress, isEnabled = true, onValueChange = clickIntents::onContractAddressChange, + onFocusExit = clickIntents::onContractAddressFocusExit, ), nameTextField = TextFieldState.Editable( value = value.name, isEnabled = false, onValueChange = clickIntents::onTokenNameChange, + onFocusExit = clickIntents::onTokenNameFocusExit, ), symbolTextField = TextFieldState.Editable( value = value.symbol, isEnabled = false, onValueChange = clickIntents::onSymbolChange, + onFocusExit = clickIntents::onSymbolFocusExit, ), decimalsTextField = TextFieldState.Editable( value = value.decimals.toString(), isEnabled = false, onValueChange = clickIntents::onDecimalsChange, + onFocusExit = clickIntents::onDecimalsFocusExit, ), ) } diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/previewdata/AddCustomTokenPreviewData.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/previewdata/AddCustomTokenPreviewData.kt index 998c00a3e7..9be15589aa 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/previewdata/AddCustomTokenPreviewData.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/previewdata/AddCustomTokenPreviewData.kt @@ -38,6 +38,7 @@ internal object AddCustomTokenPreviewData { value = "0x4ace7262705b68bcba5b91de96889349394", isEnabled = false, onValueChange = {}, + onFocusExit = {}, ), nameTextField = TextFieldState.Loading, symbolTextField = TextFieldState.Loading, diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/TokenTextField.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/TokenTextField.kt index a615d22738..e5b7eef118 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/TokenTextField.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/TokenTextField.kt @@ -6,7 +6,11 @@ import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction @@ -20,6 +24,11 @@ internal fun TokenTextField( placeholder: String, keyboardType: KeyboardType = KeyboardType.Text, ) { + val isInitiallyComposed = remember { mutableStateOf(false) } + LaunchedEffect(key1 = true) { + isInitiallyComposed.value = true + } + BasicTextField( value = state.value, onValueChange = state.onValueChange, @@ -32,7 +41,12 @@ internal fun TokenTextField( ), cursorBrush = SolidColor(TangemTheme.colors.icon.primary1), modifier = Modifier - .fillMaxWidth(), + .fillMaxWidth() + .onFocusChanged { + if (!it.isFocused && isInitiallyComposed.value) { + state.onFocusExit() + } + }, decorationBox = { innerTextField -> Row(modifier = Modifier.fillMaxWidth()) { if (state.value.isEmpty()) { diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/viewmodels/CustomTokensClickIntents.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/viewmodels/CustomTokensClickIntents.kt index 6601f3e270..2dd2151beb 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/viewmodels/CustomTokensClickIntents.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/viewmodels/CustomTokensClickIntents.kt @@ -3,6 +3,7 @@ package com.tangem.managetokens.presentation.customtokens.viewmodels import com.tangem.managetokens.presentation.common.state.NetworkItemState import com.tangem.managetokens.presentation.customtokens.state.Derivation +@Suppress("TooManyFunctions") internal interface CustomTokensClickIntents { fun onNetworkSelected(networkItemState: NetworkItemState) @@ -25,6 +26,14 @@ internal interface CustomTokensClickIntents { fun onDecimalsChange(input: String) + fun onContractAddressFocusExit() + + fun onTokenNameFocusExit() + + fun onSymbolFocusExit() + + fun onDecimalsFocusExit() + fun onDerivationSelected(derivation: Derivation) fun onChooseDerivationClick() diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/viewmodels/CustomTokensViewModel.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/viewmodels/CustomTokensViewModel.kt index cc265a1bc2..2ccbd92705 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/viewmodels/CustomTokensViewModel.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/viewmodels/CustomTokensViewModel.kt @@ -6,16 +6,20 @@ import androidx.compose.runtime.setValue import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import arrow.core.getOrElse +import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.crypto.hdWallet.HDWalletError import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.tokens.* +import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.Network import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.domain.wallets.usecase.GetWalletsUseCase import com.tangem.domain.wallets.usecase.SelectWalletUseCase +import com.tangem.managetokens.presentation.common.analytics.ManageTokens import com.tangem.managetokens.presentation.common.state.AlertState import com.tangem.managetokens.presentation.common.state.Event import com.tangem.managetokens.presentation.common.state.NetworkItemState @@ -39,7 +43,7 @@ import kotlinx.coroutines.withContext import javax.inject.Inject import kotlin.properties.Delegates -@Suppress("LongParameterList") +@Suppress("LongParameterList", "TooManyFunctions", "LargeClass") @HiltViewModel internal class CustomTokensViewModel @Inject constructor( private val dispatchers: CoroutineDispatcherProvider, @@ -52,6 +56,7 @@ internal class CustomTokensViewModel @Inject constructor( private val validateContractAddressUseCase: ValidateContractAddressUseCase, private val getNetworksSupportedByWallet: GetNetworksSupportedByWallet, private val areTokensSupportedByNetworkUseCase: AreTokensSupportedByNetworkUseCase, + private val analyticsEventHandler: AnalyticsEventHandler, ) : ViewModel(), CustomTokensClickIntents, DefaultLifecycleObserver { private val debouncer = Debouncer() @@ -110,6 +115,7 @@ internal class CustomTokensViewModel @Inject constructor( } override fun onNetworkSelected(networkItemState: NetworkItemState) { + analyticsEventHandler.send(ManageTokens.CustomTokenNetworkSelected(networkItemState.name)) selectNetwork(networkItemState) router.popBackStack() } @@ -136,6 +142,7 @@ internal class CustomTokensViewModel @Inject constructor( } override fun onWalletSelected(walletId: String) { + analyticsEventHandler.send(ManageTokens.WalletSelected(ManageTokens.WalletSelected.Source.CustomToken)) viewModelScope.launch(dispatchers.io) { val userWalletId = UserWalletId(walletId) selectWalletUseCase(userWalletId) @@ -166,6 +173,7 @@ internal class CustomTokensViewModel @Inject constructor( value = input, isEnabled = true, onValueChange = this::onContractAddressChange, + onFocusExit = this::onContractAddressFocusExit, ), ), ) @@ -202,9 +210,14 @@ internal class CustomTokensViewModel @Inject constructor( ifLeft = { val tokenData = ContractAddressToCustomTokenDataConverter(this@CustomTokensViewModel) .convert(contractAddress) + + val isButtonEnabled = tokenData.isRequiredInformationProvided() uiState = uiState.copy( tokenData = tokenData, warnings = (uiState.warnings + AddCustomTokenWarning.PotentialScamToken).toPersistentSet(), + addTokenButton = uiState.addTokenButton.copy( + isEnabled = isButtonEnabled, + ), ) }, ifRight = { token -> @@ -215,7 +228,14 @@ internal class CustomTokensViewModel @Inject constructor( contractAddress, ) } - uiState = uiState.copy(tokenData = tokenData) + + val isButtonEnabled = tokenData.isRequiredInformationProvided() + uiState = uiState.copy( + tokenData = tokenData, + addTokenButton = uiState.addTokenButton.copy( + isEnabled = isButtonEnabled, + ), + ) }, ) } @@ -229,8 +249,12 @@ internal class CustomTokensViewModel @Inject constructor( value = input, isEnabled = true, onValueChange = this::onTokenNameChange, + onFocusExit = this::onTokenNameFocusExit, ), ), + addTokenButton = uiState.addTokenButton.copy( + isEnabled = uiState.tokenData?.isRequiredInformationProvided() == true, + ), ) } @@ -241,8 +265,12 @@ internal class CustomTokensViewModel @Inject constructor( value = input, isEnabled = true, onValueChange = this::onSymbolChange, + onFocusExit = this::onSymbolFocusExit, ), ), + addTokenButton = uiState.addTokenButton.copy( + isEnabled = uiState.tokenData?.isRequiredInformationProvided() == true, + ), ) } @@ -260,14 +288,40 @@ internal class CustomTokensViewModel @Inject constructor( isEnabled = true, onValueChange = this::onDecimalsChange, error = error, + onFocusExit = this::onDecimalsFocusExit, ), ), + addTokenButton = uiState.addTokenButton.copy( + isEnabled = uiState.tokenData?.isRequiredInformationProvided() == true, + ), ) } + override fun onContractAddressFocusExit() { + val error = (uiState.tokenData?.contractAddressTextField as? TextFieldState.Editable)?.error + val validated = error !is AddCustomTokenWarning.InvalidContractAddress + analyticsEventHandler.send(ManageTokens.CustomTokenAddress(validated = validated)) + } + + override fun onTokenNameFocusExit() { + analyticsEventHandler.send(ManageTokens.CustomTokenName) + } + + override fun onSymbolFocusExit() { + analyticsEventHandler.send(ManageTokens.CustomTokenSymbol) + } + + override fun onDecimalsFocusExit() { + analyticsEventHandler.send(ManageTokens.CustomTokenDecimals) + } + override fun onDerivationSelected(derivation: Derivation) { - uiState = - uiState.copy(chooseDerivationState = uiState.chooseDerivationState?.copy(selectedDerivation = derivation)) + derivation.standardType?.let { + analyticsEventHandler.send(ManageTokens.CustomTokenDerivationSelected(derivation.networkName)) + } + uiState = uiState.copy( + chooseDerivationState = uiState.chooseDerivationState?.copy(selectedDerivation = derivation), + ) router.popBackStack() } @@ -280,6 +334,7 @@ internal class CustomTokensViewModel @Inject constructor( } override fun onCustomDerivationChange(input: String) { + analyticsEventHandler.send(ManageTokens.CustomTokenDerivationSelected(ManageTokens.Derivation.CUSTOM.value)) uiState = uiState.copy( chooseDerivationState = uiState.chooseDerivationState?.copy( enterCustomDerivationState = uiState.chooseDerivationState?.enterCustomDerivationState?.copy( @@ -332,21 +387,77 @@ internal class CustomTokensViewModel @Inject constructor( val cryptoCurrency = AddCustomTokenStateToCryptoCurrencyConverter( selectedWallet.scanResponse.derivationStyleProvider, ).convert(uiState) - val alreadyAdded = - getCurrenciesUseCase(selectedWallet.walletId).getOrNull()?.any { it == cryptoCurrency } - if (alreadyAdded == true) { + val alreadyAdded = isCryptoCurrencyAlreadyAdded(selectedWallet, cryptoCurrency) + if (alreadyAdded) { uiState = stateFactory.getStateAndTriggerEvent( state = uiState, event = Event.ShowAlert(AlertState.TokenAlreadyAdded), setUiState = { uiState = it }, ) } else { + sendTokenAddedEvent(cryptoCurrency) addCryptoCurrenciesUseCase(selectedWallet.walletId, currency = cryptoCurrency) withContext(dispatchers.main) { router.popBackStack() } } } } + private suspend fun isCryptoCurrencyAlreadyAdded( + selectedWallet: UserWallet, + cryptoCurrency: CryptoCurrency, + ): Boolean { + val currenciesList = getCurrenciesUseCase(selectedWallet.walletId).getOrElse { emptyList() } + return when (cryptoCurrency) { + is CryptoCurrency.Coin -> { + currenciesList.any { + it is CryptoCurrency.Coin && + it.id == cryptoCurrency.id && + it.network.derivationPath == cryptoCurrency.network.derivationPath + } + } + is CryptoCurrency.Token -> { + currenciesList.any { + (it as? CryptoCurrency.Token)?.let { + it.id == cryptoCurrency.id && + it.contractAddress == cryptoCurrency.contractAddress && + it.network.id == cryptoCurrency.network.id && + it.network.derivationPath == cryptoCurrency.network.derivationPath + } ?: false + } + } + } + } + + private fun sendTokenAddedEvent(cryptoCurrency: CryptoCurrency) { + val selectedDerivation = uiState.chooseDerivationState?.selectedDerivation + + val derivation = when { + selectedDerivation == null -> ManageTokens.Derivation.DEFAULT.value + selectedDerivation.networkName.isNotEmpty() -> selectedDerivation.networkName + else -> ManageTokens.Derivation.CUSTOM.value + } + when (cryptoCurrency) { + is CryptoCurrency.Token -> { + analyticsEventHandler.send( + ManageTokens.CustomTokenWasAdded( + derivation = derivation, + networkId = cryptoCurrency.network.name, + contractAddress = cryptoCurrency.contractAddress, + token = cryptoCurrency.symbol, + ), + ) + } + is CryptoCurrency.Coin -> { + analyticsEventHandler.send( + ManageTokens.CustomTokenWasAdded( + derivation = derivation, + networkId = cryptoCurrency.network.name, + ), + ) + } + } + } + override fun onBack() { router.popBackStack() } diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/ManageTokensState.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/ManageTokensState.kt index 1ee0ecebce..8b4c05217c 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/ManageTokensState.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/ManageTokensState.kt @@ -16,6 +16,7 @@ internal data class ManageTokensState( val selectedToken: TokenItemState.Loaded? = null, val showChooseWalletScreen: Boolean = false, val event: StateEvent, + val onEmptySearchResult: (String) -> Unit, ) data class AddCustomTokenButton( diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/factory/ManageTokensStateFactory.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/factory/ManageTokensStateFactory.kt index bf81e5bc4c..a32b887e26 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/factory/ManageTokensStateFactory.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/factory/ManageTokensStateFactory.kt @@ -10,6 +10,7 @@ import com.tangem.managetokens.presentation.common.state.* import com.tangem.managetokens.presentation.common.utils.CurrencyUtils import com.tangem.managetokens.presentation.managetokens.state.* import com.tangem.managetokens.presentation.managetokens.viewmodels.ManageTokensClickIntents +import com.tangem.managetokens.presentation.managetokens.viewmodels.ManageTokensUiEvents import com.tangem.utils.Provider import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.flow.Flow @@ -17,6 +18,7 @@ import kotlinx.coroutines.flow.Flow internal class ManageTokensStateFactory( private val currentStateProvider: Provider, private val clickIntents: ManageTokensClickIntents, + private val uiIntents: ManageTokensUiEvents, ) { fun getInitialState(tokens: Flow>): ManageTokensState { @@ -36,6 +38,7 @@ internal class ManageTokensStateFactory( isLoading = false, event = consumedEvent(), chooseWalletState = ChooseWalletState.NoSelection, + onEmptySearchResult = uiIntents::onEmptySearchResult, ) } @@ -176,7 +179,7 @@ internal class ManageTokensStateFactory( totalNeeded = totalNeeded, totalWallets = totalWallets, walletsToDerive = walletsToDerive, - onGenerateClick = clickIntents::onGenerateDerivationClick, + onGenerateClick = clickIntents::onGetAddressesClick, ) } return currentStateProvider().copy(derivationNotification = derivationNotificationState) diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/previewdata/ChooseNetworkStatePreviewData.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/previewdata/ChooseNetworkStatePreviewData.kt index e9fedc1587..f66b65bc1b 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/previewdata/ChooseNetworkStatePreviewData.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/previewdata/ChooseNetworkStatePreviewData.kt @@ -37,7 +37,7 @@ internal val nonNativeNetworks = listOf( iconResId = mutableStateOf(R.drawable.img_kusama_22), isMainNetwork = false, isAdded = mutableStateOf(true), - id = "", + id = "1", onToggleClick = { _, _ -> }, address = "", decimals = 0, @@ -48,7 +48,7 @@ internal val nonNativeNetworks = listOf( iconResId = mutableStateOf(R.drawable.ic_bsc_16), isMainNetwork = false, isAdded = mutableStateOf(false), - id = "", + id = "2", onToggleClick = { _, _ -> }, address = "", decimals = 0, diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/previewdata/ManageTokensStatePreviewData.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/previewdata/ManageTokensStatePreviewData.kt index 6ea8316ba5..5d141ddbaa 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/previewdata/ManageTokensStatePreviewData.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/previewdata/ManageTokensStatePreviewData.kt @@ -19,6 +19,7 @@ internal object ManageTokensStatePreviewData { derivationNotification = DerivationNotificationStatePreviewData.state, event = consumedEvent(), chooseWalletState = ChooseWalletStatePreviewData.state, + onEmptySearchResult = {}, ) val loadingState: ManageTokensState diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ManageTokensScreen.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ManageTokensScreen.kt index 1bbf0c61a9..fb67526346 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ManageTokensScreen.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ManageTokensScreen.kt @@ -14,6 +14,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider +import androidx.paging.LoadState +import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.res.TangemTheme @@ -77,6 +79,14 @@ private fun Content(state: ManageTokensState) { ) } val tokens = state.tokens.collectAsLazyPagingItems() + val query = state.searchBarState.query + + TrackPossibleEmptySearchResult( + tokens = tokens, + query = query, + onEmptySearchResult = state.onEmptySearchResult, + ) + TokensList(tokens = tokens, addCustomTokenButton = state.addCustomTokenButton) } state.derivationNotification?.let { @@ -92,6 +102,27 @@ private fun Content(state: ManageTokensState) { } } +@Composable +private fun TrackPossibleEmptySearchResult( + tokens: LazyPagingItems, + query: String, + onEmptySearchResult: (String) -> Unit, +) { + val wasLoading = remember { mutableStateOf(false) } + + LaunchedEffect(tokens.loadState) { + val isLoading = tokens.loadState.refresh == LoadState.Loading + val stoppedLoading = wasLoading.value && !isLoading + val queryAndTokensCondition = query.isNotEmpty() && tokens.itemSnapshotList.isEmpty() + + if (stoppedLoading && queryAndTokensCondition) { + onEmptySearchResult(query) + } + + wasLoading.value = isLoading + } +} + @Composable private fun ManageTokensBottomSheet(selectedToken: TokenItemState.Loaded, state: ManageTokensState) { if (state.showChooseWalletScreen && state.chooseWalletState is ChooseWalletState.Choose) { diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensClickIntents.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensClickIntents.kt index a86b2cd1bb..2b6ba5b3bd 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensClickIntents.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensClickIntents.kt @@ -4,23 +4,24 @@ import com.tangem.managetokens.presentation.common.state.NetworkItemState import com.tangem.managetokens.presentation.managetokens.state.TokenItemState internal interface ManageTokensClickIntents { + fun onAddCustomTokensButtonClick() fun onSearchQueryChange(query: String) + fun onSearchActiveChange(active: Boolean) fun onTokenItemButtonClick(token: TokenItemState.Loaded) - fun onGenerateDerivationClick() + fun onGetAddressesClick() fun onBackClick() fun onCloseChooseNetworkScreen() fun onNetworkToggleClick(token: TokenItemState.Loaded, network: NetworkItemState.Toggleable) - fun onNonNativeNetworkHintClick() - fun onSelectWalletsClick() + fun onNonNativeNetworkHintClick() fun onChooseWalletClick() diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensUiEvents.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensUiEvents.kt new file mode 100644 index 0000000000..a5ce66e8d5 --- /dev/null +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensUiEvents.kt @@ -0,0 +1,6 @@ +package com.tangem.managetokens.presentation.managetokens.viewmodels + +internal interface ManageTokensUiEvents { + + fun onEmptySearchResult(query: String) +} \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensViewModel.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensViewModel.kt index ae04c6d7cd..e0630d462e 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensViewModel.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensViewModel.kt @@ -9,6 +9,8 @@ import androidx.lifecycle.viewModelScope import androidx.paging.PagingData import androidx.paging.map import arrow.core.getOrElse +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.card.DerivePublicKeysUseCase @@ -20,6 +22,7 @@ import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.domain.wallets.usecase.GetWalletsUseCase import com.tangem.domain.wallets.usecase.SelectWalletUseCase +import com.tangem.managetokens.presentation.common.analytics.ManageTokens import com.tangem.managetokens.presentation.common.state.AlertState import com.tangem.managetokens.presentation.common.state.Event import com.tangem.managetokens.presentation.common.state.NetworkItemState @@ -58,13 +61,15 @@ internal class ManageTokensViewModel @Inject constructor( private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val checkCurrencyCompatibilityUseCase: CheckCurrencyCompatibilityUseCase, private val isCryptoCurrencyCoinCouldHide: IsCryptoCurrencyCoinCouldHideUseCase, -) : ViewModel(), ManageTokensClickIntents, DefaultLifecycleObserver { + private val analyticsEventHandler: AnalyticsEventHandler, +) : ViewModel(), ManageTokensClickIntents, ManageTokensUiEvents, DefaultLifecycleObserver { private val debouncer = Debouncer() private val stateFactory = ManageTokensStateFactory( currentStateProvider = Provider { uiState }, clickIntents = this, + uiIntents = this, ) var router: InnerManageTokensRouter by Delegates.notNull() @@ -80,7 +85,7 @@ internal class ManageTokensViewModel @Inject constructor( private var selectedWallet: UserWallet? = null - private var neededDerivations: Map> = emptyMap() + private var currenciesToGenerateAddresses: Map> = emptyMap() private val selectedAppCurrencyFlow: StateFlow = createSelectedAppCurrencyFlow() @@ -107,6 +112,8 @@ internal class ManageTokensViewModel @Inject constructor( ) init { + analyticsEventHandler.send(ManageTokens.ScreenOpened()) + viewModelScope.launch(dispatchers.io) { getWalletsUseCase() .distinctUntilChanged() @@ -151,7 +158,7 @@ internal class ManageTokensViewModel @Inject constructor( .distinctUntilChanged() .collectLatest { it.onRight { mapOfMissingDerivations -> - neededDerivations = mapOfMissingDerivations + currenciesToGenerateAddresses = mapOfMissingDerivations withContext(dispatchers.main) { updateDerivation() } } } @@ -159,8 +166,8 @@ internal class ManageTokensViewModel @Inject constructor( } private fun updateDerivation() { - val totalNeeded = neededDerivations.values.sumOf { derivations -> derivations.size } - val walletsToDerive = neededDerivations.values + val totalNeeded = currenciesToGenerateAddresses.values.sumOf { derivations -> derivations.size } + val walletsToDerive = currenciesToGenerateAddresses.values .filter { derivations -> derivations.isNotEmpty() }.size uiState = stateFactory.updateDerivationNotification( totalNeeded = totalNeeded, @@ -182,6 +189,7 @@ internal class ManageTokensViewModel @Inject constructor( } override fun onAddCustomTokensButtonClick() { + analyticsEventHandler.send(ManageTokens.ButtonCustomToken) router.openCustomTokensScreen() } @@ -201,6 +209,13 @@ internal class ManageTokensViewModel @Inject constructor( override fun onTokenItemButtonClick(token: TokenItemState.Loaded) { when (token.availableAction.value) { TokenButtonType.ADD, TokenButtonType.EDIT -> { + if (token.availableAction.value == TokenButtonType.ADD) { + analyticsEventHandler.send(ManageTokens.ButtonAdd(token.currencySymbol)) + } + if (token.availableAction.value == TokenButtonType.EDIT) { + analyticsEventHandler.send(ManageTokens.ButtonEdit(token.currencySymbol)) + } + uiState = uiState.copy(selectedToken = token) val addedCurrenciesOnWallet = addedCurrenciesByWallet[selectedWallet] ?: listOf() stateFactory.updateTokenNetworksOnTokenSelection(token, addedCurrenciesOnWallet) @@ -219,17 +234,21 @@ internal class ManageTokensViewModel @Inject constructor( } } - override fun onGenerateDerivationClick() { - if (neededDerivations.isNotEmpty()) { + override fun onGetAddressesClick() { + if (currenciesToGenerateAddresses.isNotEmpty()) { viewModelScope.launch(dispatchers.io) { - val walletId = neededDerivations.keys.firstOrNull() - val currenciesToDerive = neededDerivations[walletId] - if (walletId == null || currenciesToDerive.isNullOrEmpty()) return@launch - derivePublicKeysUseCase(walletId, currenciesToDerive) - .onRight { - updateDerivationNotificationState() - fetchTokenListUseCase(userWalletId = walletId) + val cardCount = currenciesToGenerateAddresses.count { it.value.isNotEmpty() } + analyticsEventHandler.send(ManageTokens.ButtonGenerateAddresses(cardCount)) + + currenciesToGenerateAddresses.forEach { (walletId, currenciesToDerive) -> + if (currenciesToDerive.isNotEmpty()) { + derivePublicKeysUseCase(walletId, currenciesToDerive) + .onRight { + updateDerivationNotificationState() + fetchTokenListUseCase(userWalletId = walletId) + } } + } } } } @@ -247,8 +266,14 @@ internal class ManageTokensViewModel @Inject constructor( if (!selectedWallet.isMultiCurrency || selectedWallet.isLocked) return if (network.isAdded.value) { + analyticsEventHandler.send( + ManageTokens.TokenSwitcherChanged(token = token.currencySymbol, AnalyticsParam.OnOffState.Off), + ) toggleToken(token, network, selectedWallet) } else { + analyticsEventHandler.send( + ManageTokens.TokenSwitcherChanged(token = token.currencySymbol, AnalyticsParam.OnOffState.On), + ) viewModelScope.launch(dispatchers.io) { checkCompatibilityAndToggleToken(token, network, selectedWallet) } @@ -348,6 +373,7 @@ internal class ManageTokensViewModel @Inject constructor( } override fun onNonNativeNetworkHintClick() { + analyticsEventHandler.send(ManageTokens.NoticeNonNativeNetworkClicked) uiState = stateFactory.getStateAndTriggerEvent( state = uiState, event = Event.ShowAlert(AlertState.NonNative), @@ -355,13 +381,8 @@ internal class ManageTokensViewModel @Inject constructor( ) } - override fun onSelectWalletsClick() { - uiState = uiState.copy( - showChooseWalletScreen = true, - ) - } - override fun onChooseWalletClick() { + analyticsEventHandler.send(ManageTokens.ButtonChooseWallet) uiState = uiState.copy( showChooseWalletScreen = true, ) @@ -374,6 +395,7 @@ internal class ManageTokensViewModel @Inject constructor( } override fun onWalletSelected(walletId: String) { + analyticsEventHandler.send(ManageTokens.WalletSelected(ManageTokens.WalletSelected.Source.MainToken)) viewModelScope.launch(dispatchers.io) { selectWalletUseCase(UserWalletId(walletId)) } @@ -381,4 +403,8 @@ internal class ManageTokensViewModel @Inject constructor( uiState.selectedToken?.let { onTokenItemButtonClick(it) } uiState = stateFactory.updateSelectedWallet(selectedWalletId = selectedWallet?.walletId?.stringValue) } + + override fun onEmptySearchResult(query: String) { + analyticsEventHandler.send(ManageTokens.TokenIsNotFound(query)) + } } \ No newline at end of file diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index bdb562b050..266de7e848 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -92,7 +92,7 @@ tangemCardSdk = "develop-324" # endregion Tangem # region Tools -detektComposeRules = "1.2.2" +detektComposeRules = "1.3.0" detekt = "1.22.0" # endregion Tools From 368066a4dad742d7d4b7508a11c8baadf2f1d5a6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 9 Feb 2024 13:11:39 +0300 Subject: [PATCH 20/53] Updated on 2026-08-14 --- app/src/main/assets/testnet_tokens.json | 10 +++ .../impl/presentation/ui/BriefNetworksList.kt | 4 +- .../presentation/ui/TokenListPreviewData.kt | 7 ++ .../core/ui/extensions/BlockchainIcons.kt | 5 ++ .../src/main/res/drawable/ic_shibarium_22.xml | 57 ++++++++++++ .../main/res/drawable/img_shibarium_22.xml | 90 +++++++++++++++++++ .../domain/common/extensions/Blockchain.kt | 9 ++ gradle/dependencies.toml | 2 +- 8 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 core/ui/src/main/res/drawable/ic_shibarium_22.xml create mode 100644 core/ui/src/main/res/drawable/img_shibarium_22.xml diff --git a/app/src/main/assets/testnet_tokens.json b/app/src/main/assets/testnet_tokens.json index 6be450bd32..ada88c6bc7 100644 --- a/app/src/main/assets/testnet_tokens.json +++ b/app/src/main/assets/testnet_tokens.json @@ -569,6 +569,16 @@ } ] }, + { + "id": "shibarium", + "name": "Shibarium", + "symbol": "BONE", + "networks": [ + { + "networkId": "shibarium/test" + } + ] + }, { "id": "aptos", "name": "Aptos", diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/BriefNetworksList.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/BriefNetworksList.kt index ea2c77ed2a..d5328878eb 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/BriefNetworksList.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/BriefNetworksList.kt @@ -83,7 +83,9 @@ internal fun BriefNetworkItem(model: NetworkItemState, modifier: Modifier = Modi Icon( painter = painterResource(id = model.iconResId.value), contentDescription = null, - modifier = Modifier.size(size = TangemTheme.dimens.size20), + modifier = Modifier + .size(size = TangemTheme.dimens.size20) + .clip(CircleShape), tint = if (isAdded) Color.Unspecified else TangemTheme.colors.text.tertiary, ) diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/TokenListPreviewData.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/TokenListPreviewData.kt index f5481080e5..fafeb5d244 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/TokenListPreviewData.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/TokenListPreviewData.kt @@ -80,6 +80,13 @@ object TokenListPreviewData { iconResId = mutableStateOf(R.drawable.ic_bsc_16), isMainNetwork = false, ), + // check icon clipping + NetworkItemState.ReadContent( + name = "SHIBARIUM", + protocolName = "BEP20", + iconResId = mutableStateOf(R.drawable.ic_shibarium_22), + isMainNetwork = false, + ), ) } } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt b/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt index e3170a4467..33e08b6314 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt @@ -49,6 +49,7 @@ fun getActiveIconRes(blockchainId: String): Int { "xdc", "xdc/test" -> R.drawable.img_xdc_22 "vechain", "vechain/test" -> R.drawable.img_vechain_22 "aptos", "aptos/test" -> R.drawable.img_aptos_22 + "shibarium", "shibarium/test" -> R.drawable.img_shibarium_22 else -> R.drawable.ic_alert_24 } } @@ -99,6 +100,7 @@ fun getActiveIconResByNetworkId(networkId: String): Int { "xdc-network", "xdc-network/test" -> R.drawable.img_xdc_22 "vechain", "vechain/test" -> R.drawable.img_vechain_22 "aptos", "aptos/test" -> R.drawable.img_aptos_22 + "shibarium", "shibarium/test" -> R.drawable.img_shibarium_22 else -> R.drawable.ic_alert_24 } } @@ -146,6 +148,7 @@ fun getActiveIconResByCoinId(coinId: String): Int { "xdce-crowd-sale" -> R.drawable.img_xdc_22 "vechain" -> R.drawable.img_vechain_22 "aptos" -> R.drawable.img_aptos_22 + "shibarium" -> R.drawable.img_shibarium_22 else -> R.drawable.ic_alert_24 } } @@ -196,6 +199,7 @@ fun getGreyedOutIconRes(blockchainId: String): Int { "xdc", "xdc/test" -> R.drawable.ic_xdc_22 "vechain", "vechain/test" -> R.drawable.ic_vechain_22 "aptos", "aptos/test" -> R.drawable.ic_aptos_22 + "shibarium", "shibarium/test" -> R.drawable.ic_shibarium_22 else -> R.drawable.ic_alert_24 } } @@ -246,6 +250,7 @@ fun getGreyedOutIconResByNetworkId(networkId: String): Int { "xdc-network", "xdc-network/test" -> R.drawable.ic_xdc_22 "vechain", "vechain/test" -> R.drawable.ic_vechain_22 "aptos", "aptos/test" -> R.drawable.ic_aptos_22 + "shibarium", "shibarium/test" -> R.drawable.ic_shibarium_22 else -> R.drawable.ic_alert_24 } } \ No newline at end of file diff --git a/core/ui/src/main/res/drawable/ic_shibarium_22.xml b/core/ui/src/main/res/drawable/ic_shibarium_22.xml new file mode 100644 index 0000000000..359f911c8f --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_shibarium_22.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + diff --git a/core/ui/src/main/res/drawable/img_shibarium_22.xml b/core/ui/src/main/res/drawable/img_shibarium_22.xml new file mode 100644 index 0000000000..683c3555ca --- /dev/null +++ b/core/ui/src/main/res/drawable/img_shibarium_22.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt index 9d0357e1f1..f25e9a4f57 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt @@ -80,6 +80,8 @@ fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? { "vechain/test" -> Blockchain.VeChainTestnet "aptos" -> Blockchain.Aptos "aptos/test" -> Blockchain.AptosTestnet + "shibarium" -> Blockchain.Shibarium + "shibarium/test" -> Blockchain.ShibariumTestnet else -> null } } @@ -161,6 +163,9 @@ fun Blockchain.toNetworkId(): String { Blockchain.VeChainTestnet -> "vechain/test" Blockchain.Aptos -> "aptos" Blockchain.AptosTestnet -> "aptos/test" + Blockchain.Shibarium -> "shibarium" + Blockchain.ShibariumTestnet -> "shibarium/test" + Blockchain.Playa3ull -> "" // FIXME } } @@ -213,6 +218,9 @@ fun Blockchain.toCoinId(): String { Blockchain.Aptos -> "aptos" Blockchain.AptosTestnet -> "aptos/test" Blockchain.Unknown -> "unknown" + Blockchain.Shibarium -> "bone-shibaswap" + Blockchain.ShibariumTestnet -> "bone-shibaswap/test" + Blockchain.Playa3ull -> "" // FIXME } } @@ -241,4 +249,5 @@ private val excludedBlockchains = listOf( Blockchain.Unknown, Blockchain.Ducatus, Blockchain.Aptos, + Blockchain.Playa3ull, ) \ No newline at end of file diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 266de7e848..297f61b088 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -85,7 +85,7 @@ web3j = "4.10.1" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-475" +tangemBlockchainSdk = "develop-482" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-324" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From 0b8c00c15622edefe44dc674daf5a9a6f6c3854a Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 9 Feb 2024 14:09:56 +0300 Subject: [PATCH 21/53] Updated on 2026-08-14 --- .../configs/feature_toggles_config.json | 4 - .../featuretoggles/WalletFeatureToggles.kt | 11 - .../wallet/di/WalletFeatureTogglesModule.kt | 21 - .../feature/wallet/di/WalletRouterModule.kt | 8 +- .../DefaultWalletFeatureToggles.kt | 19 - .../presentation/common/WalletPreviewData.kt | 166 +- .../router/DefaultWalletRouter.kt | 23 +- .../utils/TokenListAnalyticsSender.kt | 2 +- .../utils/WalletWarningsAnalyticsSender.kt | 4 +- .../domain/GetMultiWalletWarningsFactory.kt | 14 +- .../domain/GetSingleWalletWarningsFactory.kt | 10 +- .../domain/WalletAdditionalInfoFactory.kt | 2 +- .../loaders/WalletContentLoaderFactory.kt | 4 +- .../loaders/WalletScreenContentLoader.kt | 6 +- .../implementors/MultiWalletContentLoader.kt | 6 +- .../MultiWalletContentLoaderFactory.kt | 6 +- .../implementors/SingleWalletContentLoader.kt | 6 +- .../SingleWalletContentLoaderFactory.kt | 6 +- .../SingleWalletWithTokenContentLoader.kt | 6 +- ...ngleWalletWithTokenContentLoaderFactory.kt | 6 +- .../implementors/VisaWalletContentLoader.kt | 6 +- .../VisaWalletContentLoaderFactory.kt | 6 +- .../wallet/state/WalletLockedState.kt | 32 - .../wallet/state/WalletMultiCurrencyState.kt | 63 - .../wallet/state/WalletSingleCurrencyState.kt | 91 - .../presentation/wallet/state/WalletState.kt | 101 -- .../WalletStateController.kt | 16 +- .../state/components/WalletTokensListState.kt | 109 -- .../state/components/WalletsListConfig.kt | 18 - .../state/factory/TokenActionsProvider.kt | 104 -- .../state/factory/TokenListWithWallet.kt | 9 - .../WalletCryptoCurrencyActionsConverter.kt | 76 - .../factory/WalletDeleteStateConverter.kt | 50 - .../WalletLoadedTokensListConverter.kt | 49 - .../state/factory/WalletLockedConverter.kt | 80 - .../factory/WalletRefreshStateConverter.kt | 115 -- .../factory/WalletRenameStateConverter.kt | 33 - ...letSingleCurrencyLoadedBalanceConverter.kt | 181 -- .../factory/WalletSkeletonStateConverter.kt | 157 -- .../state/factory/WalletStateFactory.kt | 288 ---- .../factory/WalletUpdateCardCountConverter.kt | 51 - .../factory/WalletsUnlockStateConverter.kt | 132 -- .../WalletLoadedTxHistoryConverter.kt | 80 - .../WalletLoadingTxHistoryConverter.kt | 105 -- .../WalletTxHistoryItemFlowConverter.kt | 131 -- ...alletTxHistoryTransactionStateConverter.kt | 103 -- .../{ => model}/ActionsBottomSheetConfig.kt | 2 +- .../model/BalancesAndLimitsBlockState.kt | 2 +- .../BalancesAndLimitsBottomSheetConfig.kt | 2 +- .../model/DepositButtonState.kt | 2 +- .../model/ManageTokensButtonConfig.kt | 2 +- .../{ => model}/TokenActionButtonConfig.kt | 2 +- .../WalletAdditionalInfo.kt | 2 +- .../state/{ => model}/WalletAlertState.kt | 2 +- .../WalletBottomSheetConfig.kt | 2 +- .../{components => model}/WalletCardState.kt | 2 +- .../wallet/state/{ => model}/WalletEvent.kt | 2 +- .../WalletManageButton.kt | 2 +- .../WalletNotification.kt | 2 +- .../WalletPullToRefreshConfig.kt | 2 +- .../model/WalletScreenState.kt | 4 +- .../{state2 => state}/model/WalletState.kt | 14 +- .../model/WalletTokensListState.kt | 2 +- .../WalletTopBarConfig.kt | 2 +- .../model/holder/LockedWalletStateHolder.kt | 10 +- .../model/holder/TxHistoryStateHolder.kt | 2 +- .../transformers/AddWalletTransformer.kt | 10 +- .../CloseBottomSheetTransformer.kt | 4 +- .../transformers/DeleteWalletTransformer.kt | 6 +- .../InitializeWalletsTransformer.kt | 16 +- .../OpenBottomSheetTransformer.kt | 4 +- .../ReinitializeWalletTransformer.kt | 10 +- .../transformers/RenameWalletTransformer.kt | 4 +- .../transformers/ScrollToWalletTransformer.kt | 6 +- .../transformers/SendEventTransformer.kt | 6 +- .../SetBalancesAndLimitsTransformer.kt | 14 +- .../SetCryptoCurrencyActionsTransformer.kt | 10 +- .../SetPrimaryCurrencyTransformer.kt | 10 +- .../SetRefreshStateTransformer.kt | 9 +- .../SetTokenListErrorTransformer.kt | 6 +- .../transformers/SetTokenListTransformer.kt | 18 +- .../SetTxHistoryCountErrorTransformer.kt | 10 +- .../SetTxHistoryCountTransformer.kt | 8 +- .../SetTxHistoryItemsErrorTransformer.kt | 8 +- .../SetTxHistoryItemsTransformer.kt | 10 +- .../transformers/SetWarningsTransformer.kt | 6 +- .../transformers/UnlockWalletTransformer.kt | 14 +- .../UpdateBalanceHidingModeTransformer.kt | 4 +- .../UpdateWalletCardsCountTransformer.kt | 6 +- .../WalletScreenStateTransformer.kt | 8 + .../transformers/WalletStateTransformer.kt | 6 +- .../BalancesAndLimitsBottomSheetConverter.kt | 10 +- .../MultiWalletCardStateConverter.kt | 4 +- .../MultiWalletCurrencyActionsConverter.kt | 4 +- .../SingleWalletCardStateConverter.kt | 4 +- .../SingleWalletMarketPriceConverter.kt | 2 +- .../converter/TokenItemStateConverter.kt | 6 +- .../converter/TokenListStateConverter.kt | 12 +- .../converter/TxHistoryItemFlowConverter.kt | 6 +- .../converter/TxHistoryItemStateConverter.kt | 6 +- .../VisaTxHistoryItemStateConverter.kt | 2 +- .../utils/UserWalletConverterExt.kt | 4 +- .../utils/WalletEventSender.kt | 8 +- .../utils/WalletLoadingStateFactory.kt | 11 +- .../WalletScreenStateTransformer.kt | 8 - .../subscribers/BasicTokenListSubscriber.kt | 10 +- .../MultiWalletTokenListSubscriber.kt | 6 +- .../MultiWalletWarningsSubscriber.kt | 10 +- .../subscribers/PrimaryCurrencySubscriber.kt | 4 +- .../SingleWalletButtonsSubscriber.kt | 8 +- .../SingleWalletNotificationsSubscriber.kt | 10 +- .../SingleWalletWithTokenListSubscriber.kt | 6 +- .../wallet/subscribers/TxHistorySubscriber.kt | 16 +- .../subscribers/VisaWalletSubscriber.kt | 16 +- .../presentation/wallet/ui/WalletAlert.kt | 2 +- .../wallet/ui/WalletEventEffect.kt | 66 +- .../wallet/ui/WalletEventEffectV2.kt | 62 - .../presentation/wallet/ui/WalletScreen.kt | 337 ++-- .../presentation/wallet/ui/WalletScreenV2.kt | 257 --- .../wallet/ui/WalletsListEffects.kt | 20 +- .../wallet/ui/WalletsListEffectsV2.kt | 45 - .../ui/components/TokenActionsBottomSheet.kt | 4 +- .../wallet/ui/components/WalletsList.kt | 44 +- .../ui/components/common/WalletBottomSheet.kt | 5 +- .../wallet/ui/components/common/WalletCard.kt | 16 +- .../ui/components/common/WalletContent.kt | 28 +- .../components/common/WalletNotifications.kt | 2 +- .../ui/components/common/WalletTopBar.kt | 2 +- .../multicurrency/MultiCurrencyContent.kt | 50 +- .../multicurrency/MultiCurrencyContentItem.kt | 19 +- .../SingleCurrencyControlButtons.kt | 2 +- .../components/visa/BalancesAndLimitsBlock.kt | 2 +- .../visa/BalancesAndLimitsBottomSheet.kt | 2 +- .../ui/components/visa/VisaDepositButton.kt | 2 +- .../wallet/ui/utils/ScrollOffsetCollector.kt | 26 +- .../ui/utils/ScrollOffsetCollectorV2.kt | 49 - .../ui/utils/WalletsScrollPreviewExt.kt | 2 +- ...ryptoCurrencyStatusToTokenItemConverter.kt | 122 -- .../utils/CurrencyStatusErrorConverter.kt | 17 - .../utils/FiatBalanceToWalletCardConverter.kt | 60 - .../wallet/utils/HiddenStateConverter.kt | 26 - .../wallet/utils/TokenListErrorConverter.kt | 35 - .../utils/TokenListToContentItemsConverter.kt | 114 -- .../utils/TokenListToWalletStateConverter.kt | 63 - .../wallet/viewmodels/WalletClickIntents.kt | 77 - .../WalletNotificationsListFactory.kt | 230 --- .../wallet/viewmodels/WalletStateCache.kt | 29 - .../wallet/viewmodels/WalletStateHolder.kt | 40 - .../viewmodels/WalletStateHolderDelegate.kt | 20 - .../wallet/viewmodels/WalletViewModel.kt | 1525 +++-------------- .../wallet/viewmodels/WalletViewModelV2.kt | 319 ---- .../viewmodels/WalletsUpdateActionResolver.kt | 282 ++- .../WalletsUpdateActionResolverV2.kt | 312 ---- .../viewmodels/intents/VisaWalletIntents.kt | 6 +- .../intents/WalletCardClickIntents.kt | 12 +- ...lickIntentsV2.kt => WalletClickIntents.kt} | 14 +- .../intents/WalletContentClickIntents.kt | 6 +- .../WalletCurrencyActionsClickIntents.kt | 10 +- .../intents/WalletWarningsClickIntents.kt | 10 +- 159 files changed, 1006 insertions(+), 6331 deletions(-) delete mode 100644 features/wallet/api/src/main/kotlin/com/tangem/features/wallet/featuretoggles/WalletFeatureToggles.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/di/WalletFeatureTogglesModule.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/featuretoggles/DefaultWalletFeatureToggles.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletLockedState.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletMultiCurrencyState.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletState.kt rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/WalletStateController.kt (78%) delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletsListConfig.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/TokenActionsProvider.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/TokenListWithWallet.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletCryptoCurrencyActionsConverter.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletDeleteStateConverter.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLockedConverter.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletRefreshStateConverter.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletRenameStateConverter.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletUpdateCardCountConverter.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletsUnlockStateConverter.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryTransactionStateConverter.kt rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/{ => model}/ActionsBottomSheetConfig.kt (84%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/model/BalancesAndLimitsBlockState.kt (85%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/model/BalancesAndLimitsBottomSheetConfig.kt (91%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/model/DepositButtonState.kt (59%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/model/ManageTokensButtonConfig.kt (51%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/{ => model}/TokenActionButtonConfig.kt (88%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/{components => model}/WalletAdditionalInfo.kt (73%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/{ => model}/WalletAlertState.kt (97%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/{components => model}/WalletBottomSheetConfig.kt (95%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/{components => model}/WalletCardState.kt (98%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/{ => model}/WalletEvent.kt (92%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/{components => model}/WalletManageButton.kt (97%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/{components => model}/WalletNotification.kt (99%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/{components => model}/WalletPullToRefreshConfig.kt (78%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/model/WalletScreenState.kt (63%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/model/WalletState.kt (86%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/model/WalletTokensListState.kt (96%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/{components => model}/WalletTopBarConfig.kt (72%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/model/holder/LockedWalletStateHolder.kt (75%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/model/holder/TxHistoryStateHolder.kt (92%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/AddWalletTransformer.kt (65%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/CloseBottomSheetTransformer.kt (87%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/DeleteWalletTransformer.kt (79%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/InitializeWalletsTransformer.kt (85%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/OpenBottomSheetTransformer.kt (93%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/ReinitializeWalletTransformer.kt (66%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/RenameWalletTransformer.kt (88%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/ScrollToWalletTransformer.kt (79%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/SendEventTransformer.kt (62%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/SetBalancesAndLimitsTransformer.kt (88%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/SetCryptoCurrencyActionsTransformer.kt (91%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/SetPrimaryCurrencyTransformer.kt (79%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/SetRefreshStateTransformer.kt (84%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/SetTokenListErrorTransformer.kt (85%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/SetTokenListTransformer.kt (75%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/SetTxHistoryCountErrorTransformer.kt (87%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/SetTxHistoryCountTransformer.kt (91%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/SetTxHistoryItemsErrorTransformer.kt (86%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/SetTxHistoryItemsTransformer.kt (82%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/SetWarningsTransformer.kt (79%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/UnlockWalletTransformer.kt (79%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/UpdateBalanceHidingModeTransformer.kt (63%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/UpdateWalletCardsCountTransformer.kt (88%) create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/WalletScreenStateTransformer.kt rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/WalletStateTransformer.kt (81%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/converter/BalancesAndLimitsBottomSheetConverter.kt (82%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/converter/MultiWalletCardStateConverter.kt (93%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/converter/MultiWalletCurrencyActionsConverter.kt (96%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/converter/SingleWalletCardStateConverter.kt (94%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/converter/SingleWalletMarketPriceConverter.kt (97%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/converter/TokenItemStateConverter.kt (96%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/converter/TokenListStateConverter.kt (87%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/converter/TxHistoryItemFlowConverter.kt (95%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/converter/TxHistoryItemStateConverter.kt (96%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/transformers/converter/VisaTxHistoryItemStateConverter.kt (96%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/utils/UserWalletConverterExt.kt (83%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/utils/WalletEventSender.kt (72%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/{state2 => state}/utils/WalletLoadingStateFactory.kt (90%) delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/WalletScreenStateTransformer.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletEventEffectV2.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreenV2.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletsListEffectsV2.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/utils/ScrollOffsetCollectorV2.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CurrencyStatusErrorConverter.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/HiddenStateConverter.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorConverter.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletStateCache.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletStateHolder.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletStateHolderDelegate.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModelV2.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletsUpdateActionResolverV2.kt rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/{WalletClickIntentsV2.kt => WalletClickIntents.kt} (92%) diff --git a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json index 13b20fb797..e76218c369 100644 --- a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json +++ b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json @@ -14,9 +14,5 @@ { "name": "REDESIGNED_SEND_SCREEN_ENABLED", "version": "undefined" - }, - { - "name": "WALLETS_SCROLLING_PREVIEW_ENABLED", - "version": "5.5.0" } ] diff --git a/features/wallet/api/src/main/kotlin/com/tangem/features/wallet/featuretoggles/WalletFeatureToggles.kt b/features/wallet/api/src/main/kotlin/com/tangem/features/wallet/featuretoggles/WalletFeatureToggles.kt deleted file mode 100644 index f2bd26a6ba..0000000000 --- a/features/wallet/api/src/main/kotlin/com/tangem/features/wallet/featuretoggles/WalletFeatureToggles.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.tangem.features.wallet.featuretoggles - -/** - * Wallet feature toggles - * -[REDACTED_AUTHOR] - */ -interface WalletFeatureToggles { - - val isWalletsScrollingPreviewEnabled: Boolean -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/di/WalletFeatureTogglesModule.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/di/WalletFeatureTogglesModule.kt deleted file mode 100644 index ed23c5d4c3..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/di/WalletFeatureTogglesModule.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.tangem.feature.wallet.di - -import com.tangem.core.featuretoggle.manager.FeatureTogglesManager -import com.tangem.feature.wallet.featuretoggles.DefaultWalletFeatureToggles -import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -internal object WalletFeatureTogglesModule { - - @Provides - @Singleton - fun provideWalletFeatureToggles(featureTogglesManager: FeatureTogglesManager): WalletFeatureToggles { - return DefaultWalletFeatureToggles(featureTogglesManager = featureTogglesManager) - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/di/WalletRouterModule.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/di/WalletRouterModule.kt index 81c4fd83ef..1e64fc4122 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/di/WalletRouterModule.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/di/WalletRouterModule.kt @@ -2,7 +2,6 @@ package com.tangem.feature.wallet.di import com.tangem.core.navigation.ReduxNavController import com.tangem.feature.wallet.presentation.router.DefaultWalletRouter -import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles import com.tangem.features.wallet.navigation.WalletRouter import dagger.Module import dagger.Provides @@ -16,10 +15,7 @@ internal object WalletRouterModule { @Provides @ActivityScoped - fun provideWalletRouter( - reduxNavController: ReduxNavController, - walletFeatureToggles: WalletFeatureToggles, - ): WalletRouter { - return DefaultWalletRouter(reduxNavController = reduxNavController, walletFeatureToggles = walletFeatureToggles) + fun provideWalletRouter(reduxNavController: ReduxNavController): WalletRouter { + return DefaultWalletRouter(reduxNavController = reduxNavController) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/featuretoggles/DefaultWalletFeatureToggles.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/featuretoggles/DefaultWalletFeatureToggles.kt deleted file mode 100644 index 628055d566..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/featuretoggles/DefaultWalletFeatureToggles.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.tangem.feature.wallet.featuretoggles - -import com.tangem.core.featuretoggle.manager.FeatureTogglesManager -import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles - -/** - * Default implementation of Wallet feature toggles - * - * @property featureTogglesManager manager for getting information about the availability of feature toggles - * -[REDACTED_AUTHOR] - */ -internal class DefaultWalletFeatureToggles( - private val featureTogglesManager: FeatureTogglesManager, -) : WalletFeatureToggles { - - override val isWalletsScrollingPreviewEnabled: Boolean - get() = featureTogglesManager.isFeatureEnabled(name = "WALLETS_SCROLLING_PREVIEW_ENABLED") -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt index 727545a58c..aed97693c7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt @@ -1,17 +1,10 @@ package com.tangem.feature.wallet.presentation.common -import androidx.paging.PagingData import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.currency.tokenicon.TokenIconState -import com.tangem.core.ui.components.marketprice.MarketPriceBlockState -import com.tangem.core.ui.components.marketprice.PriceChangeState import com.tangem.core.ui.components.marketprice.PriceChangeType -import com.tangem.core.ui.components.transactions.state.TransactionState -import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.event.consumedEvent import com.tangem.core.ui.extensions.TextReference -import com.tangem.core.ui.extensions.resourceReference -import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.res.TangemColorPalette import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.wallet.impl.R @@ -19,16 +12,9 @@ import com.tangem.feature.wallet.presentation.common.state.TokenItemState import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState -import com.tangem.feature.wallet.presentation.wallet.state.ActionsBottomSheetConfig -import com.tangem.feature.wallet.presentation.wallet.state.TokenActionButtonConfig -import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.components.* -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState.TokensListItemState -import kotlinx.collections.immutable.persistentListOf +import com.tangem.feature.wallet.presentation.wallet.state.model.* import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toPersistentList -import kotlinx.coroutines.flow.MutableStateFlow import java.util.UUID @Suppress("LargeClass") @@ -80,14 +66,6 @@ internal object WalletPreviewData { ) } - val walletListConfig by lazy { - WalletsListConfig( - selectedWalletIndex = 0, - wallets = wallets.values.toPersistentList(), - onWalletChange = {}, - ) - } - val coinIconState get() = TokenIconState.CoinIcon( url = null, @@ -320,146 +298,4 @@ internal object WalletPreviewData { ), ).toImmutableList(), ) - - private val manageButtons by lazy { - persistentListOf( - WalletManageButton.Buy(enabled = true, onClick = {}), - WalletManageButton.Send(enabled = true, onClick = {}), - WalletManageButton.Receive(enabled = true, onClick = {}), - WalletManageButton.Sell(enabled = true, onClick = {}), - WalletManageButton.Swap(enabled = true, onClick = {}), - ) - } - - val multicurrencyWalletScreenState by lazy { - WalletMultiCurrencyState.Content( - onBackClick = {}, - topBarConfig = topBarConfig, - walletsListConfig = walletListConfig, - tokensListState = WalletTokensListState.Content( - persistentListOf( - TokensListItemState.NetworkGroupTitle(id = 0, stringReference("Bitcoin")), - TokensListItemState.Token( - tokenItemVisibleState.copy( - id = "token_1", - titleState = TokenItemState.TitleState.Content(text = "Ethereum"), - cryptoAmountState = TokenItemState.CryptoAmountState.Content("1,89340821 ETH"), - ), - ), - TokensListItemState.Token( - tokenItemVisibleState.copy( - id = "token_2", - titleState = TokenItemState.TitleState.Content(text = "Ethereum"), - cryptoAmountState = TokenItemState.CryptoAmountState.Content("1,89340821 ETH"), - ), - ), - TokensListItemState.Token( - tokenItemVisibleState.copy( - id = "token_3", - titleState = TokenItemState.TitleState.Content(text = "Ethereum"), - cryptoAmountState = TokenItemState.CryptoAmountState.Content("1,89340821 ETH"), - ), - ), - TokensListItemState.Token( - tokenItemVisibleState.copy( - id = "token_4", - titleState = TokenItemState.TitleState.Content(text = "Ethereum"), - cryptoAmountState = TokenItemState.CryptoAmountState.Content("1,89340821 ETH"), - ), - ), - TokensListItemState.NetworkGroupTitle(id = 1, stringReference("Ethereum")), - TokensListItemState.Token( - tokenItemVisibleState.copy( - id = "token_5", - titleState = TokenItemState.TitleState.Content(text = "Ethereum"), - cryptoAmountState = TokenItemState.CryptoAmountState.Content("1,89340821 ETH"), - ), - ), - ), - organizeTokensButton = WalletTokensListState.OrganizeTokensButtonState.Visible(isEnabled = true, {}), - ), - pullToRefreshConfig = WalletPullToRefreshConfig( - isRefreshing = false, - onRefresh = {}, - ), - notifications = persistentListOf( - WalletNotification.Critical.DevCard, - WalletNotification.Informational.MissingAddresses(missingAddressesCount = 0, onGenerateClick = {}), - WalletNotification.Warning.NetworksUnreachable, - ), - bottomSheetConfig = bottomSheet, - onManageTokensClick = {}, - event = consumedEvent(), - isBalanceHidden = false, - ) - } - - val singleWalletScreenState by lazy { - WalletSingleCurrencyState.Content( - onBackClick = {}, - topBarConfig = topBarConfig, - walletsListConfig = walletListConfig, - pullToRefreshConfig = WalletPullToRefreshConfig( - isRefreshing = false, - onRefresh = {}, - ), - notifications = persistentListOf(WalletNotification.Warning.NetworksUnreachable), - buttons = manageButtons, - bottomSheetConfig = bottomSheet, - marketPriceBlockState = MarketPriceBlockState.Content( - currencySymbol = "BTC", - price = "98900.12$", - priceChangeConfig = PriceChangeState.Content( - valueInPercent = "5.16%", - type = PriceChangeType.UP, - ), - ), - txHistoryState = TxHistoryState.Content( - contentItems = MutableStateFlow( - PagingData.from( - listOf( - TxHistoryState.TxHistoryItemState.GroupTitle( - title = "Today", - itemKey = UUID.randomUUID().toString(), - ), - TxHistoryState.TxHistoryItemState.Transaction( - TransactionState.Content( - txHash = UUID.randomUUID().toString(), - amount = "-0.500913 BTC", - time = "8:41", - status = TransactionState.Content.Status.Unconfirmed, - direction = TransactionState.Content.Direction.OUTGOING, - iconRes = com.tangem.core.ui.R.drawable.ic_arrow_up_24, - title = resourceReference(com.tangem.core.ui.R.string.common_transfer), - subtitle = TextReference.Str("33BddS...ga2B"), - timestamp = 0L, - onClick = {}, - ), - ), - TxHistoryState.TxHistoryItemState.GroupTitle( - title = "Yesterday", - itemKey = UUID.randomUUID().toString(), - ), - TxHistoryState.TxHistoryItemState.Transaction( - TransactionState.Content( - txHash = UUID.randomUUID().toString(), - amount = "-0.500913 BTC", - time = "8:41", - status = TransactionState.Content.Status.Confirmed, - direction = TransactionState.Content.Direction.OUTGOING, - iconRes = com.tangem.core.ui.R.drawable.ic_arrow_up_24, - title = resourceReference(com.tangem.core.ui.R.string.common_transfer), - subtitle = TextReference.Str("33BddS...ga2B"), - timestamp = 0L, - onClick = {}, - ), - ), - ), - ), - ), - ), - event = consumedEvent(), - isBalanceHidden = false, - ) - } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index fa60d699bf..5a7e3675da 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -26,17 +26,13 @@ import com.tangem.feature.wallet.presentation.WalletFragment import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensScreen import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensViewModel import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen -import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreenV2 import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletViewModel -import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletViewModelV2 import com.tangem.features.tokendetails.navigation.TokenDetailsRouter -import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles import kotlin.properties.Delegates /** Default implementation of wallet feature router */ internal class DefaultWalletRouter( private val reduxNavController: ReduxNavController, - private val walletFeatureToggles: WalletFeatureToggles, ) : InnerWalletRouter { private var navController: NavHostController by Delegates.notNull() @@ -53,21 +49,12 @@ internal class DefaultWalletRouter( startDestination = WalletRoute.Wallet.route, ) { composable(WalletRoute.Wallet.route) { - if (walletFeatureToggles.isWalletsScrollingPreviewEnabled) { - val viewModel = hiltViewModel().apply { - setWalletRouter(router = this@DefaultWalletRouter) - subscribeToLifecycle(LocalLifecycleOwner.current) - } - - WalletScreenV2(state = viewModel.uiState.collectAsStateWithLifecycle().value) - } else { - val viewModel = hiltViewModel().apply { - router = this@DefaultWalletRouter - } - LocalLifecycleOwner.current.lifecycle.addObserver(viewModel) - - WalletScreen(state = viewModel.uiState) + val viewModel = hiltViewModel().apply { + setWalletRouter(router = this@DefaultWalletRouter) + subscribeToLifecycle(LocalLifecycleOwner.current) } + + WalletScreen(state = viewModel.uiState.collectAsStateWithLifecycle().value) } composable( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/TokenListAnalyticsSender.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/TokenListAnalyticsSender.kt index 8ddb6b3b90..563ab70f1e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/TokenListAnalyticsSender.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/TokenListAnalyticsSender.kt @@ -11,7 +11,7 @@ import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.Basic import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider import dagger.hilt.android.scopes.ViewModelScoped import java.math.BigDecimal diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt index d8f8fa7457..899dd5a09a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/utils/WalletWarningsAnalyticsSender.kt @@ -3,8 +3,8 @@ package com.tangem.feature.wallet.presentation.wallet.analytics.utils import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider import dagger.hilt.android.scopes.ViewModelScoped import javax.inject.Inject diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt index 574705c9f4..6b9d320700 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetMultiWalletWarningsFactory.kt @@ -13,8 +13,8 @@ import com.tangem.domain.tokens.model.NetworkGroup import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import dagger.hilt.android.scopes.ViewModelScoped import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -37,7 +37,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( private var readyForRateAppNotification = false - fun create(clickIntents: WalletClickIntentsV2): Flow> { + fun create(clickIntents: WalletClickIntents): Flow> { val userWallet = getSelectedWalletSyncUseCase().fold( ifLeft = { Timber.e("Failed to get selected wallet $it") @@ -88,7 +88,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( private fun MutableList.addInformationalNotifications( cardTypesResolver: CardTypesResolver, maybeTokenList: Either, - clickIntents: WalletClickIntentsV2, + clickIntents: WalletClickIntents, ) { addIf( element = WalletNotification.Informational.DemoCard, @@ -100,7 +100,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( private fun MutableList.addMissingAddressesNotification( maybeTokenList: Either, - clickIntents: WalletClickIntentsV2, + clickIntents: WalletClickIntents, ) { val currencies = maybeTokenList.getMissingAddressCurrencies() @@ -136,7 +136,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( cardTypesResolver: CardTypesResolver, tokenList: Either, isNeedToBackup: Boolean, - clickIntents: WalletClickIntentsV2, + clickIntents: WalletClickIntents, ) { addIf( element = WalletNotification.Warning.MissingBackup( @@ -173,7 +173,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor( private fun MutableList.addRateTheAppNotification( isReadyToShowRating: Boolean, - clickIntents: WalletClickIntentsV2, + clickIntents: WalletClickIntents, ) { addIf( element = WalletNotification.RateApp( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetSingleWalletWarningsFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetSingleWalletWarningsFactory.kt index a248e07dbe..edcbb820d8 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetSingleWalletWarningsFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/GetSingleWalletWarningsFactory.kt @@ -11,8 +11,8 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import dagger.hilt.android.scopes.ViewModelScoped import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -33,7 +33,7 @@ internal class GetSingleWalletWarningsFactory @Inject constructor( private var readyForRateAppNotification = false - fun create(clickIntents: WalletClickIntentsV2): Flow> { + fun create(clickIntents: WalletClickIntents): Flow> { val userWallet = getSelectedWalletSyncUseCase().fold( ifLeft = { Timber.e("Failed to get selected wallet $it") @@ -99,7 +99,7 @@ internal class GetSingleWalletWarningsFactory @Inject constructor( cardTypesResolver: CardTypesResolver, maybePrimaryCurrencyStatus: Either, isNeedToBackup: Boolean, - clickIntents: WalletClickIntentsV2, + clickIntents: WalletClickIntents, ) { val cryptoCurrencyStatus = maybePrimaryCurrencyStatus.fold(ifLeft = { null }, ifRight = { it }) @@ -157,7 +157,7 @@ internal class GetSingleWalletWarningsFactory @Inject constructor( private fun MutableList.addRateTheAppNotification( isReadyToShowRating: Boolean, - clickIntents: WalletClickIntentsV2, + clickIntents: WalletClickIntents, ) { addIf( element = WalletNotification.RateApp( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/WalletAdditionalInfoFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/WalletAdditionalInfoFactory.kt index 40ba99fbde..1f34e460f2 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/WalletAdditionalInfoFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/WalletAdditionalInfoFactory.kt @@ -8,7 +8,7 @@ import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.impl.R -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletAdditionalInfo +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAdditionalInfo import java.math.BigDecimal /** diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/WalletContentLoaderFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/WalletContentLoaderFactory.kt index f897350e0e..cbb2dad87f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/WalletContentLoaderFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/WalletContentLoaderFactory.kt @@ -3,7 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.loaders import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.loaders.implementors.* -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import dagger.hilt.android.scopes.ViewModelScoped import javax.inject.Inject @@ -17,7 +17,7 @@ internal class WalletContentLoaderFactory @Inject constructor( fun create( userWallet: UserWallet, - clickIntents: WalletClickIntentsV2, + clickIntents: WalletClickIntents, isRefresh: Boolean = false, ): WalletContentLoader? { return when { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/WalletScreenContentLoader.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/WalletScreenContentLoader.kt index 4742aea08d..66990ac417 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/WalletScreenContentLoader.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/WalletScreenContentLoader.kt @@ -2,7 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.loaders import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.hilt.android.scopes.ViewModelScoped import kotlinx.coroutines.CoroutineScope @@ -35,7 +35,7 @@ internal class WalletScreenContentLoader @Inject constructor( */ fun load( userWallet: UserWallet, - clickIntents: WalletClickIntentsV2, + clickIntents: WalletClickIntents, isRefresh: Boolean = false, coroutineScope: CoroutineScope, ) { @@ -67,7 +67,7 @@ internal class WalletScreenContentLoader @Inject constructor( private fun loadInternal( userWallet: UserWallet, - clickIntents: WalletClickIntentsV2, + clickIntents: WalletClickIntents, coroutineScope: CoroutineScope, isRefresh: Boolean, ) { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoader.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoader.kt index 4fba2e23e0..91b6a97137 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoader.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoader.kt @@ -8,18 +8,18 @@ import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAn import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletTokenListSubscriber import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletWarningsSubscriber import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletConnectNetworksSubscriber import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletSubscriber -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents @Suppress("LongParameterList") internal class MultiWalletContentLoader( private val userWallet: UserWallet, private val stateHolder: WalletStateController, - private val clickIntents: WalletClickIntentsV2, + private val clickIntents: WalletClickIntents, private val tokenListAnalyticsSender: TokenListAnalyticsSender, private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender, private val walletWithFundsChecker: WalletWithFundsChecker, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoaderFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoaderFactory.kt index 59ed8ae50f..a434e73c58 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoaderFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoaderFactory.kt @@ -8,8 +8,8 @@ import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAn import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import dagger.hilt.android.scopes.ViewModelScoped import javax.inject.Inject @@ -26,7 +26,7 @@ internal class MultiWalletContentLoaderFactory @Inject constructor( private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender, ) { - fun create(userWallet: UserWallet, clickIntents: WalletClickIntentsV2): WalletContentLoader { + fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): WalletContentLoader { return MultiWalletContentLoader( userWallet = userWallet, clickIntents = clickIntents, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletContentLoader.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletContentLoader.kt index e9ed0d42aa..45b3b831a5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletContentLoader.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletContentLoader.kt @@ -10,14 +10,14 @@ import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.domain.GetSingleWalletWarningsFactory -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController import com.tangem.feature.wallet.presentation.wallet.subscribers.* -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents @Suppress("LongParameterList") internal class SingleWalletContentLoader( private val userWallet: UserWallet, - private val clickIntents: WalletClickIntentsV2, + private val clickIntents: WalletClickIntents, private val isRefresh: Boolean, private val stateHolder: WalletStateController, private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletContentLoaderFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletContentLoaderFactory.kt index 1dce97e173..c6be994278 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletContentLoaderFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletContentLoaderFactory.kt @@ -10,8 +10,8 @@ import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.domain.GetSingleWalletWarningsFactory -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import dagger.hilt.android.scopes.ViewModelScoped import javax.inject.Inject @@ -30,7 +30,7 @@ internal class SingleWalletContentLoaderFactory @Inject constructor( private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender, ) { - fun create(userWallet: UserWallet, clickIntents: WalletClickIntentsV2, isRefresh: Boolean): WalletContentLoader { + fun create(userWallet: UserWallet, clickIntents: WalletClickIntents, isRefresh: Boolean): WalletContentLoader { return SingleWalletContentLoader( userWallet = userWallet, clickIntents = clickIntents, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoader.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoader.kt index 8e436284e8..42e26f47f2 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoader.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoader.kt @@ -7,16 +7,16 @@ import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAn import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController import com.tangem.feature.wallet.presentation.wallet.subscribers.MultiWalletWarningsSubscriber import com.tangem.feature.wallet.presentation.wallet.subscribers.SingleWalletWithTokenListSubscriber import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletSubscriber -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents @Suppress("LongParameterList") internal class SingleWalletWithTokenContentLoader( private val userWallet: UserWallet, - private val clickIntents: WalletClickIntentsV2, + private val clickIntents: WalletClickIntents, private val stateHolder: WalletStateController, private val tokenListAnalyticsSender: TokenListAnalyticsSender, private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoaderFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoaderFactory.kt index 6afb0a59bf..e92241ad9a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoaderFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/SingleWalletWithTokenContentLoaderFactory.kt @@ -7,8 +7,8 @@ import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAn import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import javax.inject.Inject // TODO: Refactor @@ -23,7 +23,7 @@ internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor( private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender, ) { - fun create(userWallet: UserWallet, clickIntents: WalletClickIntentsV2): SingleWalletWithTokenContentLoader { + fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): SingleWalletWithTokenContentLoader { return SingleWalletWithTokenContentLoader( userWallet = userWallet, clickIntents = clickIntents, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/VisaWalletContentLoader.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/VisaWalletContentLoader.kt index 990533203a..63c0590221 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/VisaWalletContentLoader.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/VisaWalletContentLoader.kt @@ -3,14 +3,14 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors import com.tangem.domain.visa.GetVisaCurrencyUseCase import com.tangem.domain.visa.GetVisaTxHistoryUseCase import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController import com.tangem.feature.wallet.presentation.wallet.subscribers.VisaWalletSubscriber import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletSubscriber -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents internal class VisaWalletContentLoader( private val userWallet: UserWallet, - private val clickIntents: WalletClickIntentsV2, + private val clickIntents: WalletClickIntents, private val isRefresh: Boolean, private val stateController: WalletStateController, private val getVisaTxHistoryUseCase: GetVisaTxHistoryUseCase, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/VisaWalletContentLoaderFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/VisaWalletContentLoaderFactory.kt index 1d00369116..ff7033b81d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/VisaWalletContentLoaderFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/VisaWalletContentLoaderFactory.kt @@ -3,8 +3,8 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors import com.tangem.domain.visa.GetVisaCurrencyUseCase import com.tangem.domain.visa.GetVisaTxHistoryUseCase import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import dagger.hilt.android.scopes.ViewModelScoped import javax.inject.Inject @@ -15,7 +15,7 @@ internal class VisaWalletContentLoaderFactory @Inject constructor( private val getVisaTxHistoryUseCase: GetVisaTxHistoryUseCase, ) { - fun create(userWallet: UserWallet, clickIntents: WalletClickIntentsV2, isRefresh: Boolean): WalletContentLoader { + fun create(userWallet: UserWallet, clickIntents: WalletClickIntents, isRefresh: Boolean): WalletContentLoader { return VisaWalletContentLoader( userWallet = userWallet, clickIntents = clickIntents, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletLockedState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletLockedState.kt deleted file mode 100644 index dd3bbbeee7..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletLockedState.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state - -/** - * Locked wallet state - * -[REDACTED_AUTHOR] - */ -internal sealed interface WalletLockedState { - - /** Lambda be invoked when unlock wallet notification is clicked */ - val onUnlockWalletsNotificationClick: () -> Unit - - /** Lambda be invoked when unlock wallet button is clicked */ - val onUnlockClick: () -> Unit - - /** Lambda be invoked when scan button is clicked */ - val onScanClick: () -> Unit - - /** Bottom sheet visibility */ - val isBottomSheetShow: Boolean - - /** Lambda be invoked when bottom sheet is dismissed */ - val onBottomSheetDismiss: () -> Unit - - /** Get selected wallet index */ - fun getSelectedWalletIndex(): Int { - return when (this) { - is WalletMultiCurrencyState.Locked -> walletsListConfig.selectedWalletIndex - is WalletSingleCurrencyState.Locked -> walletsListConfig.selectedWalletIndex - } - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletMultiCurrencyState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletMultiCurrencyState.kt deleted file mode 100644 index 109b58c2b2..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletMultiCurrencyState.kt +++ /dev/null @@ -1,63 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state - -import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig -import com.tangem.core.ui.event.StateEvent -import com.tangem.core.ui.event.consumedEvent -import com.tangem.feature.wallet.presentation.wallet.state.components.* -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.persistentListOf - -/** - * Multi currency wallet state - * -[REDACTED_AUTHOR] - */ -internal sealed class WalletMultiCurrencyState : WalletState.ContentState() { - - /** Tokens list state */ - abstract val tokensListState: WalletTokensListState - - data class Content( - override val onBackClick: () -> Unit, - override val topBarConfig: WalletTopBarConfig, - override val walletsListConfig: WalletsListConfig, - override val pullToRefreshConfig: WalletPullToRefreshConfig, - override val notifications: ImmutableList, - override val bottomSheetConfig: TangemBottomSheetConfig?, - override val tokensListState: WalletTokensListState, - override val event: StateEvent = consumedEvent(), - override val isBalanceHidden: Boolean, - val isManageTokensAvailable: Boolean = true, - val onManageTokensClick: () -> Unit, - ) : WalletMultiCurrencyState() - - data class Locked( - override val onBackClick: () -> Unit, - override val topBarConfig: WalletTopBarConfig, - override val walletsListConfig: WalletsListConfig, - override val pullToRefreshConfig: WalletPullToRefreshConfig, - override val onUnlockWalletsNotificationClick: () -> Unit, - override val onUnlockClick: () -> Unit, - override val onScanClick: () -> Unit, - override val isBottomSheetShow: Boolean = false, - override val onBottomSheetDismiss: () -> Unit = {}, - override val event: StateEvent = consumedEvent(), - override val isBalanceHidden: Boolean, - ) : WalletMultiCurrencyState(), WalletLockedState { - - override val notifications = persistentListOf( - WalletNotification.UnlockWallets(onUnlockWalletsNotificationClick), - ) - - override val bottomSheetConfig = TangemBottomSheetConfig( - isShow = isBottomSheetShow, - onDismissRequest = onBottomSheetDismiss, - content = WalletBottomSheetConfig.UnlockWallets( - onUnlockClick = onUnlockClick, - onScanClick = onScanClick, - ), - ) - - override val tokensListState: WalletTokensListState = WalletTokensListState.Locked - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt deleted file mode 100644 index 4bb761d008..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt +++ /dev/null @@ -1,91 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state - -import androidx.compose.runtime.Immutable -import androidx.paging.PagingData -import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig -import com.tangem.core.ui.components.marketprice.MarketPriceBlockState -import com.tangem.core.ui.components.transactions.state.TransactionState -import com.tangem.core.ui.components.transactions.state.TxHistoryState -import com.tangem.core.ui.event.StateEvent -import com.tangem.core.ui.event.consumedEvent -import com.tangem.feature.wallet.presentation.wallet.state.components.* -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.PersistentList -import kotlinx.collections.immutable.persistentListOf -import kotlinx.coroutines.flow.MutableStateFlow - -/** - * Single currency wallet content state - * -[REDACTED_AUTHOR] - */ -@Immutable -internal sealed class WalletSingleCurrencyState : WalletState.ContentState() { - - /** Manage buttons */ - abstract val buttons: PersistentList - - /** Transactions history state */ - abstract val txHistoryState: TxHistoryState - - data class Content( - override val onBackClick: () -> Unit, - override val topBarConfig: WalletTopBarConfig, - override val walletsListConfig: WalletsListConfig, - override val pullToRefreshConfig: WalletPullToRefreshConfig, - override val notifications: ImmutableList, - override val bottomSheetConfig: TangemBottomSheetConfig?, - override val buttons: PersistentList, - override val txHistoryState: TxHistoryState, - override val event: StateEvent = consumedEvent(), - override val isBalanceHidden: Boolean, - val marketPriceBlockState: MarketPriceBlockState, - ) : WalletSingleCurrencyState() - - data class Locked( - override val onBackClick: () -> Unit, - override val topBarConfig: WalletTopBarConfig, - override val walletsListConfig: WalletsListConfig, - override val pullToRefreshConfig: WalletPullToRefreshConfig, - override val buttons: PersistentList, - override val onUnlockWalletsNotificationClick: () -> Unit, - override val onUnlockClick: () -> Unit, - override val onScanClick: () -> Unit, - override val isBottomSheetShow: Boolean = false, - override val onBottomSheetDismiss: () -> Unit = {}, - override val event: StateEvent = consumedEvent(), - override val isBalanceHidden: Boolean, - val onExploreClick: () -> Unit, - ) : WalletSingleCurrencyState(), WalletLockedState { - - override val notifications = persistentListOf( - WalletNotification.UnlockWallets(onUnlockWalletsNotificationClick), - ) - - override val bottomSheetConfig = TangemBottomSheetConfig( - isShow = isBottomSheetShow, - onDismissRequest = onBottomSheetDismiss, - content = WalletBottomSheetConfig.UnlockWallets( - onUnlockClick = onUnlockClick, - onScanClick = onScanClick, - ), - ) - - override val txHistoryState: TxHistoryState = TxHistoryState.Content( - contentItems = MutableStateFlow( - value = PagingData.from( - data = listOf( - TxHistoryState.TxHistoryItemState.Title(onExploreClick = onExploreClick), - TxHistoryState.TxHistoryItemState.Transaction( - state = TransactionState.Locked(txHash = LOCKED_TX_HASH), - ), - ), - ), - ), - ) - - private companion object { - const val LOCKED_TX_HASH = "LOCKED_TX_HASH" - } - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletState.kt deleted file mode 100644 index a3e9904881..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletState.kt +++ /dev/null @@ -1,101 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state - -import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig -import com.tangem.core.ui.event.StateEvent -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletPullToRefreshConfig -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTopBarConfig -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletsListConfig -import kotlinx.collections.immutable.ImmutableList - -/** - * Wallet screen state - * -[REDACTED_AUTHOR] - */ -internal sealed class WalletState { - - /** Lambda be invoked when back button is clicked */ - abstract val onBackClick: () -> Unit - - /** Wallet screen content state */ - sealed class ContentState : WalletState() { - - /** Top bar config */ - abstract val topBarConfig: WalletTopBarConfig - - /** Wallets list config */ - abstract val walletsListConfig: WalletsListConfig - - /** Pull to refresh config */ - abstract val pullToRefreshConfig: WalletPullToRefreshConfig - - /** Notifications */ - abstract val notifications: ImmutableList - - /** Bottom sheet config */ - abstract val bottomSheetConfig: TangemBottomSheetConfig? - - /** State event */ - abstract val event: StateEvent - - /** Whether balance should be hidden */ - abstract val isBalanceHidden: Boolean - - /** - * Util function that allow to make a copy - * - * @param walletsListConfig wallets list config - * @param pullToRefreshConfig pull to refresh config - * @param event state event - */ - fun copySealed( - walletsListConfig: WalletsListConfig = this.walletsListConfig, - pullToRefreshConfig: WalletPullToRefreshConfig = this.pullToRefreshConfig, - event: StateEvent = this.event, - isBalanceHidden: Boolean = this.isBalanceHidden, - ): ContentState { - return when (this) { - is WalletMultiCurrencyState.Content -> { - copy( - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - event = event, - isBalanceHidden = isBalanceHidden, - ) - } - is WalletMultiCurrencyState.Locked -> { - copy( - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - event = event, - isBalanceHidden = isBalanceHidden, - ) - } - is WalletSingleCurrencyState.Content -> { - copy( - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - event = event, - isBalanceHidden = isBalanceHidden, - ) - } - is WalletSingleCurrencyState.Locked -> { - copy( - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - event = event, - isBalanceHidden = isBalanceHidden, - ) - } - } - } - } - - /** - * Initial state - * - * @property onBackClick lambda be invoked when back button is clicked - */ - data class Initial(override val onBackClick: () -> Unit) : WalletState() -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/WalletStateController.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateController.kt similarity index 78% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/WalletStateController.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateController.kt index 0ef36a7f4a..6f86d37b02 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/WalletStateController.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateController.kt @@ -1,15 +1,15 @@ -package com.tangem.feature.wallet.presentation.wallet.state2 +package com.tangem.feature.wallet.presentation.wallet.state import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import com.tangem.core.ui.event.consumedEvent import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTopBarConfig -import com.tangem.feature.wallet.presentation.wallet.state2.model.NOT_INITIALIZED_WALLET_INDEX -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletScreenState -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.CloseBottomSheetTransformer -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.OpenBottomSheetTransformer -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.WalletScreenStateTransformer +import com.tangem.feature.wallet.presentation.wallet.state.model.NOT_INITIALIZED_WALLET_INDEX +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTopBarConfig +import com.tangem.feature.wallet.presentation.wallet.state.transformers.CloseBottomSheetTransformer +import com.tangem.feature.wallet.presentation.wallet.state.transformers.OpenBottomSheetTransformer +import com.tangem.feature.wallet.presentation.wallet.state.transformers.WalletScreenStateTransformer import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt deleted file mode 100644 index fe750ba585..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt +++ /dev/null @@ -1,109 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state.components - -import com.tangem.core.ui.extensions.TextReference -import com.tangem.feature.wallet.impl.R -import com.tangem.feature.wallet.presentation.common.state.TokenItemState -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.persistentListOf -import javax.annotation.concurrent.Immutable - -/** - * Wallet tokens list state - * -[REDACTED_AUTHOR] - */ -internal sealed class WalletTokensListState { - - /** Empty token list state */ - object Empty : WalletTokensListState() - - /** - * Wallet content token list state - * - * @property items content items - */ - sealed class ContentState( - open val items: ImmutableList, - open val organizeTokensButton: OrganizeTokensButtonState, - ) : WalletTokensListState() - - /** - * Loading content state - * - * @property items content items - */ - data class Loading( - override val items: ImmutableList = persistentListOf(), - ) : ContentState(items = items, organizeTokensButton = OrganizeTokensButtonState.Hidden) - - /** - * Content state - * - * @property items content items - * @property organizeTokensButton represents the state of the 'Organize Tokens' button - */ - data class Content( - override val items: ImmutableList, - override val organizeTokensButton: OrganizeTokensButtonState, - ) : ContentState(items, organizeTokensButton) - - /** Locked content state */ - object Locked : ContentState( - items = persistentListOf( - TokensListItemState.NetworkGroupTitle(id = 42, name = TextReference.Res(id = R.string.main_tokens)), - TokensListItemState.Token(state = TokenItemState.Locked(id = LOCKED_TOKEN_ID)), - ), - organizeTokensButton = OrganizeTokensButtonState.Hidden, - ) - - /** - * Represents the state of the 'Organize Tokens' button. - */ - @Immutable - sealed class OrganizeTokensButtonState { - - /** Represents the state where the 'Organize Tokens' button is hidden. */ - object Hidden : OrganizeTokensButtonState() - - /** - * Represents the state where the 'Organize Tokens' button is visible. - * - * @property isEnabled Indicates if the button is enabled or not. - * @property onClick Callback to be executed when the button is clicked. - */ - data class Visible( - val isEnabled: Boolean, - val onClick: () -> Unit, - ) : OrganizeTokensButtonState() - } - - /** Tokens list item state */ - @Immutable - sealed interface TokensListItemState { - - val id: Any - - /** - * Network group title item - * - * @property name network name - */ - data class NetworkGroupTitle( - override val id: Int, - val name: TextReference, - ) : TokensListItemState - - /** - * Token item - * - * @property state token item state - */ - data class Token(val state: TokenItemState) : TokensListItemState { - override val id: String = state.id - } - } - - private companion object { - const val LOCKED_TOKEN_ID = "Locked#1" - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletsListConfig.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletsListConfig.kt deleted file mode 100644 index 20eac2dec5..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletsListConfig.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state.components - -import kotlinx.collections.immutable.ImmutableList - -/** - * Wallets list config - * - * @property selectedWalletIndex selected wallet index - * @property wallets wallets list - * @property onWalletChange lambda be invoked when wallet is swiped - * -[REDACTED_AUTHOR] - */ -internal data class WalletsListConfig( - val selectedWalletIndex: Int, - val wallets: ImmutableList, - val onWalletChange: (Int) -> Unit, -) \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/TokenActionsProvider.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/TokenActionsProvider.kt deleted file mode 100644 index 652a516847..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/TokenActionsProvider.kt +++ /dev/null @@ -1,104 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state.factory - -import com.tangem.core.ui.extensions.TextReference -import com.tangem.core.ui.extensions.resourceReference -import com.tangem.domain.common.util.cardTypesResolver -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.tokens.model.TokenActionsState -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.impl.R -import com.tangem.feature.wallet.presentation.common.state.TokenItemState -import com.tangem.feature.wallet.presentation.wallet.state.TokenActionButtonConfig -import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents -import com.tangem.utils.Provider -import com.tangem.utils.isNullOrZero -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toImmutableList - -/** - * Converter from loaded [TokenItemState.Content] to ImmutableList<[TokenActionButtonConfig]> - * - * @property currentWalletProvider current wallet provider - * @property clickIntents screen click intents - */ -internal class TokenActionsProvider( - private val currentWalletProvider: Provider, - private val clickIntents: WalletClickIntents, -) { - - fun provideActions(tokenActions: TokenActionsState): ImmutableList { - return tokenActions.states - .filterIfSingleWithToken() - .mapNotNull { - mapTokenActionState( - actionsState = it, - cryptoCurrencyStatus = tokenActions.cryptoCurrencyStatus, - ) - } - .toImmutableList() - } - - private fun List.filterIfSingleWithToken(): List { - return if (currentWalletProvider().scanResponse.cardTypesResolver.isSingleWalletWithToken()) { - filter { it !is TokenActionsState.ActionState.HideToken } - } else { - this - } - } - - private fun mapTokenActionState( - actionsState: TokenActionsState.ActionState, - cryptoCurrencyStatus: CryptoCurrencyStatus, - ): TokenActionButtonConfig? { - if (actionsState is TokenActionsState.ActionState.Send && cryptoCurrencyStatus.value.amount.isNullOrZero()) { - return null - } - val title: TextReference - val icon: Int - val action: () -> Unit - when (actionsState) { - is TokenActionsState.ActionState.Buy -> { - title = resourceReference(R.string.common_buy) - icon = R.drawable.ic_plus_24 - action = { clickIntents.onBuyClick(cryptoCurrencyStatus) } - } - is TokenActionsState.ActionState.Receive -> { - title = resourceReference(R.string.common_receive) - icon = R.drawable.ic_arrow_down_24 - action = { clickIntents.onReceiveClick(cryptoCurrencyStatus) } - } - is TokenActionsState.ActionState.Sell -> { - title = resourceReference(R.string.common_sell) - icon = R.drawable.ic_currency_24 - action = { clickIntents.onSellClick(cryptoCurrencyStatus) } - } - is TokenActionsState.ActionState.Send -> { - title = resourceReference(R.string.common_send) - icon = R.drawable.ic_arrow_up_24 - action = { clickIntents.onMultiCurrencySendClick(cryptoCurrencyStatus) } - } - is TokenActionsState.ActionState.Swap -> { - title = resourceReference(R.string.swapping_swap_action) - icon = R.drawable.ic_exchange_horizontal_24 - action = { clickIntents.onSwapClick(cryptoCurrencyStatus) } - } - is TokenActionsState.ActionState.CopyAddress -> { - title = resourceReference(R.string.common_copy_address) - icon = R.drawable.ic_copy_24 - action = { clickIntents.onCopyAddressClick(cryptoCurrencyStatus) } - } - is TokenActionsState.ActionState.HideToken -> { - title = resourceReference(R.string.token_details_hide_token) - icon = R.drawable.ic_hide_24 - action = { clickIntents.onHideTokensClick(cryptoCurrencyStatus) } - } - } - return TokenActionButtonConfig( - text = title, - iconResId = icon, - onClick = action, - isWarning = actionsState is TokenActionsState.ActionState.HideToken, - enabled = actionsState.enabled, - ) - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/TokenListWithWallet.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/TokenListWithWallet.kt deleted file mode 100644 index bd9060c2ce..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/TokenListWithWallet.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state.factory - -import com.tangem.domain.tokens.model.TokenList -import com.tangem.domain.wallets.models.UserWallet - -data class TokenListWithWallet( - val tokenList: TokenList, - val wallet: UserWallet, -) \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletCryptoCurrencyActionsConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletCryptoCurrencyActionsConverter.kt deleted file mode 100644 index ef2281406c..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletCryptoCurrencyActionsConverter.kt +++ /dev/null @@ -1,76 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state.factory - -import com.tangem.domain.common.util.cardTypesResolver -import com.tangem.domain.tokens.model.TokenActionsState -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton -import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents -import com.tangem.utils.Provider -import com.tangem.utils.converter.Converter -import kotlinx.collections.immutable.PersistentList -import kotlinx.collections.immutable.toPersistentList - -internal class WalletCryptoCurrencyActionsConverter( - private val currentWalletProvider: Provider, - private val currentStateProvider: Provider, - private val clickIntents: WalletClickIntents, -) : Converter { - - override fun convert(value: TokenActionsState): WalletState { - return when (val state = currentStateProvider()) { - is WalletSingleCurrencyState.Content -> state.copy(buttons = value.mapToManageButtons()) - is WalletSingleCurrencyState.Locked, - is WalletMultiCurrencyState, - is WalletState.Initial, - -> state - } - } - - private fun TokenActionsState.mapToManageButtons(): PersistentList { - return this.states - .filterIfS2C() - .mapNotNull { action -> - when (action) { - is TokenActionsState.ActionState.Buy -> { - WalletManageButton.Buy( - enabled = action.enabled, - onClick = { clickIntents.onBuyClick(cryptoCurrencyStatus) }, - ) - } - is TokenActionsState.ActionState.Receive -> { - WalletManageButton.Receive( - enabled = action.enabled, - onClick = { clickIntents.onReceiveClick(cryptoCurrencyStatus) }, - ) - } - is TokenActionsState.ActionState.Sell -> { - WalletManageButton.Sell( - enabled = action.enabled, - onClick = { clickIntents.onSellClick(cryptoCurrencyStatus) }, - ) - } - is TokenActionsState.ActionState.Send -> { - WalletManageButton.Send( - enabled = action.enabled, - onClick = { clickIntents.onSingleCurrencySendClick(cryptoCurrencyStatus) }, - ) - } - else -> { - null - } - } - } - .toPersistentList() - } - - private fun List.filterIfS2C(): List { - return if (currentWalletProvider().scanResponse.cardTypesResolver.isStart2Coin()) { - filterNot { it is TokenActionsState.ActionState.Buy || it is TokenActionsState.ActionState.Sell } - } else { - this - } - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletDeleteStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletDeleteStateConverter.kt deleted file mode 100644 index 94b4b07979..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletDeleteStateConverter.kt +++ /dev/null @@ -1,50 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state.factory - -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.wallet.presentation.wallet.state.WalletState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState -import com.tangem.feature.wallet.presentation.wallet.state.factory.WalletDeleteStateConverter.DeleteWalletModel -import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletsUpdateActionResolver -import com.tangem.utils.Provider -import com.tangem.utils.converter.Converter -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toImmutableList - -/** - * Converter that responds on wallet deleting action. Returns [WalletState] without deleted wallet. - * - * @property currentStateProvider current state provider - */ -internal class WalletDeleteStateConverter( - private val currentStateProvider: Provider, -) : Converter { - - override fun convert(value: DeleteWalletModel): WalletState { - return when (val state = currentStateProvider()) { - is WalletState.ContentState -> { - value.cacheState.copySealed( - walletsListConfig = state.walletsListConfig.copy( - selectedWalletIndex = value.action.selectedWalletIndex, - wallets = state.walletsListConfig.wallets.deleteWallet(id = value.action.deletedWalletId), - ), - pullToRefreshConfig = value.cacheState.pullToRefreshConfig.copy(isRefreshing = false), - ) - } - is WalletState.Initial -> state - } - } - - private fun List.deleteWallet(id: UserWalletId): ImmutableList { - return this - .mapIndexedNotNull { index, currentWallet -> - if (currentWallet.id == id) return@mapIndexedNotNull null - getOrNull(index) ?: return@mapIndexedNotNull null - } - .toImmutableList() - } - - data class DeleteWalletModel( - val cacheState: WalletState.ContentState, - val action: WalletsUpdateActionResolver.Action.DeleteWallet, - ) -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt deleted file mode 100644 index 1a1b95cedc..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state.factory - -import arrow.core.Either -import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.error.TokenListError -import com.tangem.domain.tokens.model.TokenList -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletState -import com.tangem.feature.wallet.presentation.wallet.utils.TokenListErrorConverter -import com.tangem.feature.wallet.presentation.wallet.utils.TokenListToWalletStateConverter -import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents -import com.tangem.utils.Provider -import com.tangem.utils.converter.Converter - -/** - * Converter from loaded [TokenListError] or [TokenList] to [WalletMultiCurrencyState] - * - * @property currentStateProvider current ui state provider - * @property tokenListErrorConverter converter of tokens list - * @param appCurrencyProvider app currency provider - * @param currentWalletProvider current wallet provider - * @param clickIntents screen click intents - * -[REDACTED_AUTHOR] - */ -@Suppress("LongParameterList") -internal class WalletLoadedTokensListConverter( - private val currentStateProvider: Provider, - private val tokenListErrorConverter: TokenListErrorConverter, - appCurrencyProvider: Provider, - currentWalletProvider: Provider, - clickIntents: WalletClickIntents, -) : Converter, WalletState> { - - private val tokenListStateConverter = TokenListToWalletStateConverter( - currentStateProvider = currentStateProvider, - currentWalletProvider = currentWalletProvider, - appCurrencyProvider = appCurrencyProvider, - clickIntents = clickIntents, - ) - - override fun convert(value: Either): WalletState { - return value.fold( - ifLeft = tokenListErrorConverter::convert, - ifRight = tokenListStateConverter::convert, - ) - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLockedConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLockedConverter.kt deleted file mode 100644 index ac45532c00..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLockedConverter.kt +++ /dev/null @@ -1,80 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state.factory - -import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletPullToRefreshConfig -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTopBarConfig -import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents -import com.tangem.utils.Provider -import com.tangem.utils.converter.Converter -import kotlinx.collections.immutable.PersistentList -import kotlinx.collections.immutable.toPersistentList - -internal class WalletLockedConverter( - private val currentStateProvider: Provider, - private val clickIntents: WalletClickIntents, -) : Converter { - - override fun convert(value: Unit): WalletState { - return when (val state = currentStateProvider()) { - is WalletMultiCurrencyState.Content -> state.toMultiCurrencyLockedState() - is WalletSingleCurrencyState.Content -> state.toSingleCurrencyLockedState() - is WalletMultiCurrencyState.Locked, - is WalletSingleCurrencyState.Locked, - is WalletState.Initial, - -> state - } - } - - private fun WalletMultiCurrencyState.Content.toMultiCurrencyLockedState(): WalletState { - return WalletMultiCurrencyState.Locked( - onBackClick = onBackClick, - topBarConfig = topBarConfig.updateCallback(), - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig.stopRefreshing(), - onUnlockWalletsNotificationClick = clickIntents::onUnlockWalletNotificationClick, - onUnlockClick = clickIntents::onUnlockWalletClick, - onScanClick = clickIntents::onScanToUnlockWalletClick, - isBalanceHidden = isBalanceHidden, - ) - } - - private fun WalletSingleCurrencyState.Content.toSingleCurrencyLockedState(): WalletState { - return WalletSingleCurrencyState.Locked( - onBackClick = onBackClick, - topBarConfig = topBarConfig.updateCallback(), - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig.stopRefreshing(), - buttons = buttons.disableButtons(), - onUnlockWalletsNotificationClick = clickIntents::onUnlockWalletNotificationClick, - onUnlockClick = clickIntents::onUnlockWalletClick, - onScanClick = clickIntents::onScanToUnlockWalletClick, - onExploreClick = clickIntents::onExploreClick, - isBalanceHidden = isBalanceHidden, - ) - } - - private fun WalletTopBarConfig.updateCallback(): WalletTopBarConfig { - return copy(onDetailsClick = clickIntents::onUnlockWalletNotificationClick) - } - - private fun WalletPullToRefreshConfig.stopRefreshing(): WalletPullToRefreshConfig { - return copy(isRefreshing = false) - } - - private fun PersistentList.disableButtons(): PersistentList { - return this - .map { button -> - when (button) { - is WalletManageButton.Buy -> button.copy(enabled = false) - is WalletManageButton.Sell -> button.copy(enabled = false) - is WalletManageButton.Send -> button.copy(enabled = false) - is WalletManageButton.Swap -> button.copy(enabled = false) - is WalletManageButton.Receive -> button.copy(enabled = false) - } - } - .toPersistentList() - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletRefreshStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletRefreshStateConverter.kt deleted file mode 100644 index d0e92be7ec..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletRefreshStateConverter.kt +++ /dev/null @@ -1,115 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state.factory - -import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletPullToRefreshConfig -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState -import com.tangem.utils.Provider -import com.tangem.utils.converter.Converter -import kotlinx.collections.immutable.PersistentList -import kotlinx.collections.immutable.mutate - -internal class WalletRefreshStateConverter( - private val currentStateProvider: Provider, -) : Converter { - - override fun convert(value: Boolean): WalletState { - val state = currentStateProvider() - val contentState = state as? WalletState.ContentState ?: return state - - return if (value) { - contentState.getRefreshingState() - } else { - contentState.getRefreshedState() - } - } - - private fun WalletState.ContentState.getRefreshingState(): WalletState { - return when (this) { - is WalletMultiCurrencyState.Content -> getRefreshingState() - is WalletSingleCurrencyState.Content -> getRefreshingState() - is WalletMultiCurrencyState.Locked, - is WalletSingleCurrencyState.Locked, - -> this - } - } - - private fun WalletState.ContentState.getRefreshedState(): WalletState { - return when (this) { - is WalletMultiCurrencyState.Content -> getRefreshedState() - is WalletSingleCurrencyState.Content -> getRefreshedState() - is WalletMultiCurrencyState.Locked, - is WalletSingleCurrencyState.Locked, - -> this - } - } - - private fun WalletMultiCurrencyState.Content.getRefreshingState(): WalletMultiCurrencyState { - return copy( - pullToRefreshConfig = updatePullToRefreshConfig(isRefreshing = true), - tokensListState = updateTokenListState(isRefreshing = true), - ) - } - - private fun WalletSingleCurrencyState.Content.getRefreshingState(): WalletSingleCurrencyState { - return copy( - pullToRefreshConfig = updatePullToRefreshConfig(isRefreshing = true), - buttons = updateButtons(isRefreshing = true), - ) - } - - private fun WalletMultiCurrencyState.Content.getRefreshedState(): WalletMultiCurrencyState { - return copy( - pullToRefreshConfig = updatePullToRefreshConfig(isRefreshing = false), - tokensListState = updateTokenListState(isRefreshing = false), - ) - } - - private fun WalletSingleCurrencyState.Content.getRefreshedState(): WalletSingleCurrencyState { - return copy( - pullToRefreshConfig = updatePullToRefreshConfig(isRefreshing = false), - buttons = updateButtons(isRefreshing = false), - ) - } - - private fun WalletMultiCurrencyState.updateTokenListState(isRefreshing: Boolean): WalletTokensListState { - return when (val listState = tokensListState) { - is WalletTokensListState.Content -> { - when (listState.organizeTokensButton) { - is WalletTokensListState.OrganizeTokensButtonState.Hidden -> listState - is WalletTokensListState.OrganizeTokensButtonState.Visible -> listState.copy( - organizeTokensButton = listState.organizeTokensButton.copy( - isEnabled = !isRefreshing, - ), - ) - } - } - is WalletTokensListState.Locked, - is WalletTokensListState.Loading, - is WalletTokensListState.Empty, - -> listState - } - } - - private fun WalletSingleCurrencyState.updateButtons(isRefreshing: Boolean): PersistentList { - val isButtonsEnabled = !isRefreshing - - return buttons.mutate { - it.mapNotNull { button -> - when (button) { - is WalletManageButton.Buy -> button.copy(enabled = isButtonsEnabled) - is WalletManageButton.Send -> button.copy(enabled = isButtonsEnabled) - is WalletManageButton.Sell -> button.copy(enabled = isButtonsEnabled) - is WalletManageButton.Receive -> button - is WalletManageButton.Swap -> null - } - } - } - } - - private fun WalletState.ContentState.updatePullToRefreshConfig(isRefreshing: Boolean): WalletPullToRefreshConfig { - return pullToRefreshConfig.copy(isRefreshing = isRefreshing) - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletRenameStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletRenameStateConverter.kt deleted file mode 100644 index aec0b673f7..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletRenameStateConverter.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state.factory - -import com.tangem.feature.wallet.presentation.wallet.state.WalletState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletsListConfig -import com.tangem.utils.Provider -import com.tangem.utils.converter.Converter -import kotlinx.collections.immutable.toImmutableList - -internal class WalletRenameStateConverter( - private val currentStateProvider: Provider, -) : Converter { - - override fun convert(value: String): WalletState { - return when (val state = currentStateProvider()) { - is WalletState.ContentState -> { - state.copySealed( - walletsListConfig = state.walletsListConfig.renameSelectedWallet(name = value), - ) - } - is WalletState.Initial -> state - } - } - - private fun WalletsListConfig.renameSelectedWallet(name: String): WalletsListConfig { - return copy( - wallets = wallets - .mapIndexed { index, walletCard -> - if (index == selectedWalletIndex) walletCard.copySealed(title = name) else walletCard - } - .toImmutableList(), - ) - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt deleted file mode 100644 index 75ce18f490..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt +++ /dev/null @@ -1,181 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state.factory - -import arrow.core.Either -import com.tangem.core.ui.components.marketprice.MarketPriceBlockState -import com.tangem.core.ui.components.marketprice.PriceChangeState -import com.tangem.core.ui.components.marketprice.PriceChangeType -import com.tangem.core.ui.utils.BigDecimalFormatter -import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.error.CurrencyStatusError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory -import com.tangem.feature.wallet.presentation.wallet.domain.getCardsCount -import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletsListConfig -import com.tangem.feature.wallet.presentation.wallet.utils.CurrencyStatusErrorConverter -import com.tangem.utils.Provider -import com.tangem.utils.converter.Converter -import kotlinx.collections.immutable.toPersistentList -import java.math.BigDecimal - -internal class WalletSingleCurrencyLoadedBalanceConverter( - private val currentStateProvider: Provider, - private val appCurrencyProvider: Provider, - private val currentWalletProvider: Provider, - private val currencyStatusErrorConverter: CurrencyStatusErrorConverter, -) : Converter, WalletState> { - - override fun convert(value: Either): WalletState { - return value.fold( - ifLeft = currencyStatusErrorConverter::convert, - ifRight = ::convertContent, - ) - } - - private fun convertContent(status: CryptoCurrencyStatus): WalletState { - return when (val state = currentStateProvider()) { - is WalletSingleCurrencyState.Content -> { - val currencyName = state.marketPriceBlockState.currencySymbol - - state.copy( - walletsListConfig = getUpdatedSelectedWallet(status = status.value, state = state), - marketPriceBlockState = getMarketPriceState(status = status.value, currencySymbol = currencyName), - ) - } - is WalletMultiCurrencyState.Content, - is WalletMultiCurrencyState.Locked, - is WalletSingleCurrencyState.Locked, - is WalletState.Initial, - -> state - } - } - - private fun getMarketPriceState( - status: CryptoCurrencyStatus.Status, - currencySymbol: String, - ): MarketPriceBlockState { - return when (status) { - is CryptoCurrencyStatus.Loaded, - is CryptoCurrencyStatus.NoAmount, - -> status.toContentConfig(currencySymbol) - is CryptoCurrencyStatus.NoAccount -> { - if (status.fiatRate == null) { - MarketPriceBlockState.Error(currencySymbol) - } else { - status.toContentConfig(currencySymbol) - } - } - is CryptoCurrencyStatus.Loading -> MarketPriceBlockState.Loading(currencySymbol) - is CryptoCurrencyStatus.Custom, - is CryptoCurrencyStatus.MissedDerivation, - is CryptoCurrencyStatus.Unreachable, - is CryptoCurrencyStatus.NoQuote, - -> MarketPriceBlockState.Error(currencySymbol) - } - } - - private fun getUpdatedSelectedWallet( - status: CryptoCurrencyStatus.Status, - state: WalletSingleCurrencyState, - ): WalletsListConfig { - val selectedWallet = state.walletsListConfig.wallets[state.walletsListConfig.selectedWalletIndex] - - val updatedWallet = when (status) { - is CryptoCurrencyStatus.NoQuote, - is CryptoCurrencyStatus.Loaded, - is CryptoCurrencyStatus.NoAccount, - is CryptoCurrencyStatus.NoAmount, - -> { - WalletCardState.Content( - id = selectedWallet.id, - title = selectedWallet.title, - additionalInfo = WalletAdditionalInfoFactory.resolve( - wallet = currentWalletProvider(), - currencyAmount = status.amount, - ), - imageResId = selectedWallet.imageResId, - onRenameClick = selectedWallet.onRenameClick, - onDeleteClick = selectedWallet.onDeleteClick, - balance = formatFiatAmount(status = status, appCurrency = appCurrencyProvider()), - cardCount = currentWalletProvider().getCardsCount(), - ) - } - is CryptoCurrencyStatus.Loading -> { - WalletCardState.Loading( - id = selectedWallet.id, - title = selectedWallet.title, - imageResId = selectedWallet.imageResId, - onRenameClick = selectedWallet.onRenameClick, - onDeleteClick = selectedWallet.onDeleteClick, - ) - } - is CryptoCurrencyStatus.MissedDerivation, - is CryptoCurrencyStatus.Custom, - is CryptoCurrencyStatus.Unreachable, - -> { - WalletCardState.Error( - id = selectedWallet.id, - title = selectedWallet.title, - imageResId = selectedWallet.imageResId, - onRenameClick = selectedWallet.onRenameClick, - onDeleteClick = selectedWallet.onDeleteClick, - ) - } - } - - return state.walletsListConfig.copy( - wallets = state.walletsListConfig.wallets.toPersistentList() - .set(index = state.walletsListConfig.selectedWalletIndex, element = updatedWallet), - ) - } - - private fun CryptoCurrencyStatus.Status.toContentConfig(currencySymbol: String): MarketPriceBlockState.Content { - return MarketPriceBlockState.Content( - currencySymbol = currencySymbol, - price = formatPrice(status = this, appCurrency = appCurrencyProvider()), - priceChangeConfig = PriceChangeState.Content( - valueInPercent = formatPriceChange(status = this), - type = getPriceChangeType(status = this), - ), - ) - } - - private fun getPriceChangeType(status: CryptoCurrencyStatus.Status): PriceChangeType { - val priceChange = status.priceChange ?: return PriceChangeType.DOWN - - return if (priceChange > BigDecimal.ZERO) PriceChangeType.UP else PriceChangeType.DOWN - } - - private fun formatPriceChange(status: CryptoCurrencyStatus.Status): String { - val priceChange = status.priceChange ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN - - return BigDecimalFormatter.formatPercent( - percent = priceChange, - useAbsoluteValue = true, - ) - } - - private fun formatPrice(status: CryptoCurrencyStatus.Status, appCurrency: AppCurrency): String { - val fiatRate = status.fiatRate ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN - - return BigDecimalFormatter.formatFiatAmount( - fiatAmount = fiatRate, - fiatCurrencyCode = appCurrency.code, - fiatCurrencySymbol = appCurrency.symbol, - ) - } - - private fun formatFiatAmount(status: CryptoCurrencyStatus.Status, appCurrency: AppCurrency): String { - val fiatAmount = status.fiatAmount ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN - - return BigDecimalFormatter.formatFiatAmount( - fiatAmount = fiatAmount, - fiatCurrencyCode = appCurrency.code, - fiatCurrencySymbol = appCurrency.symbol, - ) - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt deleted file mode 100644 index 1c3c556b1f..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt +++ /dev/null @@ -1,157 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state.factory - -import androidx.annotation.DrawableRes -import com.tangem.core.ui.components.marketprice.MarketPriceBlockState -import com.tangem.core.ui.components.transactions.state.TxHistoryState -import com.tangem.domain.common.util.cardTypesResolver -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory -import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver -import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletState -import com.tangem.feature.wallet.presentation.wallet.state.components.* -import com.tangem.feature.wallet.presentation.wallet.state.factory.WalletSkeletonStateConverter.SkeletonModel -import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents -import com.tangem.utils.Provider -import com.tangem.utils.converter.Converter -import kotlinx.collections.immutable.PersistentList -import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toImmutableList -import kotlinx.coroutines.flow.MutableStateFlow - -/** - * Converter from loaded list of [UserWallet] to skeleton state of screen [WalletState.ContentState] - * - * @property currentStateProvider current ui state provider - * @property clickIntents screen click intents - * -[REDACTED_AUTHOR] - */ -internal class WalletSkeletonStateConverter( - private val currentStateProvider: Provider, - private val isBalanceHiddenProvider: Provider, - private val clickIntents: WalletClickIntents, -) : Converter { - - override fun convert(value: SkeletonModel): WalletState.ContentState { - val selectedWallet = value.wallets[value.selectedWalletIndex] - - val isSingleWalletWithToken = !selectedWallet.isMultiCurrency && - selectedWallet.scanResponse.walletData?.token != null - return if (selectedWallet.isMultiCurrency || isSingleWalletWithToken) { - createMultiCurrencyState(value = value) - } else { - createSingleCurrencyState(value = value, currencyName = selectedWallet.getPrimaryCurrencyName()) - } - } - - private fun createMultiCurrencyState(value: SkeletonModel): WalletMultiCurrencyState { - return WalletMultiCurrencyState.Content( - onBackClick = clickIntents::onBackClick, - topBarConfig = createTopBarConfig(), - walletsListConfig = createWalletsListConfig(value), - pullToRefreshConfig = createPullToRefreshConfig(), - tokensListState = WalletTokensListState.Loading(), - notifications = persistentListOf(), - bottomSheetConfig = null, - onManageTokensClick = clickIntents::onManageTokensClick, - isBalanceHidden = isBalanceHiddenProvider(), - ) - } - - private fun createSingleCurrencyState(value: SkeletonModel, currencyName: String): WalletSingleCurrencyState { - return WalletSingleCurrencyState.Content( - onBackClick = clickIntents::onBackClick, - topBarConfig = createTopBarConfig(), - walletsListConfig = createWalletsListConfig(value), - pullToRefreshConfig = createPullToRefreshConfig(), - notifications = persistentListOf(), - bottomSheetConfig = null, - buttons = createButtons(), - marketPriceBlockState = MarketPriceBlockState.Loading(currencySymbol = currencyName), - txHistoryState = TxHistoryState.Content( - contentItems = MutableStateFlow( - value = TxHistoryState.getDefaultLoadingTransactions(clickIntents::onExploreClick), - ), - ), - isBalanceHidden = isBalanceHiddenProvider(), - ) - } - - private fun UserWallet.getPrimaryCurrencyName(): String { - return scanResponse.cardTypesResolver.getBlockchain().currency - } - - private fun createTopBarConfig(): WalletTopBarConfig { - return WalletTopBarConfig(onDetailsClick = clickIntents::onDetailsClick) - } - - private fun createWalletsListConfig(value: SkeletonModel): WalletsListConfig { - return WalletsListConfig( - selectedWalletIndex = value.selectedWalletIndex, - wallets = value.wallets.map(::createWalletCardState).toImmutableList(), - onWalletChange = clickIntents::onWalletChange, - ) - } - - /** - * Create wallet card state by [wallet]. - * If current wallet card state is initialized, then method returns it. - * Otherwise, returns loading wallet card state. - */ - private fun createWalletCardState(wallet: UserWallet): WalletCardState { - return currentStateProvider().getInitializedWalletCardState(wallet.walletId) ?: wallet.mapToWalletCardState() - } - - private fun WalletState.getInitializedWalletCardState(walledId: UserWalletId): WalletCardState? { - return (this as? WalletState.ContentState)?.walletsListConfig?.wallets?.firstOrNull { it.id == walledId } - } - - private fun UserWallet.mapToWalletCardState(): WalletCardState { - return if (isLocked) mapToLockedWalletCardState() else mapToLoadingWalletCardState() - } - - private fun UserWallet.mapToLockedWalletCardState(): WalletCardState { - return WalletCardState.LockedContent( - id = walletId, - title = name, - additionalInfo = WalletAdditionalInfoFactory.resolve(wallet = this), - imageResId = createImageResId(), - onRenameClick = clickIntents::onRenameClick, - onDeleteClick = clickIntents::onDeleteBeforeConfirmationClick, - ) - } - - private fun UserWallet.mapToLoadingWalletCardState(): WalletCardState { - return WalletCardState.Loading( - id = walletId, - title = name, - additionalInfo = if (isMultiCurrency) WalletAdditionalInfoFactory.resolve(wallet = this) else null, - imageResId = createImageResId(), - onRenameClick = clickIntents::onRenameClick, - onDeleteClick = clickIntents::onDeleteBeforeConfirmationClick, - ) - } - - @DrawableRes - private fun UserWallet.createImageResId(): Int? { - return WalletImageResolver.resolve(userWallet = this) - } - - private fun createPullToRefreshConfig(): WalletPullToRefreshConfig { - return WalletPullToRefreshConfig(isRefreshing = false, onRefresh = clickIntents::onRefreshSwipe) - } - - private fun createButtons(): PersistentList { - return persistentListOf( - WalletManageButton.Buy(enabled = false, onClick = {}), - WalletManageButton.Send(enabled = false, onClick = {}), - WalletManageButton.Receive(enabled = false, onClick = {}), - WalletManageButton.Sell(enabled = false, onClick = {}), - ) - } - - data class SkeletonModel(val wallets: List, val selectedWalletIndex: Int) -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt deleted file mode 100644 index e803ae15cd..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt +++ /dev/null @@ -1,288 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state.factory - -import androidx.paging.PagingData -import arrow.core.Either -import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig -import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent -import com.tangem.core.ui.event.consumedEvent -import com.tangem.core.ui.event.triggeredEvent -import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.common.CardTypesResolver -import com.tangem.domain.tokens.error.CurrencyStatusError -import com.tangem.domain.tokens.error.TokenListError -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.tokens.model.TokenActionsState -import com.tangem.domain.txhistory.models.TxHistoryItem -import com.tangem.domain.txhistory.models.TxHistoryListError -import com.tangem.domain.txhistory.models.TxHistoryStateError -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.presentation.wallet.state.* -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification -import com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory.WalletLoadedTxHistoryConverter -import com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory.WalletLoadingTxHistoryConverter -import com.tangem.feature.wallet.presentation.wallet.utils.CurrencyStatusErrorConverter -import com.tangem.feature.wallet.presentation.wallet.utils.HiddenStateConverter -import com.tangem.feature.wallet.presentation.wallet.utils.TokenListErrorConverter -import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents -import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletsUpdateActionResolver -import com.tangem.utils.Provider -import kotlinx.collections.immutable.ImmutableList -import kotlinx.coroutines.flow.Flow - -/** - * Main factory for creating [WalletState] - * - * @property currentStateProvider current ui state provider - * @property currentCardTypeResolverProvider current card type resolver - * @property currentWalletProvider current wallet - * @property appCurrencyProvider app currency provider - * @property clickIntents screen click intents - */ -@Suppress("TooManyFunctions") -internal class WalletStateFactory( - private val currentStateProvider: Provider, - private val currentCardTypeResolverProvider: Provider, - private val currentWalletProvider: Provider, - private val appCurrencyProvider: Provider, - private val isBalanceHiddenProvider: Provider, - private val clickIntents: WalletClickIntents, -) { - - private val tokenActionsProvider by lazy { TokenActionsProvider(currentWalletProvider, clickIntents) } - - private val skeletonConverter by lazy { - WalletSkeletonStateConverter( - currentStateProvider = currentStateProvider, - isBalanceHiddenProvider = isBalanceHiddenProvider, - clickIntents = clickIntents, - ) - } - - private val walletsUnlockStateConverter by lazy { WalletsUnlockStateConverter(currentStateProvider, clickIntents) } - - private val walletRenameStateConverter by lazy { WalletRenameStateConverter(currentStateProvider) } - - private val walletDeleteStateConverter by lazy { WalletDeleteStateConverter(currentStateProvider) } - - private val hiddenStateConverter by lazy { HiddenStateConverter(currentStateProvider) } - - private val walletUpdateCardCountConverter by lazy { - WalletUpdateCardCountConverter( - currentStateProvider, - currentWalletProvider, - ) - } - - private val tokenListErrorConverter by lazy { - TokenListErrorConverter(currentStateProvider) - } - private val currencyStatusErrorConverter by lazy { - CurrencyStatusErrorConverter(currentStateProvider) - } - private val loadedTokensListConverter by lazy { - WalletLoadedTokensListConverter( - currentStateProvider = currentStateProvider, - tokenListErrorConverter = tokenListErrorConverter, - appCurrencyProvider = appCurrencyProvider, - currentWalletProvider = currentWalletProvider, - clickIntents = clickIntents, - ) - } - - private val loadingTransactionsStateConverter by lazy { - WalletLoadingTxHistoryConverter( - currentStateProvider = currentStateProvider, - currentCardTypeResolverProvider = currentCardTypeResolverProvider, - clickIntents = clickIntents, - ) - } - - private val loadedTxHistoryConverter by lazy { - WalletLoadedTxHistoryConverter( - currentStateProvider = currentStateProvider, - currentCardTypeResolverProvider = currentCardTypeResolverProvider, - clickIntents = clickIntents, - ) - } - - private val singleCurrencyLoadedBalanceConverter by lazy { - WalletSingleCurrencyLoadedBalanceConverter( - currentStateProvider = currentStateProvider, - appCurrencyProvider = appCurrencyProvider, - currentWalletProvider = currentWalletProvider, - currencyStatusErrorConverter = currencyStatusErrorConverter, - ) - } - - private val lockedConverter by lazy { - WalletLockedConverter( - currentStateProvider = currentStateProvider, - clickIntents = clickIntents, - ) - } - - private val refreshStateConverter by lazy { - WalletRefreshStateConverter(currentStateProvider) - } - - private val cryptoCurrencyActionsConverter by lazy { - WalletCryptoCurrencyActionsConverter( - currentWalletProvider = currentWalletProvider, - currentStateProvider = currentStateProvider, - clickIntents = clickIntents, - ) - } - - fun getInitialState(): WalletState = WalletState.Initial(onBackClick = clickIntents::onBackClick) - - fun getSkeletonState(wallets: List, selectedWalletIndex: Int): WalletState { - return skeletonConverter.convert( - value = WalletSkeletonStateConverter.SkeletonModel( - wallets = wallets, - selectedWalletIndex = selectedWalletIndex, - ), - ) - } - - fun getStateWithUpdatedWalletName(name: String): WalletState = walletRenameStateConverter.convert(value = name) - - fun getStateWithUpdatedWalletCardCount(): WalletState = walletUpdateCardCountConverter.convert(Unit) - - fun getUnlockedState(action: WalletsUpdateActionResolver.Action.UnlockWallet): WalletState { - return walletsUnlockStateConverter.convert(value = action) - } - - fun getStateWithoutDeletedWallet( - cacheState: WalletState.ContentState, - action: WalletsUpdateActionResolver.Action.DeleteWallet, - ): WalletState { - return walletDeleteStateConverter.convert( - value = WalletDeleteStateConverter.DeleteWalletModel(cacheState = cacheState, action = action), - ) - } - - fun getStateByTokensList(maybeTokenListWithWallet: Either): WalletState { - return loadedTokensListConverter.convert(maybeTokenListWithWallet) - } - - fun getStateByTokenListError(error: TokenListError): WalletState { - return tokenListErrorConverter.convert(error) - } - - fun getStateByNotifications(notifications: ImmutableList): WalletState { - return when (val state = currentStateProvider()) { - is WalletMultiCurrencyState.Content -> state.copy(notifications = notifications) - is WalletSingleCurrencyState.Content -> state.copy(notifications = notifications) - else -> state - } - } - - fun getRefreshingState(): WalletState = refreshStateConverter.convert(value = true) - - fun getRefreshedState(): WalletState = refreshStateConverter.convert(value = false) - - fun getStateWithOpenWalletBottomSheet(content: TangemBottomSheetConfigContent): WalletState { - return when (val state = currentStateProvider() as WalletState.ContentState) { - is WalletMultiCurrencyState.Content -> state.copy( - bottomSheetConfig = TangemBottomSheetConfig( - isShow = true, - onDismissRequest = clickIntents::onDismissBottomSheet, - content = content, - ), - ) - is WalletMultiCurrencyState.Locked -> state.copy( - isBottomSheetShow = true, - onBottomSheetDismiss = clickIntents::onDismissBottomSheet, - ) - is WalletSingleCurrencyState.Content -> state.copy( - bottomSheetConfig = TangemBottomSheetConfig( - isShow = true, - onDismissRequest = clickIntents::onDismissBottomSheet, - content = content, - ), - ) - is WalletSingleCurrencyState.Locked -> state.copy( - isBottomSheetShow = true, - onBottomSheetDismiss = clickIntents::onDismissBottomSheet, - ) - } - } - - fun getStateWithClosedBottomSheet(): WalletState { - return when (val state = currentStateProvider() as WalletState.ContentState) { - is WalletMultiCurrencyState.Content -> state.copy( - bottomSheetConfig = state.bottomSheetConfig?.copy(isShow = false), - ) - is WalletMultiCurrencyState.Locked -> state.copy(isBottomSheetShow = false) - is WalletSingleCurrencyState.Content -> state.copy( - bottomSheetConfig = state.bottomSheetConfig?.copy(isShow = false), - ) - is WalletSingleCurrencyState.Locked -> state.copy(isBottomSheetShow = false) - } - } - - fun getStateWithTokenActionBottomSheet(tokenActions: TokenActionsState): WalletState { - return getStateWithOpenWalletBottomSheet( - content = ActionsBottomSheetConfig(actions = tokenActionsProvider.provideActions(tokenActions)), - ) - } - - fun getLoadingTxHistoryState( - itemsCountEither: Either, - pendingTransactions: Set, - ): WalletState { - return loadingTransactionsStateConverter.convert( - WalletLoadingTxHistoryConverter.WalletLoadingTxHistoryModel( - historyLoadingState = itemsCountEither, - pendingTransactions = pendingTransactions, - ), - ) - } - - fun getLoadedTxHistoryState( - txHistoryEither: Either>>, - ): WalletState { - return loadedTxHistoryConverter.convert(txHistoryEither) - } - - fun getLockedState(): WalletState = lockedConverter.convert(Unit) - - fun getSingleCurrencyLoadedBalanceState( - maybeCryptoCurrencyStatus: Either, - ): WalletState { - return singleCurrencyLoadedBalanceConverter.convert(maybeCryptoCurrencyStatus) - } - - fun getSingleCurrencyManageButtonsState(actionsState: TokenActionsState): WalletState { - return cryptoCurrencyActionsConverter.convert(value = actionsState) - } - - fun getStateByCurrencyStatusError(error: CurrencyStatusError): WalletState { - return currencyStatusErrorConverter.convert(error) - } - - fun getHiddenBalanceState(isBalanceHidden: Boolean): WalletState { - return hiddenStateConverter.convert(isBalanceHidden) - } - - fun getStateAndTriggerEvent( - state: WalletState, - event: WalletEvent, - setUiState: (WalletState) -> Unit, - ): WalletState { - return when (state) { - is WalletState.ContentState -> state.copySealed( - event = triggeredEvent( - data = event, - onConsume = { - val currentState = currentStateProvider() - if (currentState is WalletState.ContentState) { - setUiState(currentState.copySealed(event = consumedEvent())) - } - }, - ), - ) - is WalletState.Initial -> state - } - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletUpdateCardCountConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletUpdateCardCountConverter.kt deleted file mode 100644 index 3092c454c6..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletUpdateCardCountConverter.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state.factory - -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory -import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver -import com.tangem.feature.wallet.presentation.wallet.domain.getCardsCount -import com.tangem.feature.wallet.presentation.wallet.state.WalletState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletsListConfig -import com.tangem.utils.Provider -import com.tangem.utils.converter.Converter -import kotlinx.collections.immutable.toImmutableList - -internal class WalletUpdateCardCountConverter( - private val currentStateProvider: Provider, - private val currentWalletProvider: Provider, -) : Converter { - - override fun convert(value: Unit): WalletState { - return when (val state = currentStateProvider()) { - is WalletState.ContentState -> { - state.copySealed( - walletsListConfig = state.walletsListConfig.refreshCardCount(), - ) - } - is WalletState.Initial -> state - } - } - - private fun WalletsListConfig.refreshCardCount(): WalletsListConfig { - val selectedWallet = currentWalletProvider() - return copy( - wallets = wallets - .mapIndexed { index, walletCard -> - if (index == selectedWalletIndex) { - when (walletCard) { - is WalletCardState.Content -> walletCard.copy( - additionalInfo = WalletAdditionalInfoFactory.resolve(wallet = selectedWallet), - imageResId = WalletImageResolver.resolve(userWallet = selectedWallet), - cardCount = selectedWallet.getCardsCount(), - ) - else -> walletCard - } - } else { - walletCard - } - } - .toImmutableList(), - ) - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletsUnlockStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletsUnlockStateConverter.kt deleted file mode 100644 index 4dc8b76098..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletsUnlockStateConverter.kt +++ /dev/null @@ -1,132 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state.factory - -import com.tangem.core.ui.components.marketprice.MarketPriceBlockState -import com.tangem.core.ui.components.transactions.state.TxHistoryState -import com.tangem.domain.common.util.cardTypesResolver -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver -import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletState -import com.tangem.feature.wallet.presentation.wallet.state.components.* -import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents -import com.tangem.utils.Provider -import com.tangem.utils.converter.Converter -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toImmutableList -import kotlinx.coroutines.flow.MutableStateFlow -import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletsUpdateActionResolver.Action.UnlockWallet as UnlockWalletAction - -/** - * Converter that responds on wallets unlocking action. Returns [WalletState] with unlocked wallets. - * - * @property currentStateProvider current ui state provider - * @property clickIntents screen click intents - * -[REDACTED_AUTHOR] - */ -internal class WalletsUnlockStateConverter( - private val currentStateProvider: Provider, - private val clickIntents: WalletClickIntents, -) : Converter { - - override fun convert(value: UnlockWalletAction): WalletState { - return when (val state = currentStateProvider()) { - is WalletMultiCurrencyState.Locked -> state.toMultiCurrencyContentState(value) - is WalletSingleCurrencyState.Locked -> state.toSingleCurrencyContentState(value) - is WalletState.Initial, - is WalletMultiCurrencyState.Content, - is WalletSingleCurrencyState.Content, - -> state - } - } - - private fun WalletMultiCurrencyState.Locked.toMultiCurrencyContentState(action: UnlockWalletAction): WalletState { - return WalletMultiCurrencyState.Content( - onBackClick = onBackClick, - topBarConfig = topBarConfig.updateCallback(), - walletsListConfig = walletsListConfig.unlockWallets(action), - pullToRefreshConfig = pullToRefreshConfig.stopRefreshing(), - tokensListState = WalletTokensListState.Loading(), - notifications = persistentListOf(), - bottomSheetConfig = null, - onManageTokensClick = clickIntents::onManageTokensClick, - isBalanceHidden = isBalanceHidden, - ) - } - - private fun WalletSingleCurrencyState.Locked.toSingleCurrencyContentState(action: UnlockWalletAction): WalletState { - return WalletSingleCurrencyState.Content( - onBackClick = onBackClick, - topBarConfig = topBarConfig.updateCallback(), - walletsListConfig = walletsListConfig.unlockWallets(action), - pullToRefreshConfig = pullToRefreshConfig.stopRefreshing(), - notifications = persistentListOf(), - bottomSheetConfig = null, - buttons = buttons, - marketPriceBlockState = MarketPriceBlockState.Loading( - currencySymbol = action.selectedWallet.getPrimaryCurrencyName(), - ), - txHistoryState = TxHistoryState.Content( - contentItems = MutableStateFlow( - value = TxHistoryState.getDefaultLoadingTransactions(clickIntents::onExploreClick), - ), - ), - isBalanceHidden = isBalanceHidden, - ) - } - - private fun WalletTopBarConfig.updateCallback(): WalletTopBarConfig { - return copy(onDetailsClick = clickIntents::onDetailsClick) - } - - private fun WalletsListConfig.unlockWallets(action: UnlockWalletAction): WalletsListConfig { - return this.copy( - selectedWalletIndex = action.selectedWalletIndex, - wallets = wallets.unlockWallets(action), - ) - } - - private fun List.unlockWallets(action: UnlockWalletAction): ImmutableList { - return this - .map { prevWallet -> - if (prevWallet is WalletCardState.LockedContent && action.isUnlockedWallet(prevWallet.id)) { - prevWallet.mapToLoadingWalletCardState( - userWallet = action.getUnlockWallet(prevWallet.id), - ) - } else { - prevWallet - } - } - .toImmutableList() - } - - private fun UnlockWalletAction.isUnlockedWallet(walletId: UserWalletId): Boolean { - return unlockedWallets.any { it.walletId == walletId } - } - - private fun UnlockWalletAction.getUnlockWallet(walletId: UserWalletId): UserWallet { - return unlockedWallets.firstOrNull { it.walletId == walletId } - ?: error("Unlocked wallet with id $walletId not found") - } - - private fun WalletCardState.mapToLoadingWalletCardState(userWallet: UserWallet): WalletCardState { - return WalletCardState.Loading( - id = id, - title = title, - imageResId = WalletImageResolver.resolve(userWallet = userWallet), - onRenameClick = onRenameClick, - onDeleteClick = onDeleteClick, - ) - } - - private fun WalletPullToRefreshConfig.stopRefreshing(): WalletPullToRefreshConfig { - return copy(isRefreshing = false) - } - - private fun UserWallet.getPrimaryCurrencyName(): String { - return scanResponse.cardTypesResolver.getBlockchain().currency - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt deleted file mode 100644 index d2fd2b4f17..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt +++ /dev/null @@ -1,80 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory - -import androidx.paging.PagingData -import arrow.core.Either -import com.tangem.core.ui.components.transactions.state.TxHistoryState -import com.tangem.domain.common.CardTypesResolver -import com.tangem.domain.txhistory.models.TxHistoryItem -import com.tangem.domain.txhistory.models.TxHistoryListError -import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletState -import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents -import com.tangem.utils.Provider -import com.tangem.utils.converter.Converter -import kotlinx.coroutines.flow.Flow - -/** - * Converter from loaded tx history to [TxHistoryState] - * - * @property currentStateProvider current state provider - * @property currentCardTypeResolverProvider current card type resolver provider - * @property clickIntents screen click intents - * -[REDACTED_AUTHOR] - */ -internal class WalletLoadedTxHistoryConverter( - private val currentStateProvider: Provider, - private val currentCardTypeResolverProvider: Provider, - private val clickIntents: WalletClickIntents, -) : Converter>>, WalletState> { - - private val walletTxHistoryItemFlowConverter by lazy { - WalletTxHistoryItemFlowConverter( - currentStateProvider = currentStateProvider, - blockchain = currentCardTypeResolverProvider().getBlockchain(), - clickIntents = clickIntents, - ) - } - - override fun convert(value: Either>>): WalletState { - return value.fold(ifLeft = ::convertError, ifRight = ::convert) - } - - private fun convertError(error: TxHistoryListError): WalletState { - return when (val state = currentStateProvider()) { - is WalletSingleCurrencyState.Content -> { - state.copy( - txHistoryState = when (error) { - is TxHistoryListError.DataError -> { - TxHistoryState.Error( - onReloadClick = clickIntents::onReloadClick, - onExploreClick = clickIntents::onExploreClick, - ) - } - }, - ) - } - is WalletMultiCurrencyState.Content, - is WalletMultiCurrencyState.Locked, - is WalletSingleCurrencyState.Locked, - is WalletState.Initial, - -> state - } - } - - private fun convert(items: Flow>): WalletState { - return when (val state = currentStateProvider()) { - is WalletSingleCurrencyState.Content -> { - return state.copy( - txHistoryState = walletTxHistoryItemFlowConverter.convert(value = items) ?: state.txHistoryState, - ) - } - is WalletMultiCurrencyState.Content, - is WalletMultiCurrencyState.Locked, - is WalletSingleCurrencyState.Locked, - is WalletState.Initial, - -> state - } - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt deleted file mode 100644 index 3f4b843c86..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt +++ /dev/null @@ -1,105 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory - -import androidx.paging.PagingData -import arrow.core.Either -import com.tangem.core.ui.components.transactions.state.TransactionState -import com.tangem.core.ui.components.transactions.state.TxHistoryState.* -import com.tangem.domain.common.CardTypesResolver -import com.tangem.domain.txhistory.models.TxHistoryItem -import com.tangem.domain.txhistory.models.TxHistoryStateError -import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletState -import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents -import com.tangem.utils.Provider -import com.tangem.utils.converter.Converter -import kotlinx.collections.immutable.toImmutableList -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.update - -/** - * Converter from loading tx history state to [WalletSingleCurrencyState.Content] - * - * @property currentStateProvider current state provider - * @property clickIntents screen click intents - * -[REDACTED_AUTHOR] - */ -internal class WalletLoadingTxHistoryConverter( - private val currentStateProvider: Provider, - private val currentCardTypeResolverProvider: Provider, - private val clickIntents: WalletClickIntents, -) : Converter { - - private val txHistoryItemConverter by lazy { - val blockchain = currentCardTypeResolverProvider().getBlockchain() - WalletTxHistoryTransactionStateConverter( - symbol = blockchain.currency, - decimals = blockchain.decimals(), - clickIntents = clickIntents, - ) - } - - override fun convert(value: WalletLoadingTxHistoryModel): WalletState { - return value.historyLoadingState.fold( - ifLeft = { convertError(it, value.pendingTransactions) }, - ifRight = ::convertRight, - ) - } - - private fun convertError(error: TxHistoryStateError, pendingTransactions: Set): WalletState { - val state = currentStateProvider() - - return if (state is WalletSingleCurrencyState.Content) { - state.copy( - txHistoryState = when (error) { - is TxHistoryStateError.EmptyTxHistories -> Empty(onExploreClick = clickIntents::onExploreClick) - is TxHistoryStateError.DataError -> Error( - onReloadClick = clickIntents::onReloadClick, - onExploreClick = clickIntents::onExploreClick, - ) - is TxHistoryStateError.TxHistoryNotImplemented -> { - NotSupported( - pendingTransactions = txHistoryItemConverter.convertList(pendingTransactions) - .toImmutableList(), - onExploreClick = clickIntents::onExploreClick, - ) - } - }, - ) - } else { - state - } - } - - private fun convertRight(value: Int): WalletState { - val state = currentStateProvider() - val singleCurrencyContentState = state as? WalletSingleCurrencyState.Content ?: return state - return if (singleCurrencyContentState.txHistoryState is Content) { - singleCurrencyContentState.txHistoryState.contentItems.update { - PagingData.from(data = createLoadingItems(value)) - } - state - } else { - val txHistoryContent = Content( - contentItems = MutableStateFlow( - value = PagingData.from(data = createLoadingItems(value)), - ), - ) - state.copy(txHistoryState = txHistoryContent) - } - } - - private fun createLoadingItems(size: Int): List { - return buildList { - add(TxHistoryItemState.Title(onExploreClick = clickIntents::onExploreClick)) - (1..size).forEach { - add(TxHistoryItemState.Transaction(state = TransactionState.Loading(it.toString()))) - } - } - } - - data class WalletLoadingTxHistoryModel( - val historyLoadingState: Either, - val pendingTransactions: Set, - ) -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt deleted file mode 100644 index f0ab700230..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt +++ /dev/null @@ -1,131 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory - -import android.text.format.DateUtils -import androidx.paging.* -import com.tangem.blockchain.common.Blockchain -import com.tangem.core.ui.components.transactions.state.TransactionState -import com.tangem.core.ui.components.transactions.state.TxHistoryState -import com.tangem.core.ui.components.transactions.state.TxHistoryState.TxHistoryItemState -import com.tangem.core.ui.utils.DateTimeFormatters -import com.tangem.domain.txhistory.models.TxHistoryItem -import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletState -import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents -import com.tangem.utils.Provider -import com.tangem.utils.converter.Converter -import com.tangem.utils.extensions.isToday -import com.tangem.utils.extensions.isYesterday -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.* -import org.joda.time.DateTime -import org.joda.time.DateTimeZone -import java.util.UUID - -/** - * Convert from [Flow] of [TxHistoryItem] to [TxHistoryState] - * - * @property currentStateProvider current state provider - * @property blockchain blockchain of transactions history - * @property clickIntents screen click intents - * -[REDACTED_AUTHOR] - */ -internal class WalletTxHistoryItemFlowConverter( - private val currentStateProvider: Provider, - private val blockchain: Blockchain, - private val clickIntents: WalletClickIntents, -) : Converter>, TxHistoryState?> { - - private val txHistoryItemConverter by lazy { - WalletTxHistoryTransactionStateConverter( - symbol = blockchain.currency, - decimals = blockchain.decimals(), - clickIntents = clickIntents, - ) - } - - override fun convert(value: Flow>): TxHistoryState? { - val state = currentStateProvider() as? WalletSingleCurrencyState ?: return null - val txHistoryContent = state.txHistoryState as? TxHistoryState.Content - ?: TxHistoryState.Content(contentItems = MutableStateFlow(PagingData.empty())) - - // FIXME: TxHistoryRepository should send loading transactions - // [REDACTED_JIRA] - value - .onEach { txHistoryStatePagingData -> - txHistoryContent.contentItems.update { - txHistoryStatePagingData - .map { item -> - // [createTransactionState] returns timestamp without formatting - TxHistoryItemState.Transaction(state = createTransactionState(item)) - } - .insertHeaderItem( - terminalSeparatorType = TerminalSeparatorType.SOURCE_COMPLETE, - item = TxHistoryItemState.Title(clickIntents::onExploreClick), - ) - .insertGroupTitle() - } - } - .launchIn(CoroutineScope(Dispatchers.IO)) - - return txHistoryContent - } - - private fun createTransactionState(item: TxHistoryItem): TransactionState { - return txHistoryItemConverter.convert(value = item) - } - - private fun PagingData.insertGroupTitle(): PagingData { - return insertSeparators(terminalSeparatorType = TerminalSeparatorType.SOURCE_COMPLETE) { before, after -> - // Use raw timestamp to get date - - // If [afterDate] is the first transaction in the flow, add the group title - val afterDate = after.getTimestamp()?.toDateFormat() ?: return@insertSeparators null - if (before is TxHistoryItemState.Title) { - return@insertSeparators TxHistoryItemState.GroupTitle( - title = afterDate, - itemKey = UUID.randomUUID().toString(), - ) - } - - /* - * If [beforeDate] is not equals to [afterDate], then [afterDate] is first transaction in - * the new group - */ - val beforeDate = before.getTimestamp()?.toDateFormat() ?: return@insertSeparators null - return@insertSeparators if (beforeDate != afterDate) { - TxHistoryItemState.GroupTitle(title = afterDate, itemKey = UUID.randomUUID().toString()) - } else { - null - } - } - } - - private fun TxHistoryItemState?.getTimestamp(): Long? { - return if (this is TxHistoryItemState.Transaction && this.state is TransactionState.Content) { - val txContent = this.state as TransactionState.Content - txContent.timestamp - } else { - null - } - } - - /** - * If [this] timestamp is today or yesterday, returns relative date, - * otherwise returns formatting date. - */ - private fun Long.toDateFormat(): String { - val localDate = DateTime(this, DateTimeZone.getDefault()) - return if (localDate.isToday() || localDate.isYesterday()) { - DateUtils.getRelativeTimeSpanString( - this, - DateTime.now().millis, - DateUtils.DAY_IN_MILLIS, - DateUtils.FORMAT_ABBREV_RELATIVE, - ).toString() - } else { - DateTimeFormatters.formatDate(date = localDate) - } - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryTransactionStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryTransactionStateConverter.kt deleted file mode 100644 index 720addb22a..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryTransactionStateConverter.kt +++ /dev/null @@ -1,103 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory - -import com.tangem.core.ui.components.transactions.state.TransactionState -import com.tangem.core.ui.extensions.TextReference -import com.tangem.core.ui.extensions.resourceReference -import com.tangem.core.ui.extensions.stringReference -import com.tangem.core.ui.extensions.wrappedList -import com.tangem.core.ui.utils.toTimeFormat -import com.tangem.domain.txhistory.models.TxHistoryItem -import com.tangem.feature.wallet.impl.R -import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents -import com.tangem.utils.converter.Converter -import com.tangem.utils.toBriefAddressFormat -import com.tangem.utils.toFormattedCurrencyString - -internal class WalletTxHistoryTransactionStateConverter( - private val symbol: String, - private val decimals: Int, - private val clickIntents: WalletClickIntents, -) : Converter { - - override fun convert(value: TxHistoryItem): TransactionState { - return createTransactionStateItem(item = value) - } - - @Suppress("LongMethod") - private fun createTransactionStateItem(item: TxHistoryItem): TransactionState { - return TransactionState.Content( - txHash = item.txHash, - amount = item.getAmount(), - time = item.timestampInMillis.toTimeFormat(), - status = item.status.tiUiStatus(), - direction = item.extractDirection(), - iconRes = item.extractIcon(), - title = item.extractTitle(), - subtitle = item.extractSubtitle(), - timestamp = item.timestampInMillis, - onClick = { clickIntents.onTransactionClick(item.txHash) }, - ) - } - - private fun TxHistoryItem.extractIcon(): Int = if (status == TxHistoryItem.TransactionStatus.Failed) { - R.drawable.ic_close_24 - } else { - when (type) { - is TxHistoryItem.TransactionType.Approve -> R.drawable.ic_doc_24 - is TxHistoryItem.TransactionType.Operation, - is TxHistoryItem.TransactionType.Swap, - is TxHistoryItem.TransactionType.Transfer, - is TxHistoryItem.TransactionType.UnknownOperation, - -> if (isOutgoing) R.drawable.ic_arrow_up_24 else R.drawable.ic_arrow_down_24 - } - } - - private fun TxHistoryItem.extractTitle(): TextReference = when (val type = type) { - is TxHistoryItem.TransactionType.Approve -> resourceReference(R.string.common_approval) - is TxHistoryItem.TransactionType.Operation -> stringReference(type.name) - is TxHistoryItem.TransactionType.Swap -> resourceReference(R.string.common_swap) - is TxHistoryItem.TransactionType.Transfer -> resourceReference(R.string.common_transfer) - is TxHistoryItem.TransactionType.UnknownOperation -> resourceReference(R.string.transaction_history_operation) - } - - private fun TxHistoryItem.extractSubtitle(): TextReference = - when (val interactionAddress = interactionAddressType) { - is TxHistoryItem.InteractionAddressType.Contract -> resourceReference( - id = R.string.transaction_history_contract_address, - formatArgs = wrappedList(interactionAddress.address.toBriefAddressFormat()), - ) - is TxHistoryItem.InteractionAddressType.Multiple -> resourceReference( - id = if (isOutgoing) { - R.string.transaction_history_transaction_to_address - } else { - R.string.transaction_history_transaction_from_address - }, - formatArgs = wrappedList(resourceReference(R.string.transaction_history_multiple_addresses)), - ) - is TxHistoryItem.InteractionAddressType.User -> resourceReference( - id = if (isOutgoing) { - R.string.transaction_history_transaction_to_address - } else { - R.string.transaction_history_transaction_from_address - }, - formatArgs = wrappedList(interactionAddress.address.toBriefAddressFormat()), - ) - } - - private fun TxHistoryItem.extractDirection() = - if (isOutgoing) TransactionState.Content.Direction.OUTGOING else TransactionState.Content.Direction.INCOMING - - private fun TxHistoryItem.TransactionStatus.tiUiStatus() = when (this) { - TxHistoryItem.TransactionStatus.Confirmed -> TransactionState.Content.Status.Confirmed - TxHistoryItem.TransactionStatus.Failed -> TransactionState.Content.Status.Failed - TxHistoryItem.TransactionStatus.Unconfirmed -> TransactionState.Content.Status.Unconfirmed - } - - private fun TxHistoryItem.getAmount(): String { - val prefix = when (status) { - TxHistoryItem.TransactionStatus.Failed -> "" - else -> if (isOutgoing) "-" else "+" - } - return prefix + amount.toFormattedCurrencyString(currency = symbol, decimals = decimals) - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/ActionsBottomSheetConfig.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/ActionsBottomSheetConfig.kt similarity index 84% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/ActionsBottomSheetConfig.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/ActionsBottomSheetConfig.kt index 42cf37464b..84ec1221bd 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/ActionsBottomSheetConfig.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/ActionsBottomSheetConfig.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state +package com.tangem.feature.wallet.presentation.wallet.state.model import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import kotlinx.collections.immutable.ImmutableList diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/BalancesAndLimitsBlockState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/BalancesAndLimitsBlockState.kt similarity index 85% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/BalancesAndLimitsBlockState.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/BalancesAndLimitsBlockState.kt index a64e659900..85ebece2af 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/BalancesAndLimitsBlockState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/BalancesAndLimitsBlockState.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.model +package com.tangem.feature.wallet.presentation.wallet.state.model import androidx.compose.runtime.Immutable diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/BalancesAndLimitsBottomSheetConfig.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/BalancesAndLimitsBottomSheetConfig.kt similarity index 91% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/BalancesAndLimitsBottomSheetConfig.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/BalancesAndLimitsBottomSheetConfig.kt index dc15fc27b1..5945197384 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/BalancesAndLimitsBottomSheetConfig.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/BalancesAndLimitsBottomSheetConfig.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.model +package com.tangem.feature.wallet.presentation.wallet.state.model import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/DepositButtonState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/DepositButtonState.kt similarity index 59% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/DepositButtonState.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/DepositButtonState.kt index 9577abc99e..9a490bf52e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/DepositButtonState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/DepositButtonState.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.model +package com.tangem.feature.wallet.presentation.wallet.state.model internal data class DepositButtonState( val isEnabled: Boolean, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/ManageTokensButtonConfig.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/ManageTokensButtonConfig.kt similarity index 51% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/ManageTokensButtonConfig.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/ManageTokensButtonConfig.kt index 0548298dc0..52c4b9f6dc 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/ManageTokensButtonConfig.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/ManageTokensButtonConfig.kt @@ -1,3 +1,3 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.model +package com.tangem.feature.wallet.presentation.wallet.state.model internal data class ManageTokensButtonConfig(val onClick: () -> Unit) \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/TokenActionButtonConfig.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/TokenActionButtonConfig.kt similarity index 88% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/TokenActionButtonConfig.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/TokenActionButtonConfig.kt index db94522760..12c3c648f1 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/TokenActionButtonConfig.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/TokenActionButtonConfig.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state +package com.tangem.feature.wallet.presentation.wallet.state.model import androidx.annotation.DrawableRes import com.tangem.core.ui.extensions.TextReference diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletAdditionalInfo.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletAdditionalInfo.kt similarity index 73% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletAdditionalInfo.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletAdditionalInfo.kt index 612f2111e5..2e210c2221 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletAdditionalInfo.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletAdditionalInfo.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state.components +package com.tangem.feature.wallet.presentation.wallet.state.model import androidx.compose.runtime.Immutable import com.tangem.core.ui.extensions.TextReference diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletAlertState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletAlertState.kt similarity index 97% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletAlertState.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletAlertState.kt index 300369728f..74b0c80769 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletAlertState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletAlertState.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state +package com.tangem.feature.wallet.presentation.wallet.state.model import androidx.compose.runtime.Immutable import com.tangem.core.ui.extensions.TextReference diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletBottomSheetConfig.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletBottomSheetConfig.kt similarity index 95% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletBottomSheetConfig.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletBottomSheetConfig.kt index 31291aabe5..d6ec5fcc93 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletBottomSheetConfig.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletBottomSheetConfig.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state.components +package com.tangem.feature.wallet.presentation.wallet.state.model import androidx.annotation.DrawableRes import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletCardState.kt similarity index 98% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletCardState.kt index 094d5338c1..a533eeabfb 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletCardState.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state.components +package com.tangem.feature.wallet.presentation.wallet.state.model import androidx.annotation.DrawableRes import androidx.compose.runtime.Immutable diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletEvent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletEvent.kt similarity index 92% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletEvent.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletEvent.kt index a269d87651..5ee98fda79 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletEvent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletEvent.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state +package com.tangem.feature.wallet.presentation.wallet.state.model import androidx.compose.runtime.Immutable import com.tangem.core.ui.extensions.TextReference diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletManageButton.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletManageButton.kt similarity index 97% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletManageButton.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletManageButton.kt index 797aaa2711..124d3bf6a9 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletManageButton.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletManageButton.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state.components +package com.tangem.feature.wallet.presentation.wallet.state.model import androidx.compose.runtime.Immutable import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletNotification.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt similarity index 99% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletNotification.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt index 7f6bfd6b46..1b9721b2cf 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletNotification.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state.components +package com.tangem.feature.wallet.presentation.wallet.state.model import androidx.compose.runtime.Immutable import com.tangem.core.ui.components.notifications.NotificationConfig diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletPullToRefreshConfig.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletPullToRefreshConfig.kt similarity index 78% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletPullToRefreshConfig.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletPullToRefreshConfig.kt index 6714af85cc..ca91846374 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletPullToRefreshConfig.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletPullToRefreshConfig.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state.components +package com.tangem.feature.wallet.presentation.wallet.state.model /** * Wallet screen top bar config diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/WalletScreenState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletScreenState.kt similarity index 63% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/WalletScreenState.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletScreenState.kt index 8b092c830c..9285575d9e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/WalletScreenState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletScreenState.kt @@ -1,8 +1,6 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.model +package com.tangem.feature.wallet.presentation.wallet.state.model import com.tangem.core.ui.event.StateEvent -import com.tangem.feature.wallet.presentation.wallet.state.WalletEvent -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTopBarConfig import kotlinx.collections.immutable.ImmutableList internal data class WalletScreenState( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/WalletState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletState.kt similarity index 86% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/WalletState.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletState.kt index f3b70aa395..0792d8f25a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/WalletState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletState.kt @@ -1,17 +1,13 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.model +package com.tangem.feature.wallet.presentation.wallet.state.model import androidx.compose.runtime.Immutable import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.core.ui.components.transactions.state.TxHistoryState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletPullToRefreshConfig -import com.tangem.feature.wallet.presentation.wallet.state2.model.holder.LockedTxHistoryStateHolder -import com.tangem.feature.wallet.presentation.wallet.state2.model.holder.LockedWalletStateHolder -import com.tangem.feature.wallet.presentation.wallet.state2.model.holder.TxHistoryStateHolder -import com.tangem.feature.wallet.presentation.wallet.state2.model.holder.WalletStateHolder +import com.tangem.feature.wallet.presentation.wallet.state.model.holder.LockedTxHistoryStateHolder +import com.tangem.feature.wallet.presentation.wallet.state.model.holder.LockedWalletStateHolder +import com.tangem.feature.wallet.presentation.wallet.state.model.holder.TxHistoryStateHolder +import com.tangem.feature.wallet.presentation.wallet.state.model.holder.WalletStateHolder import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.PersistentList diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/WalletTokensListState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletTokensListState.kt similarity index 96% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/WalletTokensListState.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletTokensListState.kt index 0cddc53540..e861c77579 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/WalletTokensListState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletTokensListState.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.model +package com.tangem.feature.wallet.presentation.wallet.state.model import androidx.compose.runtime.Immutable import com.tangem.core.ui.extensions.TextReference diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTopBarConfig.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletTopBarConfig.kt similarity index 72% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTopBarConfig.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletTopBarConfig.kt index 9b3cf42edf..0bf162e9c7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTopBarConfig.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletTopBarConfig.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state.components +package com.tangem.feature.wallet.presentation.wallet.state.model /** * Wallet screen top bar config diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/holder/LockedWalletStateHolder.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/holder/LockedWalletStateHolder.kt similarity index 75% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/holder/LockedWalletStateHolder.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/holder/LockedWalletStateHolder.kt index c54e99c560..6c361d5da7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/holder/LockedWalletStateHolder.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/holder/LockedWalletStateHolder.kt @@ -1,10 +1,10 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.model.holder +package com.tangem.feature.wallet.presentation.wallet.state.model.holder import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletBottomSheetConfig -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletPullToRefreshConfig +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletBottomSheetConfig +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletPullToRefreshConfig import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/holder/TxHistoryStateHolder.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/holder/TxHistoryStateHolder.kt similarity index 92% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/holder/TxHistoryStateHolder.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/holder/TxHistoryStateHolder.kt index 3094cfe505..0350800f16 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/holder/TxHistoryStateHolder.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/holder/TxHistoryStateHolder.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.model.holder +package com.tangem.feature.wallet.presentation.wallet.state.model.holder import androidx.paging.PagingData import com.tangem.core.ui.components.transactions.state.TransactionState diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/AddWalletTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/AddWalletTransformer.kt similarity index 65% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/AddWalletTransformer.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/AddWalletTransformer.kt index 5100664912..dd9b3ee7d1 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/AddWalletTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/AddWalletTransformer.kt @@ -1,14 +1,14 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers +package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletScreenState -import com.tangem.feature.wallet.presentation.wallet.state2.utils.WalletLoadingStateFactory -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState +import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletLoadingStateFactory +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import kotlinx.collections.immutable.toImmutableList internal class AddWalletTransformer( private val userWallet: UserWallet, - private val clickIntents: WalletClickIntentsV2, + private val clickIntents: WalletClickIntents, ) : WalletScreenStateTransformer { private val walletLoadingStateFactory by lazy { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/CloseBottomSheetTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/CloseBottomSheetTransformer.kt similarity index 87% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/CloseBottomSheetTransformer.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/CloseBottomSheetTransformer.kt index 28e7b364c6..81106b0db4 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/CloseBottomSheetTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/CloseBottomSheetTransformer.kt @@ -1,7 +1,7 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers +package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState internal class CloseBottomSheetTransformer(userWalletId: UserWalletId) : WalletStateTransformer(userWalletId) { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/DeleteWalletTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/DeleteWalletTransformer.kt similarity index 79% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/DeleteWalletTransformer.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/DeleteWalletTransformer.kt index f36010a664..3c2590c4da 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/DeleteWalletTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/DeleteWalletTransformer.kt @@ -1,8 +1,8 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers +package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletScreenState -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState import kotlinx.collections.immutable.toImmutableList import timber.log.Timber diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/InitializeWalletsTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/InitializeWalletsTransformer.kt similarity index 85% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/InitializeWalletsTransformer.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/InitializeWalletsTransformer.kt index 46b15ef604..630c7dfb2c 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/InitializeWalletsTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/InitializeWalletsTransformer.kt @@ -1,16 +1,12 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers +package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTopBarConfig -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletScreenState -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState -import com.tangem.feature.wallet.presentation.wallet.state2.utils.WalletLoadingStateFactory -import com.tangem.feature.wallet.presentation.wallet.state2.utils.createStateByWalletType -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.model.* +import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletLoadingStateFactory +import com.tangem.feature.wallet.presentation.wallet.state.utils.createStateByWalletType +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList @@ -19,7 +15,7 @@ internal class InitializeWalletsTransformer( private val selectedWalletIndex: Int, private val selectedWallet: UserWallet, private val wallets: List, - private val clickIntents: WalletClickIntentsV2, + private val clickIntents: WalletClickIntents, ) : WalletScreenStateTransformer { private val walletLoadingStateFactory by lazy { WalletLoadingStateFactory(clickIntents = clickIntents) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/OpenBottomSheetTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/OpenBottomSheetTransformer.kt similarity index 93% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/OpenBottomSheetTransformer.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/OpenBottomSheetTransformer.kt index 9d6737e940..141f4a57d0 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/OpenBottomSheetTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/OpenBottomSheetTransformer.kt @@ -1,9 +1,9 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers +package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState internal class OpenBottomSheetTransformer( userWalletId: UserWalletId, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/ReinitializeWalletTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/ReinitializeWalletTransformer.kt similarity index 66% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/ReinitializeWalletTransformer.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/ReinitializeWalletTransformer.kt index dac563b4cc..7caf5d79e0 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/ReinitializeWalletTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/ReinitializeWalletTransformer.kt @@ -1,9 +1,9 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers +package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletScreenState -import com.tangem.feature.wallet.presentation.wallet.state2.utils.WalletLoadingStateFactory -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState +import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletLoadingStateFactory +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import kotlinx.collections.immutable.persistentListOf /** @@ -11,7 +11,7 @@ import kotlinx.collections.immutable.persistentListOf */ internal class ReinitializeWalletTransformer( private val userWallet: UserWallet, - private val clickIntents: WalletClickIntentsV2, + private val clickIntents: WalletClickIntents, ) : WalletScreenStateTransformer { private val walletLoadingStateFactory by lazy { WalletLoadingStateFactory(clickIntents = clickIntents) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/RenameWalletTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/RenameWalletTransformer.kt similarity index 88% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/RenameWalletTransformer.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/RenameWalletTransformer.kt index b218f47ee2..f0b5cf9e20 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/RenameWalletTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/RenameWalletTransformer.kt @@ -1,7 +1,7 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers +package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState import timber.log.Timber internal class RenameWalletTransformer( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/ScrollToWalletTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/ScrollToWalletTransformer.kt similarity index 79% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/ScrollToWalletTransformer.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/ScrollToWalletTransformer.kt index 2651e80ebe..6381c98d9d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/ScrollToWalletTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/ScrollToWalletTransformer.kt @@ -1,9 +1,9 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers +package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.core.ui.event.consumedEvent import com.tangem.core.ui.event.triggeredEvent -import com.tangem.feature.wallet.presentation.wallet.state.WalletEvent -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletScreenState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState import com.tangem.utils.Provider internal class ScrollToWalletTransformer( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SendEventTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SendEventTransformer.kt similarity index 62% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SendEventTransformer.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SendEventTransformer.kt index c56f4d9918..4b9025e588 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SendEventTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SendEventTransformer.kt @@ -1,8 +1,8 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers +package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.core.ui.event.triggeredEvent -import com.tangem.feature.wallet.presentation.wallet.state.WalletEvent -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletScreenState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState internal class SendEventTransformer( private val event: WalletEvent, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetBalancesAndLimitsTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetBalancesAndLimitsTransformer.kt similarity index 88% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetBalancesAndLimitsTransformer.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetBalancesAndLimitsTransformer.kt index 959a6527ac..ea53efb4c6 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetBalancesAndLimitsTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetBalancesAndLimitsTransformer.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers +package com.tangem.feature.wallet.presentation.wallet.state.transformers import arrow.core.Either import arrow.core.getOrElse @@ -7,18 +7,18 @@ import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.domain.visa.model.VisaCurrency import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.domain.getCardsCount -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletAdditionalInfo -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState -import com.tangem.feature.wallet.presentation.wallet.state2.model.BalancesAndLimitsBlockState -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.model.BalancesAndLimitsBlockState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAdditionalInfo +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import org.joda.time.DateTime import org.joda.time.Days internal class SetBalancesAndLimitsTransformer( private val userWallet: UserWallet, private val maybeVisaCurrency: Either, - private val clickIntents: WalletClickIntentsV2, + private val clickIntents: WalletClickIntents, ) : WalletStateTransformer(userWallet.walletId) { override fun transform(prevState: WalletState): WalletState { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetCryptoCurrencyActionsTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetCryptoCurrencyActionsTransformer.kt similarity index 91% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetCryptoCurrencyActionsTransformer.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetCryptoCurrencyActionsTransformer.kt index 88ebcb4e14..e7fedeba18 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetCryptoCurrencyActionsTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetCryptoCurrencyActionsTransformer.kt @@ -1,11 +1,11 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers +package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.tokens.model.TokenActionsState import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletManageButton +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.toPersistentList import timber.log.Timber @@ -13,7 +13,7 @@ import timber.log.Timber internal class SetCryptoCurrencyActionsTransformer( private val tokenActionsState: TokenActionsState, private val userWallet: UserWallet, - private val clickIntents: WalletClickIntentsV2, + private val clickIntents: WalletClickIntents, ) : WalletStateTransformer(userWallet.walletId) { override fun transform(prevState: WalletState): WalletState { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetPrimaryCurrencyTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetPrimaryCurrencyTransformer.kt similarity index 79% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetPrimaryCurrencyTransformer.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetPrimaryCurrencyTransformer.kt index da21391c07..056d47e276 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetPrimaryCurrencyTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetPrimaryCurrencyTransformer.kt @@ -1,13 +1,13 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers +package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter.SingleWalletCardStateConverter -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter.SingleWalletMarketPriceConverter +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.SingleWalletCardStateConverter +import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.SingleWalletMarketPriceConverter import timber.log.Timber internal class SetPrimaryCurrencyTransformer( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetRefreshStateTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetRefreshStateTransformer.kt similarity index 84% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetRefreshStateTransformer.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetRefreshStateTransformer.kt index 315d64ab8f..0f6d520acf 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetRefreshStateTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetRefreshStateTransformer.kt @@ -1,12 +1,7 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers +package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletPullToRefreshConfig -import com.tangem.feature.wallet.presentation.wallet.state2.model.BalancesAndLimitsBlockState -import com.tangem.feature.wallet.presentation.wallet.state2.model.DepositButtonState -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletTokensListState +import com.tangem.feature.wallet.presentation.wallet.state.model.* import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.mutate diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetTokenListErrorTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListErrorTransformer.kt similarity index 85% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetTokenListErrorTransformer.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListErrorTransformer.kt index 587ef5d25d..046b5fd498 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetTokenListErrorTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListErrorTransformer.kt @@ -1,9 +1,9 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers +package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletTokensListState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState import timber.log.Timber internal class SetTokenListErrorTransformer( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetTokenListTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListTransformer.kt similarity index 75% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetTokenListTransformer.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListTransformer.kt index 65c3cff6d7..b852f07fa7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetTokenListTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTokenListTransformer.kt @@ -1,23 +1,23 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers +package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState -import com.tangem.feature.wallet.presentation.wallet.state2.model.ManageTokensButtonConfig -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletTokensListState -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter.MultiWalletCardStateConverter -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter.TokenListStateConverter -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.model.ManageTokensButtonConfig +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState +import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.MultiWalletCardStateConverter +import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.TokenListStateConverter +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import timber.log.Timber internal class SetTokenListTransformer( private val tokenList: TokenList, private val userWallet: UserWallet, private val appCurrency: AppCurrency, - private val clickIntents: WalletClickIntentsV2, + private val clickIntents: WalletClickIntents, ) : WalletStateTransformer(userWallet.walletId) { override fun transform(prevState: WalletState): WalletState { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetTxHistoryCountErrorTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTxHistoryCountErrorTransformer.kt similarity index 87% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetTxHistoryCountErrorTransformer.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTxHistoryCountErrorTransformer.kt index 745028a37a..9d97991b83 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetTxHistoryCountErrorTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTxHistoryCountErrorTransformer.kt @@ -1,13 +1,13 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers +package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.txhistory.models.TxHistoryStateError import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter.TxHistoryItemStateConverter -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.TxHistoryItemStateConverter +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import kotlinx.collections.immutable.toImmutableList import timber.log.Timber @@ -15,7 +15,7 @@ internal class SetTxHistoryCountErrorTransformer( private val userWallet: UserWallet, private val error: TxHistoryStateError, private val pendingTransactions: Set, - private val clickIntents: WalletClickIntentsV2, + private val clickIntents: WalletClickIntents, ) : WalletStateTransformer(userWallet.walletId) { private val txHistoryItemConverter by lazy { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetTxHistoryCountTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTxHistoryCountTransformer.kt similarity index 91% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetTxHistoryCountTransformer.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTxHistoryCountTransformer.kt index 74fa8ea8a3..f61b4961fe 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetTxHistoryCountTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTxHistoryCountTransformer.kt @@ -1,11 +1,11 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers +package com.tangem.feature.wallet.presentation.wallet.state.transformers import androidx.paging.PagingData import com.tangem.core.ui.components.transactions.state.TransactionState import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update import timber.log.Timber @@ -13,7 +13,7 @@ import timber.log.Timber internal class SetTxHistoryCountTransformer( userWalletId: UserWalletId, private val transactionsCount: Int, - private val clickIntents: WalletClickIntentsV2, + private val clickIntents: WalletClickIntents, ) : WalletStateTransformer(userWalletId) { override fun transform(prevState: WalletState): WalletState { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetTxHistoryItemsErrorTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTxHistoryItemsErrorTransformer.kt similarity index 86% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetTxHistoryItemsErrorTransformer.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTxHistoryItemsErrorTransformer.kt index 204b038dd8..5eda4b196e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetTxHistoryItemsErrorTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTxHistoryItemsErrorTransformer.kt @@ -1,16 +1,16 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers +package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.domain.txhistory.models.TxHistoryListError import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import timber.log.Timber internal class SetTxHistoryItemsErrorTransformer( userWalletId: UserWalletId, private val error: TxHistoryListError, - private val clickIntents: WalletClickIntentsV2, + private val clickIntents: WalletClickIntents, ) : WalletStateTransformer(userWalletId) { override fun transform(prevState: WalletState): WalletState { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetTxHistoryItemsTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTxHistoryItemsTransformer.kt similarity index 82% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetTxHistoryItemsTransformer.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTxHistoryItemsTransformer.kt index f1001f3495..09c90b2841 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetTxHistoryItemsTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetTxHistoryItemsTransformer.kt @@ -1,19 +1,19 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers +package com.tangem.feature.wallet.presentation.wallet.state.transformers import androidx.paging.PagingData import com.tangem.core.ui.components.transactions.state.TransactionState import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter.TxHistoryItemFlowConverter -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.TxHistoryItemFlowConverter +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import kotlinx.coroutines.flow.Flow import timber.log.Timber internal class SetTxHistoryItemsTransformer( userWallet: UserWallet, private val flow: Flow>, - private val clickIntents: WalletClickIntentsV2, + private val clickIntents: WalletClickIntents, ) : WalletStateTransformer(userWallet.walletId) { override fun transform(prevState: WalletState): WalletState { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetWarningsTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetWarningsTransformer.kt similarity index 79% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetWarningsTransformer.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetWarningsTransformer.kt index 1fcac2e3d4..7d7246a01e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetWarningsTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/SetWarningsTransformer.kt @@ -1,8 +1,8 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers +package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState import kotlinx.collections.immutable.ImmutableList import timber.log.Timber diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/UnlockWalletTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/UnlockWalletTransformer.kt similarity index 79% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/UnlockWalletTransformer.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/UnlockWalletTransformer.kt index c905844bb0..f22dedbc22 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/UnlockWalletTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/UnlockWalletTransformer.kt @@ -1,18 +1,18 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers +package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTopBarConfig -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletScreenState -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState -import com.tangem.feature.wallet.presentation.wallet.state2.utils.WalletLoadingStateFactory -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTopBarConfig +import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletLoadingStateFactory +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import kotlinx.collections.immutable.toImmutableList import timber.log.Timber internal class UnlockWalletTransformer( private val unlockedWallets: List, - private val clickIntents: WalletClickIntentsV2, + private val clickIntents: WalletClickIntents, ) : WalletScreenStateTransformer { private val walletLoadingStateFactory by lazy { WalletLoadingStateFactory(clickIntents = clickIntents) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/UpdateBalanceHidingModeTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/UpdateBalanceHidingModeTransformer.kt similarity index 63% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/UpdateBalanceHidingModeTransformer.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/UpdateBalanceHidingModeTransformer.kt index 73be934720..e866e02864 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/UpdateBalanceHidingModeTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/UpdateBalanceHidingModeTransformer.kt @@ -1,6 +1,6 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers +package com.tangem.feature.wallet.presentation.wallet.state.transformers -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletScreenState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState internal class UpdateBalanceHidingModeTransformer( private val isHidingMode: Boolean, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/UpdateWalletCardsCountTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/UpdateWalletCardsCountTransformer.kt similarity index 88% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/UpdateWalletCardsCountTransformer.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/UpdateWalletCardsCountTransformer.kt index f88cebf21d..1994185f73 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/UpdateWalletCardsCountTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/UpdateWalletCardsCountTransformer.kt @@ -1,11 +1,11 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers +package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver import com.tangem.feature.wallet.presentation.wallet.domain.getCardsCount -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState import timber.log.Timber internal class UpdateWalletCardsCountTransformer( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/WalletScreenStateTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/WalletScreenStateTransformer.kt new file mode 100644 index 0000000000..7bfc34bd81 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/WalletScreenStateTransformer.kt @@ -0,0 +1,8 @@ +package com.tangem.feature.wallet.presentation.wallet.state.transformers + +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState + +internal interface WalletScreenStateTransformer { + + fun transform(prevState: WalletScreenState): WalletScreenState +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/WalletStateTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/WalletStateTransformer.kt similarity index 81% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/WalletStateTransformer.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/WalletStateTransformer.kt index a96c554e7e..f02bd16531 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/WalletStateTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/WalletStateTransformer.kt @@ -1,8 +1,8 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers +package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletScreenState -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState import kotlinx.collections.immutable.toImmutableList import timber.log.Timber diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/BalancesAndLimitsBottomSheetConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/BalancesAndLimitsBottomSheetConverter.kt similarity index 82% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/BalancesAndLimitsBottomSheetConverter.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/BalancesAndLimitsBottomSheetConverter.kt index 2a6139aa60..14f9493984 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/BalancesAndLimitsBottomSheetConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/BalancesAndLimitsBottomSheetConverter.kt @@ -1,12 +1,12 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter +package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.core.ui.utils.DateTimeFormatters import com.tangem.domain.visa.model.VisaCurrency -import com.tangem.feature.wallet.presentation.wallet.state.WalletAlertState -import com.tangem.feature.wallet.presentation.wallet.state.WalletEvent -import com.tangem.feature.wallet.presentation.wallet.state2.model.BalancesAndLimitsBottomSheetConfig -import com.tangem.feature.wallet.presentation.wallet.state2.utils.WalletEventSender +import com.tangem.feature.wallet.presentation.wallet.state.model.BalancesAndLimitsBottomSheetConfig +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAlertState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent +import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender import com.tangem.utils.converter.Converter import java.math.BigDecimal diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/MultiWalletCardStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/MultiWalletCardStateConverter.kt similarity index 93% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/MultiWalletCardStateConverter.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/MultiWalletCardStateConverter.kt index 8c3b84bf05..1e1d944584 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/MultiWalletCardStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/MultiWalletCardStateConverter.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter +package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.domain.appcurrency.model.AppCurrency @@ -6,7 +6,7 @@ import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory import com.tangem.feature.wallet.presentation.wallet.domain.getCardsCount -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState import com.tangem.utils.converter.Converter internal class MultiWalletCardStateConverter( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/MultiWalletCurrencyActionsConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/MultiWalletCurrencyActionsConverter.kt similarity index 96% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/MultiWalletCurrencyActionsConverter.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/MultiWalletCurrencyActionsConverter.kt index e43e0fb2a6..16876a9396 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/MultiWalletCurrencyActionsConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/MultiWalletCurrencyActionsConverter.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter +package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference @@ -7,7 +7,7 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TokenActionsState import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.impl.R -import com.tangem.feature.wallet.presentation.wallet.state.TokenActionButtonConfig +import com.tangem.feature.wallet.presentation.wallet.state.model.TokenActionButtonConfig import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletCurrencyActionsClickIntentsImplementor import com.tangem.utils.converter.Converter import com.tangem.utils.isNullOrZero diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/SingleWalletCardStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/SingleWalletCardStateConverter.kt similarity index 94% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/SingleWalletCardStateConverter.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/SingleWalletCardStateConverter.kt index e54cf09dfa..028d2ea966 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/SingleWalletCardStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/SingleWalletCardStateConverter.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter +package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.domain.appcurrency.model.AppCurrency @@ -6,7 +6,7 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory import com.tangem.feature.wallet.presentation.wallet.domain.getCardsCount -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState import com.tangem.utils.converter.Converter internal class SingleWalletCardStateConverter( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/SingleWalletMarketPriceConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/SingleWalletMarketPriceConverter.kt similarity index 97% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/SingleWalletMarketPriceConverter.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/SingleWalletMarketPriceConverter.kt index 1f1237f976..60fcaa1c3c 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/SingleWalletMarketPriceConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/SingleWalletMarketPriceConverter.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter +package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.core.ui.components.marketprice.PriceChangeState diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/TokenItemStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenItemStateConverter.kt similarity index 96% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/TokenItemStateConverter.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenItemStateConverter.kt index e3b9517efb..66311796de 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/TokenItemStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenItemStateConverter.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter +package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter import com.tangem.core.ui.components.currency.tokenicon.converter.CryptoCurrencyToIconStateConverter import com.tangem.core.ui.components.marketprice.PriceChangeType @@ -6,14 +6,14 @@ 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.wallet.presentation.common.state.TokenItemState -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import com.tangem.utils.Provider import com.tangem.utils.converter.Converter import java.math.BigDecimal internal class TokenItemStateConverter( private val appCurrencyProvider: Provider, - private val clickIntents: WalletClickIntentsV2, + private val clickIntents: WalletClickIntents, ) : Converter { private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/TokenListStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt similarity index 87% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/TokenListStateConverter.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt index 77b6288d67..02c8d42705 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/TokenListStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter +package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter import com.tangem.core.ui.extensions.stringReference import com.tangem.domain.appcurrency.model.AppCurrency @@ -7,21 +7,21 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.NetworkGroup import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletTokensListState -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletTokensListState.TokensListItemState -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState.TokensListItemState +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import com.tangem.utils.Provider import com.tangem.utils.converter.Converter import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.mutate import kotlinx.collections.immutable.persistentListOf -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletTokensListState.OrganizeTokensButtonConfig as WalletOrganizeTokensButtonConfig +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState.OrganizeTokensButtonConfig as WalletOrganizeTokensButtonConfig internal class TokenListStateConverter( private val tokenList: TokenList, private val selectedWallet: UserWallet, private val appCurrency: AppCurrency, - private val clickIntents: WalletClickIntentsV2, + private val clickIntents: WalletClickIntents, ) : Converter { private val tokenStatusConverter = TokenItemStateConverter( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/TxHistoryItemFlowConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TxHistoryItemFlowConverter.kt similarity index 95% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/TxHistoryItemFlowConverter.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TxHistoryItemFlowConverter.kt index 6372e43581..62c49d5586 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/TxHistoryItemFlowConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TxHistoryItemFlowConverter.kt @@ -1,11 +1,11 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter +package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter import androidx.paging.* import com.tangem.core.ui.components.transactions.state.TransactionState import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.components.transactions.state.TxHistoryState.TxHistoryItemState import com.tangem.core.ui.utils.toDateFormat -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import com.tangem.utils.converter.Converter import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -16,7 +16,7 @@ private val scope = CoroutineScope(Dispatchers.IO) internal class TxHistoryItemFlowConverter( private val currentState: TxHistoryState, - private val clickIntents: WalletClickIntentsV2, + private val clickIntents: WalletClickIntents, ) : Converter>, TxHistoryState?> { override fun convert(value: Flow>): TxHistoryState { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/TxHistoryItemStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TxHistoryItemStateConverter.kt similarity index 96% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/TxHistoryItemStateConverter.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TxHistoryItemStateConverter.kt index 888144c3eb..d3fd7a6127 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/TxHistoryItemStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TxHistoryItemStateConverter.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter +package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter import com.tangem.core.ui.components.transactions.state.TransactionState import com.tangem.core.ui.extensions.TextReference @@ -8,7 +8,7 @@ import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.utils.toTimeFormat import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.feature.wallet.impl.R -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import com.tangem.utils.converter.Converter import com.tangem.utils.toBriefAddressFormat import com.tangem.utils.toFormattedCurrencyString @@ -16,7 +16,7 @@ import com.tangem.utils.toFormattedCurrencyString internal class TxHistoryItemStateConverter( private val symbol: String, private val decimals: Int, - private val clickIntents: WalletClickIntentsV2, + private val clickIntents: WalletClickIntents, ) : Converter { override fun convert(value: TxHistoryItem): TransactionState { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/VisaTxHistoryItemStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/VisaTxHistoryItemStateConverter.kt similarity index 96% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/VisaTxHistoryItemStateConverter.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/VisaTxHistoryItemStateConverter.kt index c1f801a339..bddc7ec7aa 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/VisaTxHistoryItemStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/VisaTxHistoryItemStateConverter.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter +package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter import com.tangem.core.ui.components.transactions.state.TransactionState import com.tangem.core.ui.extensions.stringReference diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/utils/UserWalletConverterExt.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/UserWalletConverterExt.kt similarity index 83% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/utils/UserWalletConverterExt.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/UserWalletConverterExt.kt index 9d03b341a7..c7a8830b71 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/utils/UserWalletConverterExt.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/UserWalletConverterExt.kt @@ -1,8 +1,8 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.utils +package com.tangem.feature.wallet.presentation.wallet.state.utils import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState internal inline fun UserWallet.createStateByWalletType( multiCurrencyCreator: () -> WalletState.MultiCurrency, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/utils/WalletEventSender.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletEventSender.kt similarity index 72% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/utils/WalletEventSender.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletEventSender.kt index 7b225aa521..34859d54d9 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/utils/WalletEventSender.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletEventSender.kt @@ -1,9 +1,9 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.utils +package com.tangem.feature.wallet.presentation.wallet.state.utils import com.tangem.core.ui.event.consumedEvent -import com.tangem.feature.wallet.presentation.wallet.state.WalletEvent -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SendEventTransformer +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent +import com.tangem.feature.wallet.presentation.wallet.state.transformers.SendEventTransformer import javax.inject.Inject /** diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/utils/WalletLoadingStateFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletLoadingStateFactory.kt similarity index 90% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/utils/WalletLoadingStateFactory.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletLoadingStateFactory.kt index fdf4fb6287..9315797f9a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/utils/WalletLoadingStateFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletLoadingStateFactory.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.utils +package com.tangem.feature.wallet.presentation.wallet.state.utils import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.core.ui.components.transactions.state.TxHistoryState @@ -6,11 +6,8 @@ import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletPullToRefreshConfig -import com.tangem.feature.wallet.presentation.wallet.state2.model.* -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.model.* +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.MutableStateFlow @@ -20,7 +17,7 @@ import kotlinx.coroutines.flow.MutableStateFlow * * @property clickIntents click intents */ -internal class WalletLoadingStateFactory(private val clickIntents: WalletClickIntentsV2) { +internal class WalletLoadingStateFactory(private val clickIntents: WalletClickIntents) { fun create(userWallet: UserWallet): WalletState { return userWallet.createStateByWalletType( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/WalletScreenStateTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/WalletScreenStateTransformer.kt deleted file mode 100644 index 9cae2fd8e8..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/WalletScreenStateTransformer.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers - -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletScreenState - -internal interface WalletScreenStateTransformer { - - fun transform(prevState: WalletScreenState): WalletScreenState -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt index ddc8f7fb6a..a1f88b718d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt @@ -9,10 +9,10 @@ import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetTokenListErrorTransformer -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetTokenListTransformer -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTokenListErrorTransformer +import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTokenListTransformer +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -25,7 +25,7 @@ internal typealias MaybeTokenListFlow = Flow> internal abstract class BasicTokenListSubscriber( private val userWallet: UserWallet, private val stateHolder: WalletStateController, - private val clickIntents: WalletClickIntentsV2, + private val clickIntents: WalletClickIntents, private val tokenListAnalyticsSender: TokenListAnalyticsSender, private val walletWithFundsChecker: WalletWithFundsChecker, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletTokenListSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletTokenListSubscriber.kt index 46de409a7d..cb5fdbc166 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletTokenListSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletTokenListSubscriber.kt @@ -5,15 +5,15 @@ import com.tangem.domain.tokens.GetTokenListUseCase import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents @Suppress("LongParameterList") internal class MultiWalletTokenListSubscriber( private val userWallet: UserWallet, private val getTokenListUseCase: GetTokenListUseCase, stateHolder: WalletStateController, - clickIntents: WalletClickIntentsV2, + clickIntents: WalletClickIntents, tokenListAnalyticsSender: TokenListAnalyticsSender, walletWithFundsChecker: WalletWithFundsChecker, getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletWarningsSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletWarningsSubscriber.kt index 152aa30cae..d9029393c1 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletWarningsSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletWarningsSubscriber.kt @@ -3,10 +3,10 @@ package com.tangem.feature.wallet.presentation.wallet.subscribers import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.domain.GetMultiWalletWarningsFactory -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetWarningsTransformer -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification +import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetWarningsTransformer +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow @@ -17,7 +17,7 @@ import kotlinx.coroutines.flow.onEach internal class MultiWalletWarningsSubscriber( private val userWalletId: UserWalletId, private val stateHolder: WalletStateController, - private val clickIntents: WalletClickIntentsV2, + private val clickIntents: WalletClickIntents, private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory, private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender, ) : WalletSubscriber() { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/PrimaryCurrencySubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/PrimaryCurrencySubscriber.kt index de0c3f2634..41fef9b424 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/PrimaryCurrencySubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/PrimaryCurrencySubscriber.kt @@ -13,8 +13,8 @@ import com.tangem.domain.tokens.error.CurrencyStatusError import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetPrimaryCurrencyTransformer +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetPrimaryCurrencyTransformer import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.* import timber.log.Timber diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletButtonsSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletButtonsSubscriber.kt index 21664ba8bd..4751fed097 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletButtonsSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletButtonsSubscriber.kt @@ -5,16 +5,16 @@ import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase import com.tangem.domain.tokens.model.TokenActionsState import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.domain.collectLatest -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetCryptoCurrencyActionsTransformer -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetCryptoCurrencyActionsTransformer +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.* internal class SingleWalletButtonsSubscriber( private val userWallet: UserWallet, private val stateHolder: WalletStateController, - private val clickIntents: WalletClickIntentsV2, + private val clickIntents: WalletClickIntents, private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase, private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCase, ) : WalletSubscriber() { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletNotificationsSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletNotificationsSubscriber.kt index edb19f0254..f266b681fd 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletNotificationsSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletNotificationsSubscriber.kt @@ -3,10 +3,10 @@ package com.tangem.feature.wallet.presentation.wallet.subscribers import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.domain.GetSingleWalletWarningsFactory -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetWarningsTransformer -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification +import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetWarningsTransformer +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow @@ -22,7 +22,7 @@ internal class SingleWalletNotificationsSubscriber( private val stateHolder: WalletStateController, private val getSingleWalletWarningsFactory: GetSingleWalletWarningsFactory, private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender, - private val clickIntents: WalletClickIntentsV2, + private val clickIntents: WalletClickIntents, ) : WalletSubscriber() { override fun create(coroutineScope: CoroutineScope): Flow> { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletWithTokenListSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletWithTokenListSubscriber.kt index 3fe184f524..54c8ca07de 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletWithTokenListSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/SingleWalletWithTokenListSubscriber.kt @@ -5,15 +5,15 @@ import com.tangem.domain.tokens.GetCardTokensListUseCase import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents @Suppress("LongParameterList") internal class SingleWalletWithTokenListSubscriber( private val userWallet: UserWallet, private val getCardTokensListUseCase: GetCardTokensListUseCase, stateHolder: WalletStateController, - clickIntents: WalletClickIntentsV2, + clickIntents: WalletClickIntents, tokenListAnalyticsSender: TokenListAnalyticsSender, walletWithFundsChecker: WalletWithFundsChecker, getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/TxHistorySubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/TxHistorySubscriber.kt index c30daff4df..7561ea68c5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/TxHistorySubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/TxHistorySubscriber.kt @@ -14,13 +14,13 @@ import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.domain.collectLatest -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetTxHistoryCountErrorTransformer -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetTxHistoryCountTransformer -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetTxHistoryItemsErrorTransformer -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetTxHistoryItemsTransformer -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter.TxHistoryItemStateConverter -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHistoryCountErrorTransformer +import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHistoryCountTransformer +import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHistoryItemsErrorTransformer +import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHistoryItemsTransformer +import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.TxHistoryItemStateConverter +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow @@ -34,7 +34,7 @@ internal class TxHistorySubscriber( private val userWallet: UserWallet, private val isRefresh: Boolean, private val stateHolder: WalletStateController, - private val clickIntents: WalletClickIntentsV2, + private val clickIntents: WalletClickIntents, private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase, private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase, private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/VisaWalletSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/VisaWalletSubscriber.kt index ea540ec981..552dbba1a5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/VisaWalletSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/VisaWalletSubscriber.kt @@ -11,13 +11,13 @@ import com.tangem.domain.visa.GetVisaTxHistoryUseCase import com.tangem.domain.visa.model.VisaCurrency import com.tangem.domain.visa.model.VisaTxHistoryItem import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetBalancesAndLimitsTransformer -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetTxHistoryCountTransformer -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetTxHistoryItemsErrorTransformer -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetTxHistoryItemsTransformer -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter.VisaTxHistoryItemStateConverter -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetBalancesAndLimitsTransformer +import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHistoryCountTransformer +import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHistoryItemsErrorTransformer +import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTxHistoryItemsTransformer +import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.VisaTxHistoryItemStateConverter +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.catch @@ -31,7 +31,7 @@ internal class VisaWalletSubscriber( private val isRefresh: Boolean, private val getVisaCurrencyUseCase: GetVisaCurrencyUseCase, private val getVisaTxHistoryUseCase: GetVisaTxHistoryUseCase, - private val clickIntents: WalletClickIntentsV2, + private val clickIntents: WalletClickIntents, ) : WalletSubscriber() { override fun create(coroutineScope: CoroutineScope): Flow<*> { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletAlert.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletAlert.kt index 1edcb46df5..ab8a96b36b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletAlert.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletAlert.kt @@ -6,7 +6,7 @@ import com.tangem.core.ui.components.BasicDialog import com.tangem.core.ui.components.DialogButton import com.tangem.core.ui.extensions.resolveReference import com.tangem.feature.wallet.impl.R -import com.tangem.feature.wallet.presentation.wallet.state.WalletAlertState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAlertState @Composable internal fun WalletAlert(state: WalletAlertState, onDismiss: () -> Unit) { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletEventEffect.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletEventEffect.kt index dbb5f28cfa..cce277150d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletEventEffect.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletEventEffect.kt @@ -1,34 +1,33 @@ package com.tangem.feature.wallet.presentation.wallet.ui -import android.app.Activity -import android.content.Context -import android.content.ContextWrapper import android.widget.Toast import androidx.compose.foundation.lazy.LazyListState import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.AnnotatedString -import com.google.android.play.core.review.ReviewInfo -import com.google.android.play.core.review.ReviewManager -import com.google.android.play.core.review.ReviewManagerFactory -import com.google.android.play.core.tasks.Task import com.tangem.core.ui.event.EventEffect import com.tangem.core.ui.event.StateEvent import com.tangem.core.ui.extensions.resolveReference -import com.tangem.feature.wallet.presentation.wallet.state.WalletAlertState -import com.tangem.feature.wallet.presentation.wallet.state.WalletEvent -import timber.log.Timber +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAlertState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent +import com.tangem.feature.wallet.presentation.wallet.ui.utils.ReviewManagerRequester +import com.tangem.feature.wallet.presentation.wallet.ui.utils.animateScrollByIndex +import com.tangem.feature.wallet.presentation.wallet.ui.utils.demonstrateScrolling +@Suppress("LongParameterList") @Composable internal fun WalletEventEffect( walletsListState: LazyListState, snackbarHostState: SnackbarHostState, event: StateEvent, + selectedWalletIndex: Int, onAutoScrollSet: () -> Unit, onAlertConfigSet: (WalletAlertState) -> Unit, ) { + val coroutineScope = rememberCoroutineScope() val context = LocalContext.current val resources = LocalContext.current.resources val clipboardManager = LocalClipboardManager.current @@ -38,7 +37,7 @@ internal fun WalletEventEffect( when (value) { is WalletEvent.ChangeWallet -> { onAutoScrollSet() - walletsListState.animateScrollToItem(index = value.index) + walletsListState.animateScrollByIndex(prevIndex = selectedWalletIndex, newIndex = value.index) } is WalletEvent.ShowError -> { snackbarHostState.showSnackbar(message = value.text.resolveReference(resources)) @@ -52,49 +51,12 @@ internal fun WalletEventEffect( } is WalletEvent.ShowAlert -> onAlertConfigSet(value.state) is WalletEvent.RateApp -> { - val reviewManager = ReviewManagerFactory.create(context) - val requestTask = reviewManager.requestReviewFlow() - - requestTask - .addOnCompleteListener { - handleOnCompleteRequestTask( - reviewManager = reviewManager, - activity = context.findActivity(), - task = it, - onDismissClick = value.onDismissClick, - ) - } - .addOnFailureListener(Timber::e) + ReviewManagerRequester.request(context = context, onDismissClick = value.onDismissClick) + } + is WalletEvent.DemonstrateWalletsScrollPreview -> { + walletsListState.demonstrateScrolling(coroutineScope = coroutineScope, direction = value.direction) } - is WalletEvent.DemonstrateWalletsScrollPreview -> Unit } }, ) -} - -private fun Context.findActivity(): Activity { - var context = this - while (context is ContextWrapper) { - if (context is Activity) return context - context = context.baseContext - } - error("Permissions should be called in the context of an Activity") -} - -private fun handleOnCompleteRequestTask( - reviewManager: ReviewManager, - activity: Activity, - task: Task, - onDismissClick: () -> Unit, -) { - if (task.isSuccessful) { - val reviewFlow = reviewManager.launchReviewFlow(activity, task.result) - reviewFlow - .addOnCompleteListener { resultReviewTask -> - if (!resultReviewTask.isSuccessful) onDismissClick() - } - .addOnFailureListener(Timber::e) - } else { - Timber.e(task.exception) - } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletEventEffectV2.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletEventEffectV2.kt deleted file mode 100644 index 73d54f4ddc..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletEventEffectV2.kt +++ /dev/null @@ -1,62 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.ui - -import android.widget.Toast -import androidx.compose.foundation.lazy.LazyListState -import androidx.compose.material3.SnackbarHostState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.platform.LocalClipboardManager -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.AnnotatedString -import com.tangem.core.ui.event.EventEffect -import com.tangem.core.ui.event.StateEvent -import com.tangem.core.ui.extensions.resolveReference -import com.tangem.feature.wallet.presentation.wallet.state.WalletAlertState -import com.tangem.feature.wallet.presentation.wallet.state.WalletEvent -import com.tangem.feature.wallet.presentation.wallet.ui.utils.ReviewManagerRequester -import com.tangem.feature.wallet.presentation.wallet.ui.utils.animateScrollByIndex -import com.tangem.feature.wallet.presentation.wallet.ui.utils.demonstrateScrolling - -@Suppress("LongParameterList") -@Composable -internal fun WalletEventEffectV2( - walletsListState: LazyListState, - snackbarHostState: SnackbarHostState, - event: StateEvent, - selectedWalletIndex: Int, - onAutoScrollSet: () -> Unit, - onAlertConfigSet: (WalletAlertState) -> Unit, -) { - val coroutineScope = rememberCoroutineScope() - val context = LocalContext.current - val resources = LocalContext.current.resources - val clipboardManager = LocalClipboardManager.current - EventEffect( - event = event, - onTrigger = { value -> - when (value) { - is WalletEvent.ChangeWallet -> { - onAutoScrollSet() - walletsListState.animateScrollByIndex(prevIndex = selectedWalletIndex, newIndex = value.index) - } - is WalletEvent.ShowError -> { - snackbarHostState.showSnackbar(message = value.text.resolveReference(resources)) - } - is WalletEvent.ShowToast -> { - Toast.makeText(context, value.text.resolveReference(resources), Toast.LENGTH_SHORT).show() - } - is WalletEvent.CopyAddress -> { - clipboardManager.setText(AnnotatedString(value.address)) - Toast.makeText(context, value.toast.resolveReference(resources), Toast.LENGTH_SHORT).show() - } - is WalletEvent.ShowAlert -> onAlertConfigSet(value.state) - is WalletEvent.RateApp -> { - ReviewManagerRequester.request(context = context, onDismissClick = value.onDismissClick) - } - is WalletEvent.DemonstrateWalletsScrollPreview -> { - walletsListState.demonstrateScrolling(coroutineScope = coroutineScope, direction = value.direction) - } - } - }, - ) -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index cd3312809b..3dcd34d73e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -17,9 +17,6 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider import androidx.paging.compose.collectAsLazyPagingItems import com.tangem.core.ui.components.PrimaryButton import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheet @@ -29,249 +26,229 @@ import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBotto import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.impl.R -import com.tangem.feature.wallet.presentation.common.WalletPreviewData -import com.tangem.feature.wallet.presentation.wallet.state.* -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletBottomSheetConfig -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletPullToRefreshConfig -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState.OrganizeTokensButtonState +import com.tangem.feature.wallet.presentation.wallet.state.model.* +import com.tangem.feature.wallet.presentation.wallet.state.model.holder.TxHistoryStateHolder import com.tangem.feature.wallet.presentation.wallet.ui.components.TokenActionsBottomSheet import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletsList import com.tangem.feature.wallet.presentation.wallet.ui.components.common.* import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.organizeTokensButton import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.controlButtons import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.marketPriceBlock +import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.BalancesAndLimitsBottomSheet +import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.balancesAndLimitsBlock +import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.depositButton import com.tangem.feature.wallet.presentation.wallet.ui.utils.changeWalletAnimator +import kotlinx.collections.immutable.toImmutableList -/** - * Wallet screen - * - * @param state screen state - * -[REDACTED_AUTHOR] - */ @Composable -internal fun WalletScreen(state: WalletState) { +internal fun WalletScreen(state: WalletScreenState) { BackHandler(onBack = state.onBackClick) - when (state) { - is WalletState.ContentState -> { - val walletsListState = rememberLazyListState( - initialFirstVisibleItemIndex = state.walletsListConfig.selectedWalletIndex, - ) - val snackbarHostState = remember { SnackbarHostState() } - val isAutoScroll = remember { mutableStateOf(value = false) } + // It means that screen is still initializing + if (state.selectedWalletIndex == NOT_INITIALIZED_WALLET_INDEX) return - WalletContent( - state = state, - walletsListState = walletsListState, - snackbarHostState = snackbarHostState, - isAutoScroll = isAutoScroll, - onAutoScrollReset = { isAutoScroll.value = false }, - ) + val walletsListState = rememberLazyListState(initialFirstVisibleItemIndex = state.selectedWalletIndex) + val snackbarHostState = remember(::SnackbarHostState) + val isAutoScroll = remember { mutableStateOf(value = false) } - var alertConfig by remember { mutableStateOf(value = null) } + WalletContent( + state = state, + walletsListState = walletsListState, + snackbarHostState = snackbarHostState, + isAutoScroll = isAutoScroll, + onAutoScrollReset = { isAutoScroll.value = false }, + ) - WalletEventEffect( - walletsListState = walletsListState, - snackbarHostState = snackbarHostState, - event = state.event, - onAutoScrollSet = { isAutoScroll.value = true }, - onAlertConfigSet = { alertConfig = it }, - ) + var alertConfig by remember { mutableStateOf(value = null) } - alertConfig?.let { - WalletAlert(state = it, onDismiss = { alertConfig = null }) - } - } - is WalletState.Initial -> Unit + alertConfig?.let { + WalletAlert(state = it, onDismiss = { alertConfig = null }) } + + WalletEventEffect( + event = state.event, + selectedWalletIndex = state.selectedWalletIndex, + walletsListState = walletsListState, + snackbarHostState = snackbarHostState, + onAlertConfigSet = { alertConfig = it }, + onAutoScrollSet = { isAutoScroll.value = true }, + ) } +@Suppress("LongMethod") @Composable private fun WalletContent( - state: WalletState.ContentState, + state: WalletScreenState, walletsListState: LazyListState, snackbarHostState: SnackbarHostState, isAutoScroll: State, onAutoScrollReset: () -> Unit, ) { - BaseScaffold(state = state, snackbarHostState) { scaffoldPaddings -> + var selectedWalletIndex by remember { mutableIntStateOf(state.selectedWalletIndex) } + val selectedWallet = state.wallets[selectedWalletIndex] + + BaseScaffold(state = state, selectedWallet = selectedWallet, snackbarHostState = snackbarHostState) { val movableItemModifier = Modifier.changeWalletAnimator(walletsListState) - UpdatableContainer( - pullToRefreshConfig = state.pullToRefreshConfig, - modifier = Modifier.padding(paddingValues = scaffoldPaddings), + val lazyTxHistoryItems = (selectedWallet as? TxHistoryStateHolder)?.let { walletState -> + (walletState.txHistoryState as? TxHistoryState.Content)?.contentItems?.collectAsLazyPagingItems() + } + + val txHistoryItems by remember(selectedWallet.walletCardState.id, lazyTxHistoryItems?.itemCount) { + mutableStateOf(value = lazyTxHistoryItems) + } + + val betweenItemsPadding = TangemTheme.dimens.spacing14 + val horizontalPadding = TangemTheme.dimens.spacing16 + val itemModifier = movableItemModifier + .padding(top = betweenItemsPadding) + .padding(horizontal = horizontalPadding) + + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = PaddingValues( + top = TangemTheme.dimens.spacing8, + bottom = TangemTheme.dimens.spacing92, + ), + horizontalAlignment = Alignment.CenterHorizontally, ) { - val txHistoryItems = if (state is WalletSingleCurrencyState && - state.txHistoryState is TxHistoryState.Content + item( + key = state.wallets.map { it.walletCardState.id }, + contentType = state.wallets.map { it.walletCardState.id }, ) { - (state.txHistoryState as? TxHistoryState.Content)?.contentItems?.collectAsLazyPagingItems() - } else { - null + WalletsList( + lazyListState = walletsListState, + wallets = state.wallets.map(WalletState::walletCardState).toImmutableList(), + isBalanceHidden = state.isHidingMode, + ) } - val betweenItemsPadding = TangemTheme.dimens.spacing14 - val horizontalPadding = TangemTheme.dimens.spacing16 - val itemModifier = movableItemModifier - .padding(top = betweenItemsPadding) - .padding(horizontal = horizontalPadding) + (selectedWallet as? WalletState.SingleCurrency)?.let { + controlButtons( + configs = it.buttons, + selectedWalletIndex = selectedWalletIndex, + modifier = movableItemModifier.padding(top = betweenItemsPadding), + ) + } - LazyColumn( - modifier = Modifier.fillMaxSize(), - contentPadding = PaddingValues( - top = TangemTheme.dimens.spacing8, - bottom = TangemTheme.dimens.spacing92, - ), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - item { - WalletsList( - config = state.walletsListConfig, - lazyListState = walletsListState, - isBalanceHidden = state.isBalanceHidden, - ) + notifications(configs = selectedWallet.warnings, modifier = itemModifier) + + (selectedWallet as? WalletState.SingleCurrency)?.let { walletState -> + walletState.marketPriceBlockState?.let { marketPriceBlockState -> + marketPriceBlock(state = marketPriceBlockState, modifier = itemModifier) } + } - if (state is WalletSingleCurrencyState) { - controlButtons( - configs = state.buttons, - selectedWalletIndex = state.walletsListConfig.selectedWalletIndex, - modifier = movableItemModifier.padding(top = betweenItemsPadding), - ) - } - - notifications(configs = state.notifications, modifier = itemModifier) - - if (state is WalletSingleCurrencyState.Content) { - marketPriceBlock(state = state.marketPriceBlockState, modifier = itemModifier) - } - - contentItems( - state = state, - txHistoryItems = txHistoryItems, - isBalanceHidden = state.isBalanceHidden, - modifier = movableItemModifier, + (selectedWallet as? WalletState.Visa.Content)?.let { + depositButton( + modifier = itemModifier.fillMaxWidth(), + state = it.depositButtonState, ) - organizeTokens(state = state, itemModifier = itemModifier) + balancesAndLimitsBlock( + modifier = itemModifier, + state = it.balancesAndLimitBlockState, + ) + } + + contentItems( + state = selectedWallet, + txHistoryItems = txHistoryItems, + isBalanceHidden = state.isHidingMode, + modifier = movableItemModifier, + ) + + organizeTokens(state = selectedWallet, itemModifier = itemModifier) + } + + val bottomSheetConfig = selectedWallet.bottomSheetConfig + if (bottomSheetConfig != null) { + when (bottomSheetConfig.content) { + is WalletBottomSheetConfig -> WalletBottomSheet(config = bottomSheetConfig) + is TokenReceiveBottomSheetConfig -> TokenReceiveBottomSheet(config = bottomSheetConfig) + is ActionsBottomSheetConfig -> TokenActionsBottomSheet(config = bottomSheetConfig) + is ChooseAddressBottomSheetConfig -> ChooseAddressBottomSheet(config = bottomSheetConfig) + is BalancesAndLimitsBottomSheetConfig -> BalancesAndLimitsBottomSheet(config = bottomSheetConfig) } } - } - WalletBottomSheets(state = state) - - WalletsListEffects( - lazyListState = walletsListState, - walletsListConfig = state.walletsListConfig, - isAutoScroll = isAutoScroll, - onAutoScrollReset = onAutoScrollReset, - ) -} - -internal fun LazyListScope.organizeTokens(state: WalletState.ContentState, itemModifier: Modifier) { - if (state is WalletMultiCurrencyState) { - val contentTokenListState = state.tokensListState as? WalletTokensListState.ContentState - val organizeTokensButton = contentTokenListState?.organizeTokensButton - - if (organizeTokensButton is OrganizeTokensButtonState.Visible) { - organizeTokensButton( - modifier = itemModifier, - isEnabled = organizeTokensButton.isEnabled, - onClick = organizeTokensButton.onClick, - ) - } + WalletsListEffects( + lazyListState = walletsListState, + selectedWalletIndex = selectedWalletIndex, + onWalletChange = state.onWalletChange, + onSelectedWalletIndexSet = { selectedWalletIndex = it }, + isAutoScroll = isAutoScroll, + onAutoScrollReset = onAutoScrollReset, + ) } } @OptIn(ExperimentalMaterialApi::class) -@Composable -private fun UpdatableContainer( - pullToRefreshConfig: WalletPullToRefreshConfig, - modifier: Modifier = Modifier, - content: @Composable BoxScope.() -> Unit, -) { - val pullRefreshState = rememberPullRefreshState( - refreshing = pullToRefreshConfig.isRefreshing, - onRefresh = pullToRefreshConfig.onRefresh, - ) - - Box(modifier = modifier.pullRefresh(pullRefreshState)) { - content() - - WalletPullToRefreshIndicator( - isRefreshing = pullToRefreshConfig.isRefreshing, - state = pullRefreshState, - modifier = Modifier.align(Alignment.TopCenter), - ) - } -} - @Composable private fun BaseScaffold( - state: WalletState.ContentState, + state: WalletScreenState, + selectedWallet: WalletState, snackbarHostState: SnackbarHostState, - content: @Composable (PaddingValues) -> Unit, + content: @Composable () -> Unit, ) { Scaffold( topBar = { WalletTopBar(config = state.topBarConfig) }, snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, floatingActionButton = { - if (state is WalletMultiCurrencyState.Content && state.isManageTokensAvailable) { - ManageTokensButton(onManageTokensClick = state.onManageTokensClick) + val manageTokensButtonConfig by remember(state.selectedWalletIndex) { + mutableStateOf( + (state.wallets[state.selectedWalletIndex] as? WalletState.MultiCurrency)?.manageTokensButtonConfig, + ) } + + manageTokensButtonConfig?.let { ManageTokensButton(onClick = it.onClick) } }, floatingActionButtonPosition = FabPosition.Center, containerColor = TangemTheme.colors.background.secondary, - content = content, + content = { + val pullRefreshState = rememberPullRefreshState( + refreshing = selectedWallet.pullToRefreshConfig.isRefreshing, + onRefresh = selectedWallet.pullToRefreshConfig.onRefresh, + ) + + Box( + modifier = Modifier + .pullRefresh(pullRefreshState) + .padding(it), + ) { + content() + + WalletPullToRefreshIndicator( + isRefreshing = selectedWallet.pullToRefreshConfig.isRefreshing, + state = pullRefreshState, + modifier = Modifier.align(Alignment.TopCenter), + ) + } + }, ) } @Composable -private fun ManageTokensButton(onManageTokensClick: () -> Unit) { +private fun ManageTokensButton(onClick: () -> Unit) { PrimaryButton( text = stringResource(id = R.string.main_manage_tokens), - onClick = onManageTokensClick, + onClick = onClick, modifier = Modifier .fillMaxWidth() .padding(horizontal = TangemTheme.dimens.spacing16), ) } -@Composable -private fun WalletBottomSheets(state: WalletState) { - val bottomSheetConfig = (state as? WalletState.ContentState)?.bottomSheetConfig - if (bottomSheetConfig != null) { - when (bottomSheetConfig.content) { - is WalletBottomSheetConfig -> WalletBottomSheet(config = bottomSheetConfig) - is TokenReceiveBottomSheetConfig -> TokenReceiveBottomSheet(config = bottomSheetConfig) - is ActionsBottomSheetConfig -> TokenActionsBottomSheet(config = bottomSheetConfig) - is ChooseAddressBottomSheetConfig -> ChooseAddressBottomSheet(config = bottomSheetConfig) +internal fun LazyListScope.organizeTokens(state: WalletState, itemModifier: Modifier) { + (state as? WalletState.MultiCurrency)?.let { + (state.tokensListState as? WalletTokensListState.ContentState)?.let { + it.organizeTokensButtonConfig?.let { config -> + organizeTokensButton( + modifier = itemModifier, + isEnabled = config.isEnabled, + onClick = config.onClick, + ) + } } } -} - -// region Preview -@Preview -@Composable -private fun WalletScreenPreview_Light(@PreviewParameter(WalletScreenParameterProvider::class) state: WalletState) { - TangemTheme { - WalletScreen(state = state) - } -} - -@Preview -@Composable -private fun WalletScreenPreview_Dark(@PreviewParameter(WalletScreenParameterProvider::class) state: WalletState) { - TangemTheme(isDark = true) { - WalletScreen(state = state) - } -} - -private class WalletScreenParameterProvider : CollectionPreviewParameterProvider( - collection = listOf( - WalletPreviewData.multicurrencyWalletScreenState, - WalletPreviewData.singleWalletScreenState, - ), -) -// endregion Preview \ No newline at end of file +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreenV2.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreenV2.kt deleted file mode 100644 index 15e0698e0d..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreenV2.kt +++ /dev/null @@ -1,257 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.ui - -import androidx.activity.compose.BackHandler -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.LazyListScope -import androidx.compose.foundation.lazy.LazyListState -import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.pullrefresh.pullRefresh -import androidx.compose.material.pullrefresh.rememberPullRefreshState -import androidx.compose.material3.FabPosition -import androidx.compose.material3.Scaffold -import androidx.compose.material3.SnackbarHost -import androidx.compose.material3.SnackbarHostState -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.paging.compose.collectAsLazyPagingItems -import com.tangem.core.ui.components.PrimaryButton -import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheet -import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheetConfig -import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheet -import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheetConfig -import com.tangem.core.ui.components.transactions.state.TxHistoryState -import com.tangem.core.ui.res.TangemTheme -import com.tangem.feature.wallet.impl.R -import com.tangem.feature.wallet.presentation.wallet.state.ActionsBottomSheetConfig -import com.tangem.feature.wallet.presentation.wallet.state.WalletAlertState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletBottomSheetConfig -import com.tangem.feature.wallet.presentation.wallet.state2.model.* -import com.tangem.feature.wallet.presentation.wallet.state2.model.holder.TxHistoryStateHolder -import com.tangem.feature.wallet.presentation.wallet.ui.components.TokenActionsBottomSheet -import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletsList -import com.tangem.feature.wallet.presentation.wallet.ui.components.common.* -import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.organizeTokensButton -import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.controlButtons -import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.marketPriceBlock -import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.BalancesAndLimitsBottomSheet -import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.balancesAndLimitsBlock -import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.depositButton -import com.tangem.feature.wallet.presentation.wallet.ui.utils.changeWalletAnimator -import kotlinx.collections.immutable.toImmutableList - -@Composable -internal fun WalletScreenV2(state: WalletScreenState) { - BackHandler(onBack = state.onBackClick) - - // It means that screen is still initializing - if (state.selectedWalletIndex == NOT_INITIALIZED_WALLET_INDEX) return - - val walletsListState = rememberLazyListState(initialFirstVisibleItemIndex = state.selectedWalletIndex) - val snackbarHostState = remember(::SnackbarHostState) - val isAutoScroll = remember { mutableStateOf(value = false) } - - WalletContent( - state = state, - walletsListState = walletsListState, - snackbarHostState = snackbarHostState, - isAutoScroll = isAutoScroll, - onAutoScrollReset = { isAutoScroll.value = false }, - ) - - var alertConfig by remember { mutableStateOf(value = null) } - - alertConfig?.let { - WalletAlert(state = it, onDismiss = { alertConfig = null }) - } - - WalletEventEffectV2( - event = state.event, - selectedWalletIndex = state.selectedWalletIndex, - walletsListState = walletsListState, - snackbarHostState = snackbarHostState, - onAlertConfigSet = { alertConfig = it }, - onAutoScrollSet = { isAutoScroll.value = true }, - ) -} - -@Suppress("LongMethod") -@Composable -private fun WalletContent( - state: WalletScreenState, - walletsListState: LazyListState, - snackbarHostState: SnackbarHostState, - isAutoScroll: State, - onAutoScrollReset: () -> Unit, -) { - var selectedWalletIndex by remember { mutableIntStateOf(state.selectedWalletIndex) } - val selectedWallet = state.wallets[selectedWalletIndex] - - BaseScaffold(state = state, selectedWallet = selectedWallet, snackbarHostState = snackbarHostState) { - val movableItemModifier = Modifier.changeWalletAnimator(walletsListState) - - val lazyTxHistoryItems = (selectedWallet as? TxHistoryStateHolder)?.let { walletState -> - (walletState.txHistoryState as? TxHistoryState.Content)?.contentItems?.collectAsLazyPagingItems() - } - - val txHistoryItems by remember(selectedWallet.walletCardState.id, lazyTxHistoryItems?.itemCount) { - mutableStateOf(value = lazyTxHistoryItems) - } - - val betweenItemsPadding = TangemTheme.dimens.spacing14 - val horizontalPadding = TangemTheme.dimens.spacing16 - val itemModifier = movableItemModifier - .padding(top = betweenItemsPadding) - .padding(horizontal = horizontalPadding) - - LazyColumn( - modifier = Modifier.fillMaxSize(), - contentPadding = PaddingValues( - top = TangemTheme.dimens.spacing8, - bottom = TangemTheme.dimens.spacing92, - ), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - item( - key = state.wallets.map { it.walletCardState.id }, - contentType = state.wallets.map { it.walletCardState.id }, - ) { - WalletsList( - lazyListState = walletsListState, - wallets = state.wallets.map(WalletState::walletCardState).toImmutableList(), - isBalanceHidden = state.isHidingMode, - ) - } - - (selectedWallet as? WalletState.SingleCurrency)?.let { - controlButtons( - configs = it.buttons, - selectedWalletIndex = selectedWalletIndex, - modifier = movableItemModifier.padding(top = betweenItemsPadding), - ) - } - - notifications(configs = selectedWallet.warnings, modifier = itemModifier) - - (selectedWallet as? WalletState.SingleCurrency)?.let { walletState -> - walletState.marketPriceBlockState?.let { marketPriceBlockState -> - marketPriceBlock(state = marketPriceBlockState, modifier = itemModifier) - } - } - - (selectedWallet as? WalletState.Visa.Content)?.let { - depositButton( - modifier = itemModifier.fillMaxWidth(), - state = it.depositButtonState, - ) - - balancesAndLimitsBlock( - modifier = itemModifier, - state = it.balancesAndLimitBlockState, - ) - } - - contentItemsV2( - state = selectedWallet, - txHistoryItems = txHistoryItems, - isBalanceHidden = state.isHidingMode, - modifier = movableItemModifier, - ) - - organizeTokens(state = selectedWallet, itemModifier = itemModifier) - } - - val bottomSheetConfig = selectedWallet.bottomSheetConfig - if (bottomSheetConfig != null) { - when (bottomSheetConfig.content) { - is WalletBottomSheetConfig -> WalletBottomSheet(config = bottomSheetConfig) - is TokenReceiveBottomSheetConfig -> TokenReceiveBottomSheet(config = bottomSheetConfig) - is ActionsBottomSheetConfig -> TokenActionsBottomSheet(config = bottomSheetConfig) - is ChooseAddressBottomSheetConfig -> ChooseAddressBottomSheet(config = bottomSheetConfig) - is BalancesAndLimitsBottomSheetConfig -> BalancesAndLimitsBottomSheet(config = bottomSheetConfig) - } - } - - WalletsListEffectsV2( - lazyListState = walletsListState, - selectedWalletIndex = selectedWalletIndex, - onWalletChange = state.onWalletChange, - onSelectedWalletIndexSet = { selectedWalletIndex = it }, - isAutoScroll = isAutoScroll, - onAutoScrollReset = onAutoScrollReset, - ) - } -} - -@OptIn(ExperimentalMaterialApi::class) -@Composable -private fun BaseScaffold( - state: WalletScreenState, - selectedWallet: WalletState, - snackbarHostState: SnackbarHostState, - content: @Composable () -> Unit, -) { - Scaffold( - topBar = { WalletTopBar(config = state.topBarConfig) }, - snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, - floatingActionButton = { - val manageTokensButtonConfig by remember(state.selectedWalletIndex) { - mutableStateOf( - (state.wallets[state.selectedWalletIndex] as? WalletState.MultiCurrency)?.manageTokensButtonConfig, - ) - } - - manageTokensButtonConfig?.let { ManageTokensButton(onClick = it.onClick) } - }, - floatingActionButtonPosition = FabPosition.Center, - containerColor = TangemTheme.colors.background.secondary, - content = { - val pullRefreshState = rememberPullRefreshState( - refreshing = selectedWallet.pullToRefreshConfig.isRefreshing, - onRefresh = selectedWallet.pullToRefreshConfig.onRefresh, - ) - - Box( - modifier = Modifier - .pullRefresh(pullRefreshState) - .padding(it), - ) { - content() - - WalletPullToRefreshIndicator( - isRefreshing = selectedWallet.pullToRefreshConfig.isRefreshing, - state = pullRefreshState, - modifier = Modifier.align(Alignment.TopCenter), - ) - } - }, - ) -} - -@Composable -private fun ManageTokensButton(onClick: () -> Unit) { - PrimaryButton( - text = stringResource(id = R.string.main_manage_tokens), - onClick = onClick, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = TangemTheme.dimens.spacing16), - ) -} - -internal fun LazyListScope.organizeTokens(state: WalletState, itemModifier: Modifier) { - (state as? WalletState.MultiCurrency)?.let { - (state.tokensListState as? WalletTokensListState.ContentState)?.let { - it.organizeTokensButtonConfig?.let { config -> - organizeTokensButton( - modifier = itemModifier, - isEnabled = config.isEnabled, - onClick = config.onClick, - ) - } - } - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletsListEffects.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletsListEffects.kt index 482d206fbf..5765897931 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletsListEffects.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletsListEffects.kt @@ -5,24 +5,34 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.State import androidx.compose.runtime.snapshotFlow -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletsListConfig import com.tangem.feature.wallet.presentation.wallet.ui.utils.ScrollOffsetCollector import com.tangem.feature.wallet.presentation.wallet.ui.utils.WalletsListInteractionsCollector +@Suppress("LongParameterList") @Composable internal fun WalletsListEffects( lazyListState: LazyListState, - walletsListConfig: WalletsListConfig, + selectedWalletIndex: Int, + onWalletChange: (Int) -> Unit, + onSelectedWalletIndexSet: (Int) -> Unit, isAutoScroll: State, onAutoScrollReset: () -> Unit, ) { - LaunchedEffect(key1 = lazyListState, key2 = walletsListConfig.onWalletChange) { + LaunchedEffect(key1 = lazyListState, key2 = onWalletChange) { snapshotFlow { lazyListState.layoutInfo.visibleItemsInfo } .collect( collector = ScrollOffsetCollector( + selectedWalletIndex = selectedWalletIndex, lazyListState = lazyListState, - walletsListConfig = walletsListConfig, - isAutoScroll = isAutoScroll, + onWalletChange = { newIndex -> + // Auto scroll must not change wallet + if (isAutoScroll.value) { + onSelectedWalletIndexSet(newIndex) + } else { + onSelectedWalletIndexSet(newIndex) + onWalletChange(newIndex) + } + }, ), ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletsListEffectsV2.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletsListEffectsV2.kt deleted file mode 100644 index d7dc0bb129..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletsListEffectsV2.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.ui - -import androidx.compose.foundation.lazy.LazyListState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.State -import androidx.compose.runtime.snapshotFlow -import com.tangem.feature.wallet.presentation.wallet.ui.utils.ScrollOffsetCollectorV2 -import com.tangem.feature.wallet.presentation.wallet.ui.utils.WalletsListInteractionsCollector - -@Suppress("LongParameterList") -@Composable -internal fun WalletsListEffectsV2( - lazyListState: LazyListState, - selectedWalletIndex: Int, - onWalletChange: (Int) -> Unit, - onSelectedWalletIndexSet: (Int) -> Unit, - isAutoScroll: State, - onAutoScrollReset: () -> Unit, -) { - LaunchedEffect(key1 = lazyListState, key2 = onWalletChange) { - snapshotFlow { lazyListState.layoutInfo.visibleItemsInfo } - .collect( - collector = ScrollOffsetCollectorV2( - selectedWalletIndex = selectedWalletIndex, - lazyListState = lazyListState, - onWalletChange = { newIndex -> - // Auto scroll must not change wallet - if (isAutoScroll.value) { - onSelectedWalletIndexSet(newIndex) - } else { - onSelectedWalletIndexSet(newIndex) - onWalletChange(newIndex) - } - }, - ), - ) - } - - LaunchedEffect(Unit) { - lazyListState.interactionSource.interactions.collect( - collector = WalletsListInteractionsCollector(onDragStart = onAutoScrollReset), - ) - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/TokenActionsBottomSheet.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/TokenActionsBottomSheet.kt index 104b25308d..865a52268f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/TokenActionsBottomSheet.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/TokenActionsBottomSheet.kt @@ -15,8 +15,8 @@ import com.tangem.core.ui.components.getWarningRowColors import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.presentation.common.WalletPreviewData -import com.tangem.feature.wallet.presentation.wallet.state.ActionsBottomSheetConfig -import com.tangem.feature.wallet.presentation.wallet.state.TokenActionButtonConfig +import com.tangem.feature.wallet.presentation.wallet.state.model.ActionsBottomSheetConfig +import com.tangem.feature.wallet.presentation.wallet.state.model.TokenActionButtonConfig import kotlinx.collections.immutable.ImmutableList @Composable diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt index ff70bf8486..455cf810c4 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt @@ -14,7 +14,10 @@ import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalDensity @@ -23,51 +26,20 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.presentation.common.WalletPreviewData -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletsListConfig +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState import com.tangem.feature.wallet.presentation.wallet.ui.components.common.WalletCard import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toPersistentList private const val SHORT_SNAP_ELEMENT_COUNT = 50 /** * Wallets list component * - * @param config config * @param lazyListState main content container list state * [REDACTED_AUTHOR] */ -@OptIn(ExperimentalFoundationApi::class) -@Composable -internal fun WalletsList(config: WalletsListConfig, lazyListState: LazyListState, isBalanceHidden: Boolean) { - val horizontalCardPadding = TangemTheme.dimens.spacing16 - val screenWidth = LocalConfiguration.current.screenWidthDp.dp - val itemWidth by remember(screenWidth) { derivedStateOf { screenWidth - horizontalCardPadding * 2 } } - - LazyRow( - modifier = Modifier.background(color = TangemTheme.colors.background.secondary), - state = lazyListState, - contentPadding = PaddingValues(horizontal = TangemTheme.dimens.spacing16), - horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8), - flingBehavior = rememberWalletsFlingBehaviour(lazyListState = lazyListState, itemWidth = itemWidth), - ) { - items( - items = config.wallets, - key = { it.id.stringValue }, - contentType = { it::class.java }, - ) { state -> - WalletCard( - state = state, - isBalanceHidden = isBalanceHidden, - modifier = Modifier - .animateItemPlacement() - .width(itemWidth), - ) - } - } -} - @OptIn(ExperimentalFoundationApi::class) @Composable internal fun WalletsList( @@ -136,8 +108,8 @@ private fun rememberWalletsFlingBehaviour(lazyListState: LazyListState, itemWidt private fun Preview_WalletsList_LightTheme() { TangemTheme(isDark = false) { WalletsList( - config = WalletPreviewData.walletListConfig, lazyListState = rememberLazyListState(), + wallets = WalletPreviewData.wallets.values.toPersistentList(), isBalanceHidden = false, ) } @@ -148,8 +120,8 @@ private fun Preview_WalletsList_LightTheme() { private fun Preview_WalletsList_DarkTheme() { TangemTheme(isDark = true) { WalletsList( - config = WalletPreviewData.walletListConfig, lazyListState = rememberLazyListState(), + wallets = WalletPreviewData.wallets.values.toPersistentList(), isBalanceHidden = false, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletBottomSheet.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletBottomSheet.kt index b77393f8d6..1c16a6fc78 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletBottomSheet.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletBottomSheet.kt @@ -1,7 +1,8 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.common import androidx.compose.foundation.layout.* -import androidx.compose.material3.* +import androidx.compose.material3.Icon +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -19,7 +20,7 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.presentation.common.WalletPreviewData -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletBottomSheetConfig +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletBottomSheetConfig /** * Wallet bottom sheet with detail notification information diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt index 345627ff8d..35a4a9bb1a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt @@ -4,8 +4,11 @@ import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.compose.animation.* import androidx.compose.animation.core.tween -import androidx.compose.foundation.* +import androidx.compose.foundation.Image +import androidx.compose.foundation.LocalIndication +import androidx.compose.foundation.background import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.indication import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.PressInteraction import androidx.compose.foundation.layout.* @@ -31,8 +34,13 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider -import androidx.compose.ui.unit.* -import androidx.constraintlayout.compose.* +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.DpOffset +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.sp +import androidx.constraintlayout.compose.ConstraintLayout +import androidx.constraintlayout.compose.ConstraintLayoutScope +import androidx.constraintlayout.compose.Dimension import com.tangem.common.Strings import com.tangem.core.ui.components.FontSizeRange import com.tangem.core.ui.components.RectangleShimmer @@ -44,7 +52,7 @@ import com.tangem.core.ui.res.TangemDimens import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.common.WalletPreviewData -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState private const val HALF_OF_ITEM_WIDTH = 0.5 diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt index 30cf4bb156..c4ffea5ea7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt @@ -5,12 +5,8 @@ import androidx.compose.ui.Modifier import androidx.paging.compose.LazyPagingItems import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.components.transactions.txHistoryItems -import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.tokensListItems -import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.tokensListItemsV2 -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState as WalletStateV2 /** * Wallet content @@ -22,31 +18,19 @@ import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState as [REDACTED_AUTHOR] */ internal fun LazyListScope.contentItems( - state: WalletState.ContentState, + state: WalletState, txHistoryItems: LazyPagingItems?, isBalanceHidden: Boolean, modifier: Modifier = Modifier, ) { when (state) { - is WalletMultiCurrencyState -> tokensListItems(state.tokensListState, modifier, isBalanceHidden) - is WalletSingleCurrencyState -> txHistoryItems(state.txHistoryState, txHistoryItems, isBalanceHidden, modifier) - } -} - -internal fun LazyListScope.contentItemsV2( - state: WalletStateV2, - txHistoryItems: LazyPagingItems?, - isBalanceHidden: Boolean, - modifier: Modifier = Modifier, -) { - when (state) { - is WalletStateV2.MultiCurrency -> { - tokensListItemsV2(state.tokensListState, modifier, isBalanceHidden) + is WalletState.MultiCurrency -> { + tokensListItems(state.tokensListState, modifier, isBalanceHidden) } - is WalletStateV2.SingleCurrency -> { + is WalletState.SingleCurrency -> { txHistoryItems(state.txHistoryState, txHistoryItems, isBalanceHidden, modifier) } - is WalletStateV2.Visa -> { + is WalletState.Visa -> { txHistoryItems(state.txHistoryState, txHistoryItems, isBalanceHidden, modifier) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt index f258a71ce9..476f3701e4 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt @@ -7,7 +7,7 @@ import androidx.compose.ui.Modifier import com.tangem.core.ui.components.notifications.Notification import com.tangem.core.ui.components.notifications.NotificationWithBackground import com.tangem.core.ui.res.TangemTheme -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification import kotlinx.collections.immutable.ImmutableList /** diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletTopBar.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletTopBar.kt index d22e971baf..4813eaff9f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletTopBar.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletTopBar.kt @@ -7,7 +7,7 @@ import androidx.compose.ui.tooling.preview.Preview import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.common.WalletPreviewData -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTopBarConfig +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTopBarConfig /** * Wallet screen top bar diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt index 9a855c797d..e0563a4400 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt @@ -17,10 +17,9 @@ import androidx.compose.ui.text.style.TextAlign import com.tangem.core.ui.decorations.roundedShapeItemDecoration import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.impl.R -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState.TokensListItemState import kotlinx.collections.immutable.ImmutableList -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletTokensListState as WalletTokensListStateV2 -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletTokensListState.TokensListItemState as TokensListItemStateV2 private const val NON_CONTENT_TOKENS_LIST_KEY = "NON_CONTENT_TOKENS_LIST" @@ -38,56 +37,19 @@ internal fun LazyListScope.tokensListItems( isBalanceHidden: Boolean, ) { when (state) { - is WalletTokensListState.ContentState -> contentItems( - items = state.items, - isBalanceHidden = isBalanceHidden, - modifier = modifier, - ) - WalletTokensListState.Empty -> nonContentItem(modifier = modifier) - } -} - -internal fun LazyListScope.tokensListItemsV2( - state: WalletTokensListStateV2, - modifier: Modifier = Modifier, - isBalanceHidden: Boolean, -) { - when (state) { - is WalletTokensListStateV2.ContentState -> { - contentItemsV2( + is WalletTokensListState.ContentState -> { + contentItems( items = state.items, isBalanceHidden = isBalanceHidden, modifier = modifier, ) } - WalletTokensListStateV2.Empty -> nonContentItem(modifier = modifier) + WalletTokensListState.Empty -> nonContentItem(modifier = modifier) } } private fun LazyListScope.contentItems( - items: ImmutableList, - modifier: Modifier = Modifier, - isBalanceHidden: Boolean, -) { - itemsIndexed( - items = items, - key = { _, item -> item.id }, - contentType = { _, item -> item::class.java }, - itemContent = { index, item -> - MultiCurrencyContentItem( - state = item, - isBalanceHidden = isBalanceHidden, - modifier = modifier.roundedShapeItemDecoration( - currentIndex = index, - lastIndex = items.lastIndex, - ), - ) - }, - ) -} - -private fun LazyListScope.contentItemsV2( - items: ImmutableList, + items: ImmutableList, modifier: Modifier = Modifier, isBalanceHidden: Boolean, ) { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContentItem.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContentItem.kt index d94ee34b63..9d39d7fcbc 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContentItem.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContentItem.kt @@ -5,8 +5,7 @@ import androidx.compose.ui.Modifier import com.tangem.core.ui.extensions.resolveReference import com.tangem.feature.wallet.presentation.common.component.NetworkGroupItem import com.tangem.feature.wallet.presentation.common.component.TokenItem -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletTokensListState.TokensListItemState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTokensListState.TokensListItemState /** * Multi-currency content item @@ -16,22 +15,6 @@ import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletTokensLi * [REDACTED_AUTHOR] */ -@Composable -internal fun MultiCurrencyContentItem( - state: WalletTokensListState.TokensListItemState, - isBalanceHidden: Boolean, - modifier: Modifier = Modifier, -) { - when (state) { - is WalletTokensListState.TokensListItemState.NetworkGroupTitle -> { - NetworkGroupItem(networkName = state.name.resolveReference(), modifier = modifier) - } - is WalletTokensListState.TokensListItemState.Token -> { - TokenItem(state = state.state, isBalanceHidden = isBalanceHidden, modifier = modifier) - } - } -} - @Composable internal fun MultiCurrencyContentItem( state: TokensListItemState, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyControlButtons.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyControlButtons.kt index af4d8c17ae..4b73b51b81 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyControlButtons.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyControlButtons.kt @@ -6,7 +6,7 @@ import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.ui.Modifier import com.tangem.core.ui.components.buttons.HorizontalActionChips import com.tangem.core.ui.res.TangemTheme -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletManageButton import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/BalancesAndLimitsBlock.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/BalancesAndLimitsBlock.kt index 5855ab22f6..6fe4af13e5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/BalancesAndLimitsBlock.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/BalancesAndLimitsBlock.kt @@ -16,7 +16,7 @@ import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameter import com.tangem.core.ui.components.RectangleShimmer import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.impl.R -import com.tangem.feature.wallet.presentation.wallet.state2.model.BalancesAndLimitsBlockState +import com.tangem.feature.wallet.presentation.wallet.state.model.BalancesAndLimitsBlockState private const val BALANCES_AND_LIMITS_BLOCK_KEY = "BalancesAndLimitsBlock" diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/BalancesAndLimitsBottomSheet.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/BalancesAndLimitsBottomSheet.kt index bd590c3ac4..fce6384945 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/BalancesAndLimitsBottomSheet.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/BalancesAndLimitsBottomSheet.kt @@ -21,7 +21,7 @@ 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.wallet.impl.R -import com.tangem.feature.wallet.presentation.wallet.state2.model.BalancesAndLimitsBottomSheetConfig +import com.tangem.feature.wallet.presentation.wallet.state.model.BalancesAndLimitsBottomSheetConfig @Composable internal fun BalancesAndLimitsBottomSheet(config: TangemBottomSheetConfig) { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/VisaDepositButton.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/VisaDepositButton.kt index 85dab308e0..30bb253d24 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/VisaDepositButton.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/VisaDepositButton.kt @@ -4,7 +4,7 @@ import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.ui.Modifier import com.tangem.core.ui.components.SecondaryButtonIconStart import com.tangem.feature.wallet.impl.R -import com.tangem.feature.wallet.presentation.wallet.state2.model.DepositButtonState +import com.tangem.feature.wallet.presentation.wallet.state.model.DepositButtonState private const val DEPOSIT_BUTTON_CONTENT_TYPE = "DepositButton" diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/utils/ScrollOffsetCollector.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/utils/ScrollOffsetCollector.kt index 2119079f09..939886db4a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/utils/ScrollOffsetCollector.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/utils/ScrollOffsetCollector.kt @@ -2,8 +2,6 @@ package com.tangem.feature.wallet.presentation.wallet.ui.utils import androidx.compose.foundation.lazy.LazyListItemInfo import androidx.compose.foundation.lazy.LazyListState -import androidx.compose.runtime.State -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletsListConfig import kotlinx.coroutines.flow.FlowCollector import kotlin.math.abs @@ -12,46 +10,40 @@ import kotlin.math.abs * If first visible item offset is greater than half item size, then change selected wallet index. * If last visible item offset is greater than half item size, then change selected wallet index. * + * @param selectedWalletIndex selected wallet index * @property lazyListState lazy list state - * @property walletsListConfig wallets list config - * @property isAutoScroll check if last scrolling is auto scroll + * @property onWalletChange callback that will be invoked on wallet change * [REDACTED_AUTHOR] */ internal class ScrollOffsetCollector( + selectedWalletIndex: Int, private val lazyListState: LazyListState, - private val walletsListConfig: WalletsListConfig, - private val isAutoScroll: State, + private val onWalletChange: (Int) -> Unit, ) : FlowCollector> { private val LazyListItemInfo.halfItemSize get() = size.div(other = 2) - private var currentIndex = walletsListConfig.selectedWalletIndex + private var currentIndex = selectedWalletIndex override suspend fun emit(value: List) { - // Auto scroll must not change wallet - if (isAutoScroll.value) { - currentIndex = walletsListConfig.selectedWalletIndex - return - } - if (!lazyListState.isScrollInProgress || value.size <= 1) return val firstItem = value.firstOrNull() ?: return val lastItem = value.lastOrNull() ?: return if (abs(firstItem.offset) > firstItem.halfItemSize) { - onWalletChange(newIndex = firstItem.index + 1) + selectIndex(newIndex = firstItem.index + 1) } else if (abs(lastItem.offset) > lastItem.halfItemSize) { - onWalletChange(newIndex = lastItem.index - 1) + selectIndex(newIndex = lastItem.index - 1) } } - private fun onWalletChange(newIndex: Int) { + private fun selectIndex(newIndex: Int) { if (currentIndex != newIndex) { currentIndex = newIndex - walletsListConfig.onWalletChange(newIndex) + onWalletChange(newIndex) } } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/utils/ScrollOffsetCollectorV2.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/utils/ScrollOffsetCollectorV2.kt deleted file mode 100644 index 43a4af298b..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/utils/ScrollOffsetCollectorV2.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.ui.utils - -import androidx.compose.foundation.lazy.LazyListItemInfo -import androidx.compose.foundation.lazy.LazyListState -import kotlinx.coroutines.flow.FlowCollector -import kotlin.math.abs - -/** - * Flow collector for scroll items tracking. - * If first visible item offset is greater than half item size, then change selected wallet index. - * If last visible item offset is greater than half item size, then change selected wallet index. - * - * @param selectedWalletIndex selected wallet index - * @property lazyListState lazy list state - * @property onWalletChange callback that will be invoked on wallet change - * -[REDACTED_AUTHOR] - */ -internal class ScrollOffsetCollectorV2( - selectedWalletIndex: Int, - private val lazyListState: LazyListState, - private val onWalletChange: (Int) -> Unit, -) : FlowCollector> { - - private val LazyListItemInfo.halfItemSize - get() = size.div(other = 2) - - private var currentIndex = selectedWalletIndex - - override suspend fun emit(value: List) { - if (!lazyListState.isScrollInProgress || value.size <= 1) return - - val firstItem = value.firstOrNull() ?: return - val lastItem = value.lastOrNull() ?: return - - if (abs(firstItem.offset) > firstItem.halfItemSize) { - selectIndex(newIndex = firstItem.index + 1) - } else if (abs(lastItem.offset) > lastItem.halfItemSize) { - selectIndex(newIndex = lastItem.index - 1) - } - } - - private fun selectIndex(newIndex: Int) { - if (currentIndex != newIndex) { - currentIndex = newIndex - onWalletChange(newIndex) - } - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/utils/WalletsScrollPreviewExt.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/utils/WalletsScrollPreviewExt.kt index 3dbb6ccb11..f75f58b867 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/utils/WalletsScrollPreviewExt.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/utils/WalletsScrollPreviewExt.kt @@ -4,7 +4,7 @@ import androidx.compose.animation.core.Spring import androidx.compose.animation.core.spring import androidx.compose.foundation.gestures.animateScrollBy import androidx.compose.foundation.lazy.LazyListState -import com.tangem.feature.wallet.presentation.wallet.state.WalletEvent.DemonstrateWalletsScrollPreview +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent.DemonstrateWalletsScrollPreview import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt deleted file mode 100644 index e9ef5cc769..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt +++ /dev/null @@ -1,122 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.utils - -import com.tangem.core.ui.components.currency.tokenicon.converter.CryptoCurrencyToIconStateConverter -import com.tangem.core.ui.components.marketprice.PriceChangeType -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.wallet.presentation.common.state.TokenItemState -import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents -import com.tangem.utils.Provider -import com.tangem.utils.converter.Converter -import java.math.BigDecimal - -internal class CryptoCurrencyStatusToTokenItemConverter( - private val appCurrencyProvider: Provider, - private val clickIntents: WalletClickIntents, -) : Converter { - - private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter) - - override fun convert(value: CryptoCurrencyStatus): TokenItemState { - return when (value.value) { - is CryptoCurrencyStatus.Loading -> value.mapToLoadingState() - is CryptoCurrencyStatus.Loaded, - is CryptoCurrencyStatus.Custom, - is CryptoCurrencyStatus.NoQuote, - is CryptoCurrencyStatus.NoAccount, - -> value.mapToTokenItemState() - is CryptoCurrencyStatus.MissedDerivation -> value.mapToNoAddressTokenItemState() - is CryptoCurrencyStatus.Unreachable, - is CryptoCurrencyStatus.NoAmount, - -> value.mapToUnreachableTokenItemState() - } - } - - private fun CryptoCurrencyStatus.mapToLoadingState(): TokenItemState.Loading { - return TokenItemState.Loading( - id = currency.id.value, - iconState = iconStateConverter.convert(value = this), - titleState = TokenItemState.TitleState.Content(text = currency.name), - ) - } - - private fun CryptoCurrencyStatus.mapToTokenItemState(): TokenItemState.Content { - return TokenItemState.Content( - id = currency.id.value, - iconState = iconStateConverter.convert(value = this), - titleState = TokenItemState.TitleState.Content( - text = currency.name, - hasPending = value.hasCurrentNetworkTransactions, - ), - fiatAmountState = TokenItemState.FiatAmountState.Content( - text = getFormattedFiatAmount(), - ), - cryptoAmountState = TokenItemState.CryptoAmountState.Content(text = getFormattedAmount()), - cryptoPriceState = getCryptoPriceState(), - onItemClick = { clickIntents.onTokenItemClick(currency) }, - onItemLongClick = { clickIntents.onTokenItemLongClick(cryptoCurrencyStatus = this) }, - ) - } - - private fun CryptoCurrencyStatus.getFormattedAmount(): String { - val amount = value.amount ?: return TokenItemState.UNKNOWN_AMOUNT_SIGN - - return BigDecimalFormatter.formatCryptoAmount(amount, currency.symbol, currency.decimals) - } - - private fun CryptoCurrencyStatus.getFormattedFiatAmount(): String { - val fiatAmount = value.fiatAmount ?: return TokenItemState.UNKNOWN_AMOUNT_SIGN - val appCurrency = appCurrencyProvider() - - return BigDecimalFormatter.formatFiatAmount(fiatAmount, appCurrency.code, appCurrency.symbol) - } - - private fun CryptoCurrencyStatus.mapToUnreachableTokenItemState() = TokenItemState.Unreachable( - id = currency.id.value, - iconState = iconStateConverter.convert(value = this), - titleState = TokenItemState.TitleState.Content(text = currency.name), - onItemClick = { clickIntents.onTokenItemClick(currency) }, - onItemLongClick = { clickIntents.onTokenItemLongClick(cryptoCurrencyStatus = this) }, - ) - - private fun CryptoCurrencyStatus.mapToNoAddressTokenItemState() = TokenItemState.NoAddress( - id = currency.id.value, - iconState = iconStateConverter.convert(this), - titleState = TokenItemState.TitleState.Content(text = currency.name), - onItemLongClick = { clickIntents.onTokenItemLongClick(cryptoCurrencyStatus = this) }, - ) - - private fun CryptoCurrencyStatus.getCryptoPriceState(): TokenItemState.CryptoPriceState { - val fiatRate = value.fiatRate - val priceChange = value.priceChange - - return if (fiatRate != null && priceChange != null) { - TokenItemState.CryptoPriceState.Content( - price = fiatRate.getFormattedCryptoPrice(), - priceChangePercent = BigDecimalFormatter.formatPercent( - percent = priceChange, - useAbsoluteValue = true, - maxFractionDigits = 1, - minFractionDigits = 1, - ), - type = priceChange.getPriceChangeType(), - ) - } else { - TokenItemState.CryptoPriceState.Unknown - } - } - - private fun BigDecimal.getFormattedCryptoPrice(): String { - val appCurrency = appCurrencyProvider() - return BigDecimalFormatter.formatFiatAmount( - fiatAmount = this, - fiatCurrencyCode = appCurrency.code, - fiatCurrencySymbol = appCurrency.symbol, - ) - } - - private fun BigDecimal.getPriceChangeType(): PriceChangeType { - return if (this > BigDecimal.ZERO) PriceChangeType.UP else PriceChangeType.DOWN - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CurrencyStatusErrorConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CurrencyStatusErrorConverter.kt deleted file mode 100644 index 19f35776b0..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CurrencyStatusErrorConverter.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.utils - -import com.tangem.domain.tokens.error.CurrencyStatusError -import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletState -import com.tangem.utils.Provider -import com.tangem.utils.converter.Converter - -// TODO: Implement this -internal class CurrencyStatusErrorConverter( - private val currentStateProvider: Provider, -) : Converter { - - override fun convert(value: CurrencyStatusError): WalletSingleCurrencyState.Content { - return requireNotNull(currentStateProvider() as? WalletSingleCurrencyState.Content) - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt deleted file mode 100644 index 3bd3dc16d5..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.utils - -import com.tangem.core.ui.utils.BigDecimalFormatter.formatFiatAmount -import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.TokenList.FiatBalance -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory -import com.tangem.feature.wallet.presentation.wallet.domain.getCardsCount -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState -import com.tangem.utils.Provider -import com.tangem.utils.converter.Converter - -internal class FiatBalanceToWalletCardConverter( - private val currentState: WalletCardState, - private val appCurrencyProvider: Provider, - private val currentWalletProvider: Provider, -) : Converter { - - override fun convert(value: FiatBalance): WalletCardState { - return when (value) { - is FiatBalance.Loading -> currentState.toLoadingWalletCardState() - is FiatBalance.Failed -> currentState.toErrorWalletCardState() - is FiatBalance.Loaded -> value.convertToWalletCardState() - } - } - - private fun WalletCardState.toLoadingWalletCardState(): WalletCardState { - return WalletCardState.Loading(id, title, additionalInfo, imageResId, onRenameClick, onDeleteClick) - } - - private fun WalletCardState.toErrorWalletCardState(): WalletCardState { - return WalletCardState.Error( - id = id, - title = title, - additionalInfo = additionalInfo, - imageResId = imageResId, - onDeleteClick = onDeleteClick, - onRenameClick = onRenameClick, - ) - } - - private fun FiatBalance.Loaded.convertToWalletCardState(): WalletCardState { - val appCurrency = appCurrencyProvider() - - return WalletCardState.Content( - id = currentState.id, - title = currentState.title, - additionalInfo = WalletAdditionalInfoFactory.resolve(wallet = currentWalletProvider()), - imageResId = currentState.imageResId, - onRenameClick = currentState.onRenameClick, - onDeleteClick = currentState.onDeleteClick, - balance = formatFiatAmount( - fiatAmount = this.amount, - fiatCurrencyCode = appCurrency.code, - fiatCurrencySymbol = appCurrency.symbol, - ), - cardCount = currentWalletProvider().getCardsCount(), - ) - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/HiddenStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/HiddenStateConverter.kt deleted file mode 100644 index 66508d3110..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/HiddenStateConverter.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.utils - -import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletState -import com.tangem.utils.Provider -import com.tangem.utils.converter.Converter - -internal class HiddenStateConverter( - private val currentStateProvider: Provider, -) : Converter { - - override fun convert(value: Boolean): WalletState { - return when (val state = currentStateProvider() as? WalletState.ContentState) { - is WalletMultiCurrencyState.Content -> { - state.copy(isBalanceHidden = value) - } - - is WalletSingleCurrencyState.Content -> { - state.copy(isBalanceHidden = value) - } - - else -> currentStateProvider() - } - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorConverter.kt deleted file mode 100644 index 512fd08483..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorConverter.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.utils - -import com.tangem.domain.tokens.error.TokenListError -import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState -import com.tangem.utils.Provider -import com.tangem.utils.converter.Converter - -internal class TokenListErrorConverter( - private val currentStateProvider: Provider, -) : Converter { - - override fun convert(value: TokenListError): WalletState { - val state = currentStateProvider() - return when (value) { - is TokenListError.EmptyTokens -> state.mapToEmptyTokensState() - is TokenListError.DataError, - is TokenListError.UnableToSortTokenList, - -> state - } - } - - private fun WalletState.mapToEmptyTokensState(): WalletState { - return when (this) { - is WalletMultiCurrencyState.Content -> copy(tokensListState = WalletTokensListState.Empty) - is WalletMultiCurrencyState.Locked, - is WalletSingleCurrencyState.Content, - is WalletSingleCurrencyState.Locked, - is WalletState.Initial, - -> this - } - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt deleted file mode 100644 index 0f40074e67..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt +++ /dev/null @@ -1,114 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.utils - -import com.tangem.core.ui.extensions.stringReference -import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.tokens.model.NetworkGroup -import com.tangem.domain.tokens.model.TokenList -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState.OrganizeTokensButtonState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState.TokensListItemState -import com.tangem.feature.wallet.presentation.wallet.state.factory.TokenListWithWallet -import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents -import com.tangem.utils.Provider -import com.tangem.utils.converter.Converter -import kotlinx.collections.immutable.PersistentList -import kotlinx.collections.immutable.mutate -import kotlinx.collections.immutable.persistentListOf - -internal class TokenListToContentItemsConverter( - appCurrencyProvider: Provider, - private val clickIntents: WalletClickIntents, -) : Converter { - - private val tokenStatusConverter = CryptoCurrencyStatusToTokenItemConverter( - appCurrencyProvider = appCurrencyProvider, - clickIntents = clickIntents, - ) - - override fun convert(value: TokenListWithWallet): WalletTokensListState { - val isSingleCurrencyWalletWithToken = !value.wallet.isMultiCurrency && - value.wallet.scanResponse.walletData?.token != null - return when (val tokenList = value.tokenList) { - is TokenList.Empty -> WalletTokensListState.Empty - is TokenList.GroupedByNetwork -> WalletTokensListState.Content( - items = tokenList.mapToMultiCurrencyItems(), - organizeTokensButton = tokenList.mapToOrganizeTokensButtonState(isSingleCurrencyWalletWithToken), - ) - is TokenList.Ungrouped -> WalletTokensListState.Content( - items = tokenList.mapToMultiCurrencyItems(), - organizeTokensButton = tokenList.mapToOrganizeTokensButtonState(isSingleCurrencyWalletWithToken), - ) - } - } - - private fun TokenList.GroupedByNetwork.mapToMultiCurrencyItems(): PersistentList { - return groups.fold(initial = persistentListOf()) { acc, group -> - acc.mutate { it.addGroup(group) } - } - } - - private fun TokenList.Ungrouped.mapToMultiCurrencyItems(): PersistentList { - return currencies.fold(initial = persistentListOf()) { acc, token -> - acc.mutate { it.addToken(token) } - } - } - - private fun TokenList.GroupedByNetwork.mapToOrganizeTokensButtonState( - isSingleCurrencyWithTokenWallet: Boolean, - ): OrganizeTokensButtonState { - return getOrganizeTokensButtonState( - isLoading = totalFiatBalance is TokenList.FiatBalance.Loading, - currenciesSize = groups.flatMap(NetworkGroup::currencies).size, - isSingleCurrencyWithTokenWallet = isSingleCurrencyWithTokenWallet, - ) - } - - private fun TokenList.Ungrouped.mapToOrganizeTokensButtonState( - isSingleCurrencyWithTokenWallet: Boolean, - ): OrganizeTokensButtonState { - return getOrganizeTokensButtonState( - isLoading = totalFiatBalance is TokenList.FiatBalance.Loading, - currenciesSize = currencies.size, - isSingleCurrencyWithTokenWallet = isSingleCurrencyWithTokenWallet, - ) - } - - private fun MutableList.addGroup(group: NetworkGroup): List { - val groupTitle = TokensListItemState.NetworkGroupTitle( - id = group.network.hashCode(), - name = stringReference(group.network.name), - ) - - this.add(groupTitle) - - group.currencies.forEach { token -> - this.addToken(token) - } - - return this - } - - private fun MutableList.addToken(token: CryptoCurrencyStatus): List { - val tokenItemState = tokenStatusConverter.convert(token) - - this.add(TokensListItemState.Token(tokenItemState)) - - return this - } - - private fun getOrganizeTokensButtonState( - isLoading: Boolean, - currenciesSize: Int, - isSingleCurrencyWithTokenWallet: Boolean, - ): OrganizeTokensButtonState { - return if (currenciesSize > 1 && !isSingleCurrencyWithTokenWallet) { - OrganizeTokensButtonState.Visible( - isEnabled = !isLoading, - onClick = clickIntents::onOrganizeTokensClick, - ) - } else { - OrganizeTokensButtonState.Hidden - } - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt deleted file mode 100644 index 823ce6e272..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt +++ /dev/null @@ -1,63 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.utils - -import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.common.util.cardTypesResolver -import com.tangem.domain.tokens.model.TokenList -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletsListConfig -import com.tangem.feature.wallet.presentation.wallet.state.factory.TokenListWithWallet -import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents -import com.tangem.utils.Provider -import com.tangem.utils.converter.Converter -import kotlinx.collections.immutable.toPersistentList - -@Suppress("LongParameterList") -internal class TokenListToWalletStateConverter( - private val currentStateProvider: Provider, - private val currentWalletProvider: Provider, - private val appCurrencyProvider: Provider, - clickIntents: WalletClickIntents, -) : Converter { - - private val tokenListToContentConverter = TokenListToContentItemsConverter( - appCurrencyProvider = appCurrencyProvider, - clickIntents = clickIntents, - ) - - override fun convert(value: TokenListWithWallet): WalletState { - val tokenList = value.tokenList - val isSingleCurrencyWalletWithToken = value.wallet.scanResponse.cardTypesResolver.isSingleWalletWithToken() - return when (val state = currentStateProvider()) { - is WalletMultiCurrencyState.Content -> { - state.copy( - walletsListConfig = state.updateSelectedWallet(fiatBalance = tokenList.totalFiatBalance), - tokensListState = tokenListToContentConverter.convert(value = value), - isManageTokensAvailable = !isSingleCurrencyWalletWithToken, - ) - } - is WalletMultiCurrencyState.Locked, - is WalletSingleCurrencyState.Content, - is WalletSingleCurrencyState.Locked, - is WalletState.Initial, - -> state - } - } - - private fun WalletMultiCurrencyState.updateSelectedWallet(fiatBalance: TokenList.FiatBalance): WalletsListConfig { - val selectedWalletIndex = walletsListConfig.selectedWalletIndex - val selectedWalletCard = walletsListConfig.wallets[selectedWalletIndex] - val converter = FiatBalanceToWalletCardConverter( - currentState = selectedWalletCard, - currentWalletProvider = currentWalletProvider, - appCurrencyProvider = appCurrencyProvider, - ) - - return walletsListConfig.copy( - wallets = walletsListConfig.wallets.toPersistentList() - .set(index = selectedWalletIndex, element = converter.convert(fiatBalance)), - ) - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt deleted file mode 100644 index dad5efb741..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt +++ /dev/null @@ -1,77 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.viewmodels - -import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.wallets.models.UserWalletId - -@Suppress("TooManyFunctions") -internal interface WalletClickIntents { - - fun onBackClick() - - fun onGenerateMissedAddressesClick(missedAddressCurrencies: List) - - fun onScanToUnlockWalletClick() - - fun onDetailsClick() - - fun onBackupCardClick() - - fun onSignedHashesNotificationCloseClick() - - fun onLikeAppClick() - - fun onDislikeAppClick() - - fun onCloseRateAppNotificationClick() - - fun onWalletChange(index: Int) - - fun onRefreshSwipe() - - fun onOrganizeTokensClick() - - fun onUnlockWalletClick() - - fun onUnlockWalletNotificationClick() - - fun onDismissBottomSheet() - - fun onTokenItemClick(currency: CryptoCurrency) - - fun onTokenItemLongClick(cryptoCurrencyStatus: CryptoCurrencyStatus) - - fun onRenameClick(userWalletId: UserWalletId, name: String) - - fun onDeleteBeforeConfirmationClick(userWalletId: UserWalletId) - - fun onDeleteAfterConfirmationClick(userWalletId: UserWalletId) - - fun onSingleCurrencySendClick(cryptoCurrencyStatus: CryptoCurrencyStatus? = null) - - fun onMultiCurrencySendClick(cryptoCurrencyStatus: CryptoCurrencyStatus) - - fun onReceiveClick(cryptoCurrencyStatus: CryptoCurrencyStatus) - - fun onCopyAddressClick(cryptoCurrencyStatus: CryptoCurrencyStatus) - - fun onHideTokensClick(cryptoCurrencyStatus: CryptoCurrencyStatus) - - fun onPerformHideToken(cryptoCurrencyStatus: CryptoCurrencyStatus) - - fun onSellClick(cryptoCurrencyStatus: CryptoCurrencyStatus) - - fun onManageTokensClick() - - fun onBuyClick(cryptoCurrencyStatus: CryptoCurrencyStatus) - - fun onSwapClick(cryptoCurrencyStatus: CryptoCurrencyStatus) - - fun onReloadClick() - - fun onExploreClick() - - fun onTransactionClick(txHash: String) - - fun onCloseSwapPromoNotificationClick() -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt deleted file mode 100644 index c0145c919b..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt +++ /dev/null @@ -1,230 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.viewmodels - -import arrow.core.Either -import com.tangem.domain.common.CardTypesResolver -import com.tangem.domain.common.util.cardTypesResolver -import com.tangem.domain.demo.IsDemoCardUseCase -import com.tangem.domain.settings.IsReadyToShowRateAppUseCase -import com.tangem.domain.settings.ShouldShowSwapPromoWalletUseCase -import com.tangem.domain.tokens.GetMissedAddressesCryptoCurrenciesUseCase -import com.tangem.domain.tokens.error.GetCurrenciesError -import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase -import com.tangem.feature.wallet.presentation.wallet.domain.HasSingleWalletSignedHashesUseCase -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.toImmutableList -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.conflate -import kotlinx.coroutines.flow.flowOf - -/** - * Wallet notifications list factory - * - * @property isDemoCardUseCase use case that checks if card is demo - * @property isReadyToShowRateAppUseCase use case that checks if card is user already rate app - * @property isNeedToBackupUseCase use case that checks if wallet need backup cards - * @property getMissedAddressCryptoCurrenciesUseCase use case that gets missed address crypto currencies - * @property hasSingleWalletSignedHashesUseCase use case that checks if single wallet signed hashes - * @property shouldShowSwapPromoWalletUseCase use case that checks if should show swap promo - * @property clickIntents screen click intents - * -[REDACTED_AUTHOR] - */ -@Suppress("LongParameterList") -internal class WalletNotificationsListFactory( - private val isDemoCardUseCase: IsDemoCardUseCase, - private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase, - private val isNeedToBackupUseCase: IsNeedToBackupUseCase, - private val getMissedAddressCryptoCurrenciesUseCase: GetMissedAddressesCryptoCurrenciesUseCase, - private val hasSingleWalletSignedHashesUseCase: HasSingleWalletSignedHashesUseCase, - private val shouldShowSwapPromoWalletUseCase: ShouldShowSwapPromoWalletUseCase, - private val clickIntents: WalletClickIntents, -) { - - private var readyForRateAppNotification = false - - fun create( - selectedWallet: UserWallet, - cryptoCurrencyList: List, - ): Flow> { - val cardTypesResolver = selectedWallet.scanResponse.cardTypesResolver - return combine( - flow = hasSingleWalletSignedHashesFlow(selectedWallet, cryptoCurrencyList), - flow2 = isReadyToShowRateAppUseCase().conflate(), - flow3 = isNeedToBackupUseCase(selectedWallet.walletId).conflate(), - flow4 = getMissedAddressCryptoCurrenciesUseCase(selectedWallet.walletId).conflate(), - flow5 = shouldShowSwapPromoWalletUseCase().conflate(), - ) { hasSignedHashes, isReadyToShowRating, isNeedToBackup, maybeMissedAddressCurrencies, isShowSwapPromo -> - readyForRateAppNotification = true - buildList { - addSwapPromoNotification(isShowSwapPromo, cardTypesResolver) - - addCriticalNotifications(cardTypesResolver) - - addInformationalNotifications(cardTypesResolver, maybeMissedAddressCurrencies) - - addWarningNotifications(cardTypesResolver, cryptoCurrencyList, hasSignedHashes, isNeedToBackup) - - addRateTheAppNotification(isReadyToShowRating) - }.toImmutableList() - } - } - - private fun hasSingleWalletSignedHashesFlow( - selectedWallet: UserWallet, - cryptoCurrencyList: List, - ): Flow { - return if (selectedWallet.scanResponse.cardTypesResolver.isMultiwalletAllowed()) { - flowOf(value = false) - } else { - val network = requireNotNull(cryptoCurrencyList.firstOrNull()?.currency?.network) - hasSingleWalletSignedHashesUseCase(userWallet = selectedWallet, network = network).conflate() - } - } - - private fun MutableList.addSwapPromoNotification( - showSwapPromo: Boolean, - cardTypesResolver: CardTypesResolver, - ) { - addIf( - element = WalletNotification.SwapPromo( - clickIntents::onCloseSwapPromoNotificationClick, - ), - condition = showSwapPromo && cardTypesResolver.isMultiwalletAllowed(), - ) - } - - private fun MutableList.addCriticalNotifications(cardTypesResolver: CardTypesResolver) { - addIf( - element = WalletNotification.Critical.DevCard, - condition = !cardTypesResolver.isReleaseFirmwareType(), - ) - - addIf( - element = WalletNotification.Critical.FailedCardValidation, - condition = cardTypesResolver.isReleaseFirmwareType() && cardTypesResolver.isAttestationFailed(), - ) - - cardTypesResolver.getRemainingSignatures()?.let { remainingSignatures -> - addIf( - element = WalletNotification.Warning.LowSignatures(count = remainingSignatures), - condition = remainingSignatures <= MAX_REMAINING_SIGNATURES_COUNT, - ) - } - } - - private fun MutableList.addRateTheAppNotification(isReadyToShowRating: Boolean) { - addIf( - element = WalletNotification.RateApp( - onLikeClick = clickIntents::onLikeAppClick, - onDislikeClick = clickIntents::onDislikeAppClick, - onCloseClick = clickIntents::onCloseRateAppNotificationClick, - ), - condition = isReadyToShowRating && readyForRateAppNotification, - ) - } - - private fun MutableList.addWarningNotifications( - cardTypesResolver: CardTypesResolver, - cryptoCurrencyList: List, - hasSignedHashes: Boolean, - isNeedToBackup: Boolean, - ) { - addIf( - element = WalletNotification.Warning.MissingBackup( - onStartBackupClick = clickIntents::onBackupCardClick, - ), - condition = isNeedToBackup, - ) - - addIf( - element = WalletNotification.Warning.TestNetCard, - condition = cardTypesResolver.isTestCard(), - ) - - if (cardTypesResolver.isMultiwalletAllowed()) { - addIf( - element = WalletNotification.Warning.SomeNetworksUnreachable, - condition = cryptoCurrencyList.hasUnreachableNetworks(), - ) - } else { - addIf( - element = WalletNotification.Warning.NetworksUnreachable, - condition = cryptoCurrencyList.hasUnreachableNetworks(), - ) - - addNoAccountWarning(cryptoCurrencyList) - - addIf( - element = WalletNotification.Warning.NumberOfSignedHashesIncorrect( - onCloseClick = clickIntents::onSignedHashesNotificationCloseClick, - ), - condition = hasSignedHashes, - ) - } - } - - private fun MutableList.addInformationalNotifications( - cardTypesResolver: CardTypesResolver, - maybeMissedAddressCurrencies: Either>, - ) { - addIf( - element = WalletNotification.Informational.DemoCard, - condition = isDemoCardUseCase(cardId = cardTypesResolver.getCardId()), - ) - - addMissingAddressesNotification(maybeMissedAddressCurrencies) - } - - private fun MutableList.addIf(element: WalletNotification, condition: Boolean) { - if (condition) { - add(element = element) - if (element is WalletNotification.Critical || element is WalletNotification.Warning) { - readyForRateAppNotification = false - } - } - } - - private fun MutableList.addMissingAddressesNotification( - maybeCurrencies: Either>, - ) { - val missingAddressCurrencies = (maybeCurrencies as? Either.Right)?.value ?: return - - addIf( - element = WalletNotification.Informational.MissingAddresses( - missingAddressesCount = missingAddressCurrencies.count(), - onGenerateClick = { - clickIntents.onGenerateMissedAddressesClick(missedAddressCurrencies = missingAddressCurrencies) - }, - ), - condition = missingAddressCurrencies.isNotEmpty(), - ) - } - - private fun List.hasUnreachableNetworks(): Boolean { - return any { it.value is CryptoCurrencyStatus.Unreachable } - } - - private fun MutableList.addNoAccountWarning(cryptoCurrencyList: List) { - val noAccountNetwork = cryptoCurrencyList.firstOrNull { it.value is CryptoCurrencyStatus.NoAccount } - if (noAccountNetwork != null) { - val amountToCreateAccount = (noAccountNetwork.value as? CryptoCurrencyStatus.NoAccount) - ?.amountToCreateAccount.toString() - add( - element = WalletNotification.Informational.NoAccount( - network = noAccountNetwork.currency.name, - amount = amountToCreateAccount, - symbol = noAccountNetwork.currency.symbol, - ), - ) - } - } - - private companion object { - const val MAX_REMAINING_SIGNATURES_COUNT = 10 - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletStateCache.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletStateCache.kt deleted file mode 100644 index 0c81b8e3ee..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletStateCache.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.viewmodels - -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.wallet.presentation.wallet.state.WalletState - -/** - * Wallet state cache. It allows to switch the wallets without additional loading like a PagerView. - * -[REDACTED_AUTHOR] - */ -internal object WalletStateCache { - - private val states = mutableMapOf() - - /** Get state by [userWalletId] */ - fun getState(userWalletId: UserWalletId): WalletState.ContentState? = states[userWalletId] - - /** Add or update [state] by [userWalletId] */ - fun update(userWalletId: UserWalletId, state: WalletState.ContentState) { - states[userWalletId] = state - } - - /** Update all content states */ - fun updateAll(block: (WalletState.ContentState.() -> WalletState.ContentState)) { - states.keys.forEach { - states[it] = block(requireNotNull(states[it])) - } - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletStateHolder.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletStateHolder.kt deleted file mode 100644 index 59c1f39f13..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletStateHolder.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.viewmodels - -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import com.tangem.feature.wallet.presentation.wallet.state.WalletState - -/** - * Wallet state holder - * - * @param initialState initial ui state - * -[REDACTED_AUTHOR] - */ -internal class WalletStateHolder(initialState: WalletState) { - - /** Screen state */ - var uiState: WalletState by mutableStateOf(initialState) - private set - - /** Set screen [state] */ - fun setState(state: WalletState) { - when (state) { - is WalletState.ContentState -> { - cache(state = state) - - uiState = state - } - is WalletState.Initial -> Unit - } - } - - /** Cache [state] [WalletState.ContentState] to [WalletStateCache] */ - private fun cache(state: WalletState.ContentState) { - val selectedWalletIndex = state.walletsListConfig.selectedWalletIndex - val selectedWalletId = state.walletsListConfig.wallets[selectedWalletIndex].id - - WalletStateCache.update(userWalletId = selectedWalletId, state = state) - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletStateHolderDelegate.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletStateHolderDelegate.kt deleted file mode 100644 index 25c3d12698..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletStateHolderDelegate.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.viewmodels - -import com.tangem.feature.wallet.presentation.wallet.state.WalletState -import kotlin.properties.ReadWriteProperty -import kotlin.reflect.KProperty - -internal class WalletStateHolderDelegate( - private val uiStateHolder: WalletStateHolder, -) : ReadWriteProperty { - - override fun getValue(thisRef: Any?, property: KProperty<*>): WalletState = uiStateHolder.uiState - - override fun setValue(thisRef: Any?, property: KProperty<*>, value: WalletState) { - uiStateHolder.setState(value) - } -} - -internal fun uiStateHolder(initialState: WalletState): ReadWriteProperty { - return WalletStateHolderDelegate(uiStateHolder = WalletStateHolder(initialState = initialState)) -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index 71d9d95d97..6c4a46d2ee 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -1,1340 +1,319 @@ package com.tangem.feature.wallet.presentation.wallet.viewmodels -import androidx.lifecycle.* -import androidx.paging.cachedIn -import arrow.core.Either -import arrow.core.getOrElse -import arrow.core.right -import com.tangem.blockchain.common.address.AddressType -import com.tangem.common.extensions.isZero +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import com.tangem.core.analytics.api.AnalyticsEventHandler -import com.tangem.core.analytics.models.AnalyticsParam -import com.tangem.core.navigation.AppScreen -import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheetConfig -import com.tangem.core.ui.components.bottomsheets.tokenreceive.AddressModel -import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheetConfig -import com.tangem.core.ui.components.bottomsheets.tokenreceive.mapToAddressModels -import com.tangem.core.ui.components.transactions.state.TxHistoryState -import com.tangem.core.ui.extensions.TextReference -import com.tangem.core.ui.extensions.WrappedList -import com.tangem.core.ui.extensions.resourceReference -import com.tangem.domain.analytics.ChangeCardAnalyticsContextUseCase -import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase -import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase -import com.tangem.domain.card.SetCardWasScannedUseCase -import com.tangem.domain.common.CardTypesResolver -import com.tangem.domain.common.util.cardTypesResolver -import com.tangem.domain.demo.IsDemoCardUseCase -import com.tangem.domain.redux.LegacyAction import com.tangem.domain.redux.ReduxStateHolder -import com.tangem.domain.settings.* -import com.tangem.domain.tokens.* -import com.tangem.domain.tokens.error.TokenListError -import com.tangem.domain.tokens.legacy.TradeCryptoAction -import com.tangem.domain.tokens.model.* -import com.tangem.domain.tokens.models.analytics.TokenReceiveAnalyticsEvent -import com.tangem.domain.tokens.models.analytics.TokenScreenAnalyticsEvent -import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase -import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase -import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase +import com.tangem.domain.settings.CanUseBiometryUseCase +import com.tangem.domain.settings.IsWalletsScrollPreviewEnabled +import com.tangem.domain.settings.ShouldShowSaveWalletScreenUseCase import com.tangem.domain.walletconnect.WalletConnectActions -import com.tangem.domain.walletmanager.WalletManagersFacade -import com.tangem.domain.wallets.models.UnlockWalletsError -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.domain.wallets.usecase.* -import com.tangem.feature.wallet.impl.R +import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase +import com.tangem.domain.wallets.usecase.GetWalletsUseCase +import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase +import com.tangem.feature.wallet.presentation.deeplink.WalletDeepLinksHandler import com.tangem.feature.wallet.presentation.router.InnerWalletRouter -import com.tangem.feature.wallet.presentation.wallet.analytics.PortfolioEvent import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent -import com.tangem.feature.wallet.presentation.wallet.domain.HasSingleWalletSignedHashesUseCase -import com.tangem.feature.wallet.presentation.wallet.domain.ScanCardToUnlockWalletClickHandler -import com.tangem.feature.wallet.presentation.wallet.domain.ScanCardToUnlockWalletError -import com.tangem.feature.wallet.presentation.wallet.state.* -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState -import com.tangem.feature.wallet.presentation.wallet.state.factory.TokenListWithWallet -import com.tangem.feature.wallet.presentation.wallet.state.factory.WalletStateFactory -import com.tangem.feature.wallet.presentation.wallet.subscribers.MaybeTokenListFlow +import com.tangem.feature.wallet.presentation.wallet.analytics.utils.SelectedWalletAnalyticsSender +import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContentLoader +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent.DemonstrateWalletsScrollPreview.Direction +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState +import com.tangem.feature.wallet.presentation.wallet.state.transformers.* +import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender +import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntents import com.tangem.utils.Provider import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.JobHolder import com.tangem.utils.coroutines.saveIn -import com.tangem.utils.extensions.DELAY_SDK_DIALOG_CLOSE import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.delay import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import java.math.BigDecimal +import timber.log.Timber import javax.inject.Inject -import kotlin.properties.Delegates -/** - * Wallet screen view model - * -[REDACTED_AUTHOR] - */ -@Suppress("LargeClass", "LongParameterList", "TooManyFunctions") +@Suppress("LongParameterList") @HiltViewModel internal class WalletViewModel @Inject constructor( - // region Parameters + private val stateHolder: WalletStateController, + private val clickIntents: WalletClickIntents, + private val walletEventSender: WalletEventSender, + private val walletsUpdateActionResolver: WalletsUpdateActionResolver, + private val walletScreenContentLoader: WalletScreenContentLoader, + private val getSelectedWalletUseCase: GetSelectedWalletUseCase, private val getWalletsUseCase: GetWalletsUseCase, - getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, - private val selectWalletUseCase: SelectWalletUseCase, - private val updateWalletUseCase: UpdateWalletUseCase, - private val deleteWalletUseCase: DeleteWalletUseCase, - private val getTokenListUseCase: GetTokenListUseCase, - private val getCardTokensListUseCase: GetCardTokensListUseCase, - private val fetchTokenListUseCase: FetchTokenListUseCase, - private val fetchCardTokenListUseCase: FetchCardTokenListUseCase, - private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase, - private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase, - private val getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase, - private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase, - private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase, - private val getExploreUrlUseCase: GetExploreUrlUseCase, - private val unlockWalletsUseCase: UnlockWalletsUseCase, - private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, - private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCase, - private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase, private val shouldShowSaveWalletScreenUseCase: ShouldShowSaveWalletScreenUseCase, private val canUseBiometryUseCase: CanUseBiometryUseCase, private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase, + private val isWalletsScrollPreviewEnabled: IsWalletsScrollPreviewEnabled, private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, - private val removeCurrencyUseCase: RemoveCurrencyUseCase, - private val isCryptoCurrencyCoinCouldHide: IsCryptoCurrencyCoinCouldHideUseCase, - private val walletManagersFacade: WalletManagersFacade, - private val reduxStateHolder: ReduxStateHolder, + analyticsEventsHandler: AnalyticsEventHandler, private val dispatchers: CoroutineDispatcherProvider, - private val analyticsEventsHandler: AnalyticsEventHandler, - private val changeCardAnalyticsContextUseCase: ChangeCardAnalyticsContextUseCase, - private val setCardWasScannedUseCase: SetCardWasScannedUseCase, - private val remindToRateAppLaterUseCase: RemindToRateAppLaterUseCase, - private val neverToSuggestRateAppUseCase: NeverToSuggestRateAppUseCase, - private val setWalletWithFundsFoundUseCase: SetWalletWithFundsFoundUseCase, - private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase, - private val isDemoCardUseCase: IsDemoCardUseCase, - private val scanCardToUnlockWalletUseCase: ScanCardToUnlockWalletClickHandler, - private val shouldShowSwapPromoWalletUseCase: ShouldShowSwapPromoWalletUseCase, - isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase, - isNeedToBackupUseCase: IsNeedToBackupUseCase, - getMissedAddressesCryptoCurrenciesUseCase: GetMissedAddressesCryptoCurrenciesUseCase, - hasSingleWalletSignedHashesUseCase: HasSingleWalletSignedHashesUseCase, - // endregion Parameters -) : ViewModel(), DefaultLifecycleObserver, WalletClickIntents { + private val reduxStateHolder: ReduxStateHolder, + private val screenLifecycleProvider: ScreenLifecycleProvider, + private val selectedWalletAnalyticsSender: SelectedWalletAnalyticsSender, + private val walletDeepLinksHandler: WalletDeepLinksHandler, +) : ViewModel() { - /** Feature router */ - var router: InnerWalletRouter by Delegates.notNull() + val uiState: StateFlow = stateHolder.uiState - private val selectedAppCurrencyFlow: StateFlow = createSelectedAppCurrencyFlow() + private lateinit var router: InnerWalletRouter + private var walletsUpdateJobHolder: JobHolder = JobHolder() - private val notificationsListFactory = WalletNotificationsListFactory( - isDemoCardUseCase = isDemoCardUseCase, - isReadyToShowRateAppUseCase = isReadyToShowRateAppUseCase, - isNeedToBackupUseCase = isNeedToBackupUseCase, - getMissedAddressCryptoCurrenciesUseCase = getMissedAddressesCryptoCurrenciesUseCase, - hasSingleWalletSignedHashesUseCase = hasSingleWalletSignedHashesUseCase, - shouldShowSwapPromoWalletUseCase = shouldShowSwapPromoWalletUseCase, - clickIntents = this, - ) - - private var isBalanceHidden = true - - private val stateFactory = WalletStateFactory( - currentStateProvider = Provider { uiState }, - currentCardTypeResolverProvider = Provider { - getCardTypeResolver( - index = requireNotNull(uiState as? WalletState.ContentState).walletsListConfig.selectedWalletIndex, - ) - }, - currentWalletProvider = Provider { - wallets[requireNotNull(uiState as? WalletState.ContentState).walletsListConfig.selectedWalletIndex] - }, - appCurrencyProvider = Provider(selectedAppCurrencyFlow::value), - isBalanceHiddenProvider = Provider { isBalanceHidden }, - clickIntents = this, - ) - - /** Screen state */ - var uiState: WalletState by uiStateHolder(initialState = stateFactory.getInitialState()) - - private var wallets: List by Delegates.notNull() - private var singleWalletCryptoCurrencyStatus: CryptoCurrencyStatus? = null - - private val tokensJobHolder = JobHolder() - private val updateWcJobHolder = JobHolder() - private val marketPriceJobHolder = JobHolder() - private val buttonsJobHolder = JobHolder() - private val notificationsJobHolder = JobHolder() - private val refreshContentJobHolder = JobHolder() - private val onWalletChangeJobHolder = JobHolder() - - private val walletsUpdateActionResolver = WalletsUpdateActionResolver( - currentStateProvider = Provider { uiState }, - getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, - ) - - override fun onCreate(owner: LifecycleOwner) { + init { analyticsEventsHandler.send(WalletScreenAnalyticsEvent.MainScreen.ScreenOpened) + suggestToEnableBiometrics() + + subscribeOnWalletsUpdateFlow() + subscribeOnBalanceHiding() + subscribeOnSelectedWalletFlow() + } + + fun setWalletRouter(router: InnerWalletRouter) { + this.router = router + clickIntents.initialize(router, viewModelScope) + } + + fun subscribeToLifecycle(lifecycleOwner: LifecycleOwner) { + lifecycleOwner.lifecycle.addObserver(screenLifecycleProvider) + } + + override fun onCleared() { + super.onCleared() + stateHolder.clear() + walletScreenContentLoader.cancelAll() + } + + private fun suggestToEnableBiometrics() { viewModelScope.launch(dispatchers.main) { - delay(timeMillis = 1_800) + withContext(dispatchers.io) { delay(timeMillis = 1_800) } - if (router.isWalletLastScreen() && shouldShowSaveWalletScreenUseCase() && canUseBiometryUseCase()) { - router.openSaveUserWalletScreen() - } + if (isShowSaveWalletScreenEnabled()) router.openSaveUserWalletScreen() } + } - viewModelScope.launch(dispatchers.io) { + private suspend fun isShowSaveWalletScreenEnabled(): Boolean { + return router.isWalletLastScreen() && shouldShowSaveWalletScreenUseCase() && canUseBiometryUseCase() + } + + private fun subscribeOnWalletsUpdateFlow() { + viewModelScope.launch(dispatchers.main) { shouldSaveUserWalletsUseCase() - .flowWithLifecycle(owner.lifecycle) - .collectLatest { - getWalletsUseCase() - .flowWithLifecycle(owner.lifecycle) - .distinctUntilChanged() - .onEach(::updateWallets) - .flowOn(dispatchers.io) - .launchIn(viewModelScope) - } + .conflate() + .distinctUntilChanged() + .collectLatest(::subscribeToUserWalletsUpdates) } + } - getBalanceHidingSettingsUseCase() - .flowWithLifecycle(owner.lifecycle) - .onEach { - isBalanceHidden = it.isBalanceHidden - WalletStateCache.updateAll { copySealed(isBalanceHidden = it.isBalanceHidden) } - uiState = stateFactory.getHiddenBalanceState(isBalanceHidden = it.isBalanceHidden) + private fun subscribeToUserWalletsUpdates(shouldSaveUserWallet: Boolean) { + getWalletsUseCase() + .conflate() + .distinctUntilChanged() + .map { + walletsUpdateActionResolver.resolve( + wallets = it, + currentState = stateHolder.value, + canSaveWallets = shouldSaveUserWallet, + ) } + .onEach(::updateWallets) + .flowOn(dispatchers.main) + .launchIn(viewModelScope) + .saveIn(walletsUpdateJobHolder) + } + + private fun subscribeOnBalanceHiding() { + getBalanceHidingSettingsUseCase() + .conflate() + .distinctUntilChanged() + .onEach { + stateHolder.update(transformer = UpdateBalanceHidingModeTransformer(it.isBalanceHidden)) + } + .flowOn(dispatchers.main) .launchIn(viewModelScope) } - private fun updateWallets(sourceList: List) { - wallets = sourceList + private fun subscribeOnSelectedWalletFlow() { + getSelectedWalletUseCase().onRight { + it + .conflate() + .distinctUntilChanged() + .onEach { selectedWallet -> + if (selectedWallet.isMultiCurrency) { + Timber.d("WalletConnect: initialize and setup networks for ${selectedWallet.walletId}") - if (sourceList.isEmpty()) return + reduxStateHolder.dispatch( + action = WalletConnectActions.New.Initialize(userWallet = selectedWallet), + ) - when (val action = walletsUpdateActionResolver.resolve(sourceList)) { - is WalletsUpdateActionResolver.Action.Initialize -> { - initializeAndLoadState(selectedWalletIndex = action.selectedWalletIndex) + reduxStateHolder.dispatch( + action = WalletConnectActions.New.SetupUserChains(userWallet = selectedWallet), + ) + + selectedWalletAnalyticsSender.send(selectedWallet) + } + + walletDeepLinksHandler.registerForSingleCurrencyWallets( + viewModel = this, + userWallet = selectedWallet, + ) + } + .flowOn(dispatchers.main) + .launchIn(viewModelScope) + } + } + + private fun updateWallets(action: WalletsUpdateActionResolver.Action) { + when (action) { + is WalletsUpdateActionResolver.Action.InitializeWallets -> initializeWallets(action) + is WalletsUpdateActionResolver.Action.ReinitializeWallets -> { + walletScreenContentLoader.load( + userWallet = action.selectedWallet, + clickIntents = clickIntents, + isRefresh = true, + coroutineScope = viewModelScope, + ) + } + is WalletsUpdateActionResolver.Action.ReinitializeWallet -> reinitializeWallet(action) + is WalletsUpdateActionResolver.Action.AddWallet -> addWallet(action) + is WalletsUpdateActionResolver.Action.DeleteWallet -> deleteWallet(action) + is WalletsUpdateActionResolver.Action.UnlockWallet -> unlockWallet(action) + is WalletsUpdateActionResolver.Action.UpdateWalletCardCount -> { + stateHolder.update(transformer = UpdateWalletCardsCountTransformer(action.selectedWallet)) } is WalletsUpdateActionResolver.Action.UpdateWalletName -> { - uiState = stateFactory.getStateWithUpdatedWalletName(name = action.name) - } - is WalletsUpdateActionResolver.Action.UnlockWallet -> { - uiState = stateFactory.getUnlockedState(action) - - getContentItemsUpdates(index = action.selectedWalletIndex) - } - is WalletsUpdateActionResolver.Action.DeleteWallet -> { - deleteWalletAndUpdateState(action = action) - } - is WalletsUpdateActionResolver.Action.AddWallet -> { - scrollAndUpdateState(action.selectedWalletIndex) - } - is WalletsUpdateActionResolver.Action.UpdateWalletCardCount -> { - uiState = stateFactory.getStateWithUpdatedWalletCardCount() + stateHolder.update(transformer = RenameWalletTransformer(action.selectedWalletId, action.name)) } is WalletsUpdateActionResolver.Action.Unknown -> Unit } } - private fun initializeAndLoadState(selectedWalletIndex: Int) { - uiState = stateFactory.getSkeletonState(wallets = wallets, selectedWalletIndex = selectedWalletIndex) - - getContentItemsUpdates(index = selectedWalletIndex) - } - - private fun deleteWalletAndUpdateState(action: WalletsUpdateActionResolver.Action.DeleteWallet) { - val cacheState = WalletStateCache.getState(userWalletId = action.selectedWalletId) - if (cacheState != null) { - uiState = stateFactory.getStateWithoutDeletedWallet(cacheState, action) - - uiState = stateFactory.getStateAndTriggerEvent( - state = uiState, - event = WalletEvent.ChangeWallet(action.selectedWalletIndex), - setUiState = { uiState = it }, - ) - getContentItemsUpdates(action.selectedWalletIndex) - } else { - /* It's impossible case because user can delete only visible state, but we support this case */ - scrollAndUpdateState(selectedWalletIndex = action.selectedWalletIndex) - } - } - - private fun scrollAndUpdateState(selectedWalletIndex: Int) { - uiState = stateFactory.getSkeletonState( - wallets = wallets, - selectedWalletIndex = selectedWalletIndex, + private fun initializeWallets(action: WalletsUpdateActionResolver.Action.InitializeWallets) { + walletScreenContentLoader.load( + userWallet = action.selectedWallet, + clickIntents = clickIntents, + coroutineScope = viewModelScope, ) - uiState = stateFactory.getStateAndTriggerEvent( - state = uiState, - event = WalletEvent.ChangeWallet(index = selectedWalletIndex), - setUiState = { uiState = it }, - ) - - getContentItemsUpdates(index = selectedWalletIndex) - } - - override fun onBackClick() { - router.popBackStack() - } - - override fun onGenerateMissedAddressesClick(missedAddressCurrencies: List) { - // TODO: delete [REDACTED_JIRA] - // val state = uiState as? WalletState.ContentState ?: return - // - // analyticsEventsHandler.send(WalletScreenAnalyticsEvent.Basic.CardWasScanned(AnalyticsParam.ScannedFrom.Main)) - // analyticsEventsHandler.send(WalletScreenAnalyticsEvent.MainScreen.NoticeScanYourCardTapped) - // - // viewModelScope.launch(dispatchers.io) { - // val userWallet = getWallet(index = state.walletsListConfig.selectedWalletIndex) - // - // deriveMissingCurrencies( - // scanResponse = userWallet.scanResponse, - // currencyList = missedAddressCurrencies, - // ) { scannedCardResponse -> - // updateWalletUseCase( - // userWalletId = userWallet.walletId, - // update = { it.copy(scanResponse = scannedCardResponse) }, - // ) - // .onRight { - // fetchTokenListUseCase(userWalletId = it.walletId) - // } - // } - // } - } - - override fun onScanToUnlockWalletClick() { - val state = uiState as? WalletState.ContentState ?: return - - analyticsEventsHandler.send(event = WalletScreenAnalyticsEvent.MainScreen.WalletUnlockTapped) - - val lockedWallet = getWallet(index = state.walletsListConfig.selectedWalletIndex) - - viewModelScope.launch(dispatchers.main) { - scanCardToUnlockWalletUseCase(walletId = lockedWallet.walletId) - .onLeft { error -> - when (error) { - ScanCardToUnlockWalletError.WrongCardIsScanned -> { - delay(timeMillis = DELAY_SDK_DIALOG_CLOSE) - - uiState = stateFactory.getStateAndTriggerEvent( - state = uiState, - event = WalletEvent.ShowAlert(state = WalletAlertState.WrongCardIsScanned), - setUiState = { uiState = it }, - ) - } - ScanCardToUnlockWalletError.ManyScanFails -> { - router.openScanFailedDialog() - } - } - } - } - } - - override fun onDetailsClick() = router.openDetailsScreen() - - override fun onBackupCardClick() { - analyticsEventsHandler.send(WalletScreenAnalyticsEvent.MainScreen.NoticeBackupYourWalletTapped) - reduxStateHolder.dispatch( - LegacyAction.StartOnboardingProcess( - scanResponse = getSelectedWallet().scanResponse, - canSkipBackup = false, + stateHolder.update( + transformer = InitializeWalletsTransformer( + selectedWalletIndex = action.selectedWalletIndex, + selectedWallet = action.selectedWallet, + wallets = action.wallets, + clickIntents = clickIntents, ), ) - router.openOnboardingScreen() + + viewModelScope.launch(dispatchers.main) { + if (action.wallets.size > 1 && isWalletsScrollPreviewEnabled()) { + withContext(dispatchers.io) { + delay(timeMillis = 1_800) + } + + walletEventSender.send( + event = WalletEvent.DemonstrateWalletsScrollPreview( + direction = if (action.selectedWalletIndex == action.wallets.lastIndex) { + Direction.RIGHT + } else { + Direction.LEFT + }, + ), + ) + } + } } - override fun onSignedHashesNotificationCloseClick() { - val state = uiState as? WalletState.ContentState ?: return + private fun reinitializeWallet(action: WalletsUpdateActionResolver.Action.ReinitializeWallet) { viewModelScope.launch(dispatchers.main) { - setCardWasScannedUseCase( - cardId = getWallet(index = state.walletsListConfig.selectedWalletIndex).cardId, + walletScreenContentLoader.cancel(action.prevWalletId) + + walletScreenContentLoader.load( + userWallet = action.selectedWallet, + clickIntents = clickIntents, + coroutineScope = viewModelScope, + ) + + stateHolder.update( + ReinitializeWalletTransformer(userWallet = action.selectedWallet, clickIntents = clickIntents), ) } } - override fun onLikeAppClick() { - analyticsEventsHandler.send( - WalletScreenAnalyticsEvent.MainScreen.NoticeRateAppButton(AnalyticsParam.RateApp.Liked), - ) - uiState = stateFactory.getStateAndTriggerEvent( - state = uiState, - event = WalletEvent.RateApp( - onDismissClick = { - viewModelScope.launch(dispatchers.main) { - neverToSuggestRateAppUseCase() - } - }, + private fun addWallet(action: WalletsUpdateActionResolver.Action.AddWallet) { + viewModelScope.launch(dispatchers.main) { + stateHolder.update( + AddWalletTransformer( + userWallet = action.selectedWallet, + clickIntents = clickIntents, + ), + ) + + walletScreenContentLoader.load( + userWallet = action.selectedWallet, + clickIntents = clickIntents, + coroutineScope = viewModelScope, + ) + + withContext(dispatchers.io) { delay(timeMillis = 700) } + + scrollToWallet(index = action.selectedWalletIndex) + } + } + + private fun deleteWallet(action: WalletsUpdateActionResolver.Action.DeleteWallet) { + viewModelScope.launch(dispatchers.main) { + walletScreenContentLoader.load( + userWallet = action.selectedWallet, + clickIntents = clickIntents, + coroutineScope = viewModelScope, + ) + + scrollToWallet(index = action.selectedWalletIndex) + + withContext(dispatchers.io) { delay(timeMillis = 700) } + + stateHolder.update( + DeleteWalletTransformer( + selectedWalletIndex = action.selectedWalletIndex, + deletedWalletId = action.deletedWalletId, + ), + ) + } + } + + private fun unlockWallet(action: WalletsUpdateActionResolver.Action.UnlockWallet) { + viewModelScope.launch(dispatchers.main) { + withContext(dispatchers.io) { delay(timeMillis = 700) } + + stateHolder.update( + transformer = UnlockWalletTransformer( + unlockedWallets = action.unlockedWallets, + clickIntents = clickIntents, + ), + ) + + walletScreenContentLoader.load( + userWallet = action.selectedWallet, + clickIntents = clickIntents, + coroutineScope = viewModelScope, + ) + } + } + + private fun scrollToWallet(index: Int) { + stateHolder.update( + ScrollToWalletTransformer( + index = index, + currentStateProvider = Provider(action = stateHolder::value), + stateUpdater = { newState -> stateHolder.update { newState } }, ), - setUiState = { uiState = it }, ) } - - override fun onDislikeAppClick() { - analyticsEventsHandler.send( - WalletScreenAnalyticsEvent.MainScreen.NoticeRateAppButton(AnalyticsParam.RateApp.Disliked), - ) - viewModelScope.launch(dispatchers.main) { - neverToSuggestRateAppUseCase() - - reduxStateHolder.dispatch(LegacyAction.SendEmailRateCanBeBetter) - } - } - - override fun onCloseRateAppNotificationClick() { - analyticsEventsHandler.send( - WalletScreenAnalyticsEvent.MainScreen.NoticeRateAppButton(AnalyticsParam.RateApp.Closed), - ) - viewModelScope.launch(dispatchers.main) { - remindToRateAppLaterUseCase() - } - } - - override fun onWalletChange(index: Int) { - val state = uiState as? WalletState.ContentState ?: return - if (state.walletsListConfig.selectedWalletIndex == index) return - - changeCardAnalyticsContextUseCase(scanResponse = getWallet(index).scanResponse) - analyticsEventsHandler.send(WalletScreenAnalyticsEvent.MainScreen.WalletSwipe) - - // Reset the job to avoid a redundant state updating - onWalletChangeJobHolder.update(null) - - /* - * When wallet is changed it's necessary to stop the last jobs. - * If jobs aren't stopped and wallet is changed then it will update state for the prev wallet. - */ - tokensJobHolder.update(job = null) - updateWcJobHolder.update(job = null) - marketPriceJobHolder.update(job = null) - buttonsJobHolder.update(job = null) - notificationsJobHolder.update(job = null) - refreshContentJobHolder.update(job = null) - - viewModelScope.launch(dispatchers.main) { - val userWallet = state.walletsListConfig.wallets[index] - - withContext(dispatchers.io) { - selectWalletUseCase(userWallet.id) - } - - val cacheState = WalletStateCache.getState(userWalletId = userWallet.id) - if (cacheState != null && cacheState !is WalletLockedState) { - uiState = cacheState.copySealed( - walletsListConfig = state.walletsListConfig.copy( - selectedWalletIndex = index, - wallets = state.walletsListConfig.wallets - .mapIndexed { mapIndex, currentWallet -> - val cacheWallet = cacheState.walletsListConfig.wallets.getOrNull(mapIndex) - - if (currentWallet is WalletCardState.Loading && cacheWallet != null && - cacheWallet.isLoaded() - ) { - cacheWallet - } else { - currentWallet - } - } - .toImmutableList(), - ), - pullToRefreshConfig = cacheState.pullToRefreshConfig.copy(isRefreshing = false), - ) - - getContentItemsUpdates(index) - } else { - initializeAndLoadState(selectedWalletIndex = index) - } - } - .saveIn(onWalletChangeJobHolder) - } - - private fun WalletCardState.isLoaded(): Boolean { - return this !is WalletCardState.Loading && this !is WalletCardState.LockedContent - } - - override fun onRefreshSwipe() { - val selectedWalletIndex = (uiState as? WalletState.ContentState) - ?.walletsListConfig - ?.selectedWalletIndex - ?: return - - when (uiState) { - is WalletMultiCurrencyState.Content -> { - analyticsEventsHandler.send(PortfolioEvent.Refreshed) - refreshMultiCurrencyContent(selectedWalletIndex) - } - is WalletSingleCurrencyState.Content -> { - analyticsEventsHandler.send(PortfolioEvent.Refreshed) - refreshSingleCurrencyContent(selectedWalletIndex) - } - is WalletState.Initial, - is WalletMultiCurrencyState.Locked, - is WalletSingleCurrencyState.Locked, - -> Unit - } - } - - private fun refreshMultiCurrencyContent(walletIndex: Int) { - uiState = stateFactory.getRefreshingState() - - viewModelScope.launch(dispatchers.main) { - val wallet = getWallet(walletIndex) - - val maybeFetchResult = if (wallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()) { - fetchCardTokenListUseCase(userWalletId = wallet.walletId, refresh = true) - } else { - fetchTokenListUseCase(userWalletId = wallet.walletId, refresh = true) - } - - maybeFetchResult.onLeft { uiState = stateFactory.getStateByTokenListError(it) } - - uiState = stateFactory.getRefreshedState() - }.saveIn(refreshContentJobHolder) - } - - override fun onOrganizeTokensClick() { - analyticsEventsHandler.send(PortfolioEvent.OrganizeTokens) - - val state = requireNotNull(uiState as? WalletState.ContentState) - val index = state.walletsListConfig.selectedWalletIndex - val walletId = state.walletsListConfig.wallets[index].id - - router.openOrganizeTokensScreen(walletId) - } - - override fun onBuyClick(cryptoCurrencyStatus: CryptoCurrencyStatus) { - val state = uiState as? WalletState.ContentState ?: return - - analyticsEventsHandler.send( - event = TokenScreenAnalyticsEvent.ButtonBuy(cryptoCurrencyStatus.currency.symbol), - ) - - showErrorIfDemoModeOrElse { - reduxStateHolder.dispatch( - TradeCryptoAction.Buy( - userWallet = getWallet(index = state.walletsListConfig.selectedWalletIndex), - cryptoCurrencyStatus = cryptoCurrencyStatus, - appCurrencyCode = selectedAppCurrencyFlow.value.code, - ), - ) - } - } - - override fun onSwapClick(cryptoCurrencyStatus: CryptoCurrencyStatus) { - analyticsEventsHandler.send( - event = TokenScreenAnalyticsEvent.ButtonExchange(cryptoCurrencyStatus.currency.symbol), - ) - - reduxStateHolder.dispatch(TradeCryptoAction.Swap(cryptoCurrencyStatus.currency)) - } - - override fun onSingleCurrencySendClick(cryptoCurrencyStatus: CryptoCurrencyStatus?) { - val state = uiState as? WalletState.ContentState ?: return - - val userWallet = getWallet(index = state.walletsListConfig.selectedWalletIndex) - val coinStatus = if (userWallet.isMultiCurrency) cryptoCurrencyStatus else singleWalletCryptoCurrencyStatus - coinStatus ?: return - - analyticsEventsHandler.send( - event = TokenScreenAnalyticsEvent.ButtonSend(coinStatus.currency.symbol), - ) - - viewModelScope.launch(dispatchers.main) { - val maybeFeeCurrencyStatus = - getFeePaidCryptoCurrencyStatusSyncUseCase(userWallet.walletId, coinStatus).getOrNull() - reduxStateHolder.dispatch( - action = TradeCryptoAction.SendCoin( - userWallet = userWallet, - coinStatus = coinStatus, - feeCurrencyStatus = maybeFeeCurrencyStatus, - ), - ) - } - } - - override fun onMultiCurrencySendClick(cryptoCurrencyStatus: CryptoCurrencyStatus) { - val state = uiState as? WalletState.ContentState ?: return - - analyticsEventsHandler.send( - event = TokenScreenAnalyticsEvent.ButtonSend(cryptoCurrencyStatus.currency.symbol), - ) - - val userWallet = getWallet(index = state.walletsListConfig.selectedWalletIndex) - viewModelScope.launch(dispatchers.main) { - val maybeFeeCurrencyStatus = - getFeePaidCryptoCurrencyStatusSyncUseCase(userWallet.walletId, cryptoCurrencyStatus).getOrNull() - when (cryptoCurrencyStatus.currency) { - is CryptoCurrency.Coin -> { - uiState = stateFactory.getStateWithClosedBottomSheet() - reduxStateHolder.dispatch( - action = TradeCryptoAction.SendCoin( - userWallet = userWallet, - coinStatus = cryptoCurrencyStatus, - feeCurrencyStatus = maybeFeeCurrencyStatus, - ), - ) - } - is CryptoCurrency.Token -> sendToken(userWallet, cryptoCurrencyStatus, maybeFeeCurrencyStatus) - } - } - } - - private fun sendToken( - userWallet: UserWallet, - cryptoCurrencyStatus: CryptoCurrencyStatus, - feeCurrencyStatus: CryptoCurrencyStatus?, - ) { - viewModelScope.launch(dispatchers.io) { - getNetworkCoinStatusUseCase( - userWalletId = userWallet.walletId, - networkId = cryptoCurrencyStatus.currency.network.id, - derivationPath = cryptoCurrencyStatus.currency.network.derivationPath, - isSingleWalletWithTokens = userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken(), - ) - .take(count = 1) - .collectLatest { - it.onRight { coinStatus -> - uiState = stateFactory.getStateWithClosedBottomSheet() - reduxStateHolder.dispatchWithMain( - action = TradeCryptoAction.SendToken( - userWallet = userWallet, - tokenCurrency = requireNotNull(cryptoCurrencyStatus.currency as? CryptoCurrency.Token), - tokenFiatRate = cryptoCurrencyStatus.value.fiatRate, - coinFiatRate = coinStatus.value.fiatRate, - feeCurrencyStatus = feeCurrencyStatus, - ), - ) - } - } - } - } - - override fun onReceiveClick(cryptoCurrencyStatus: CryptoCurrencyStatus) { - analyticsEventsHandler.send( - event = TokenScreenAnalyticsEvent.ButtonReceive(cryptoCurrencyStatus.currency.symbol), - ) - - viewModelScope.launch(dispatchers.io) { - analyticsEventsHandler.send(event = TokenReceiveAnalyticsEvent.ReceiveScreenOpened) - - val currency = cryptoCurrencyStatus.currency - uiState = stateFactory.getStateWithOpenWalletBottomSheet( - content = TokenReceiveBottomSheetConfig( - name = currency.name, - symbol = currency.symbol, - network = currency.network.name, - addresses = cryptoCurrencyStatus.value.networkAddress - ?.availableAddresses - ?.mapToAddressModels(currency) - .orEmpty() - .toImmutableList(), - onCopyClick = { - analyticsEventsHandler.send(TokenReceiveAnalyticsEvent.ButtonCopyAddress(currency.symbol)) - }, - onShareClick = { - analyticsEventsHandler.send(TokenReceiveAnalyticsEvent.ButtonShareAddress(currency.symbol)) - }, - ), - ) - } - } - - override fun onCopyAddressClick(cryptoCurrencyStatus: CryptoCurrencyStatus) { - val state = uiState as? WalletState.ContentState ?: return - - analyticsEventsHandler.send( - event = TokenScreenAnalyticsEvent.ButtonCopyAddress(cryptoCurrencyStatus.currency.symbol), - ) - - viewModelScope.launch(dispatchers.main) { - val userWallet = getWallet(index = state.walletsListConfig.selectedWalletIndex) - - val defaultAddress = walletManagersFacade.getAddress( - userWalletId = userWallet.walletId, - network = cryptoCurrencyStatus.currency.network, - ).find { it.type == AddressType.Default } - - defaultAddress?.value?.let { address -> - uiState = stateFactory.getStateAndTriggerEvent( - state = uiState, - event = WalletEvent.CopyAddress( - address = address, - toast = resourceReference(R.string.wallet_notification_address_copied), - ), - setUiState = { uiState = it }, - ) - } - } - } - - override fun onSellClick(cryptoCurrencyStatus: CryptoCurrencyStatus) { - analyticsEventsHandler.send( - event = TokenScreenAnalyticsEvent.ButtonSell(cryptoCurrencyStatus.currency.symbol), - ) - - showErrorIfDemoModeOrElse { - reduxStateHolder.dispatch( - action = TradeCryptoAction.Sell( - cryptoCurrencyStatus = cryptoCurrencyStatus, - appCurrencyCode = selectedAppCurrencyFlow.value.code, - ), - ) - } - } - - override fun onManageTokensClick() { - analyticsEventsHandler.send(PortfolioEvent.ButtonManageTokens) - reduxStateHolder.dispatch(action = TokensAction.SetArgs.ManageAccess) - router.openManageTokensScreen() - } - - override fun onReloadClick() { - val selectedWalletIndex = (uiState as? WalletSingleCurrencyState) - ?.walletsListConfig - ?.selectedWalletIndex - ?: return - - refreshSingleCurrencyContent(selectedWalletIndex) - } - - override fun onCloseSwapPromoNotificationClick() { - viewModelScope.launch(dispatchers.main) { - shouldShowSwapPromoWalletUseCase.neverToShow() - } - } - - // FIXME: refreshSingleCurrencyContent mustn't update the TxHistory and Buttons. It only must fetch primary - // currency. Now it not works because GetPrimaryCurrency's subscriber uses .distinctUntilChanged() - private fun refreshSingleCurrencyContent(walletIndex: Int) { - uiState = stateFactory.getRefreshingState() - val wallet = getWallet(walletIndex) - - viewModelScope.launch(dispatchers.main) { - singleWalletCryptoCurrencyStatus?.let { - updateButtons(userWallet = wallet, currencyStatus = it) - } - val result = fetchCurrencyStatusUseCase(wallet.walletId, refresh = true) - - uiState = stateFactory.getRefreshedState() - uiState = result.fold(stateFactory::getStateByCurrencyStatusError) { uiState } - - singleWalletCryptoCurrencyStatus?.let { - val singleCurrencyState = uiState as WalletSingleCurrencyState - if (singleCurrencyState.txHistoryState !is TxHistoryState.Content) { - // show loading indicator while refreshing in non content state - uiState = stateFactory.getLoadingTxHistoryState( - itemsCountEither = 1.right(), - pendingTransactions = it.value.pendingTransactions, - ) - } - updateTxHistory(userWalletId = wallet.walletId, currencyStatus = it, refresh = true) - } - }.saveIn(refreshContentJobHolder) - } - - override fun onExploreClick() { - showErrorIfDemoModeOrElse(action = ::openExplorer) - } - - private fun openExplorer() { - val state = uiState as? WalletState.ContentState ?: return - val currencyStatus = singleWalletCryptoCurrencyStatus ?: return - val currency = currencyStatus.currency - - viewModelScope.launch(dispatchers.main) { - val userWalletId = getWallet(state.walletsListConfig.selectedWalletIndex).walletId - - when (val addresses = currencyStatus.value.networkAddress) { - is NetworkAddress.Selectable -> { - uiState = stateFactory.getStateWithOpenWalletBottomSheet( - ChooseAddressBottomSheetConfig( - addressModels = addresses.availableAddresses - .mapToAddressModels(currency) - .toImmutableList(), - onClick = { - onAddressTypeSelected( - userWalletId = userWalletId, - currency = currency, - addressModel = it, - ) - }, - ), - ) - } - is NetworkAddress.Single -> { - router.openUrl( - url = getExploreUrlUseCase( - userWalletId = userWalletId, - currency = currency, - addressType = AddressType.Default, - ), - ) - } - null -> Unit - } - } - } - - private fun onAddressTypeSelected( - userWalletId: UserWalletId, - currency: CryptoCurrency, - addressModel: AddressModel, - ) { - viewModelScope.launch(dispatchers.main) { - router.openUrl( - url = getExploreUrlUseCase( - userWalletId = userWalletId, - currency = currency, - addressType = AddressType.valueOf(addressModel.type.name), - ), - ) - uiState = stateFactory.getStateWithClosedBottomSheet() - } - } - - private fun showErrorIfDemoModeOrElse(action: () -> Unit) { - val state = uiState as? WalletState.ContentState ?: return - val cardId = getWallet(index = state.walletsListConfig.selectedWalletIndex).cardId - - if (isDemoCardUseCase(cardId = cardId)) { - uiState = stateFactory.getStateWithClosedBottomSheet() - uiState = stateFactory.getStateAndTriggerEvent( - state = uiState, - event = WalletEvent.ShowError( - text = resourceReference(id = R.string.alert_demo_feature_disabled), - ), - setUiState = { uiState = it }, - ) - } else { - action() - } - } - - override fun onUnlockWalletClick() { - analyticsEventsHandler.send(WalletScreenAnalyticsEvent.MainScreen.NoticeWalletLocked) - - viewModelScope.launch(dispatchers.main) { - unlockWalletsUseCase(throwIfNotAllWalletsUnlocked = true) - .onLeft(::handleUnlockWalletsError) - } - } - - private fun handleUnlockWalletsError(error: UnlockWalletsError) { - val event = when (error) { - is UnlockWalletsError.DataError, - is UnlockWalletsError.UnableToUnlockWallets, - -> WalletEvent.ShowToast(resourceReference(R.string.user_wallet_list_error_unable_to_unlock)) - is UnlockWalletsError.NoUserWalletSelected, - is UnlockWalletsError.NotAllUserWalletsUnlocked, - -> WalletEvent.ShowAlert(WalletAlertState.RescanWallets) - } - - uiState = stateFactory.getStateAndTriggerEvent( - state = uiState, - event = event, - setUiState = { uiState = it }, - ) - } - - override fun onUnlockWalletNotificationClick() { - val state = requireNotNull(uiState as? WalletLockedState) { - "Impossible to unlock wallet if state isn't WalletLockedState" - } - - uiState = stateFactory.getStateWithOpenWalletBottomSheet( - content = when (state) { - is WalletMultiCurrencyState.Locked -> state.bottomSheetConfig.content - is WalletSingleCurrencyState.Locked -> state.bottomSheetConfig.content - }, - ) - } - - override fun onTokenItemClick(currency: CryptoCurrency) { - analyticsEventsHandler.send(PortfolioEvent.TokenTapped) - router.openTokenDetails(getSelectedWallet().walletId, currency) - } - - override fun onTokenItemLongClick(cryptoCurrencyStatus: CryptoCurrencyStatus) { - val userWallet = getSelectedWallet() - viewModelScope.launch(dispatchers.io) { - getCryptoCurrencyActionsUseCase( - userWallet = userWallet, - cryptoCurrencyStatus = cryptoCurrencyStatus, - ) - .take(count = 1) - .collectLatest { - uiState = stateFactory.getStateWithTokenActionBottomSheet(it) - } - } - } - - override fun onRenameClick(userWalletId: UserWalletId, name: String) { - viewModelScope.launch(dispatchers.io) { - updateWalletUseCase(userWalletId = userWalletId, update = { it.copy(name) }) - } - } - - override fun onDeleteBeforeConfirmationClick(userWalletId: UserWalletId) { - uiState = stateFactory.getStateAndTriggerEvent( - state = uiState, - event = WalletEvent.ShowAlert( - state = WalletAlertState.RemoveWalletAlert( - onConfirmClick = { onDeleteAfterConfirmationClick(userWalletId) }, - ), - ), - setUiState = { uiState = it }, - ) - } - - override fun onDeleteAfterConfirmationClick(userWalletId: UserWalletId) { - val state = uiState as? WalletState.ContentState ?: return - viewModelScope.launch(dispatchers.io) { - deleteWalletUseCase(userWalletId) - - popBackIfAllWalletsIsLocked(wallets = state.walletsListConfig.wallets) - } - } - - private fun popBackIfAllWalletsIsLocked(wallets: List) { - val unlockedWallet = wallets.count { it !is WalletCardState.LockedContent } - - if (unlockedWallet == 1) { - router.popBackStack( - screen = if (wallets.size > 1) AppScreen.Welcome else AppScreen.Home, - ) - } - } - - override fun onPerformHideToken(cryptoCurrencyStatus: CryptoCurrencyStatus) { - val state = uiState as? WalletState.ContentState ?: return - val userWallet = getWallet(state.walletsListConfig.selectedWalletIndex) - viewModelScope.launch(dispatchers.io) { - removeCurrencyUseCase(userWallet.walletId, cryptoCurrencyStatus.currency) - .fold( - ifLeft = { showToast(resourceReference(R.string.common_error)) }, - ifRight = { uiState = stateFactory.getStateWithClosedBottomSheet() }, - ) - } - } - - override fun onHideTokensClick(cryptoCurrencyStatus: CryptoCurrencyStatus) { - analyticsEventsHandler.send( - event = TokenScreenAnalyticsEvent.ButtonRemoveToken(cryptoCurrencyStatus.currency.symbol), - ) - - viewModelScope.launch(dispatchers.main) { - val state = uiState as? WalletState.ContentState ?: return@launch - val userWallet = getWallet(state.walletsListConfig.selectedWalletIndex) - uiState = stateFactory.getStateAndTriggerEvent( - state = uiState, - event = getHideTokeAlert(userWallet.walletId, cryptoCurrencyStatus), - setUiState = { uiState = it }, - ) - } - } - - override fun onDismissBottomSheet() { - uiState = stateFactory.getStateWithClosedBottomSheet() - } - - override fun onTransactionClick(txHash: String) { - singleWalletCryptoCurrencyStatus?.let { currencyStatus -> - router.openUrl( - url = getExplorerTransactionUrlUseCase( - txHash = txHash, - networkId = currencyStatus.currency.network.id, - ), - ) - } - } - - private suspend fun getHideTokeAlert( - userWalletId: UserWalletId, - cryptoCurrencyStatus: CryptoCurrencyStatus, - ): WalletEvent.ShowAlert { - val currency = cryptoCurrencyStatus.currency - return if (currency is CryptoCurrency.Coin && !isCryptoCurrencyCoinCouldHide(userWalletId, currency)) { - WalletEvent.ShowAlert( - state = WalletAlertState.DefaultAlert( - title = resourceReference( - id = R.string.token_details_unable_hide_alert_title, - formatArgs = WrappedList(listOf(cryptoCurrencyStatus.currency.name)), - ), - message = resourceReference( - id = R.string.token_details_unable_hide_alert_message, - formatArgs = WrappedList( - listOf( - cryptoCurrencyStatus.currency.name, - cryptoCurrencyStatus.currency.network.name, - ), - ), - ), - onConfirmClick = null, - ), - ) - } else { - WalletEvent.ShowAlert( - state = WalletAlertState.DefaultAlert( - title = resourceReference( - id = R.string.token_details_hide_alert_title, - formatArgs = WrappedList(listOf(cryptoCurrencyStatus.currency.name)), - ), - message = resourceReference(R.string.token_details_hide_alert_message), - onConfirmClick = { onPerformHideToken(cryptoCurrencyStatus) }, - ), - ) - } - } - - private fun showToast(message: TextReference) { - uiState = stateFactory.getStateAndTriggerEvent( - state = uiState, - event = WalletEvent.ShowToast(message), - setUiState = { uiState = it }, - ) - } - - private fun getContentItemsUpdates(index: Int) { - /* - * When wallet is changed it's necessary to stop the last jobs. - * If jobs aren't stopped and wallet is changed then it will update state for the prev wallet. - */ - tokensJobHolder.update(job = null) - updateWcJobHolder.update(job = null) - marketPriceJobHolder.update(job = null) - buttonsJobHolder.update(job = null) - notificationsJobHolder.update(job = null) - refreshContentJobHolder.update(job = null) - - val wallet = getWallet(index) - - when { - wallet.isLocked -> { - uiState = stateFactory.getLockedState() - } - wallet.isMultiCurrency -> getMultiCurrencyContent(wallet, index) - wallet.scanResponse.cardTypesResolver.isSingleWalletWithToken() -> getSingleCurrencyWithTokenContent(index) - !wallet.isMultiCurrency -> getSingleCurrencyContent(index) - } - } - - private fun getMultiCurrencyContent(wallet: UserWallet, walletIndex: Int) { - val state = requireNotNull(uiState as? WalletMultiCurrencyState) { - "Impossible to get a token list updates if state isn't WalletMultiCurrencyState" - } - - val tokenListFlow = getTokenListUseCase(userWalletId = state.walletsListConfig.wallets[walletIndex].id) - .shareIn(viewModelScope, SharingStarted.WhileSubscribed()) - - initAndSetupWc(tokenListFlow, wallet) - - tokenListFlow - .conflate() - .distinctUntilChanged() - .onEach { maybeTokenList -> - uiState = stateFactory.getStateByTokensList(maybeTokenList.getTokenListWithWallet(wallet)) - - maybeTokenList.onRight { - analyticsEventsHandler.sendBalanceLoadedEvent(it) - checkMultiWalletWithFunds(it) - } - - updateNotifications( - index = walletIndex, - tokenList = maybeTokenList.fold(ifLeft = { null }, ifRight = { it }), - ) - } - .flowOn(dispatchers.io) - .launchIn(viewModelScope) - .saveIn(tokensJobHolder) - } - - private suspend fun checkMultiWalletWithFunds(tokenList: TokenList) { - val hasNonZeroWallets = when (tokenList) { - is TokenList.GroupedByNetwork -> { - tokenList.groups - .flatMap(NetworkGroup::currencies) - .hasNonZeroWallets() - } - is TokenList.Ungrouped -> tokenList.currencies.hasNonZeroWallets() - is TokenList.Empty -> false - } - - if (hasNonZeroWallets) { - setWalletWithFundsFoundUseCase() - } - } - - private fun initAndSetupWc(tokenListFlow: MaybeTokenListFlow, wallet: UserWallet) { - viewModelScope - .launch(dispatchers.main) { - initWalletConnectForWallet(wallet) - - tokenListFlow - .filterLoadedTokenList() - .take(count = 1) - .collect { setupWalletConnectOnWallet(wallet) } - } - .saveIn(updateWcJobHolder) - } - - private fun initWalletConnectForWallet(userWallet: UserWallet) { - reduxStateHolder.dispatch( - action = WalletConnectActions.New.Initialize(userWallet = userWallet), - ) - } - - private suspend fun setupWalletConnectOnWallet(userWallet: UserWallet) { - reduxStateHolder.dispatchWithMain( - action = WalletConnectActions.New.SetupUserChains(userWallet = userWallet), - ) - } - - private fun List.isAllCurrenciesLoaded(): Boolean { - return !this.any { it.value is CryptoCurrencyStatus.Loading } - } - - private fun MaybeTokenListFlow.filterLoadedTokenList(): MaybeTokenListFlow { - return filter { either -> - either.fold( - ifRight = { list -> - when (list) { - is TokenList.Ungrouped -> { - list.currencies.isAllCurrenciesLoaded() - } - is TokenList.GroupedByNetwork -> { - list.groups.flatMap(NetworkGroup::currencies).isAllCurrenciesLoaded() - } - else -> false - } - }, - ifLeft = { false }, - ) - } - } - - private fun List.hasNonZeroWallets(): Boolean { - return any { - val amount = it.value.amount ?: return@any false - !amount.isZero() - } - } - - private fun Either.getTokenListWithWallet( - userWallet: UserWallet, - ): Either { - return this.map { - TokenListWithWallet(it, userWallet) - } - } - - private fun getSingleCurrencyContent(index: Int) { - val wallet = getWallet(index) - getPrimaryCurrencyStatusUpdatesUseCase(wallet.walletId) - .conflate() - .distinctUntilChanged() - .onEach { maybeCryptoCurrencyStatus -> - uiState = stateFactory.getSingleCurrencyLoadedBalanceState(maybeCryptoCurrencyStatus) - - maybeCryptoCurrencyStatus.onRight { status -> - val fiatAmount = status.value.fiatAmount - - val cardBalanceState = when (status.value) { - is CryptoCurrencyStatus.Loaded, - is CryptoCurrencyStatus.NoAccount, - is CryptoCurrencyStatus.NoAmount, - -> { - when { - fiatAmount == null -> null - fiatAmount.isZero() -> AnalyticsParam.CardBalanceState.Empty - else -> AnalyticsParam.CardBalanceState.Full - } - } - is CryptoCurrencyStatus.NoQuote -> AnalyticsParam.CardBalanceState.NoRate - is CryptoCurrencyStatus.Unreachable, - -> AnalyticsParam.CardBalanceState.BlockchainError - is CryptoCurrencyStatus.MissedDerivation, - is CryptoCurrencyStatus.Loading, - is CryptoCurrencyStatus.Custom, - -> null - } - - cardBalanceState?.let { - analyticsEventsHandler.send( - event = WalletScreenAnalyticsEvent.Basic.BalanceLoaded(balance = it), - ) - } - - singleWalletCryptoCurrencyStatus = status - - if (status.value.amount?.isZero() == false) { - setWalletWithFundsFoundUseCase() - } - - updateNotifications(index) - updateButtons(userWallet = wallet, currencyStatus = status) - updateTxHistory(userWalletId = wallet.walletId, currencyStatus = status, refresh = false) - } - } - .flowOn(dispatchers.io) - .launchIn(viewModelScope) - .saveIn(marketPriceJobHolder) - } - - private fun getSingleCurrencyWithTokenContent(walletIndex: Int) { - val state = requireNotNull(uiState as? WalletMultiCurrencyState) { - "Impossible to get a token list updates if state isn't WalletMultiCurrencyState" - } - - val wallet = getWallet(walletIndex) - - getCardTokensListUseCase(userWalletId = state.walletsListConfig.wallets[walletIndex].id) - .conflate() - .distinctUntilChanged() - .onEach { maybeTokenList -> - uiState = stateFactory.getStateByTokensList(maybeTokenList.getTokenListWithWallet(wallet)) - - maybeTokenList.onRight { tokenList -> - analyticsEventsHandler.sendBalanceLoadedEvent(tokenList) - checkMultiWalletWithFunds(tokenList) - } - - updateNotifications( - index = walletIndex, - tokenList = maybeTokenList.fold(ifLeft = { null }, ifRight = { it }), - ) - } - .flowOn(dispatchers.io) - .launchIn(viewModelScope) - .saveIn(tokensJobHolder) - } - - private fun AnalyticsEventHandler.sendBalanceLoadedEvent(tokenList: TokenList) { - val cardBalanceState = when (val fiatBalance = tokenList.totalFiatBalance) { - is TokenList.FiatBalance.Failed -> { - val currenciesStatuses = when (tokenList) { - is TokenList.Empty -> emptyList() - is TokenList.GroupedByNetwork -> tokenList.groups.flatMap(NetworkGroup::currencies) - is TokenList.Ungrouped -> tokenList.currencies - } - - when { - currenciesStatuses.isEmpty() -> AnalyticsParam.CardBalanceState.Empty - currenciesStatuses.any { it.value is CryptoCurrencyStatus.NoQuote } -> { - AnalyticsParam.CardBalanceState.NoRate - } - else -> AnalyticsParam.CardBalanceState.BlockchainError - } - } - is TokenList.FiatBalance.Loaded -> { - if (fiatBalance.amount > BigDecimal.ZERO) { - AnalyticsParam.CardBalanceState.Full - } else if (fiatBalance.amount.isZero()) { - AnalyticsParam.CardBalanceState.Empty - } else { - null - } - } - TokenList.FiatBalance.Loading -> null - } - - cardBalanceState?.let { - send(event = WalletScreenAnalyticsEvent.Basic.BalanceLoaded(balance = it)) - } - } - - private fun updateTxHistory(userWalletId: UserWalletId, currencyStatus: CryptoCurrencyStatus, refresh: Boolean) { - viewModelScope.launch(dispatchers.io) { - val txHistoryItemsCountEither = txHistoryItemsCountUseCase( - userWalletId = userWalletId, - currency = currencyStatus.currency, - ) - - uiState = stateFactory.getLoadingTxHistoryState( - itemsCountEither = txHistoryItemsCountEither, - pendingTransactions = currencyStatus.value.pendingTransactions, - ) - - txHistoryItemsCountEither.onRight { - uiState = stateFactory.getLoadedTxHistoryState( - txHistoryEither = txHistoryItemsUseCase( - userWalletId = userWalletId, - currency = currencyStatus.currency, - refresh = refresh, - ).map { - it.cachedIn(viewModelScope) - }, - ) - } - } - } - - private fun updateButtons(userWallet: UserWallet, currencyStatus: CryptoCurrencyStatus) { - getCryptoCurrencyActionsUseCase( - userWallet = userWallet, - cryptoCurrencyStatus = currencyStatus, - ) - .conflate() - .distinctUntilChanged() - .onEach { uiState = stateFactory.getSingleCurrencyManageButtonsState(actionsState = it) } - .flowOn(dispatchers.io) - .launchIn(viewModelScope) - .saveIn(buttonsJobHolder) - } - - private fun updateNotifications(index: Int, tokenList: TokenList? = null) { - notificationsListFactory.create( - selectedWallet = getWallet(index), - cryptoCurrencyList = if (tokenList != null) { - when (tokenList) { - is TokenList.GroupedByNetwork -> { - tokenList.groups - .flatMap(NetworkGroup::currencies) - } - is TokenList.Ungrouped -> tokenList.currencies - is TokenList.Empty -> emptyList() - } - } else { - listOfNotNull(singleWalletCryptoCurrencyStatus) - }, - ) - .conflate() - .distinctUntilChanged() - .onEach { uiState = stateFactory.getStateByNotifications(notifications = it) } - .flowOn(dispatchers.io) - .launchIn(viewModelScope) - .saveIn(notificationsJobHolder) - } - - private fun createSelectedAppCurrencyFlow(): StateFlow { - return getSelectedAppCurrencyUseCase() - .map { maybeAppCurrency -> - maybeAppCurrency.getOrElse { AppCurrency.Default } - } - .stateIn( - scope = viewModelScope, - started = SharingStarted.Eagerly, - initialValue = AppCurrency.Default, - ) - } - - private fun getWallet(index: Int): UserWallet { - return requireNotNull( - value = wallets.getOrNull(index), - lazyMessage = { "WalletsList doesn't contain element with index = $index" }, - ) - } - - private fun getSelectedWallet(): UserWallet { - val state = uiState as? WalletState.ContentState - ?: error("Unable to get selected user wallet") - - return getWallet(state.walletsListConfig.selectedWalletIndex) - } - - private fun getCardTypeResolver(index: Int): CardTypesResolver = getWallet(index).scanResponse.cardTypesResolver } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModelV2.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModelV2.kt deleted file mode 100644 index 418da4487e..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModelV2.kt +++ /dev/null @@ -1,319 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.viewmodels - -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.tangem.core.analytics.api.AnalyticsEventHandler -import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase -import com.tangem.domain.redux.ReduxStateHolder -import com.tangem.domain.settings.CanUseBiometryUseCase -import com.tangem.domain.settings.IsWalletsScrollPreviewEnabled -import com.tangem.domain.settings.ShouldShowSaveWalletScreenUseCase -import com.tangem.domain.walletconnect.WalletConnectActions -import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase -import com.tangem.domain.wallets.usecase.GetWalletsUseCase -import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase -import com.tangem.feature.wallet.presentation.deeplink.WalletDeepLinksHandler -import com.tangem.feature.wallet.presentation.router.InnerWalletRouter -import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent -import com.tangem.feature.wallet.presentation.wallet.analytics.utils.SelectedWalletAnalyticsSender -import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContentLoader -import com.tangem.feature.wallet.presentation.wallet.state.WalletEvent -import com.tangem.feature.wallet.presentation.wallet.state.WalletEvent.DemonstrateWalletsScrollPreview.Direction -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletScreenState -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.* -import com.tangem.feature.wallet.presentation.wallet.state2.utils.WalletEventSender -import com.tangem.feature.wallet.presentation.wallet.utils.ScreenLifecycleProvider -import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 -import com.tangem.utils.Provider -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import com.tangem.utils.coroutines.JobHolder -import com.tangem.utils.coroutines.saveIn -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.* -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import timber.log.Timber -import javax.inject.Inject - -@Suppress("LongParameterList") -@HiltViewModel -internal class WalletViewModelV2 @Inject constructor( - private val stateHolder: WalletStateController, - private val clickIntents: WalletClickIntentsV2, - private val walletEventSender: WalletEventSender, - private val walletsUpdateActionResolver: WalletsUpdateActionResolverV2, - private val walletScreenContentLoader: WalletScreenContentLoader, - private val getSelectedWalletUseCase: GetSelectedWalletUseCase, - private val getWalletsUseCase: GetWalletsUseCase, - private val shouldShowSaveWalletScreenUseCase: ShouldShowSaveWalletScreenUseCase, - private val canUseBiometryUseCase: CanUseBiometryUseCase, - private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase, - private val isWalletsScrollPreviewEnabled: IsWalletsScrollPreviewEnabled, - private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, - analyticsEventsHandler: AnalyticsEventHandler, - private val dispatchers: CoroutineDispatcherProvider, - private val reduxStateHolder: ReduxStateHolder, - private val screenLifecycleProvider: ScreenLifecycleProvider, - private val selectedWalletAnalyticsSender: SelectedWalletAnalyticsSender, - private val walletDeepLinksHandler: WalletDeepLinksHandler, -) : ViewModel() { - - val uiState: StateFlow = stateHolder.uiState - - private lateinit var router: InnerWalletRouter - private var walletsUpdateJobHolder: JobHolder = JobHolder() - - init { - analyticsEventsHandler.send(WalletScreenAnalyticsEvent.MainScreen.ScreenOpened) - - suggestToEnableBiometrics() - - subscribeOnWalletsUpdateFlow() - subscribeOnBalanceHiding() - subscribeOnSelectedWalletFlow() - } - - fun setWalletRouter(router: InnerWalletRouter) { - this.router = router - clickIntents.initialize(router, viewModelScope) - } - - fun subscribeToLifecycle(lifecycleOwner: LifecycleOwner) { - lifecycleOwner.lifecycle.addObserver(screenLifecycleProvider) - } - - override fun onCleared() { - super.onCleared() - stateHolder.clear() - walletScreenContentLoader.cancelAll() - } - - private fun suggestToEnableBiometrics() { - viewModelScope.launch(dispatchers.main) { - withContext(dispatchers.io) { delay(timeMillis = 1_800) } - - if (isShowSaveWalletScreenEnabled()) router.openSaveUserWalletScreen() - } - } - - private suspend fun isShowSaveWalletScreenEnabled(): Boolean { - return router.isWalletLastScreen() && shouldShowSaveWalletScreenUseCase() && canUseBiometryUseCase() - } - - private fun subscribeOnWalletsUpdateFlow() { - viewModelScope.launch(dispatchers.main) { - shouldSaveUserWalletsUseCase() - .conflate() - .distinctUntilChanged() - .collectLatest(::subscribeToUserWalletsUpdates) - } - } - - private fun subscribeToUserWalletsUpdates(shouldSaveUserWallet: Boolean) { - getWalletsUseCase() - .conflate() - .distinctUntilChanged() - .map { - walletsUpdateActionResolver.resolve( - wallets = it, - currentState = stateHolder.value, - canSaveWallets = shouldSaveUserWallet, - ) - } - .onEach(::updateWallets) - .flowOn(dispatchers.main) - .launchIn(viewModelScope) - .saveIn(walletsUpdateJobHolder) - } - - private fun subscribeOnBalanceHiding() { - getBalanceHidingSettingsUseCase() - .conflate() - .distinctUntilChanged() - .onEach { - stateHolder.update(transformer = UpdateBalanceHidingModeTransformer(it.isBalanceHidden)) - } - .flowOn(dispatchers.main) - .launchIn(viewModelScope) - } - - private fun subscribeOnSelectedWalletFlow() { - getSelectedWalletUseCase().onRight { - it - .conflate() - .distinctUntilChanged() - .onEach { selectedWallet -> - if (selectedWallet.isMultiCurrency) { - Timber.d("WalletConnect: initialize and setup networks for ${selectedWallet.walletId}") - - reduxStateHolder.dispatch( - action = WalletConnectActions.New.Initialize(userWallet = selectedWallet), - ) - - reduxStateHolder.dispatch( - action = WalletConnectActions.New.SetupUserChains(userWallet = selectedWallet), - ) - - selectedWalletAnalyticsSender.send(selectedWallet) - } - - walletDeepLinksHandler.registerForSingleCurrencyWallets( - viewModel = this, - userWallet = selectedWallet, - ) - } - .flowOn(dispatchers.main) - .launchIn(viewModelScope) - } - } - - private fun updateWallets(action: WalletsUpdateActionResolverV2.Action) { - when (action) { - is WalletsUpdateActionResolverV2.Action.InitializeWallets -> initializeWallets(action) - is WalletsUpdateActionResolverV2.Action.ReinitializeWallets -> { - walletScreenContentLoader.load( - userWallet = action.selectedWallet, - clickIntents = clickIntents, - isRefresh = true, - coroutineScope = viewModelScope, - ) - } - is WalletsUpdateActionResolverV2.Action.ReinitializeWallet -> reinitializeWallet(action) - is WalletsUpdateActionResolverV2.Action.AddWallet -> addWallet(action) - is WalletsUpdateActionResolverV2.Action.DeleteWallet -> deleteWallet(action) - is WalletsUpdateActionResolverV2.Action.UnlockWallet -> unlockWallet(action) - is WalletsUpdateActionResolverV2.Action.UpdateWalletCardCount -> { - stateHolder.update(transformer = UpdateWalletCardsCountTransformer(action.selectedWallet)) - } - is WalletsUpdateActionResolverV2.Action.UpdateWalletName -> { - stateHolder.update(transformer = RenameWalletTransformer(action.selectedWalletId, action.name)) - } - is WalletsUpdateActionResolverV2.Action.Unknown -> Unit - } - } - - private fun initializeWallets(action: WalletsUpdateActionResolverV2.Action.InitializeWallets) { - walletScreenContentLoader.load( - userWallet = action.selectedWallet, - clickIntents = clickIntents, - coroutineScope = viewModelScope, - ) - - stateHolder.update( - transformer = InitializeWalletsTransformer( - selectedWalletIndex = action.selectedWalletIndex, - selectedWallet = action.selectedWallet, - wallets = action.wallets, - clickIntents = clickIntents, - ), - ) - - viewModelScope.launch(dispatchers.main) { - if (action.wallets.size > 1 && isWalletsScrollPreviewEnabled()) { - withContext(dispatchers.io) { - delay(timeMillis = 1_800) - } - - walletEventSender.send( - event = WalletEvent.DemonstrateWalletsScrollPreview( - direction = if (action.selectedWalletIndex == action.wallets.lastIndex) { - Direction.RIGHT - } else { - Direction.LEFT - }, - ), - ) - } - } - } - - private fun reinitializeWallet(action: WalletsUpdateActionResolverV2.Action.ReinitializeWallet) { - viewModelScope.launch(dispatchers.main) { - walletScreenContentLoader.cancel(action.prevWalletId) - - walletScreenContentLoader.load( - userWallet = action.selectedWallet, - clickIntents = clickIntents, - coroutineScope = viewModelScope, - ) - - stateHolder.update( - ReinitializeWalletTransformer(userWallet = action.selectedWallet, clickIntents = clickIntents), - ) - } - } - - private fun addWallet(action: WalletsUpdateActionResolverV2.Action.AddWallet) { - viewModelScope.launch(dispatchers.main) { - stateHolder.update( - AddWalletTransformer( - userWallet = action.selectedWallet, - clickIntents = clickIntents, - ), - ) - - walletScreenContentLoader.load( - userWallet = action.selectedWallet, - clickIntents = clickIntents, - coroutineScope = viewModelScope, - ) - - withContext(dispatchers.io) { delay(timeMillis = 700) } - - scrollToWallet(index = action.selectedWalletIndex) - } - } - - private fun deleteWallet(action: WalletsUpdateActionResolverV2.Action.DeleteWallet) { - viewModelScope.launch(dispatchers.main) { - walletScreenContentLoader.load( - userWallet = action.selectedWallet, - clickIntents = clickIntents, - coroutineScope = viewModelScope, - ) - - scrollToWallet(index = action.selectedWalletIndex) - - withContext(dispatchers.io) { delay(timeMillis = 700) } - - stateHolder.update( - DeleteWalletTransformer( - selectedWalletIndex = action.selectedWalletIndex, - deletedWalletId = action.deletedWalletId, - ), - ) - } - } - - private fun unlockWallet(action: WalletsUpdateActionResolverV2.Action.UnlockWallet) { - viewModelScope.launch(dispatchers.main) { - withContext(dispatchers.io) { delay(timeMillis = 700) } - - stateHolder.update( - transformer = UnlockWalletTransformer( - unlockedWallets = action.unlockedWallets, - clickIntents = clickIntents, - ), - ) - - walletScreenContentLoader.load( - userWallet = action.selectedWallet, - clickIntents = clickIntents, - coroutineScope = viewModelScope, - ) - } - } - - private fun scrollToWallet(index: Int) { - stateHolder.update( - ScrollToWalletTransformer( - index = index, - currentStateProvider = Provider(action = stateHolder::value), - stateUpdater = { newState -> stateHolder.update { newState } }, - ), - ) - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletsUpdateActionResolver.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletsUpdateActionResolver.kt index c8a6e6ca23..bdfd27558d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletsUpdateActionResolver.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletsUpdateActionResolver.kt @@ -4,140 +4,190 @@ import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.feature.wallet.presentation.wallet.domain.getCardsCount -import com.tangem.feature.wallet.presentation.wallet.state.WalletLockedState -import com.tangem.feature.wallet.presentation.wallet.state.WalletState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState -import com.tangem.utils.Provider +import com.tangem.feature.wallet.presentation.wallet.state.model.NOT_INITIALIZED_WALLET_INDEX +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletScreenState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState +import dagger.hilt.android.scopes.ViewModelScoped +import timber.log.Timber +import javax.inject.Inject /** * Resolver that determines which update action will be performed * - * @property currentStateProvider current state provider * @property getSelectedWalletSyncUseCase use case that returns selected wallet */ -internal class WalletsUpdateActionResolver( - private val currentStateProvider: Provider, +@ViewModelScoped +internal class WalletsUpdateActionResolver @Inject constructor( private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, ) { - fun resolve(wallets: List): Action { - val selectedWallet = wallets.getSelectedWallet() + private var isInitialized: Boolean = false + private var canSaveWallets: Boolean = false - return when (val state = currentStateProvider()) { - is WalletState.Initial -> { - Action.Initialize( - selectedWalletIndex = wallets.indexOfWallet(id = selectedWallet.walletId), - ) + fun resolve(wallets: List, currentState: WalletScreenState, canSaveWallets: Boolean): Action { + val selectedWallet = wallets.getSelectedWallet() ?: return Action.Unknown + + val action = when { + isFirstInitialization(currentState) -> { + createInitializeWalletsAction(wallets, selectedWallet, canSaveWallets) } - is WalletState.ContentState -> { - getActionToUpdateContent(state = state, wallets = wallets, selectedWallet = selectedWallet) + isReinitialization(canSaveWallets) -> { + this.canSaveWallets = canSaveWallets + Action.ReinitializeWallets(selectedWallet = selectedWallet) } + else -> getUpdateContentAction(currentState, wallets, selectedWallet) + } + + Timber.d("Resolved action: $action") + + return action + } + + private fun List.getSelectedWallet(): UserWallet? { + return when { + isEmpty() -> null + size == 1 -> first() + else -> getSelectedWalletSyncUseCase().fold(ifLeft = { null }, ifRight = { it }) } } - private fun List.getSelectedWallet(): UserWallet { - val hasUnlockedWallet = any { !it.isLocked } - return if (hasUnlockedWallet) { - val selectedWalletId = getSelectedWalletSyncUseCase().fold(ifLeft = ::error, ifRight = UserWallet::walletId) - - firstOrNull { it.walletId == selectedWalletId } - ?: error("Wallets don't contain a wallet with id: $selectedWalletId") - } else { - lastOrNull() ?: error("Wallets is empty") - } + private fun isFirstInitialization(state: WalletScreenState): Boolean { + return state.selectedWalletIndex == NOT_INITIALIZED_WALLET_INDEX } - private fun getActionToUpdateContent( - state: WalletState.ContentState, + private fun createInitializeWalletsAction( + wallets: List, + selectedWallet: UserWallet, + canSaveWallets: Boolean, + ): Action { + this.isInitialized = true + this.canSaveWallets = canSaveWallets + + return Action.InitializeWallets( + selectedWalletIndex = wallets.indexOfWallet(selectedWallet.walletId), + selectedWallet = selectedWallet, + wallets = wallets, + ) + } + + private fun isReinitialization(canSaveWallets: Boolean): Boolean { + return isInitialized && this.canSaveWallets != canSaveWallets + } + + private fun getUpdateContentAction( + state: WalletScreenState, wallets: List, selectedWallet: UserWallet, ): Action { return when { isWalletsCountChanged(state, wallets) -> { - getActionToChangeWallets(state = state, wallets = wallets, selectedWallet = selectedWallet) + getChangeWalletsListAction(state, wallets, selectedWallet) } - isSelectedWalletChanged(state, selectedWallet) -> { - Action.Initialize(wallets.indexOfWallet(selectedWallet.walletId)) + isAnotherWalletSelected(state, selectedWallet) -> { + Action.ReinitializeWallet( + prevWalletId = state.getPrevSelectedWallet().id, + selectedWallet = selectedWallet, + ) } - else -> getActionToUpdateCurrentWallet(state = state, wallets = wallets, selectedWallet = selectedWallet) + else -> getUpdateSelectedWalletAction(state, wallets, selectedWallet) } } - private fun isWalletsCountChanged(state: WalletState.ContentState, wallets: List): Boolean { - val prevWalletsSize = state.walletsListConfig.wallets.size + private fun isWalletsCountChanged(state: WalletScreenState, wallets: List): Boolean { + val prevWalletsSize = state.wallets.size val walletsSize = wallets.size return prevWalletsSize != walletsSize } - private fun getActionToChangeWallets( - state: WalletState.ContentState, + private fun getChangeWalletsListAction( + state: WalletScreenState, wallets: List, selectedWallet: UserWallet, ): Action { - val prevWalletsSize = state.walletsListConfig.wallets.size + val prevWalletsSize = state.wallets.size return when { prevWalletsSize > wallets.size -> { Action.DeleteWallet( - selectedWalletId = selectedWallet.walletId, + selectedWallet = selectedWallet, selectedWalletIndex = wallets.indexOfWallet(id = selectedWallet.walletId), - deletedWalletId = state.walletsListConfig.wallets.getDeletedWalletId(wallets), + deletedWalletId = state.wallets.getDeletedWalletId(wallets), ) } prevWalletsSize < wallets.size -> { + val newUserWallet = state.wallets.getAddedWallet(wallets) Action.AddWallet( - selectedWalletIndex = wallets.indexOfWallet(id = selectedWallet.walletId), + selectedWalletIndex = wallets.indexOfWallet(id = newUserWallet.walletId), + selectedWallet = newUserWallet, ) } - else -> Action.Unknown + else -> error("Wallets list is not changed") } } - private fun List.getDeletedWalletId(wallets: List): UserWalletId { + private fun List.getDeletedWalletId(wallets: List): UserWalletId { return this - .map(WalletCardState::id) + .map { it.walletCardState.id } .firstOrNull { !wallets.map(UserWallet::walletId).contains(it) } ?: error("Deleted wallet id is not found. Wallets contains all previous wallets ids") } - private fun isSelectedWalletChanged(state: WalletState.ContentState, selectedWallet: UserWallet): Boolean { + private fun List.getAddedWallet(wallets: List): UserWallet { + return wallets + .firstOrNull { wallet -> !this.map { it.walletCardState.id }.contains(wallet.walletId) } + ?: error("Added wallet id is not found. Wallets contains all previous wallets ids") + } + + private fun isAnotherWalletSelected(state: WalletScreenState, selectedWallet: UserWallet): Boolean { return state.getPrevSelectedWallet().id != selectedWallet.walletId } - private fun getActionToUpdateCurrentWallet( - state: WalletState.ContentState, + private fun getUpdateSelectedWalletAction( + state: WalletScreenState, wallets: List, selectedWallet: UserWallet, ): Action { - val selectedWalletName = selectedWallet.name - val previousWalletState = state.getPrevSelectedWallet() return when { - previousWalletState.title != selectedWalletName -> { - Action.UpdateWalletName(selectedWalletName) + isSelectedWalletNameChanged(state, selectedWallet) -> { + Action.UpdateWalletName(selectedWalletId = selectedWallet.walletId, name = selectedWallet.name) } - - state is WalletLockedState && !selectedWallet.isLocked -> { + isSelectedWalletUnlocked(state, selectedWallet) -> { Action.UnlockWallet( - selectedWalletIndex = wallets.indexOfWallet(id = selectedWallet.walletId), selectedWallet = selectedWallet, unlockedWallets = wallets.filterNot(UserWallet::isLocked), ) } - - previousWalletState is WalletCardState.Content && - previousWalletState.cardCount != selectedWallet.getCardsCount() -> { - Action.UpdateWalletCardCount - } - + isSelectedWalletCardsCountChanged(state, selectedWallet) -> Action.UpdateWalletCardCount(selectedWallet) else -> Action.Unknown } } - private fun WalletState.ContentState.getPrevSelectedWallet(): WalletCardState { - val prevSelectedWalletIndex = walletsListConfig.selectedWalletIndex + private fun isSelectedWalletNameChanged(state: WalletScreenState, selectedWallet: UserWallet): Boolean { + return state.getPrevSelectedWallet().title != selectedWallet.name + } - return walletsListConfig.wallets.getOrNull(prevSelectedWalletIndex) + private fun isSelectedWalletUnlocked(state: WalletScreenState, selectedWallet: UserWallet): Boolean { + return state.isSelectedWalletLocked() && !selectedWallet.isLocked + } + + private fun isSelectedWalletCardsCountChanged(state: WalletScreenState, selectedWallet: UserWallet): Boolean { + val prevSelectedWallet = state.getPrevSelectedWallet() + return prevSelectedWallet is WalletCardState.Content && + prevSelectedWallet.cardCount != selectedWallet.getCardsCount() + } + + private fun WalletScreenState.isSelectedWalletLocked(): Boolean { + val selectedWalletState = wallets.getOrNull(selectedWalletIndex) ?: error("Selected wallet is not found") + return selectedWalletState is WalletState.MultiCurrency.Locked || + selectedWalletState is WalletState.SingleCurrency.Locked + } + + private fun WalletScreenState.getPrevSelectedWallet(): WalletCardState { + return wallets + .map(WalletState::walletCardState) + .getOrNull(selectedWalletIndex) ?: error("Previous selected wallet is not found") } @@ -153,26 +203,110 @@ internal class WalletsUpdateActionResolver( sealed class Action { - data class Initialize(val selectedWalletIndex: Int) : Action() - - data class UpdateWalletName(val name: String) : Action() - - data class UnlockWallet( + data class InitializeWallets( val selectedWalletIndex: Int, val selectedWallet: UserWallet, - val unlockedWallets: List, - ) : Action() + val wallets: List, + ) : Action() { + + override fun toString(): String { + return """ + InitializeWallets( + selectedWalletIndex = $selectedWalletIndex, + selectedWallet = ${selectedWallet.walletId}, + wallets = ${wallets.joinToString { it.walletId.toString() }} + ) + """.trimIndent() + } + } + + /** + * Reinitialize wallets. Example, if user turned on wallets saving + * + * @property selectedWallet selected wallet + */ + data class ReinitializeWallets(val selectedWallet: UserWallet) : Action() { + + override fun toString(): String { + return "ReinitializeWallets(selectedWallet = ${selectedWallet.walletId})" + } + } + + /** + * Reinitialize selected wallet. Example, scanning a new card if wallets saving is turned off + * + * @property prevWalletId previous selected wallet id + * @property selectedWallet selected wallet + */ + data class ReinitializeWallet(val prevWalletId: UserWalletId, val selectedWallet: UserWallet) : Action() { + + override fun toString(): String { + return "ReinitializeWallet(prevWalletId = $prevWalletId, selectedWallet = ${selectedWallet.walletId})" + } + } + + data class UpdateWalletName(val selectedWalletId: UserWalletId, val name: String) : Action() { + + override fun toString(): String { + return "UpdateWalletName(selectedWalletId = $selectedWalletId, name = $name)" + } + } + + data class UnlockWallet(val selectedWallet: UserWallet, val unlockedWallets: List) : Action() { + + override fun toString(): String { + return """ + UnlockWallet( + selectedWallet = ${selectedWallet.walletId}, + unlockedWallets = ${unlockedWallets.joinToString { it.walletId.toString() }} + ) + """.trimIndent() + } + } data class DeleteWallet( - val selectedWalletId: UserWalletId, + val selectedWallet: UserWallet, val selectedWalletIndex: Int, val deletedWalletId: UserWalletId, - ) : Action() + ) : Action() { - data class AddWallet(val selectedWalletIndex: Int) : Action() + override fun toString(): String { + return """ + DeleteWallet( + selectedWallet = ${selectedWallet.walletId}, + selectedWalletIndex = $selectedWalletIndex, + deletedWalletId = $deletedWalletId + ) + """.trimIndent() + } + } - object UpdateWalletCardCount : Action() + data class AddWallet(val selectedWalletIndex: Int, val selectedWallet: UserWallet) : Action() { - object Unknown : Action() + override fun toString(): String { + return """ + AddWallet( + selectedWalletIndex = $selectedWalletIndex, + selectedWallet = ${selectedWallet.walletId} + ) + """.trimIndent() + } + } + + /** + * Update wallet card count. Example, if user backed up wallet + * + * @property selectedWallet selected wallet + */ + data class UpdateWalletCardCount(val selectedWallet: UserWallet) : Action() { + + override fun toString(): String { + return "UpdateWalletCardCount(selectedWallet = ${selectedWallet.walletId})" + } + } + + object Unknown : Action() { + override fun toString(): String = "Unknown" + } } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletsUpdateActionResolverV2.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletsUpdateActionResolverV2.kt deleted file mode 100644 index 1c8a3f7b26..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletsUpdateActionResolverV2.kt +++ /dev/null @@ -1,312 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.viewmodels - -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase -import com.tangem.feature.wallet.presentation.wallet.domain.getCardsCount -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState -import com.tangem.feature.wallet.presentation.wallet.state2.model.NOT_INITIALIZED_WALLET_INDEX -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletScreenState -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState -import dagger.hilt.android.scopes.ViewModelScoped -import timber.log.Timber -import javax.inject.Inject - -/** - * Resolver that determines which update action will be performed - * - * @property getSelectedWalletSyncUseCase use case that returns selected wallet - */ -@ViewModelScoped -internal class WalletsUpdateActionResolverV2 @Inject constructor( - private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, -) { - - private var isInitialized: Boolean = false - private var canSaveWallets: Boolean = false - - fun resolve(wallets: List, currentState: WalletScreenState, canSaveWallets: Boolean): Action { - val selectedWallet = wallets.getSelectedWallet() ?: return Action.Unknown - - val action = when { - isFirstInitialization(currentState) -> { - createInitializeWalletsAction(wallets, selectedWallet, canSaveWallets) - } - isReinitialization(canSaveWallets) -> { - this.canSaveWallets = canSaveWallets - Action.ReinitializeWallets(selectedWallet = selectedWallet) - } - else -> getUpdateContentAction(currentState, wallets, selectedWallet) - } - - Timber.d("Resolved action: $action") - - return action - } - - private fun List.getSelectedWallet(): UserWallet? { - return when { - isEmpty() -> null - size == 1 -> first() - else -> getSelectedWalletSyncUseCase().fold(ifLeft = { null }, ifRight = { it }) - } - } - - private fun isFirstInitialization(state: WalletScreenState): Boolean { - return state.selectedWalletIndex == NOT_INITIALIZED_WALLET_INDEX - } - - private fun createInitializeWalletsAction( - wallets: List, - selectedWallet: UserWallet, - canSaveWallets: Boolean, - ): Action { - this.isInitialized = true - this.canSaveWallets = canSaveWallets - - return Action.InitializeWallets( - selectedWalletIndex = wallets.indexOfWallet(selectedWallet.walletId), - selectedWallet = selectedWallet, - wallets = wallets, - ) - } - - private fun isReinitialization(canSaveWallets: Boolean): Boolean { - return isInitialized && this.canSaveWallets != canSaveWallets - } - - private fun getUpdateContentAction( - state: WalletScreenState, - wallets: List, - selectedWallet: UserWallet, - ): Action { - return when { - isWalletsCountChanged(state, wallets) -> { - getChangeWalletsListAction(state, wallets, selectedWallet) - } - isAnotherWalletSelected(state, selectedWallet) -> { - Action.ReinitializeWallet( - prevWalletId = state.getPrevSelectedWallet().id, - selectedWallet = selectedWallet, - ) - } - else -> getUpdateSelectedWalletAction(state, wallets, selectedWallet) - } - } - - private fun isWalletsCountChanged(state: WalletScreenState, wallets: List): Boolean { - val prevWalletsSize = state.wallets.size - val walletsSize = wallets.size - - return prevWalletsSize != walletsSize - } - - private fun getChangeWalletsListAction( - state: WalletScreenState, - wallets: List, - selectedWallet: UserWallet, - ): Action { - val prevWalletsSize = state.wallets.size - - return when { - prevWalletsSize > wallets.size -> { - Action.DeleteWallet( - selectedWallet = selectedWallet, - selectedWalletIndex = wallets.indexOfWallet(id = selectedWallet.walletId), - deletedWalletId = state.wallets.getDeletedWalletId(wallets), - ) - } - prevWalletsSize < wallets.size -> { - val newUserWallet = state.wallets.getAddedWallet(wallets) - Action.AddWallet( - selectedWalletIndex = wallets.indexOfWallet(id = newUserWallet.walletId), - selectedWallet = newUserWallet, - ) - } - else -> error("Wallets list is not changed") - } - } - - private fun List.getDeletedWalletId(wallets: List): UserWalletId { - return this - .map { it.walletCardState.id } - .firstOrNull { !wallets.map(UserWallet::walletId).contains(it) } - ?: error("Deleted wallet id is not found. Wallets contains all previous wallets ids") - } - - private fun List.getAddedWallet(wallets: List): UserWallet { - return wallets - .firstOrNull { wallet -> !this.map { it.walletCardState.id }.contains(wallet.walletId) } - ?: error("Added wallet id is not found. Wallets contains all previous wallets ids") - } - - private fun isAnotherWalletSelected(state: WalletScreenState, selectedWallet: UserWallet): Boolean { - return state.getPrevSelectedWallet().id != selectedWallet.walletId - } - - private fun getUpdateSelectedWalletAction( - state: WalletScreenState, - wallets: List, - selectedWallet: UserWallet, - ): Action { - return when { - isSelectedWalletNameChanged(state, selectedWallet) -> { - Action.UpdateWalletName(selectedWalletId = selectedWallet.walletId, name = selectedWallet.name) - } - isSelectedWalletUnlocked(state, selectedWallet) -> { - Action.UnlockWallet( - selectedWallet = selectedWallet, - unlockedWallets = wallets.filterNot(UserWallet::isLocked), - ) - } - isSelectedWalletCardsCountChanged(state, selectedWallet) -> Action.UpdateWalletCardCount(selectedWallet) - else -> Action.Unknown - } - } - - private fun isSelectedWalletNameChanged(state: WalletScreenState, selectedWallet: UserWallet): Boolean { - return state.getPrevSelectedWallet().title != selectedWallet.name - } - - private fun isSelectedWalletUnlocked(state: WalletScreenState, selectedWallet: UserWallet): Boolean { - return state.isSelectedWalletLocked() && !selectedWallet.isLocked - } - - private fun isSelectedWalletCardsCountChanged(state: WalletScreenState, selectedWallet: UserWallet): Boolean { - val prevSelectedWallet = state.getPrevSelectedWallet() - return prevSelectedWallet is WalletCardState.Content && - prevSelectedWallet.cardCount != selectedWallet.getCardsCount() - } - - private fun WalletScreenState.isSelectedWalletLocked(): Boolean { - val selectedWalletState = wallets.getOrNull(selectedWalletIndex) ?: error("Selected wallet is not found") - return selectedWalletState is WalletState.MultiCurrency.Locked || - selectedWalletState is WalletState.SingleCurrency.Locked - } - - private fun WalletScreenState.getPrevSelectedWallet(): WalletCardState { - return wallets - .map(WalletState::walletCardState) - .getOrNull(selectedWalletIndex) - ?: error("Previous selected wallet is not found") - } - - private fun List.indexOfWallet(id: UserWalletId): Int { - val selectedIndex = indexOfFirst { it.walletId == id } - - return if (selectedIndex == -1) { - error("Wallets don't contain a wallet with id: $id") - } else { - selectedIndex - } - } - - sealed class Action { - - data class InitializeWallets( - val selectedWalletIndex: Int, - val selectedWallet: UserWallet, - val wallets: List, - ) : Action() { - - override fun toString(): String { - return """ - InitializeWallets( - selectedWalletIndex = $selectedWalletIndex, - selectedWallet = ${selectedWallet.walletId}, - wallets = ${wallets.joinToString { it.walletId.toString() }} - ) - """.trimIndent() - } - } - - /** - * Reinitialize wallets. Example, if user turned on wallets saving - * - * @property selectedWallet selected wallet - */ - data class ReinitializeWallets(val selectedWallet: UserWallet) : Action() { - - override fun toString(): String { - return "ReinitializeWallets(selectedWallet = ${selectedWallet.walletId})" - } - } - - /** - * Reinitialize selected wallet. Example, scanning a new card if wallets saving is turned off - * - * @property prevWalletId previous selected wallet id - * @property selectedWallet selected wallet - */ - data class ReinitializeWallet(val prevWalletId: UserWalletId, val selectedWallet: UserWallet) : Action() { - - override fun toString(): String { - return "ReinitializeWallet(prevWalletId = $prevWalletId, selectedWallet = ${selectedWallet.walletId})" - } - } - - data class UpdateWalletName(val selectedWalletId: UserWalletId, val name: String) : Action() { - - override fun toString(): String { - return "UpdateWalletName(selectedWalletId = $selectedWalletId, name = $name)" - } - } - - data class UnlockWallet(val selectedWallet: UserWallet, val unlockedWallets: List) : Action() { - - override fun toString(): String { - return """ - UnlockWallet( - selectedWallet = ${selectedWallet.walletId}, - unlockedWallets = ${unlockedWallets.joinToString { it.walletId.toString() }} - ) - """.trimIndent() - } - } - - data class DeleteWallet( - val selectedWallet: UserWallet, - val selectedWalletIndex: Int, - val deletedWalletId: UserWalletId, - ) : Action() { - - override fun toString(): String { - return """ - DeleteWallet( - selectedWallet = ${selectedWallet.walletId}, - selectedWalletIndex = $selectedWalletIndex, - deletedWalletId = $deletedWalletId - ) - """.trimIndent() - } - } - - data class AddWallet(val selectedWalletIndex: Int, val selectedWallet: UserWallet) : Action() { - - override fun toString(): String { - return """ - AddWallet( - selectedWalletIndex = $selectedWalletIndex, - selectedWallet = ${selectedWallet.walletId} - ) - """.trimIndent() - } - } - - /** - * Update wallet card count. Example, if user backed up wallet - * - * @property selectedWallet selected wallet - */ - data class UpdateWalletCardCount(val selectedWallet: UserWallet) : Action() { - - override fun toString(): String { - return "UpdateWalletCardCount(selectedWallet = ${selectedWallet.walletId})" - } - } - - object Unknown : Action() { - override fun toString(): String = "Unknown" - } - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/VisaWalletIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/VisaWalletIntents.kt index 00c4927cde..f2f28ea189 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/VisaWalletIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/VisaWalletIntents.kt @@ -8,9 +8,9 @@ import com.tangem.domain.tokens.GetCryptoCurrencyStatusSyncUseCase import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.visa.GetVisaCurrencyUseCase import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter.BalancesAndLimitsBottomSheetConverter -import com.tangem.feature.wallet.presentation.wallet.state2.utils.WalletEventSender +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.BalancesAndLimitsBottomSheetConverter +import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.hilt.android.scopes.ViewModelScoped import kotlinx.collections.immutable.toImmutableList diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCardClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCardClickIntents.kt index e1866a709c..f3ae886366 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCardClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCardClickIntents.kt @@ -7,12 +7,12 @@ import com.tangem.domain.wallets.usecase.DeleteWalletUseCase import com.tangem.domain.wallets.usecase.UpdateWalletUseCase import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContentLoader -import com.tangem.feature.wallet.presentation.wallet.state.WalletAlertState -import com.tangem.feature.wallet.presentation.wallet.state.WalletEvent -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState -import com.tangem.feature.wallet.presentation.wallet.state2.utils.WalletEventSender +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAlertState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletCardState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.launch import timber.log.Timber diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletClickIntentsV2.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletClickIntents.kt similarity index 92% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletClickIntentsV2.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletClickIntents.kt index 19c3f6130f..c27ff4424e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletClickIntentsV2.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletClickIntents.kt @@ -12,10 +12,10 @@ import com.tangem.feature.wallet.presentation.router.InnerWalletRouter import com.tangem.feature.wallet.presentation.wallet.analytics.PortfolioEvent import com.tangem.feature.wallet.presentation.wallet.domain.unwrap import com.tangem.feature.wallet.presentation.wallet.loaders.WalletScreenContentLoader -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetRefreshStateTransformer -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetTokenListErrorTransformer +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetRefreshStateTransformer +import com.tangem.feature.wallet.presentation.wallet.state.transformers.SetTokenListErrorTransformer import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.hilt.android.scopes.ViewModelScoped import kotlinx.coroutines.CoroutineScope @@ -24,7 +24,7 @@ import javax.inject.Inject @Suppress("LongParameterList") @ViewModelScoped -internal class WalletClickIntentsV2 @Inject constructor( +internal class WalletClickIntents @Inject constructor( private val walletCardClickIntentsImplementor: WalletCardClickIntentsImplementor, private val warningsClickIntentsImplementer: WalletWarningsClickIntentsImplementer, private val currencyActionsClickIntentsImplementor: WalletCurrencyActionsClickIntentsImplementor, @@ -70,7 +70,7 @@ internal class WalletClickIntentsV2 @Inject constructor( maybeUserWallet.onRight { walletScreenContentLoader.load( userWallet = it, - clickIntents = this@WalletClickIntentsV2, + clickIntents = this@WalletClickIntents, coroutineScope = viewModelScope, ) } @@ -138,7 +138,7 @@ internal class WalletClickIntentsV2 @Inject constructor( walletScreenContentLoader.load( userWallet = userWallet, - clickIntents = this@WalletClickIntentsV2, + clickIntents = this@WalletClickIntents, isRefresh = true, coroutineScope = viewModelScope, ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletContentClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletContentClickIntents.kt index 6768550c5a..86e51c62e7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletContentClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletContentClickIntents.kt @@ -13,9 +13,9 @@ import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.feature.wallet.presentation.wallet.analytics.PortfolioEvent import com.tangem.feature.wallet.presentation.wallet.domain.unwrap -import com.tangem.feature.wallet.presentation.wallet.state.ActionsBottomSheetConfig -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter.MultiWalletCurrencyActionsConverter +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state.model.ActionsBottomSheetConfig +import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.MultiWalletCurrencyActionsConverter import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.hilt.android.scopes.ViewModelScoped import kotlinx.coroutines.flow.collectLatest diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCurrencyActionsClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCurrencyActionsClickIntents.kt index 25c2b29527..243f372827 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCurrencyActionsClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCurrencyActionsClickIntents.kt @@ -28,11 +28,11 @@ import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.wallet.domain.unwrap -import com.tangem.feature.wallet.presentation.wallet.state.WalletAlertState -import com.tangem.feature.wallet.presentation.wallet.state.WalletEvent -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.CloseBottomSheetTransformer -import com.tangem.feature.wallet.presentation.wallet.state2.utils.WalletEventSender +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAlertState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent +import com.tangem.feature.wallet.presentation.wallet.state.transformers.CloseBottomSheetTransformer +import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.hilt.android.scopes.ViewModelScoped import kotlinx.collections.immutable.toImmutableList diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt index 0a09fcf2c4..e9e03ac291 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt @@ -20,11 +20,11 @@ import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnaly import com.tangem.feature.wallet.presentation.wallet.domain.ScanCardToUnlockWalletClickHandler import com.tangem.feature.wallet.presentation.wallet.domain.ScanCardToUnlockWalletError import com.tangem.feature.wallet.presentation.wallet.domain.unwrap -import com.tangem.feature.wallet.presentation.wallet.state.WalletAlertState -import com.tangem.feature.wallet.presentation.wallet.state.WalletEvent -import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.CloseBottomSheetTransformer -import com.tangem.feature.wallet.presentation.wallet.state2.utils.WalletEventSender +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAlertState +import com.tangem.feature.wallet.presentation.wallet.state.model.WalletEvent +import com.tangem.feature.wallet.presentation.wallet.state.transformers.CloseBottomSheetTransformer +import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.hilt.android.scopes.ViewModelScoped import kotlinx.coroutines.launch From 29d577c0b3d67dfe0bba84e435dc638d7789f776 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 9 Feb 2024 14:11:02 +0300 Subject: [PATCH 22/53] Updated on 2026-08-14 --- .../main/java/com/tangem/domain/common/extensions/Blockchain.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt index f25e9a4f57..95ba582d02 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt @@ -82,6 +82,7 @@ fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? { "aptos/test" -> Blockchain.AptosTestnet "shibarium" -> Blockchain.Shibarium "shibarium/test" -> Blockchain.ShibariumTestnet + "" -> Blockchain.Playa3ull // FIXME else -> null } } From e80b02306f6bacd4ce62a09bf1c362f85e20c7d1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 9 Feb 2024 12:49:50 +0100 Subject: [PATCH 23/53] Updated on 2026-08-14 --- .../core/ui/components/buttons/SmallButton.kt | 21 ++++++- .../customtokens/ui/CustomTokensScreen.kt | 19 ++++-- .../managetokens/ui/ChooseNetworkScreen.kt | 60 +++++++++---------- .../managetokens/ui/ManageTokensScreen.kt | 7 +-- .../viewmodels/ManageTokensViewModel.kt | 4 +- 5 files changed, 66 insertions(+), 45 deletions(-) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/SmallButton.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/SmallButton.kt index 5528c5dab9..60bc4a690f 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/SmallButton.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/SmallButton.kt @@ -1,11 +1,13 @@ package com.tangem.core.ui.components.buttons +import androidx.compose.animation.animateColorAsState import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -51,12 +53,22 @@ fun SecondarySmallButton(config: SmallButtonConfig, modifier: Modifier = Modifie @Composable private fun SmallButton(config: SmallButtonConfig, isPrimary: Boolean, modifier: Modifier = Modifier) { val shape = RoundedCornerShape(size = TangemTheme.dimens.radius16) + + val backgroundColor by animateColorAsState( + targetValue = if (isPrimary) TangemTheme.colors.button.primary else TangemTheme.colors.button.secondary, + label = "Update background color", + ) + + val textColor by animateColorAsState( + targetValue = if (isPrimary) TangemTheme.colors.text.primary2 else TangemTheme.colors.text.primary1, + label = "Update text color", + ) Box( modifier = modifier .defaultMinSize(minWidth = TangemTheme.dimens.size46, minHeight = TangemTheme.dimens.size24) .clip(shape) .background( - color = if (isPrimary) TangemTheme.colors.button.primary else TangemTheme.colors.button.secondary, + color = backgroundColor, shape = shape, ) .clickable(enabled = true, onClick = config.onClick) @@ -66,8 +78,11 @@ private fun SmallButton(config: SmallButtonConfig, isPrimary: Boolean, modifier: contentAlignment = Alignment.Center, ) { Text( + modifier = Modifier.padding( + horizontal = TangemTheme.dimens.spacing10, + ), text = config.text.resolveReference(), - color = if (isPrimary) TangemTheme.colors.text.primary2 else TangemTheme.colors.text.primary1, + color = textColor, maxLines = 1, style = TangemTheme.typography.button, ) @@ -97,7 +112,7 @@ private fun ButtonsSample() { verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8), ) { val config = SmallButtonConfig( - text = TextReference.Str(value = "Add"), + text = TextReference.Str(value = "Adddddddd"), onClick = {}, ) PrimarySmallButton(config = config) diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/CustomTokensScreen.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/CustomTokensScreen.kt index 98f97905a6..ce244c3e85 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/CustomTokensScreen.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/CustomTokensScreen.kt @@ -12,9 +12,11 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview +import com.tangem.core.ui.components.Keyboard import com.tangem.core.ui.components.PrimaryButton import com.tangem.core.ui.components.RectangleShimmer import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.components.keyboardAsState import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme import com.tangem.features.managetokens.impl.R @@ -46,11 +48,14 @@ internal fun CustomTokensScreen(state: AddCustomTokenState, modifier: Modifier = @Composable private fun Content(state: AddCustomTokenState, modifier: Modifier = Modifier) { + val keyboard by keyboardAsState() + Column( modifier = modifier .background(color = TangemTheme.colors.background.tertiary) .statusBarsPadding() .navigationBarsPadding() + .imePadding() .padding( top = TangemTheme.dimens.spacing10, start = TangemTheme.dimens.spacing16, @@ -78,12 +83,14 @@ private fun Content(state: AddCustomTokenState, modifier: Modifier = Modifier) { .padding(bottom = TangemTheme.dimens.spacing16), ) CustomTokenItemsList(state = state) - PrimaryButton( - text = stringResource(id = R.string.custom_token_add_token), - onClick = state.addTokenButton.onClick, - enabled = state.addTokenButton.isEnabled, - modifier = Modifier.fillMaxWidth(), - ) + if (keyboard is Keyboard.Closed) { + PrimaryButton( + text = stringResource(id = R.string.custom_token_add_token), + onClick = state.addTokenButton.onClick, + enabled = state.addTokenButton.isEnabled, + modifier = Modifier.fillMaxWidth(), + ) + } } if (state.chooseWalletState is ChooseWalletState.Choose && state.chooseWalletState.show) { diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ChooseNetworkScreen.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ChooseNetworkScreen.kt index 444df9d0fe..ead0700da2 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ChooseNetworkScreen.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ChooseNetworkScreen.kt @@ -84,29 +84,18 @@ internal fun ChooseNetworkScreen( SpacerH(height = TangemTheme.dimens.spacing16) } - item { - if (networkState.nativeNetworks.isNotEmpty()) { - NativeNetworks(networkState = networkState, tokenState = state) - } + if (networkState.nativeNetworks.isNotEmpty()) { + this@LazyColumn.nativeNetworks(networkState = networkState, tokenState = state) } if (networkState.nonNativeNetworks.isNotEmpty()) { - item { - NonNativeNetworksHeader(networkState.onNonNativeNetworkHintClick) - } - item { - SpacerH(height = TangemTheme.dimens.spacing8) - } - item { - this@LazyColumn.NonNativeNetworks(networkState = networkState, tokenState = state) - } + this@LazyColumn.nonNativeNetworks(networkState = networkState, tokenState = state) } } } -@Composable -private fun NativeNetworks(networkState: ChooseNetworkState, tokenState: TokenItemState.Loaded) { - Column { +private fun LazyListScope.nativeNetworks(networkState: ChooseNetworkState, tokenState: TokenItemState.Loaded) { + item { Text( text = stringResource(id = R.string.manage_tokens_network_selector_native_title), color = TangemTheme.colors.text.tertiary, @@ -119,25 +108,35 @@ private fun NativeNetworks(networkState: ChooseNetworkState, tokenState: TokenIt style = TangemTheme.typography.caption2, ) SpacerH(height = TangemTheme.dimens.spacing8) + } - networkState.nativeNetworks.forEachIndexed { index, network -> - NetworkItem( - state = network, - tokenState = tokenState, - modifier = Modifier - .roundedShapeItemDecoration( - currentIndex = index, - lastIndex = networkState.nativeNetworks.lastIndex, - addDefaultPadding = false, - ), - ) - } + items( + count = networkState.nativeNetworks.count(), + key = { index -> networkState.nativeNetworks[index].id }, + ) { index -> + NetworkItem( + state = networkState.nativeNetworks[index], + tokenState = tokenState, + modifier = Modifier + .roundedShapeItemDecoration( + currentIndex = index, + lastIndex = networkState.nativeNetworks.lastIndex, + addDefaultPadding = false, + ), + ) + } + + item { SpacerH(height = TangemTheme.dimens.spacing16) } } -@Composable -private fun LazyListScope.NonNativeNetworks(networkState: ChooseNetworkState, tokenState: TokenItemState.Loaded) { +private fun LazyListScope.nonNativeNetworks(networkState: ChooseNetworkState, tokenState: TokenItemState.Loaded) { + item { + NonNativeNetworksHeader(networkState.onNonNativeNetworkHintClick) + SpacerH(height = TangemTheme.dimens.spacing8) + } + items( count = networkState.nonNativeNetworks.count(), key = { index -> networkState.nonNativeNetworks[index].id }, @@ -153,6 +152,7 @@ private fun LazyListScope.NonNativeNetworks(networkState: ChooseNetworkState, to ), ) } + item { SpacerH(height = TangemTheme.dimens.spacing16) } diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ManageTokensScreen.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ManageTokensScreen.kt index fb67526346..77c91a26e3 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ManageTokensScreen.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ManageTokensScreen.kt @@ -2,10 +2,7 @@ package com.tangem.managetokens.presentation.managetokens.ui import androidx.compose.animation.core.animateDpAsState import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.Surface import androidx.compose.runtime.* @@ -52,6 +49,8 @@ private fun Content(state: ManageTokensState) { Box( modifier = Modifier .fillMaxSize() + .navigationBarsPadding() + .imePadding() .background(color = TangemTheme.colors.background.primary) .padding(top = TangemTheme.dimens.spacing32), ) { diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensViewModel.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensViewModel.kt index e0630d462e..ac7b3cbc5d 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensViewModel.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensViewModel.kt @@ -323,7 +323,6 @@ internal class ManageTokensViewModel @Inject constructor( "It is only null if Blockchain is Unknown, which mustn't happen here" } if (!network.isAdded.value) { - updateUi(token, network) addedCurrenciesByWallet[selectedWallet]?.add(cryptoCurrency) allAddedCurrencies.add(cryptoCurrency) viewModelScope.launch(dispatchers.io) { @@ -332,13 +331,14 @@ internal class ManageTokensViewModel @Inject constructor( currency = cryptoCurrency, ) } + updateUi(token, network) } else { viewModelScope.launch(dispatchers.io) { if (canBeRemovedAndShowAlertIfNot(selectedWallet.walletId, cryptoCurrency)) { - withContext(dispatchers.main) { updateUi(token, network) } addedCurrenciesByWallet[selectedWallet]?.remove(cryptoCurrency) allAddedCurrencies.remove(cryptoCurrency) removeCurrencyUseCase(selectedWallet.walletId, cryptoCurrency) + withContext(dispatchers.main) { updateUi(token, network) } } } } From cedf6872acd9dfeb142fd8a0dc62418d23f07504 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 9 Feb 2024 13:42:08 +0300 Subject: [PATCH 24/53] Updated on 2026-08-14 --- .../features/home/compose/StoriesScreen.kt | 2 +- .../home/compose/views/HomeButtons.kt | 39 +++----------- .../compose/views/SearchCurrenciesButton.kt | 39 +++++++++----- .../home/compose/views/StoriesButton.kt | 51 +++++++++++++++++++ .../home/compose/views/StoriesProgressBar.kt | 24 ++++++--- .../components/buttons/common/TangemButton.kt | 3 +- 6 files changed, 103 insertions(+), 55 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/features/home/compose/views/StoriesButton.kt diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt b/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt index 79a40b22a9..73a454fe10 100644 --- a/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt @@ -170,7 +170,7 @@ private fun StoriesScreenContent(config: StoriesScreenContentConfig, modifier: M .padding(horizontal = TangemTheme.dimens.spacing16) .align(Alignment.BottomCenter) .fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16), + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12), ) { AnimatedVisibility( visible = config.currentStory == Stories.Currencies, diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/views/HomeButtons.kt b/app/src/main/java/com/tangem/tap/features/home/compose/views/HomeButtons.kt index aebf48b161..0533961886 100644 --- a/app/src/main/java/com/tangem/tap/features/home/compose/views/HomeButtons.kt +++ b/app/src/main/java/com/tangem/tap/features/home/compose/views/HomeButtons.kt @@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding -import androidx.compose.material.ButtonColors import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -13,15 +12,11 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider -import com.tangem.core.ui.components.SpacerW8 -import com.tangem.core.ui.components.buttons.common.TangemButton -import com.tangem.core.ui.components.buttons.common.TangemButtonColors +import com.tangem.core.ui.components.SpacerW12 import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition -import com.tangem.core.ui.res.TangemColorPalette import com.tangem.core.ui.res.TangemTheme import com.tangem.wallet.R -@Suppress("MagicNumber") @Composable internal fun HomeButtons( btnScanStateInProgress: Boolean, @@ -38,7 +33,7 @@ internal fun HomeButtons( showProgress = btnScanStateInProgress, onClick = onScanButtonClick, ) - SpacerW8() + SpacerW12() OrderCardButton( modifier = Modifier.weight(weight = 1f), onClick = onShopButtonClick, @@ -48,44 +43,26 @@ internal fun HomeButtons( @Composable private fun ScanCardButton(showProgress: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) { - TangemButton( + StoriesButton( modifier = modifier, text = stringResource(id = R.string.home_button_scan), + useDarkerColors = false, icon = TangemButtonIconPosition.End(iconResId = R.drawable.ic_tangem_24), - colors = LightBgScanCardButtonColors, - showProgress = showProgress, - enabled = true, onClick = onClick, + showProgress = showProgress, ) } @Composable private fun OrderCardButton(onClick: () -> Unit, modifier: Modifier = Modifier) { - TangemButton( + StoriesButton( modifier = modifier, text = stringResource(id = R.string.home_button_order), - icon = TangemButtonIconPosition.None, - colors = LightBgOrderCardButtonColors, - showProgress = false, - enabled = true, + useDarkerColors = true, onClick = onClick, ) } -private val LightBgScanCardButtonColors: ButtonColors = TangemButtonColors( - backgroundColor = TangemColorPalette.Light4, - contentColor = TangemColorPalette.Dark6, - disabledBackgroundColor = TangemColorPalette.Dark5, - disabledContentColor = TangemColorPalette.Dark6, -) - -private val LightBgOrderCardButtonColors: ButtonColors = TangemButtonColors( - backgroundColor = TangemColorPalette.Dark6, - contentColor = TangemColorPalette.White, - disabledBackgroundColor = TangemColorPalette.Dark6, - disabledContentColor = TangemColorPalette.White, -) - // region Preview @Preview(showBackground = true, widthDp = 360) @Composable @@ -95,10 +72,10 @@ private fun HomeButtonsPreview(@PreviewParameter(HomeButtonsParameterProvider::c modifier = Modifier.background(Color.Black), ) { HomeButtons( - modifier = Modifier.padding(all = TangemTheme.dimens.spacing16), btnScanStateInProgress = state.btnScanStateInProgress, onScanButtonClick = {}, onShopButtonClick = {}, + modifier = Modifier.padding(all = TangemTheme.dimens.spacing16), ) } } diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/views/SearchCurrenciesButton.kt b/app/src/main/java/com/tangem/tap/features/home/compose/views/SearchCurrenciesButton.kt index 722dd4f481..4e91ce98bc 100644 --- a/app/src/main/java/com/tangem/tap/features/home/compose/views/SearchCurrenciesButton.kt +++ b/app/src/main/java/com/tangem/tap/features/home/compose/views/SearchCurrenciesButton.kt @@ -1,31 +1,42 @@ package com.tangem.tap.features.home.compose.views -import androidx.compose.material.ButtonColors +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource -import com.tangem.core.ui.components.buttons.common.TangemButton -import com.tangem.core.ui.components.buttons.common.TangemButtonColors +import androidx.compose.ui.tooling.preview.Preview import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition -import com.tangem.core.ui.res.TangemColorPalette +import com.tangem.core.ui.res.TangemTheme import com.tangem.wallet.R @Composable internal fun SearchCurrenciesButton(onClick: () -> Unit, modifier: Modifier = Modifier) { - TangemButton( + StoriesButton( modifier = modifier, text = stringResource(id = R.string.common_search_tokens), icon = TangemButtonIconPosition.Start(R.drawable.ic_search_24), - onClick = onClick, - colors = SearchCurrenciesButtonColors, showProgress = false, - enabled = true, + useDarkerColors = true, + onClick = onClick, ) } -private val SearchCurrenciesButtonColors: ButtonColors = TangemButtonColors( - backgroundColor = TangemColorPalette.Dark5, - contentColor = TangemColorPalette.White, - disabledBackgroundColor = TangemColorPalette.Dark5, - disabledContentColor = TangemColorPalette.White, -) \ No newline at end of file +// region Preview +@Preview(showBackground = true, widthDp = 360) +@Composable +private fun SearchCurrenciesButtonPreview() { + TangemTheme { + Box( + modifier = Modifier + .background(color = Color.Black) + .padding(all = TangemTheme.dimens.spacing16), + ) { + SearchCurrenciesButton(modifier = Modifier.fillMaxWidth(), onClick = {}) + } + } +} +// endregion Preview \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/views/StoriesButton.kt b/app/src/main/java/com/tangem/tap/features/home/compose/views/StoriesButton.kt new file mode 100644 index 0000000000..45b8745cca --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/home/compose/views/StoriesButton.kt @@ -0,0 +1,51 @@ +package com.tangem.tap.features.home.compose.views + +import androidx.compose.material.ButtonColors +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.tangem.core.ui.components.buttons.common.TangemButton +import com.tangem.core.ui.components.buttons.common.TangemButtonColors +import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition +import com.tangem.core.ui.res.TangemColorPalette +import com.tangem.core.ui.res.TangemTheme + +@Composable +internal fun StoriesButton( + text: String, + useDarkerColors: Boolean, + onClick: () -> Unit, + modifier: Modifier = Modifier, + icon: TangemButtonIconPosition = TangemButtonIconPosition.None, + showProgress: Boolean = false, +) { + TangemButton( + modifier = modifier, + text = text, + icon = icon, + colors = if (useDarkerColors) DarkerButtonColors else LighterButtonColors, + showProgress = showProgress, + enabled = true, + shape = TangemTheme.shapes.roundedCornersXMedium, + iconPadding = when (icon) { + is TangemButtonIconPosition.Start -> TangemTheme.dimens.spacing4 + is TangemButtonIconPosition.End, + is TangemButtonIconPosition.None, + -> TangemTheme.dimens.spacing8 + }, + onClick = onClick, + ) +} + +private val LighterButtonColors: ButtonColors = TangemButtonColors( + backgroundColor = TangemColorPalette.Light4, + contentColor = TangemColorPalette.Dark6, + disabledBackgroundColor = TangemColorPalette.Dark5, + disabledContentColor = TangemColorPalette.Dark6, +) + +private val DarkerButtonColors: ButtonColors = TangemButtonColors( + backgroundColor = TangemColorPalette.Dark4, + contentColor = TangemColorPalette.White, + disabledBackgroundColor = TangemColorPalette.Dark4, + disabledContentColor = TangemColorPalette.White, +) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/views/StoriesProgressBar.kt b/app/src/main/java/com/tangem/tap/features/home/compose/views/StoriesProgressBar.kt index a8a1fd7bc1..ef3514bce4 100644 --- a/app/src/main/java/com/tangem/tap/features/home/compose/views/StoriesProgressBar.kt +++ b/app/src/main/java/com/tangem/tap/features/home/compose/views/StoriesProgressBar.kt @@ -13,10 +13,10 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview import com.tangem.core.ui.components.SpacerW4 +import com.tangem.core.ui.res.TangemColorPalette import com.tangem.core.ui.res.TangemTheme import kotlinx.coroutines.delay @@ -30,7 +30,7 @@ fun StoriesProgressBar( stepDuration: Int = 8_000, onStepFinish: () -> Unit = {}, ) { - val progress = remember(currentStep) { Animatable(0f) } + val progress = remember(currentStep) { Animatable(initialValue = 0f) } val context = LocalContext.current val animatorSpeed = Settings.Global.getFloat( @@ -75,20 +75,21 @@ fun StoriesProgressBar( .height(TangemTheme.dimens.size2) .weight(1f) .clip(RoundedCornerShape(TangemTheme.dimens.radius2)) - .background(Color.White.copy(alpha = 0.4f)), + .background(TangemColorPalette.White.copy(alpha = .2f)), ) { Box( modifier = Modifier .clip(RoundedCornerShape(TangemTheme.dimens.radius2)) - .background(Color.White) - .fillMaxHeight().let { + .background(TangemColorPalette.White) + .fillMaxHeight() + .let { when (index) { currentStep -> it.fillMaxWidth(progress.value) - in 0..currentStep -> it.fillMaxWidth(1f) + in 0..currentStep -> it.fillMaxWidth(fraction = 1f) else -> it } }, - ) {} + ) } if (index != steps) { SpacerW4() @@ -100,5 +101,12 @@ fun StoriesProgressBar( @Preview @Composable private fun StoriesProgressBarPreview() { - StoriesProgressBar(steps = 3, currentStep = 2, paused = false) { } + Box( + modifier = Modifier + .wrapContentSize() + .background(TangemColorPalette.Black) + .padding(vertical = TangemTheme.dimens.spacing16), + ) { + StoriesProgressBar(steps = 5, currentStep = 3, paused = false) + } } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButton.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButton.kt index ae762b2803..c1d501e021 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButton.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButton.kt @@ -33,6 +33,7 @@ fun TangemButton( elevation: ButtonElevation = TangemButtonsDefaults.elevation, textStyle: TextStyle = TangemTheme.typography.button, shape: Shape = size.toShape(), + iconPadding: Dp = size.toIconPadding(), ) { Button( modifier = modifier.heightIn(min = size.toHeightDp()), @@ -47,7 +48,7 @@ fun TangemButton( ButtonContentContainer( buttonIcon = icon, - iconPadding = size.toIconPadding(), + iconPadding = iconPadding, showProgress = showProgress, progressIndicator = { CircularProgressIndicator( From d7f086e1d992e54ad3393e2f18730703c7cd5c59 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 9 Feb 2024 13:40:19 +0100 Subject: [PATCH 25/53] Updated on 2026-08-14 --- .../state/AddCustomTokenState.kt | 2 +- .../state/AddCustomTokenWarning.kt | 2 +- .../state/ButtonState.kt | 2 +- .../state/ChooseDerivationState.kt | 2 +- .../state/ChooseNetworkState.kt | 2 +- .../state/CustomTokenData.kt | 2 +- .../state/Derivation.kt | 2 +- .../state/EnterCustomDerivationState.kt | 2 +- .../state/TextFieldState.kt | 2 +- .../factory/AddCustomTokenStateFactory.kt} | 10 ++-- ...stomTokenStateToCryptoCurrencyConverter.kt | 8 +-- ...ntractAddressToCustomTokenDataConverter.kt | 10 ++-- .../FoundTokenToCustomTokenDataConverter.kt | 10 ++-- .../NetworkToNetworkItemStateConverter.kt | 2 +- .../previewdata/AddCustomTokenPreviewData.kt | 4 +- .../ChooseDerivationPreviewData.kt | 6 +-- .../ChooseNetworkCustomPreviewData.kt | 4 +- .../ui/AddCustomTokenScreen.kt} | 16 +++--- .../ui/ChooseDerivationScreen.kt | 6 +-- .../ui/ChooseNetworkCustomScreen.kt | 6 +-- .../ui/CustomDerivationDialog.kt | 4 +- .../ui/CustomTokenChooseWalletScreen.kt} | 2 +- .../ui/TokenTextField.kt | 4 +- .../viewmodels/AddCustomTokenClickIntents.kt} | 6 +-- .../viewmodels/AddCustomTokenViewModel.kt} | 37 ++++++++------ .../viewmodels/ManageTokensViewModel.kt | 2 +- .../router/DefaultManageTokensRouter.kt | 49 ++++++++++--------- .../router/InnerManageTokensRouter.kt | 14 ++++-- .../presentation/router/ManageTokensRoute.kt | 10 ++-- 29 files changed, 119 insertions(+), 109 deletions(-) rename features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/{customtokens => addcustomtoken}/state/AddCustomTokenState.kt (91%) rename features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/{customtokens => addcustomtoken}/state/AddCustomTokenWarning.kt (94%) rename features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/{customtokens => addcustomtoken}/state/ButtonState.kt (58%) rename features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/{customtokens => addcustomtoken}/state/ChooseDerivationState.kt (86%) rename features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/{customtokens => addcustomtoken}/state/ChooseNetworkState.kt (85%) rename features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/{customtokens => addcustomtoken}/state/CustomTokenData.kt (87%) rename features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/{customtokens => addcustomtoken}/state/Derivation.kt (74%) rename features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/{customtokens => addcustomtoken}/state/EnterCustomDerivationState.kt (80%) rename features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/{customtokens => addcustomtoken}/state/TextFieldState.kt (93%) rename features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/{customtokens/state/factory/CustomTokensStateFactory.kt => addcustomtoken/state/factory/AddCustomTokenStateFactory.kt} (97%) rename features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/{customtokens => addcustomtoken}/state/factory/AddCustomTokenStateToCryptoCurrencyConverter.kt (87%) rename features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/{customtokens => addcustomtoken}/state/factory/ContractAddressToCustomTokenDataConverter.kt (77%) rename features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/{customtokens => addcustomtoken}/state/factory/FoundTokenToCustomTokenDataConverter.kt (78%) rename features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/{customtokens => addcustomtoken}/state/factory/NetworkToNetworkItemStateConverter.kt (91%) rename features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/{customtokens => addcustomtoken}/state/previewdata/AddCustomTokenPreviewData.kt (92%) rename features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/{customtokens => addcustomtoken}/state/previewdata/ChooseDerivationPreviewData.kt (83%) rename features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/{customtokens => addcustomtoken}/state/previewdata/ChooseNetworkCustomPreviewData.kt (85%) rename features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/{customtokens/ui/CustomTokensScreen.kt => addcustomtoken/ui/AddCustomTokenScreen.kt} (93%) rename features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/{customtokens => addcustomtoken}/ui/ChooseDerivationScreen.kt (94%) rename features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/{customtokens => addcustomtoken}/ui/ChooseNetworkCustomScreen.kt (93%) rename features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/{customtokens => addcustomtoken}/ui/CustomDerivationDialog.kt (90%) rename features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/{customtokens/ui/CustomTokensChooseWalletScreen.kt => addcustomtoken/ui/CustomTokenChooseWalletScreen.kt} (91%) rename features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/{customtokens => addcustomtoken}/ui/TokenTextField.kt (94%) rename features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/{customtokens/viewmodels/CustomTokensClickIntents.kt => addcustomtoken/viewmodels/AddCustomTokenClickIntents.kt} (83%) rename features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/{customtokens/viewmodels/CustomTokensViewModel.kt => addcustomtoken/viewmodels/AddCustomTokenViewModel.kt} (93%) diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/AddCustomTokenState.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/AddCustomTokenState.kt similarity index 91% rename from features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/AddCustomTokenState.kt rename to features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/AddCustomTokenState.kt index 5c37331d11..0eafc00654 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/AddCustomTokenState.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/AddCustomTokenState.kt @@ -1,4 +1,4 @@ -package com.tangem.managetokens.presentation.customtokens.state +package com.tangem.managetokens.presentation.addcustomtoken.state import com.tangem.core.ui.event.StateEvent import com.tangem.core.ui.event.consumedEvent diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/AddCustomTokenWarning.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/AddCustomTokenWarning.kt similarity index 94% rename from features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/AddCustomTokenWarning.kt rename to features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/AddCustomTokenWarning.kt index fdca691ce8..690178c20d 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/AddCustomTokenWarning.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/AddCustomTokenWarning.kt @@ -1,4 +1,4 @@ -package com.tangem.managetokens.presentation.customtokens.state +package com.tangem.managetokens.presentation.addcustomtoken.state import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/ButtonState.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/ButtonState.kt similarity index 58% rename from features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/ButtonState.kt rename to features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/ButtonState.kt index 0b1412cc0b..42b515ea20 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/ButtonState.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/ButtonState.kt @@ -1,4 +1,4 @@ -package com.tangem.managetokens.presentation.customtokens.state +package com.tangem.managetokens.presentation.addcustomtoken.state internal data class ButtonState( val isEnabled: Boolean, diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/ChooseDerivationState.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/ChooseDerivationState.kt similarity index 86% rename from features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/ChooseDerivationState.kt rename to features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/ChooseDerivationState.kt index c9bb1eefc4..047fae161d 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/ChooseDerivationState.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/ChooseDerivationState.kt @@ -1,4 +1,4 @@ -package com.tangem.managetokens.presentation.customtokens.state +package com.tangem.managetokens.presentation.addcustomtoken.state import kotlinx.collections.immutable.ImmutableList diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/ChooseNetworkState.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/ChooseNetworkState.kt similarity index 85% rename from features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/ChooseNetworkState.kt rename to features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/ChooseNetworkState.kt index 1a09c93ec3..56b993010b 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/ChooseNetworkState.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/ChooseNetworkState.kt @@ -1,4 +1,4 @@ -package com.tangem.managetokens.presentation.customtokens.state +package com.tangem.managetokens.presentation.addcustomtoken.state import com.tangem.managetokens.presentation.common.state.NetworkItemState import kotlinx.collections.immutable.ImmutableList diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/CustomTokenData.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/CustomTokenData.kt similarity index 87% rename from features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/CustomTokenData.kt rename to features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/CustomTokenData.kt index a586ec848a..ec93de1d4c 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/CustomTokenData.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/CustomTokenData.kt @@ -1,4 +1,4 @@ -package com.tangem.managetokens.presentation.customtokens.state +package com.tangem.managetokens.presentation.addcustomtoken.state internal data class CustomTokenData( val contractAddressTextField: TextFieldState, diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/Derivation.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/Derivation.kt similarity index 74% rename from features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/Derivation.kt rename to features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/Derivation.kt index cb06fa67b7..69f41a5f08 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/Derivation.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/Derivation.kt @@ -1,4 +1,4 @@ -package com.tangem.managetokens.presentation.customtokens.state +package com.tangem.managetokens.presentation.addcustomtoken.state internal data class Derivation( val networkName: String, diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/EnterCustomDerivationState.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/EnterCustomDerivationState.kt similarity index 80% rename from features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/EnterCustomDerivationState.kt rename to features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/EnterCustomDerivationState.kt index aefe21b295..3b6ca1c57a 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/EnterCustomDerivationState.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/EnterCustomDerivationState.kt @@ -1,4 +1,4 @@ -package com.tangem.managetokens.presentation.customtokens.state +package com.tangem.managetokens.presentation.addcustomtoken.state internal data class EnterCustomDerivationState( val value: String, diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/TextFieldState.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/TextFieldState.kt similarity index 93% rename from features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/TextFieldState.kt rename to features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/TextFieldState.kt index 3014818228..2e7f2b1168 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/TextFieldState.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/TextFieldState.kt @@ -1,4 +1,4 @@ -package com.tangem.managetokens.presentation.customtokens.state +package com.tangem.managetokens.presentation.addcustomtoken.state internal sealed class TextFieldState { object Loading : TextFieldState() diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/CustomTokensStateFactory.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/AddCustomTokenStateFactory.kt similarity index 97% rename from features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/CustomTokensStateFactory.kt rename to features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/AddCustomTokenStateFactory.kt index f32d3f3e88..6e51737162 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/CustomTokensStateFactory.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/AddCustomTokenStateFactory.kt @@ -1,4 +1,4 @@ -package com.tangem.managetokens.presentation.customtokens.state.factory +package com.tangem.managetokens.presentation.addcustomtoken.state.factory import com.tangem.core.ui.event.consumedEvent import com.tangem.core.ui.event.triggeredEvent @@ -7,17 +7,17 @@ import com.tangem.domain.tokens.model.Network import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import com.tangem.managetokens.presentation.common.state.* -import com.tangem.managetokens.presentation.customtokens.state.* -import com.tangem.managetokens.presentation.customtokens.viewmodels.CustomTokensClickIntents +import com.tangem.managetokens.presentation.addcustomtoken.state.* +import com.tangem.managetokens.presentation.addcustomtoken.viewmodels.AddCustomTokenClickIntents import com.tangem.utils.Provider import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentSetOf import kotlinx.collections.immutable.toPersistentList import kotlinx.collections.immutable.toPersistentSet -internal class CustomTokensStateFactory( +internal class AddCustomTokenStateFactory( private val currentStateProvider: Provider, - private val clickIntents: CustomTokensClickIntents, + private val clickIntents: AddCustomTokenClickIntents, ) { fun getInitialState(): AddCustomTokenState { diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/AddCustomTokenStateToCryptoCurrencyConverter.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/AddCustomTokenStateToCryptoCurrencyConverter.kt similarity index 87% rename from features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/AddCustomTokenStateToCryptoCurrencyConverter.kt rename to features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/AddCustomTokenStateToCryptoCurrencyConverter.kt index 15e17fc927..e6e1b2f84e 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/AddCustomTokenStateToCryptoCurrencyConverter.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/AddCustomTokenStateToCryptoCurrencyConverter.kt @@ -1,11 +1,11 @@ -package com.tangem.managetokens.presentation.customtokens.state.factory +package com.tangem.managetokens.presentation.addcustomtoken.state.factory import com.tangem.data.tokens.utils.CryptoCurrencyFactory import com.tangem.domain.common.DerivationStyleProvider import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.managetokens.presentation.customtokens.state.AddCustomTokenState -import com.tangem.managetokens.presentation.customtokens.state.CustomTokenData -import com.tangem.managetokens.presentation.customtokens.state.TextFieldState +import com.tangem.managetokens.presentation.addcustomtoken.state.AddCustomTokenState +import com.tangem.managetokens.presentation.addcustomtoken.state.CustomTokenData +import com.tangem.managetokens.presentation.addcustomtoken.state.TextFieldState import com.tangem.utils.converter.Converter internal class AddCustomTokenStateToCryptoCurrencyConverter( diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/ContractAddressToCustomTokenDataConverter.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/ContractAddressToCustomTokenDataConverter.kt similarity index 77% rename from features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/ContractAddressToCustomTokenDataConverter.kt rename to features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/ContractAddressToCustomTokenDataConverter.kt index 422f3c4f4b..eb0897d370 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/ContractAddressToCustomTokenDataConverter.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/ContractAddressToCustomTokenDataConverter.kt @@ -1,12 +1,12 @@ -package com.tangem.managetokens.presentation.customtokens.state.factory +package com.tangem.managetokens.presentation.addcustomtoken.state.factory -import com.tangem.managetokens.presentation.customtokens.state.CustomTokenData -import com.tangem.managetokens.presentation.customtokens.state.TextFieldState -import com.tangem.managetokens.presentation.customtokens.viewmodels.CustomTokensClickIntents +import com.tangem.managetokens.presentation.addcustomtoken.state.CustomTokenData +import com.tangem.managetokens.presentation.addcustomtoken.state.TextFieldState +import com.tangem.managetokens.presentation.addcustomtoken.viewmodels.AddCustomTokenClickIntents import com.tangem.utils.converter.Converter internal class ContractAddressToCustomTokenDataConverter( - private val clickIntents: CustomTokensClickIntents, + private val clickIntents: AddCustomTokenClickIntents, ) : Converter { override fun convert(value: String): CustomTokenData { return CustomTokenData( diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/FoundTokenToCustomTokenDataConverter.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/FoundTokenToCustomTokenDataConverter.kt similarity index 78% rename from features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/FoundTokenToCustomTokenDataConverter.kt rename to features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/FoundTokenToCustomTokenDataConverter.kt index 3aa06bdd9f..65921700dc 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/FoundTokenToCustomTokenDataConverter.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/FoundTokenToCustomTokenDataConverter.kt @@ -1,13 +1,13 @@ -package com.tangem.managetokens.presentation.customtokens.state.factory +package com.tangem.managetokens.presentation.addcustomtoken.state.factory import com.tangem.domain.tokens.model.FoundToken -import com.tangem.managetokens.presentation.customtokens.state.CustomTokenData -import com.tangem.managetokens.presentation.customtokens.state.TextFieldState -import com.tangem.managetokens.presentation.customtokens.viewmodels.CustomTokensClickIntents +import com.tangem.managetokens.presentation.addcustomtoken.state.CustomTokenData +import com.tangem.managetokens.presentation.addcustomtoken.state.TextFieldState +import com.tangem.managetokens.presentation.addcustomtoken.viewmodels.AddCustomTokenClickIntents import com.tangem.utils.converter.Converter internal class FoundTokenToCustomTokenDataConverter( - private val clickIntents: CustomTokensClickIntents, + private val clickIntents: AddCustomTokenClickIntents, ) : Converter { override fun convert(value: FoundToken): CustomTokenData { return CustomTokenData( diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/NetworkToNetworkItemStateConverter.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/NetworkToNetworkItemStateConverter.kt similarity index 91% rename from features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/NetworkToNetworkItemStateConverter.kt rename to features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/NetworkToNetworkItemStateConverter.kt index dee07b42c5..74b4eda971 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/factory/NetworkToNetworkItemStateConverter.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/NetworkToNetworkItemStateConverter.kt @@ -1,4 +1,4 @@ -package com.tangem.managetokens.presentation.customtokens.state.factory +package com.tangem.managetokens.presentation.addcustomtoken.state.factory import com.tangem.core.ui.extensions.getActiveIconResByNetworkId import com.tangem.domain.tokens.model.Network diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/previewdata/AddCustomTokenPreviewData.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/previewdata/AddCustomTokenPreviewData.kt similarity index 92% rename from features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/previewdata/AddCustomTokenPreviewData.kt rename to features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/previewdata/AddCustomTokenPreviewData.kt index 9be15589aa..96e7bf7379 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/previewdata/AddCustomTokenPreviewData.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/previewdata/AddCustomTokenPreviewData.kt @@ -1,8 +1,8 @@ -package com.tangem.managetokens.presentation.customtokens.state.previewdata +package com.tangem.managetokens.presentation.addcustomtoken.state.previewdata import com.tangem.managetokens.presentation.common.state.ChooseWalletState import com.tangem.managetokens.presentation.common.state.WalletState -import com.tangem.managetokens.presentation.customtokens.state.* +import com.tangem.managetokens.presentation.addcustomtoken.state.* import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentSetOf diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/previewdata/ChooseDerivationPreviewData.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/previewdata/ChooseDerivationPreviewData.kt similarity index 83% rename from features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/previewdata/ChooseDerivationPreviewData.kt rename to features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/previewdata/ChooseDerivationPreviewData.kt index 2bb35400d2..c4cfd15483 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/previewdata/ChooseDerivationPreviewData.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/previewdata/ChooseDerivationPreviewData.kt @@ -1,7 +1,7 @@ -package com.tangem.managetokens.presentation.customtokens.state.previewdata +package com.tangem.managetokens.presentation.addcustomtoken.state.previewdata -import com.tangem.managetokens.presentation.customtokens.state.ChooseDerivationState -import com.tangem.managetokens.presentation.customtokens.state.Derivation +import com.tangem.managetokens.presentation.addcustomtoken.state.ChooseDerivationState +import com.tangem.managetokens.presentation.addcustomtoken.state.Derivation import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/previewdata/ChooseNetworkCustomPreviewData.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/previewdata/ChooseNetworkCustomPreviewData.kt similarity index 85% rename from features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/previewdata/ChooseNetworkCustomPreviewData.kt rename to features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/previewdata/ChooseNetworkCustomPreviewData.kt index 5618ce7fb4..08987dbadf 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/state/previewdata/ChooseNetworkCustomPreviewData.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/previewdata/ChooseNetworkCustomPreviewData.kt @@ -1,8 +1,8 @@ -package com.tangem.managetokens.presentation.customtokens.state.previewdata +package com.tangem.managetokens.presentation.addcustomtoken.state.previewdata import com.tangem.features.managetokens.impl.R import com.tangem.managetokens.presentation.common.state.NetworkItemState -import com.tangem.managetokens.presentation.customtokens.state.ChooseNetworkState +import com.tangem.managetokens.presentation.addcustomtoken.state.ChooseNetworkState import kotlinx.collections.immutable.persistentListOf internal object ChooseNetworkCustomPreviewData { diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/CustomTokensScreen.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/AddCustomTokenScreen.kt similarity index 93% rename from features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/CustomTokensScreen.kt rename to features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/AddCustomTokenScreen.kt index ce244c3e85..d3291937f0 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/CustomTokensScreen.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/AddCustomTokenScreen.kt @@ -1,4 +1,4 @@ -package com.tangem.managetokens.presentation.customtokens.ui +package com.tangem.managetokens.presentation.addcustomtoken.ui import androidx.compose.foundation.background import androidx.compose.foundation.layout.* @@ -27,13 +27,13 @@ import com.tangem.managetokens.presentation.common.ui.ChooseWalletBottomSheetCon import com.tangem.managetokens.presentation.common.ui.EventEffect import com.tangem.managetokens.presentation.common.ui.components.Alert import com.tangem.managetokens.presentation.common.ui.components.SimpleSelectionBlock -import com.tangem.managetokens.presentation.customtokens.state.AddCustomTokenState -import com.tangem.managetokens.presentation.customtokens.state.CustomTokenData -import com.tangem.managetokens.presentation.customtokens.state.TextFieldState -import com.tangem.managetokens.presentation.customtokens.state.previewdata.AddCustomTokenPreviewData +import com.tangem.managetokens.presentation.addcustomtoken.state.AddCustomTokenState +import com.tangem.managetokens.presentation.addcustomtoken.state.CustomTokenData +import com.tangem.managetokens.presentation.addcustomtoken.state.TextFieldState +import com.tangem.managetokens.presentation.addcustomtoken.state.previewdata.AddCustomTokenPreviewData @Composable -internal fun CustomTokensScreen(state: AddCustomTokenState, modifier: Modifier = Modifier) { +internal fun AddCustomTokenScreen(state: AddCustomTokenState, modifier: Modifier = Modifier) { var alertState by remember { mutableStateOf(value = null) } EventEffect( @@ -250,7 +250,7 @@ private fun TokenTextFieldTitle(state: TextFieldState?, title: String) { @Composable private fun Preview_ChooseDerivationScreen_Light() { TangemTheme(isDark = false) { - CustomTokensScreen(state = AddCustomTokenPreviewData.state) + AddCustomTokenScreen(state = AddCustomTokenPreviewData.state) } } @@ -258,6 +258,6 @@ private fun Preview_ChooseDerivationScreen_Light() { @Composable private fun Preview_ChooseDerivationScreen_Dark() { TangemTheme(isDark = true) { - CustomTokensScreen(state = AddCustomTokenPreviewData.state) + AddCustomTokenScreen(state = AddCustomTokenPreviewData.state) } } \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/ChooseDerivationScreen.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/ChooseDerivationScreen.kt similarity index 94% rename from features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/ChooseDerivationScreen.kt rename to features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/ChooseDerivationScreen.kt index 625ff06edd..3422e1cf8e 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/ChooseDerivationScreen.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/ChooseDerivationScreen.kt @@ -1,4 +1,4 @@ -package com.tangem.managetokens.presentation.customtokens.ui +package com.tangem.managetokens.presentation.addcustomtoken.ui import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -16,8 +16,8 @@ import com.tangem.core.ui.decorations.roundedShapeItemDecoration import com.tangem.core.ui.res.TangemTheme import com.tangem.features.managetokens.impl.R import com.tangem.managetokens.presentation.common.ui.components.SimpleSelectionBlock -import com.tangem.managetokens.presentation.customtokens.state.ChooseDerivationState -import com.tangem.managetokens.presentation.customtokens.state.previewdata.ChooseDerivationPreviewData +import com.tangem.managetokens.presentation.addcustomtoken.state.ChooseDerivationState +import com.tangem.managetokens.presentation.addcustomtoken.state.previewdata.ChooseDerivationPreviewData @Composable internal fun ChooseDerivationScreen(state: ChooseDerivationState, modifier: Modifier = Modifier) { diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/ChooseNetworkCustomScreen.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/ChooseNetworkCustomScreen.kt similarity index 93% rename from features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/ChooseNetworkCustomScreen.kt rename to features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/ChooseNetworkCustomScreen.kt index c18fac8a6b..4ee903652e 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/ChooseNetworkCustomScreen.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/ChooseNetworkCustomScreen.kt @@ -1,4 +1,4 @@ -package com.tangem.managetokens.presentation.customtokens.ui +package com.tangem.managetokens.presentation.addcustomtoken.ui import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -16,8 +16,8 @@ import com.tangem.core.ui.decorations.roundedShapeItemDecoration import com.tangem.core.ui.res.TangemTheme import com.tangem.features.managetokens.impl.R import com.tangem.managetokens.presentation.common.ui.components.NetworkItem -import com.tangem.managetokens.presentation.customtokens.state.ChooseNetworkState -import com.tangem.managetokens.presentation.customtokens.state.previewdata.ChooseNetworkCustomPreviewData +import com.tangem.managetokens.presentation.addcustomtoken.state.ChooseNetworkState +import com.tangem.managetokens.presentation.addcustomtoken.state.previewdata.ChooseNetworkCustomPreviewData @Composable internal fun ChooseNetworkCustomScreen(state: ChooseNetworkState, modifier: Modifier = Modifier) { diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/CustomDerivationDialog.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/CustomDerivationDialog.kt similarity index 90% rename from features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/CustomDerivationDialog.kt rename to features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/CustomDerivationDialog.kt index db9a76365f..969d7f61fb 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/CustomDerivationDialog.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/CustomDerivationDialog.kt @@ -1,4 +1,4 @@ -package com.tangem.managetokens.presentation.customtokens.ui +package com.tangem.managetokens.presentation.addcustomtoken.ui import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource @@ -6,7 +6,7 @@ import com.tangem.core.ui.components.AdditionalTextInputDialogParams import com.tangem.core.ui.components.DialogButton import com.tangem.core.ui.components.TextInputDialog import com.tangem.features.managetokens.impl.R -import com.tangem.managetokens.presentation.customtokens.state.EnterCustomDerivationState +import com.tangem.managetokens.presentation.addcustomtoken.state.EnterCustomDerivationState @Composable internal fun CustomDerivationDialog(state: EnterCustomDerivationState) { diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/CustomTokensChooseWalletScreen.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/CustomTokenChooseWalletScreen.kt similarity index 91% rename from features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/CustomTokensChooseWalletScreen.kt rename to features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/CustomTokenChooseWalletScreen.kt index 358d2d6841..3b8eb7c2af 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/CustomTokensChooseWalletScreen.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/CustomTokenChooseWalletScreen.kt @@ -1,4 +1,4 @@ -package com.tangem.managetokens.presentation.customtokens.ui +package com.tangem.managetokens.presentation.addcustomtoken.ui import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.navigationBarsPadding diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/TokenTextField.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/TokenTextField.kt similarity index 94% rename from features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/TokenTextField.kt rename to features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/TokenTextField.kt index e5b7eef118..d794ab5890 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/ui/TokenTextField.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/TokenTextField.kt @@ -1,4 +1,4 @@ -package com.tangem.managetokens.presentation.customtokens.ui +package com.tangem.managetokens.presentation.addcustomtoken.ui import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth @@ -16,7 +16,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import com.tangem.core.ui.res.TangemTheme -import com.tangem.managetokens.presentation.customtokens.state.TextFieldState +import com.tangem.managetokens.presentation.addcustomtoken.state.TextFieldState @Composable internal fun TokenTextField( diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/viewmodels/CustomTokensClickIntents.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/viewmodels/AddCustomTokenClickIntents.kt similarity index 83% rename from features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/viewmodels/CustomTokensClickIntents.kt rename to features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/viewmodels/AddCustomTokenClickIntents.kt index 2dd2151beb..5a2e8e299b 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/viewmodels/CustomTokensClickIntents.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/viewmodels/AddCustomTokenClickIntents.kt @@ -1,10 +1,10 @@ -package com.tangem.managetokens.presentation.customtokens.viewmodels +package com.tangem.managetokens.presentation.addcustomtoken.viewmodels import com.tangem.managetokens.presentation.common.state.NetworkItemState -import com.tangem.managetokens.presentation.customtokens.state.Derivation +import com.tangem.managetokens.presentation.addcustomtoken.state.Derivation @Suppress("TooManyFunctions") -internal interface CustomTokensClickIntents { +internal interface AddCustomTokenClickIntents { fun onNetworkSelected(networkItemState: NetworkItemState) diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/viewmodels/CustomTokensViewModel.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/viewmodels/AddCustomTokenViewModel.kt similarity index 93% rename from features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/viewmodels/CustomTokensViewModel.kt rename to features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/viewmodels/AddCustomTokenViewModel.kt index 2ccbd92705..9211b1aaf1 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/customtokens/viewmodels/CustomTokensViewModel.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/viewmodels/AddCustomTokenViewModel.kt @@ -1,9 +1,10 @@ -package com.tangem.managetokens.presentation.customtokens.viewmodels +package com.tangem.managetokens.presentation.addcustomtoken.viewmodels import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import arrow.core.getOrElse @@ -23,11 +24,11 @@ import com.tangem.managetokens.presentation.common.analytics.ManageTokens import com.tangem.managetokens.presentation.common.state.AlertState import com.tangem.managetokens.presentation.common.state.Event import com.tangem.managetokens.presentation.common.state.NetworkItemState -import com.tangem.managetokens.presentation.customtokens.state.* -import com.tangem.managetokens.presentation.customtokens.state.factory.AddCustomTokenStateToCryptoCurrencyConverter -import com.tangem.managetokens.presentation.customtokens.state.factory.ContractAddressToCustomTokenDataConverter -import com.tangem.managetokens.presentation.customtokens.state.factory.CustomTokensStateFactory -import com.tangem.managetokens.presentation.customtokens.state.factory.FoundTokenToCustomTokenDataConverter +import com.tangem.managetokens.presentation.addcustomtoken.state.* +import com.tangem.managetokens.presentation.addcustomtoken.state.factory.AddCustomTokenStateToCryptoCurrencyConverter +import com.tangem.managetokens.presentation.addcustomtoken.state.factory.ContractAddressToCustomTokenDataConverter +import com.tangem.managetokens.presentation.addcustomtoken.state.factory.AddCustomTokenStateFactory +import com.tangem.managetokens.presentation.addcustomtoken.state.factory.FoundTokenToCustomTokenDataConverter import com.tangem.managetokens.presentation.router.InnerManageTokensRouter import com.tangem.utils.Provider import com.tangem.utils.coroutines.CoroutineDispatcherProvider @@ -45,7 +46,7 @@ import kotlin.properties.Delegates @Suppress("LongParameterList", "TooManyFunctions", "LargeClass") @HiltViewModel -internal class CustomTokensViewModel @Inject constructor( +internal class AddCustomTokenViewModel @Inject constructor( private val dispatchers: CoroutineDispatcherProvider, private val getWalletsUseCase: GetWalletsUseCase, private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, @@ -57,11 +58,11 @@ internal class CustomTokensViewModel @Inject constructor( private val getNetworksSupportedByWallet: GetNetworksSupportedByWallet, private val areTokensSupportedByNetworkUseCase: AreTokensSupportedByNetworkUseCase, private val analyticsEventHandler: AnalyticsEventHandler, -) : ViewModel(), CustomTokensClickIntents, DefaultLifecycleObserver { +) : ViewModel(), AddCustomTokenClickIntents, DefaultLifecycleObserver { private val debouncer = Debouncer() - private val stateFactory = CustomTokensStateFactory( + private val stateFactory = AddCustomTokenStateFactory( currentStateProvider = Provider { uiState }, clickIntents = this, ) @@ -71,7 +72,7 @@ internal class CustomTokensViewModel @Inject constructor( var uiState: AddCustomTokenState by mutableStateOf(stateFactory.getInitialState()) private set - init { + override fun onCreate(owner: LifecycleOwner) { viewModelScope.launch(dispatchers.io) { getWalletsUseCase() .distinctUntilChanged() @@ -91,6 +92,10 @@ internal class CustomTokensViewModel @Inject constructor( } } + override fun onDestroy(owner: LifecycleOwner) { + uiState = stateFactory.getInitialState() + } + private suspend fun selectSuitableWallet(suitableUserWallets: List): UserWalletId? { val selectedWallet = getSelectedWalletSyncUseCase().getOrNull() val selectedWalletId = if (walletSupportsAddingTokens(selectedWallet) && suitableUserWallets.isNotEmpty()) { @@ -134,7 +139,7 @@ internal class CustomTokensViewModel @Inject constructor( } override fun onChooseNetworkClick() { - router.openCustomTokensChooseNetwork() + router.openCustomTokenChooseNetwork() } override fun onCloseChoosingNetworkClick() { @@ -159,7 +164,7 @@ internal class CustomTokensViewModel @Inject constructor( } override fun onChooseWalletClick() { - router.openCustomTokensChooseWallet() + router.openCustomTokenChooseWallet() } override fun onCloseChoosingWalletClick() { @@ -208,7 +213,7 @@ internal class CustomTokensViewModel @Inject constructor( networkId = networkId, ).fold( ifLeft = { - val tokenData = ContractAddressToCustomTokenDataConverter(this@CustomTokensViewModel) + val tokenData = ContractAddressToCustomTokenDataConverter(this@AddCustomTokenViewModel) .convert(contractAddress) val isButtonEnabled = tokenData.isRequiredInformationProvided() @@ -222,9 +227,9 @@ internal class CustomTokensViewModel @Inject constructor( }, ifRight = { token -> val tokenData = if (token != null) { - FoundTokenToCustomTokenDataConverter(this@CustomTokensViewModel).convert(token) + FoundTokenToCustomTokenDataConverter(this@AddCustomTokenViewModel).convert(token) } else { - ContractAddressToCustomTokenDataConverter(this@CustomTokensViewModel).convert( + ContractAddressToCustomTokenDataConverter(this@AddCustomTokenViewModel).convert( contractAddress, ) } @@ -326,7 +331,7 @@ internal class CustomTokensViewModel @Inject constructor( } override fun onChooseDerivationClick() { - router.openCustomTokensChooseDerivation() + router.openCustomTokenChooseDerivation() } override fun onCloseChoosingDerivationClick() { diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensViewModel.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensViewModel.kt index ac7b3cbc5d..a2bb0805e8 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensViewModel.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensViewModel.kt @@ -190,7 +190,7 @@ internal class ManageTokensViewModel @Inject constructor( override fun onAddCustomTokensButtonClick() { analyticsEventHandler.send(ManageTokens.ButtonCustomToken) - router.openCustomTokensScreen() + router.openAddCustomTokenScreen() } override fun onSearchQueryChange(query: String) { diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/DefaultManageTokensRouter.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/DefaultManageTokensRouter.kt index dca7527d4b..5a4ec5b232 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/DefaultManageTokensRouter.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/DefaultManageTokensRouter.kt @@ -15,11 +15,11 @@ import com.tangem.core.navigation.NavigationAction import com.tangem.core.navigation.ReduxNavController import com.tangem.managetokens.ManageTokensFragment import com.tangem.managetokens.presentation.common.state.ChooseWalletState -import com.tangem.managetokens.presentation.customtokens.ui.ChooseDerivationScreen -import com.tangem.managetokens.presentation.customtokens.ui.ChooseNetworkCustomScreen -import com.tangem.managetokens.presentation.customtokens.ui.CustomTokensChooseWalletScreen -import com.tangem.managetokens.presentation.customtokens.ui.CustomTokensScreen -import com.tangem.managetokens.presentation.customtokens.viewmodels.CustomTokensViewModel +import com.tangem.managetokens.presentation.addcustomtoken.ui.ChooseDerivationScreen +import com.tangem.managetokens.presentation.addcustomtoken.ui.ChooseNetworkCustomScreen +import com.tangem.managetokens.presentation.addcustomtoken.ui.CustomTokensChooseWalletScreen +import com.tangem.managetokens.presentation.addcustomtoken.ui.AddCustomTokenScreen +import com.tangem.managetokens.presentation.addcustomtoken.viewmodels.AddCustomTokenViewModel import com.tangem.managetokens.presentation.managetokens.ui.ManageTokensScreen import com.tangem.managetokens.presentation.managetokens.viewmodels.ManageTokensViewModel import kotlin.properties.Delegates @@ -46,21 +46,22 @@ internal class DefaultManageTokensRouter( } navigation( - startDestination = ManageTokensRoute.CustomTokens.Main.route, - route = ManageTokensRoute.CustomTokens.route, + startDestination = ManageTokensRoute.AddCustomToken.Main.route, + route = ManageTokensRoute.AddCustomToken.route, ) { composable( - ManageTokensRoute.CustomTokens.Main.route, + ManageTokensRoute.AddCustomToken.Main.route, ) { - val viewModel = hiltViewModel(viewModelStoreOwner).apply { + val viewModel = hiltViewModel(viewModelStoreOwner).apply { router = this@DefaultManageTokensRouter } - CustomTokensScreen(state = viewModel.uiState) + LocalLifecycleOwner.current.lifecycle.addObserver(viewModel) + AddCustomTokenScreen(state = viewModel.uiState) } composable( - ManageTokensRoute.CustomTokens.ChooseNetwork.route, + ManageTokensRoute.AddCustomToken.ChooseNetwork.route, ) { - val viewModel = hiltViewModel(viewModelStoreOwner).apply { + val viewModel = hiltViewModel(viewModelStoreOwner).apply { router = this@DefaultManageTokensRouter } ChooseNetworkCustomScreen( @@ -68,9 +69,9 @@ internal class DefaultManageTokensRouter( ) } composable( - ManageTokensRoute.CustomTokens.ChooseDerivation.route, + ManageTokensRoute.AddCustomToken.ChooseDerivation.route, ) { - val viewModel = hiltViewModel(viewModelStoreOwner).apply { + val viewModel = hiltViewModel(viewModelStoreOwner).apply { router = this@DefaultManageTokensRouter } ChooseDerivationScreen( @@ -78,9 +79,9 @@ internal class DefaultManageTokensRouter( ) } composable( - ManageTokensRoute.CustomTokens.ChooseWallet.route, + ManageTokensRoute.AddCustomToken.ChooseWallet.route, ) { - val viewModel = hiltViewModel(viewModelStoreOwner).apply { + val viewModel = hiltViewModel(viewModelStoreOwner).apply { router = this@DefaultManageTokensRouter } CustomTokensChooseWalletScreen( @@ -103,19 +104,19 @@ internal class DefaultManageTokensRouter( navController.navigate(ManageTokensRoute.ManageTokens.route) } - override fun openCustomTokensScreen() { - navController.navigate(ManageTokensRoute.CustomTokens.route) + override fun openAddCustomTokenScreen() { + navController.navigate(ManageTokensRoute.AddCustomToken.route) } - override fun openCustomTokensChooseNetwork() { - navController.navigate(ManageTokensRoute.CustomTokens.ChooseNetwork.route) + override fun openCustomTokenChooseNetwork() { + navController.navigate(ManageTokensRoute.AddCustomToken.ChooseNetwork.route) } - override fun openCustomTokensChooseDerivation() { - navController.navigate(ManageTokensRoute.CustomTokens.ChooseDerivation.route) + override fun openCustomTokenChooseDerivation() { + navController.navigate(ManageTokensRoute.AddCustomToken.ChooseDerivation.route) } - override fun openCustomTokensChooseWallet() { - navController.navigate(ManageTokensRoute.CustomTokens.ChooseWallet.route) + override fun openCustomTokenChooseWallet() { + navController.navigate(ManageTokensRoute.AddCustomToken.ChooseWallet.route) } } \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/InnerManageTokensRouter.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/InnerManageTokensRouter.kt index 29d0cd4c9e..4c45b39d4a 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/InnerManageTokensRouter.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/InnerManageTokensRouter.kt @@ -6,6 +6,7 @@ import com.tangem.core.navigation.AppScreen import com.tangem.features.managetokens.navigation.ManageTokensRouter internal interface InnerManageTokensRouter : ManageTokensRouter { + /** * Initialize router **/ @@ -19,12 +20,15 @@ internal interface InnerManageTokensRouter : ManageTokensRouter { /** Open manage tokens screen */ fun openManageTokensScreen() - /** Open custom tokens screen */ - fun openCustomTokensScreen() + /** Open add custom token screen */ + fun openAddCustomTokenScreen() - fun openCustomTokensChooseNetwork() + /** Open custom token choose network screen */ + fun openCustomTokenChooseNetwork() - fun openCustomTokensChooseDerivation() + /** Open custom token choose derivation screen */ + fun openCustomTokenChooseDerivation() - fun openCustomTokensChooseWallet() + /** Open custom token choose wallet screen */ + fun openCustomTokenChooseWallet() } \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/ManageTokensRoute.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/ManageTokensRoute.kt index 0e6a358743..705769d5b0 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/ManageTokensRoute.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/ManageTokensRoute.kt @@ -10,10 +10,10 @@ internal sealed class ManageTokensRoute(val route: String) { object ManageTokens : ManageTokensRoute(route = "manage_tokens") - object CustomTokens : ManageTokensRoute(route = "manage_tokens/custom_tokens") { - object Main : ManageTokensRoute(CustomTokens.route + "/main") - object ChooseNetwork : ManageTokensRoute(CustomTokens.route + "/choose_network") - object ChooseWallet : ManageTokensRoute(CustomTokens.route + "/choose_wallet") - object ChooseDerivation : ManageTokensRoute(CustomTokens.route + "/choose_derivation") + object AddCustomToken : ManageTokensRoute(route = "manage_tokens/add_custom_token") { + object Main : ManageTokensRoute(AddCustomToken.route + "/main") + object ChooseNetwork : ManageTokensRoute(AddCustomToken.route + "/choose_network") + object ChooseWallet : ManageTokensRoute(AddCustomToken.route + "/choose_wallet") + object ChooseDerivation : ManageTokensRoute(AddCustomToken.route + "/choose_derivation") } } \ No newline at end of file From b6585c69a97bbc2183a7db151f8f20152c06d5da Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 8 Feb 2024 18:55:01 +0300 Subject: [PATCH 26/53] Updated on 2026-08-14 --- .../state/SendEventStateFactory.kt | 8 ++++---- .../presentation/state/fee/FeeConverter.kt | 8 ++++---- .../state/fee/FeeNotificationFactory.kt | 4 ++-- .../presentation/state/fee/FeeSelectorState.kt | 10 +++++----- .../presentation/state/fee/FeeStateFactory.kt | 2 +- .../ui/fee/SendCustomFeeEthereum.kt | 2 +- .../ui/fee/SendSpeedAndFeeContent.kt | 2 +- .../presentation/ui/fee/SendSpeedSelector.kt | 18 +++++++++--------- 8 files changed, 27 insertions(+), 27 deletions(-) diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendEventStateFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendEventStateFactory.kt index 2faa56655f..23c9aec225 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendEventStateFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendEventStateFactory.kt @@ -50,10 +50,10 @@ internal class SendEventStateFactory( is TransactionFee.Single -> fee.normal is TransactionFee.Choosable -> { when (feeSelector.selectedFee) { - FeeType.SLOW -> fee.minimum - FeeType.MARKET -> fee.normal - FeeType.FAST -> fee.priority - FeeType.CUSTOM -> return state + FeeType.Slow -> fee.minimum + FeeType.Market -> fee.normal + FeeType.Fast -> fee.priority + FeeType.Custom -> return state } } } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeConverter.kt index c14db17575..7b55e0100d 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeConverter.kt @@ -28,10 +28,10 @@ internal class FeeConverter( return when (val fees = value.fees) { is TransactionFee.Choosable -> { when (value.selectedFee) { - FeeType.SLOW -> fees.minimum - FeeType.MARKET -> fees.normal - FeeType.FAST -> fees.priority - FeeType.CUSTOM -> convertCustom(value, fees) + FeeType.Slow -> fees.minimum + FeeType.Market -> fees.normal + FeeType.Fast -> fees.priority + FeeType.Custom -> convertCustom(value, fees) } } is TransactionFee.Single -> fees.normal diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeNotificationFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeNotificationFactory.kt index d95031bd4e..fd4fa1da94 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeNotificationFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeNotificationFactory.kt @@ -69,7 +69,7 @@ internal class FeeNotificationFactory( val minimumValue = multipleFees.minimum.amount.value ?: return val customAmount = customFee.firstOrNull() ?: return val customValue = customAmount.value.parseToBigDecimal(customAmount.decimals) - if (selectedFee == FeeType.CUSTOM && minimumValue > customValue) { + if (selectedFee == FeeType.Custom && minimumValue > customValue) { add(SendFeeNotification.Warning.TooLow) } } @@ -84,7 +84,7 @@ internal class FeeNotificationFactory( val customAmount = customFee.firstOrNull() ?: return val customValue = customAmount.value.parseToBigDecimal(customAmount.decimals) val diff = customValue / highValue - if (selectedFee == FeeType.CUSTOM && diff > FEE_MAX_DIFF) { + if (selectedFee == FeeType.Custom && diff > FEE_MAX_DIFF) { add(SendFeeNotification.Warning.TooHigh(diff.toFormattedString(HIGH_FEE_DIFF_DECIMALS))) } } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeSelectorState.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeSelectorState.kt index 9751e61eae..e0cda82bfa 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeSelectorState.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeSelectorState.kt @@ -13,7 +13,7 @@ internal sealed class FeeSelectorState { data class Content( val fees: TransactionFee, - val selectedFee: FeeType = FeeType.MARKET, + val selectedFee: FeeType = FeeType.Market, val customValues: ImmutableList = persistentListOf(), ) : FeeSelectorState() @@ -21,8 +21,8 @@ internal sealed class FeeSelectorState { } enum class FeeType { - SLOW, - MARKET, - FAST, - CUSTOM, + Slow, + Market, + Fast, + Custom, } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeStateFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeStateFactory.kt index 91ad0f8eba..b8f99a5511 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeStateFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/FeeStateFactory.kt @@ -191,7 +191,7 @@ internal class FeeStateFactory( val fee = feeConverter.convert(feeSelectorState) val feeValue = fee.amount.value ?: BigDecimal.ZERO - val isNotCustom = feeSelectorState.selectedFee != FeeType.CUSTOM + val isNotCustom = feeSelectorState.selectedFee != FeeType.Custom val isNotEmptyCustom = if (customValue != null) { !customValue.value.parseToBigDecimal(customValue.decimals).isZero() && !isNotCustom } else { diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendCustomFeeEthereum.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendCustomFeeEthereum.kt index 8dbb522573..207b6ee43e 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendCustomFeeEthereum.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendCustomFeeEthereum.kt @@ -20,7 +20,7 @@ internal fun SendCustomFeeEthereum( selectedFee: FeeType, modifier: Modifier = Modifier, ) { - if (selectedFee == FeeType.CUSTOM && customValues.isNotEmpty()) { + if (selectedFee == FeeType.Custom && customValues.isNotEmpty()) { Column( verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12), modifier = modifier, diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedAndFeeContent.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedAndFeeContent.kt index 0c1316c178..95a469975a 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedAndFeeContent.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedAndFeeContent.kt @@ -175,7 +175,7 @@ internal fun LazyListScope.subtractButton( item { val feeStateContent = feeSendState as? FeeSelectorState.Content val isCustomAvailable = feeStateContent?.customValues.isNullOrEmpty().not() - val isCustomSelected = feeStateContent?.selectedFee == FeeType.CUSTOM + val isCustomSelected = feeStateContent?.selectedFee == FeeType.Custom val topPadding = if (isCustomSelected && isCustomAvailable) { TangemTheme.dimens.spacing12 } else { diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelector.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelector.kt index 2721c1698e..0bac355bb7 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelector.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelector.kt @@ -88,8 +88,8 @@ internal fun SendSpeedSelector( amount = getCryptoReference(minimumAmount, state.isFeeApproximate), fiatAmount = getFiatReference(minimumAmount, state.rate, state.appCurrency), symbolLength = minimumAmount.currencySymbol.length, - isSelected = isSelected == FeeType.SLOW, - onSelect = { clickIntents.onFeeSelectorClick(FeeType.SLOW) }, + isSelected = isSelected == FeeType.Slow, + onSelect = { clickIntents.onFeeSelectorClick(FeeType.Slow) }, ) val normalAmount = fees.normal.amount SendSpeedSelectorItem( @@ -98,8 +98,8 @@ internal fun SendSpeedSelector( amount = getCryptoReference(normalAmount, state.isFeeApproximate), fiatAmount = getFiatReference(normalAmount, state.rate, state.appCurrency), symbolLength = normalAmount.currencySymbol.length, - isSelected = isSelected == FeeType.MARKET, - onSelect = { clickIntents.onFeeSelectorClick(FeeType.MARKET) }, + isSelected = isSelected == FeeType.Market, + onSelect = { clickIntents.onFeeSelectorClick(FeeType.Market) }, ) val priorityAmount = fees.priority.amount SendSpeedSelectorItem( @@ -108,8 +108,8 @@ internal fun SendSpeedSelector( amount = getCryptoReference(priorityAmount, state.isFeeApproximate), fiatAmount = getFiatReference(priorityAmount, state.rate, state.appCurrency), symbolLength = priorityAmount.currencySymbol.length, - isSelected = isSelected == FeeType.FAST, - onSelect = { clickIntents.onFeeSelectorClick(FeeType.FAST) }, + isSelected = isSelected == FeeType.Fast, + onSelect = { clickIntents.onFeeSelectorClick(FeeType.Fast) }, showDivider = fees.normal is Fee.Ethereum, ) AnimatedVisibility( @@ -123,8 +123,8 @@ internal fun SendSpeedSelector( SendSpeedSelectorItem( titleRes = R.string.common_fee_selector_option_custom, iconRes = R.drawable.ic_edit_24, - isSelected = isSelected == FeeType.CUSTOM, - onSelect = { clickIntents.onFeeSelectorClick(FeeType.CUSTOM) }, + isSelected = isSelected == FeeType.Custom, + onSelect = { clickIntents.onFeeSelectorClick(FeeType.Custom) }, showDivider = fees.normal !is Fee.Ethereum, showWarning = showWarning, ) @@ -139,7 +139,7 @@ internal fun SendSpeedSelector( amount = getCryptoReference(normalAmount, state.isFeeApproximate), fiatAmount = getFiatReference(normalAmount, state.rate, state.appCurrency), symbolLength = normalAmount.currencySymbol.length, - onSelect = { clickIntents.onFeeSelectorClick(FeeType.MARKET) }, + onSelect = { clickIntents.onFeeSelectorClick(FeeType.Market) }, showDivider = false, ) } From 850bc927497f4bf6040305e87888b59a3eef5d0f Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 8 Feb 2024 18:55:24 +0300 Subject: [PATCH 27/53] Updated on 2026-08-14 --- .../core/analytics/models/AnalyticsParam.kt | 1 + features/send/impl/build.gradle.kts | 2 + .../send/impl/presentation/SendFragment.kt | 5 + .../analytics/SendAnalyticEvents.kt | 136 ++++++++++++++++++ .../utils/SendOnNextScreenAnalyticSender.kt | 44 ++++++ .../utils/SendRecipientAnalyticsSender.kt | 26 ++++ .../impl/presentation/state/StateRouter.kt | 61 ++++++-- .../presentation/ui/SendNavigationButtons.kt | 3 + .../ui/recipient/SendRecipientContent.kt | 11 +- .../viewmodel/SendClickIntents.kt | 9 +- .../presentation/viewmodel/SendViewModel.kt | 67 +++++++-- 11 files changed, 336 insertions(+), 29 deletions(-) create mode 100644 features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/analytics/SendAnalyticEvents.kt create mode 100644 features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/analytics/utils/SendOnNextScreenAnalyticSender.kt create mode 100644 features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/analytics/utils/SendRecipientAnalyticsSender.kt diff --git a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt index 830d2621c2..a684154112 100644 --- a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt +++ b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AnalyticsParam.kt @@ -125,6 +125,7 @@ sealed class AnalyticsParam { const val SOURCE = "Source" const val BALANCE = "Balance" const val BATCH = "Batch" + const val TYPE = "Type" const val FEE_TYPE = "Fee Type" const val PERMISSION_TYPE = "Permission Type" const val PRODUCT_TYPE = "Product Type" diff --git a/features/send/impl/build.gradle.kts b/features/send/impl/build.gradle.kts index b35e632634..062932fd25 100644 --- a/features/send/impl/build.gradle.kts +++ b/features/send/impl/build.gradle.kts @@ -45,6 +45,8 @@ dependencies { implementation(projects.core.ui) implementation(projects.core.utils) implementation(projects.core.navigation) + implementation(projects.core.analytics) + implementation(projects.core.analytics.models) /** Common */ implementation(projects.common) diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/SendFragment.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/SendFragment.kt index c7cbe1a802..1189a49843 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/SendFragment.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/SendFragment.kt @@ -8,6 +8,7 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import arrow.core.getOrElse +import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.ui.components.SystemBarsEffect import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.screen.ComposeFragment @@ -41,6 +42,9 @@ internal class SendFragment : ComposeFragment() { @Inject lateinit var listenToQrScanningUseCase: ListenToQrScanningUseCase + @Inject + lateinit var analyticsEventsHandler: AnalyticsEventHandler + private val viewModel by viewModels() private val innerSendRouter: InnerSendRouter get() = requireNotNull(router as? InnerSendRouter) { @@ -57,6 +61,7 @@ internal class SendFragment : ComposeFragment() { StateRouter( fragmentManager = WeakReference(parentFragmentManager), isEditingDisabled = isEditingDisabled, + analyticsEventsHandler = analyticsEventsHandler, ), ) listenToQrCode() diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/analytics/SendAnalyticEvents.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/analytics/SendAnalyticEvents.kt new file mode 100644 index 0000000000..b62f028e0a --- /dev/null +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/analytics/SendAnalyticEvents.kt @@ -0,0 +1,136 @@ +package com.tangem.features.send.impl.presentation.analytics + +import com.tangem.core.analytics.models.AnalyticsEvent +import com.tangem.core.analytics.models.AnalyticsParam.Key.SOURCE +import com.tangem.core.analytics.models.AnalyticsParam.Key.TYPE +import com.tangem.core.analytics.models.AnalyticsParam.Key.VALIDATION + +/** + * Send screen analytics + */ +internal sealed class SendAnalyticEvents( + event: String, + params: Map = mapOf(), +) : AnalyticsEvent(category = "Token / Send", event = event, params = params) { + + /** Send screen opened */ + object SendOpened : SendAnalyticEvents(event = "Send Screen Opened") + + /** Next button clicked */ + data class NextButtonClicked(val source: SendScreenSource) : SendAnalyticEvents( + event = "Button - Next", + params = mapOf(SOURCE to source.name), + ) + + /** Back button clicked */ + data class BackButtonClicked(val source: SendScreenSource) : SendAnalyticEvents( + event = "Button - Back", + params = mapOf(SOURCE to source.name), + ) + + // region Address + /** Recipient address screen opened */ + object AddressScreenOpened : SendAnalyticEvents(event = "Address Screen Opened") + + /** Address to send entered */ + data class AddressEntered(val source: EnterAddressSource, val isValid: Boolean) : SendAnalyticEvents( + event = "Address Entered", + params = mapOf( + SOURCE to source.name, + VALIDATION to if (isValid) "Success" else "Fail", + ), + ) + + /** Paste from clipboard button clicked */ + data class PasteButtonClicked(val type: PasteType) : SendAnalyticEvents( + event = "Button - Paste", + params = mapOf( + TYPE to type.name, + ), + ) + + /** Qr Code button clicked */ + object QrCodeButtonClicked : SendAnalyticEvents(event = "Button - QR Code") + // endregion + + // region Amount + /** Amount screen opened */ + object AmountScreenOpened : SendAnalyticEvents(event = "Amount Screen Opened") + + /** Selected currency */ + data class SelectedCurrency(val type: SelectedCurrencyType) : SendAnalyticEvents( + event = "Selected Currency", + params = mapOf(TYPE to type.value), + ) + + /** Currency selector button clicked */ + object SwapCurrencyButtonClicked : SendAnalyticEvents(event = "Button - Swap Currency") + // endregion + + // region Fee + /** Fee screen opened */ + object FeeScreenOpened : SendAnalyticEvents(event = "Fee Screen Opened") + + /** Selected fee (send after next screen opened) */ + data class SelectedFee(val fee: String) : SendAnalyticEvents( + event = "Fee Selected", + params = mapOf("Commission" to fee), + ) + + /** Custom fee selected */ + object CustomFeeButtonClicked : SendAnalyticEvents(event = "Custom Fee Clicked") + + /** Custom fee edited */ + object GasPriceInserter : SendAnalyticEvents(event = "Gas Price Inserted") + + /** Subtract from amount selector switched (send after next screen opened) */ + object SubtractFromAmount : SendAnalyticEvents(event = "Subtract from Amount") + // endregion + + // region Confirmation + /** Confirmation screen opened */ + object ConfirmationScreenOpened : SendAnalyticEvents(event = "Confirm Screen Opened") + + /** Send transaction button clicked */ + object SendButtonClicked : SendAnalyticEvents(event = "Button - Send") + + /** Screen reopened from confirmation screen */ + data class ScreenReopened(val source: SendScreenSource) : SendAnalyticEvents( + event = "Screen Reopened", + params = mapOf(SOURCE to source.name), + ) + // endregion + + // region Transaction Result + /** Transaction send screen opened */ + object TransactionScreenOpened : SendAnalyticEvents(event = "Transaction Sent Screen Opened") + + /** Share button clicked */ + object ShareButtonClicked : SendAnalyticEvents(event = "Button - Share") + + /** Expore button clicked */ + object ExploreButtonClicked : SendAnalyticEvents(event = "Button - Explore") + // endregion +} + +internal enum class SendScreenSource { + Address, + Amount, + Fee, +} + +internal enum class EnterAddressSource { + QRCode, + PasteButton, + RecentAddress, +} + +internal enum class PasteType { + Address, + Memo, +} + +internal enum class SelectedCurrencyType(val value: String) { + Token("Token"), + AppCurrency("App Currency"), +} \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/analytics/utils/SendOnNextScreenAnalyticSender.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/analytics/utils/SendOnNextScreenAnalyticSender.kt new file mode 100644 index 0000000000..e3f5666a1c --- /dev/null +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/analytics/utils/SendOnNextScreenAnalyticSender.kt @@ -0,0 +1,44 @@ +package com.tangem.features.send.impl.presentation.analytics.utils + +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.features.send.impl.presentation.analytics.SelectedCurrencyType +import com.tangem.features.send.impl.presentation.analytics.SendAnalyticEvents +import com.tangem.features.send.impl.presentation.state.SendUiState +import com.tangem.features.send.impl.presentation.state.SendUiStateType +import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState +import com.tangem.features.send.impl.presentation.state.fee.FeeType + +internal class SendOnNextScreenAnalyticSender( + private val analyticsEventHandler: AnalyticsEventHandler, +) { + fun send(prevScreen: SendUiStateType, state: SendUiState) { + when (prevScreen) { + SendUiStateType.Fee -> { + val feeState = state.feeState ?: return + val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content + feeSelectorState?.selectedFee?.let { selectedFee -> + val isCustomFeeEdited = feeState.fee?.amount?.value != feeSelectorState.fees.normal.amount.value + if (selectedFee == FeeType.Custom && isCustomFeeEdited) { + analyticsEventHandler.send(SendAnalyticEvents.GasPriceInserter) + } + analyticsEventHandler.send(SendAnalyticEvents.SelectedFee(selectedFee.name)) + } + if (feeState.isSubtract) { + analyticsEventHandler.send(SendAnalyticEvents.SubtractFromAmount) + } + } + SendUiStateType.Amount -> { + val isFiatSelected = state.amountState?.amountTextField?.isFiatValue ?: return + val selectedCurrency = if (isFiatSelected) { + SelectedCurrencyType.Token + } else { + SelectedCurrencyType.AppCurrency + } + analyticsEventHandler.send( + SendAnalyticEvents.SelectedCurrency(selectedCurrency), + ) + } + else -> Unit + } + } +} \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/analytics/utils/SendRecipientAnalyticsSender.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/analytics/utils/SendRecipientAnalyticsSender.kt new file mode 100644 index 0000000000..000f48dea1 --- /dev/null +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/analytics/utils/SendRecipientAnalyticsSender.kt @@ -0,0 +1,26 @@ +package com.tangem.features.send.impl.presentation.analytics.utils + +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.features.send.impl.presentation.analytics.EnterAddressSource +import com.tangem.features.send.impl.presentation.analytics.PasteType +import com.tangem.features.send.impl.presentation.analytics.SendAnalyticEvents + +internal class SendRecipientAnalyticsSender( + private val analyticsEventHandler: AnalyticsEventHandler, +) { + + fun sendAddressAnalytics(type: EnterAddressSource?, isValidAddress: Boolean) { + type?.let { + if (type == EnterAddressSource.PasteButton) { + analyticsEventHandler.send(SendAnalyticEvents.PasteButtonClicked(PasteType.Address)) + } + analyticsEventHandler.send(SendAnalyticEvents.AddressEntered(it, isValidAddress)) + } + } + + fun sendMemoAnalytics(isPasted: Boolean) { + if (isPasted) { + analyticsEventHandler.send(SendAnalyticEvents.PasteButtonClicked(PasteType.Memo)) + } + } +} \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/StateRouter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/StateRouter.kt index ea2c456a23..ea62b4700d 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/StateRouter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/StateRouter.kt @@ -1,6 +1,9 @@ package com.tangem.features.send.impl.presentation.state import androidx.fragment.app.FragmentManager +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.features.send.impl.presentation.analytics.SendAnalyticEvents +import com.tangem.features.send.impl.presentation.analytics.SendScreenSource import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.update @@ -8,6 +11,7 @@ import java.lang.ref.WeakReference internal class StateRouter( private val fragmentManager: WeakReference, + private val analyticsEventsHandler: AnalyticsEventHandler, private val isEditingDisabled: Boolean, ) { private var mutableCurrentState: MutableStateFlow = MutableStateFlow( @@ -28,26 +32,51 @@ internal class StateRouter( when { isSuccess -> popBackStack() isEditingDisabled -> when (currentState.value) { - SendUiStateType.Send -> showFee() + SendUiStateType.Send -> { + analyticsEventsHandler.send(SendAnalyticEvents.BackButtonClicked(SendScreenSource.Fee)) + showFee() + } else -> popBackStack() } else -> when (currentState.value) { - SendUiStateType.Amount -> showRecipient() - SendUiStateType.Fee -> showAmount() - SendUiStateType.Send -> showFee() + SendUiStateType.Amount -> { + analyticsEventsHandler.send(SendAnalyticEvents.BackButtonClicked(SendScreenSource.Address)) + showRecipient() + } + SendUiStateType.Fee -> { + analyticsEventsHandler.send(SendAnalyticEvents.BackButtonClicked(SendScreenSource.Amount)) + showAmount() + } + SendUiStateType.Send -> { + analyticsEventsHandler.send(SendAnalyticEvents.BackButtonClicked(SendScreenSource.Fee)) + showFee() + } else -> popBackStack() } } } - fun onNextClick() { + fun onNextClick(): SendUiStateType { + val prevState = currentState.value when (currentState.value) { - SendUiStateType.Recipient -> showAmount() - SendUiStateType.Amount -> showFee() - SendUiStateType.Fee -> showSend() - SendUiStateType.Send -> onBackClick() + SendUiStateType.Recipient -> { + analyticsEventsHandler.send(SendAnalyticEvents.NextButtonClicked(SendScreenSource.Amount)) + showAmount() + } + SendUiStateType.Amount -> { + analyticsEventsHandler.send(SendAnalyticEvents.NextButtonClicked(SendScreenSource.Fee)) + showFee() + } + SendUiStateType.Fee -> { + analyticsEventsHandler.send(SendAnalyticEvents.NextButtonClicked(SendScreenSource.Fee)) + showSend() + } + SendUiStateType.Send -> { + onBackClick() + } else -> popBackStack() } + return prevState } fun onPrevClick() { @@ -55,26 +84,36 @@ internal class StateRouter( popBackStack() } else { when (currentState.value) { - SendUiStateType.Amount -> showRecipient() - SendUiStateType.Fee -> showAmount() + SendUiStateType.Amount -> { + analyticsEventsHandler.send(SendAnalyticEvents.BackButtonClicked(SendScreenSource.Amount)) + showRecipient() + } + SendUiStateType.Fee -> { + analyticsEventsHandler.send(SendAnalyticEvents.BackButtonClicked(SendScreenSource.Fee)) + showAmount() + } else -> popBackStack() } } } fun showAmount() { + analyticsEventsHandler.send(SendAnalyticEvents.AmountScreenOpened) mutableCurrentState.update { SendUiStateType.Amount } } fun showRecipient() { + analyticsEventsHandler.send(SendAnalyticEvents.AddressScreenOpened) mutableCurrentState.update { SendUiStateType.Recipient } } fun showFee() { + analyticsEventsHandler.send(SendAnalyticEvents.FeeScreenOpened) mutableCurrentState.update { SendUiStateType.Fee } } private fun showSend() { + analyticsEventsHandler.send(SendAnalyticEvents.ConfirmationScreenOpened) mutableCurrentState.update { SendUiStateType.Send } } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt index 064ec52822..fb4fa79a9f 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt @@ -110,6 +110,7 @@ private fun SendPrimaryNavigationButton(uiState: SendUiState, modifier: Modifier textRes = textId, txUrl = txUrl, onExploreClick = { uiState.clickIntents.onExploreClick(txUrl) }, + onShareClick = uiState.clickIntents::onShareClick, onDoneClick = buttonClick, modifier = Modifier, ) @@ -130,6 +131,7 @@ private fun PrimaryButtonsDone( @StringRes textRes: Int, txUrl: String, onExploreClick: () -> Unit, + onShareClick: () -> Unit, onDoneClick: () -> Unit, modifier: Modifier = Modifier, ) { @@ -152,6 +154,7 @@ private fun PrimaryButtonsDone( onClick = { hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) context.shareText(txUrl) + onShareClick() }, modifier = Modifier.weight(1f), ) 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 3fa19bdf16..c80581a8f8 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 @@ -24,6 +24,7 @@ import com.tangem.core.ui.components.inputrow.InputRowRecipient import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme import com.tangem.features.send.impl.R +import com.tangem.features.send.impl.presentation.analytics.EnterAddressSource import com.tangem.features.send.impl.presentation.domain.SendRecipientListContent import com.tangem.features.send.impl.presentation.state.SendStates import com.tangem.features.send.impl.presentation.ui.common.FooterContainer @@ -58,7 +59,7 @@ internal fun SendRecipientContent( title = address.label, placeholder = address.placeholder, onValueChange = address.onValueChange, - onPasteClick = clickIntents::onRecipientAddressValueChange, + onPasteClick = { clickIntents.onRecipientAddressValueChange(it, EnterAddressSource.PasteButton) }, isError = isError, isLoading = isValidating, error = address.error, @@ -80,7 +81,7 @@ internal fun SendRecipientContent( placeholder = placeholder, footer = stringResource(R.string.send_recipient_memo_footer), onValueChange = memoField.onValueChange, - onPasteClick = clickIntents::onRecipientMemoValueChange, + onPasteClick = { clickIntents.onRecipientMemoValueChange(it, isPasted = true) }, modifier = Modifier.padding(top = TangemTheme.dimens.spacing20), isError = memoField.isError, error = memoField.error, @@ -165,7 +166,9 @@ private fun LazyListScope.recipientListItem( }, ) .background(TangemTheme.colors.background.action), - onClick = { clickIntents.onRecipientAddressValueChange(title) }, + onClick = { + clickIntents.onRecipientAddressValueChange(title, EnterAddressSource.RecentAddress) + }, ) } } @@ -203,7 +206,7 @@ private fun RecipientWalletListItem( ListItemWithIcon( title = wallet.title.resolveReference(), subtitle = wallet.subtitle.resolveReference(), - onClick = { clickIntents.onRecipientAddressValueChange(title) }, + onClick = { clickIntents.onRecipientAddressValueChange(title, EnterAddressSource.RecentAddress) }, ) } if (!item.isWalletsOnly) { diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendClickIntents.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendClickIntents.kt index cd3c448c3f..18252b0ed4 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendClickIntents.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendClickIntents.kt @@ -2,10 +2,11 @@ package com.tangem.features.send.impl.presentation.viewmodel import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.features.send.impl.presentation.analytics.EnterAddressSource import com.tangem.features.send.impl.presentation.state.fee.FeeType @Suppress("TooManyFunctions") -interface SendClickIntents { +internal interface SendClickIntents { fun popBackStack() @@ -30,9 +31,9 @@ interface SendClickIntents { // endregion // region Recipient - fun onRecipientAddressValueChange(value: String) + fun onRecipientAddressValueChange(value: String, type: EnterAddressSource? = null) - fun onRecipientMemoValueChange(value: String) + fun onRecipientMemoValueChange(value: String, isPasted: Boolean = false) // endregion // region Fee @@ -56,6 +57,8 @@ interface SendClickIntents { fun onExploreClick(txUrl: String) + fun onShareClick() + fun onAmountReduceClick(reducedAmount: String) fun onAmountReduceIgnoreClick() 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 d6e32da750..fa912092d9 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 @@ -9,6 +9,7 @@ import arrow.core.Either import arrow.core.getOrElse import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.transaction.TransactionFee +import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase @@ -34,6 +35,11 @@ import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.usecase.* import com.tangem.features.send.api.navigation.SendRouter import com.tangem.features.send.impl.navigation.InnerSendRouter +import com.tangem.features.send.impl.presentation.analytics.EnterAddressSource +import com.tangem.features.send.impl.presentation.analytics.SendAnalyticEvents +import com.tangem.features.send.impl.presentation.analytics.SendScreenSource +import com.tangem.features.send.impl.presentation.analytics.utils.SendOnNextScreenAnalyticSender +import com.tangem.features.send.impl.presentation.analytics.utils.SendRecipientAnalyticsSender import com.tangem.features.send.impl.presentation.domain.AvailableWallet import com.tangem.features.send.impl.presentation.state.* import com.tangem.features.send.impl.presentation.state.amount.AmountStateFactory @@ -75,6 +81,7 @@ internal class SendViewModel @Inject constructor( private val reduxStateHolder: ReduxStateHolder, private val isAmountSubtractAvailableUseCase: IsAmountSubtractAvailableUseCase, private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, + private val analyticsEventHandler: AnalyticsEventHandler, currencyChecksRepository: CurrencyChecksRepository, isFeeApproximateUseCase: IsFeeApproximateUseCase, getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase, @@ -147,6 +154,12 @@ internal class SendViewModel @Inject constructor( clickIntents = this, ) + private val sendOnNextScreenAnalyticSender by lazy(LazyThreadSafetyMode.NONE) { + SendOnNextScreenAnalyticSender(analyticsEventHandler) + } + private val sendRecipientAnalyticsSender by lazy(LazyThreadSafetyMode.NONE) { + SendRecipientAnalyticsSender(analyticsEventHandler) + } // todo convert to StateFlow var uiState: SendUiState by mutableStateOf(stateFactory.getInitialState()) private set @@ -170,6 +183,7 @@ internal class SendViewModel @Inject constructor( subscribeOnCurrencyStatusUpdates(owner) onStateActive() subscribeOnBalanceHidden(owner) + analyticsEventHandler.send(SendAnalyticEvents.SendOpened) } fun setRouter(router: InnerSendRouter, stateRouter: StateRouter) { @@ -273,11 +287,11 @@ internal class SendViewModel @Inject constructor( if (transactionId != null && amount != null && destinationAddress != null) { uiState = stateFactory.getReadyState(amount, destinationAddress) - showFee() + stateRouter.showFee() } else { getWalletsAndRecent() uiState = stateFactory.getReadyState() - showRecipient() + stateRouter.showRecipient() } updateNotifications() } @@ -392,10 +406,17 @@ internal class SendViewModel @Inject constructor( // region screen state navigation override fun popBackStack() = stateRouter.popBackStack() override fun onBackClick() = stateRouter.onBackClick(uiState.sendState.isSuccess) - override fun onNextClick() = stateRouter.onNextClick() + override fun onNextClick() { + val prevScreen = stateRouter.onNextClick() + sendOnNextScreenAnalyticSender.send(prevScreen, uiState) + } + override fun onPrevClick() = stateRouter.onPrevClick() - override fun onQrCodeScanClick() = innerRouter.openQrCodeScanner(cryptoCurrency.network.name) + override fun onQrCodeScanClick() { + analyticsEventHandler.send(SendAnalyticEvents.QrCodeButtonClicked) + innerRouter.openQrCodeScanner(cryptoCurrency.network.name) + } override fun onFailedTxEmailClick(errorMessage: String) { reduxStateHolder.dispatch(LegacyAction.SendEmailTransactionFailed(errorMessage)) @@ -407,6 +428,7 @@ internal class SendViewModel @Inject constructor( // region amount state clicks override fun onCurrencyChangeClick(isFiat: Boolean) { + analyticsEventHandler.send(SendAnalyticEvents.SwapCurrencyButtonClicked) uiState = amountStateFactory.getOnCurrencyChangedState(isFiat) } @@ -424,7 +446,7 @@ internal class SendViewModel @Inject constructor( viewModelScope.launch(dispatchers.main) { parseSharedAddressUseCase(address, cryptoCurrency.network).fold( ifRight = { parsedCode -> - onRecipientAddressValueChange(parsedCode.address) + onRecipientAddressValueChange(parsedCode.address, EnterAddressSource.QRCode) parsedCode.amount?.let { onAmountValueChange(it.toPlainString()) } parsedCode.memo?.let { onRecipientMemoValueChange(it) } }, @@ -435,24 +457,26 @@ internal class SendViewModel @Inject constructor( }.saveIn(qrScannerJobHolder) } - override fun onRecipientAddressValueChange(value: String) { + override fun onRecipientAddressValueChange(value: String, type: EnterAddressSource?) { viewModelScope.launch(dispatchers.main) { if (!checkIfXrpAddressValue(value)) { uiState = stateFactory.onRecipientAddressValueChange(value) uiState = stateFactory.getOnRecipientAddressValidationStarted() val isValidAddress = validateAddress(value) uiState = stateFactory.getOnRecipientAddressValidState(value, isValidAddress) + sendRecipientAnalyticsSender.sendAddressAnalytics(type, isValidAddress) } }.saveIn(addressValidationJobHolder) } - override fun onRecipientMemoValueChange(value: String) { + override fun onRecipientMemoValueChange(value: String, isPasted: Boolean) { viewModelScope.launch(dispatchers.main) { if (!checkIfXrpAddressValue(value)) { uiState = stateFactory.getOnRecipientMemoValueChange(value) uiState = stateFactory.getOnRecipientAddressValidationStarted() val isValidAddress = validateAddress(uiState.recipientState?.addressTextField?.value.orEmpty()) uiState = stateFactory.getOnRecipientMemoValidState(value, isValidAddress) + sendRecipientAnalyticsSender.sendMemoAnalytics(isPasted) } }.saveIn(addressValidationJobHolder) } @@ -482,6 +506,9 @@ internal class SendViewModel @Inject constructor( override fun onFeeSelectorClick(feeType: FeeType) { uiState = feeStateFactory.onFeeSelectedState(feeType) updateFeeNotifications() + if (feeType == FeeType.Custom) { + analyticsEventHandler.send(SendAnalyticEvents.CustomFeeButtonClicked) + } } override fun onCustomFeeValueChange(index: Int, value: String) { @@ -542,15 +569,32 @@ internal class SendViewModel @Inject constructor( onCheckFeeUpdate() } sendIdleTimer = System.currentTimeMillis() + analyticsEventHandler.send(SendAnalyticEvents.SendButtonClicked) } - override fun showAmount() = stateRouter.showAmount() + override fun showAmount() { + stateRouter.showAmount() + analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Amount)) + } - override fun showRecipient() = stateRouter.showRecipient() + override fun showRecipient() { + stateRouter.showRecipient() + analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Address)) + } - override fun showFee() = stateRouter.showFee() + override fun showFee() { + stateRouter.showFee() + analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Fee)) + } - override fun onExploreClick(txUrl: String) = innerRouter.openUrl(txUrl) + override fun onExploreClick(txUrl: String) { + analyticsEventHandler.send(SendAnalyticEvents.ExploreButtonClicked) + innerRouter.openUrl(txUrl) + } + + override fun onShareClick() { + analyticsEventHandler.send(SendAnalyticEvents.ShareButtonClicked) + } override fun onAmountReduceClick(reducedAmount: String) { uiState = amountStateFactory.getOnAmountValueChange(reducedAmount) @@ -616,6 +660,7 @@ internal class SendViewModel @Inject constructor( uiState = stateFactory.getSendingStateUpdate(isSending = false) uiState = stateFactory.getTransactionSendState(txData) scheduleBalanceUpdate() + analyticsEventHandler.send(SendAnalyticEvents.TransactionScreenOpened) }, ) } From f6fd1a51fbd9099c0a148b6a4672e7fd7efcf5b9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 9 Feb 2024 23:26:37 +0100 Subject: [PATCH 28/53] Updated on 2026-08-14 --- .../validators/ContractAddressValidator.kt | 6 +-- .../ContractAddressValidatorResult.kt | 2 +- .../viewmodels/AddCustomTokenViewModel.kt | 8 ++-- .../com/tangem/domain/DomainModuleMessage.kt | 39 ------------------- .../tokens/ValidateContractAddressUseCase.kt | 12 +++--- .../tokens/error/AddCustomTokenError.kt | 6 +++ features/manage-tokens/impl/build.gradle.kts | 2 - .../factory/AddCustomTokenStateFactory.kt | 6 +-- 8 files changed, 24 insertions(+), 57 deletions(-) delete mode 100644 domain/legacy/src/main/java/com/tangem/domain/DomainModuleMessage.kt create mode 100644 domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/AddCustomTokenError.kt diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/validators/ContractAddressValidator.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/validators/ContractAddressValidator.kt index 5a8c8bf385..5db91d6354 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/validators/ContractAddressValidator.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/validators/ContractAddressValidator.kt @@ -3,7 +3,7 @@ package com.tangem.tap.features.customtoken.impl.presentation.validators import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.address.AddressService import com.tangem.common.card.EllipticCurve -import com.tangem.domain.AddCustomTokenError +import com.tangem.domain.tokens.error.AddCustomTokenError /** * Validator of contract address @@ -15,9 +15,9 @@ object ContractAddressValidator { /** Validate a [address] using [blockchain] */ fun validate(address: String, blockchain: Blockchain): ContractAddressValidatorResult { return when { - address.isEmpty() -> ContractAddressValidatorResult.Error(type = AddCustomTokenError.FieldIsEmpty) + address.isEmpty() -> ContractAddressValidatorResult.Error(type = AddCustomTokenError.FIELD_IS_EMPTY) validateAddress(blockchain, address) -> ContractAddressValidatorResult.Success - else -> ContractAddressValidatorResult.Error(type = AddCustomTokenError.InvalidContractAddress) + else -> ContractAddressValidatorResult.Error(type = AddCustomTokenError.INVALID_CONTRACT_ADDRESS) } } diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/validators/ContractAddressValidatorResult.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/validators/ContractAddressValidatorResult.kt index 417b3821e0..daf1b4d159 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/validators/ContractAddressValidatorResult.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/validators/ContractAddressValidatorResult.kt @@ -1,6 +1,6 @@ package com.tangem.tap.features.customtoken.impl.presentation.validators -import com.tangem.domain.AddCustomTokenError +import com.tangem.domain.tokens.error.AddCustomTokenError /** * Result of validation contract address diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt index 0faab87b40..0825668edb 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt @@ -16,13 +16,13 @@ import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.crypto.hdWallet.HDWalletError -import com.tangem.domain.AddCustomTokenError import com.tangem.domain.common.DerivationStyleProvider import com.tangem.domain.common.extensions.* import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.features.addCustomToken.CustomCurrency import com.tangem.domain.tokens.GetCryptoCurrenciesUseCase +import com.tangem.domain.tokens.error.AddCustomTokenError import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.tap.features.customtoken.impl.domain.CustomTokenInteractor @@ -465,7 +465,7 @@ internal class AddCustomTokenViewModel @Inject constructor( address = uiState.form.contractAddressInputField.value, blockchain = networkSelectorValue, ).let { - it is ContractAddressValidatorResult.Error && it.type == AddCustomTokenError.FieldIsEmpty + it is ContractAddressValidatorResult.Error && it.type == AddCustomTokenError.FIELD_IS_EMPTY } val isSupportedToken = if (!isNetworkSelected()) { @@ -621,7 +621,7 @@ internal class AddCustomTokenViewModel @Inject constructor( private fun handleContractAddressErrorValidation(type: AddCustomTokenError) { when { - isNetworkSelected() && type == AddCustomTokenError.InvalidContractAddress -> { + isNetworkSelected() && type == AddCustomTokenError.INVALID_CONTRACT_ADDRESS -> { val isAnotherTokenFieldsFilled = isAnyTokenFieldsFilled() uiState = uiState.copySealed( form = uiState.form.copy( @@ -644,7 +644,7 @@ internal class AddCustomTokenViewModel @Inject constructor( ) } - !isNetworkSelected() || type == AddCustomTokenError.FieldIsEmpty -> { + !isNetworkSelected() || type == AddCustomTokenError.FIELD_IS_EMPTY -> { uiState = uiState.copySealed( form = uiState.form.copy( contractAddressInputField = uiState.form.contractAddressInputField.copy(isError = false), diff --git a/domain/legacy/src/main/java/com/tangem/domain/DomainModuleMessage.kt b/domain/legacy/src/main/java/com/tangem/domain/DomainModuleMessage.kt deleted file mode 100644 index af32f7c703..0000000000 --- a/domain/legacy/src/main/java/com/tangem/domain/DomainModuleMessage.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.tangem.domain - -import com.tangem.common.module.ModuleError -import com.tangem.common.module.ModuleErrorCode -import com.tangem.common.module.ModuleMessage - -/** -[REDACTED_AUTHOR] - * All DomainError descendants must use their own range of codes, but no more than 999 error codes for each. - */ -sealed interface DomainModuleMessage : ModuleMessage - -sealed class DomainModuleError( - subCode: Int, - override val message: String, - override val data: Any?, -) : DomainModuleMessage, ModuleError() { - override val code: Int = ModuleErrorCode.DOMAIN + subCode - - companion object { - // base code used for all errors in the module - internal const val ERROR_CODE_ADD_CUSTOM_TOKEN = 100 -// const val CODE_ANY_OTHER = 200..299, 300..399 etc - } -} - -sealed class AddCustomTokenError( - subCode: Int = 0, - message: String? = null, - data: Any? = null, -) : DomainModuleError( - subCode = ERROR_CODE_ADD_CUSTOM_TOKEN + subCode, - message = message ?: this::class.java.simpleName, - data = data, -) { - - object FieldIsEmpty : AddCustomTokenError() - object InvalidContractAddress : AddCustomTokenError() -} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ValidateContractAddressUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ValidateContractAddressUseCase.kt index 5ae4755564..a2c2d2c1b4 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ValidateContractAddressUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ValidateContractAddressUseCase.kt @@ -3,26 +3,28 @@ package com.tangem.domain.tokens import arrow.core.Either import arrow.core.raise.catch import arrow.core.raise.either -import com.tangem.domain.AddCustomTokenError +import com.tangem.domain.tokens.error.AddCustomTokenError import com.tangem.domain.tokens.repository.TokensListRepository -class ValidateContractAddressUseCase(private val tokensListRepository: TokensListRepository) { +class ValidateContractAddressUseCase( + private val tokensListRepository: TokensListRepository, +) { operator fun invoke(address: String, networkId: String): Either { return either { catch( block = { - if (address.isEmpty()) raise(AddCustomTokenError.FieldIsEmpty) + if (address.isEmpty()) raise(AddCustomTokenError.FIELD_IS_EMPTY) if (!tokensListRepository.validateAddress( contractAddress = address, networkId = networkId, ) ) { - raise(AddCustomTokenError.InvalidContractAddress) + raise(AddCustomTokenError.INVALID_CONTRACT_ADDRESS) } }, catch = { - AddCustomTokenError.InvalidContractAddress + AddCustomTokenError.INVALID_CONTRACT_ADDRESS }, ) } diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/AddCustomTokenError.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/AddCustomTokenError.kt new file mode 100644 index 0000000000..fcbb456b66 --- /dev/null +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/AddCustomTokenError.kt @@ -0,0 +1,6 @@ +package com.tangem.domain.tokens.error + +enum class AddCustomTokenError { + FIELD_IS_EMPTY, + INVALID_CONTRACT_ADDRESS, +} \ No newline at end of file diff --git a/features/manage-tokens/impl/build.gradle.kts b/features/manage-tokens/impl/build.gradle.kts index e388043baa..1be0e9701e 100644 --- a/features/manage-tokens/impl/build.gradle.kts +++ b/features/manage-tokens/impl/build.gradle.kts @@ -59,8 +59,6 @@ dependencies { implementation(projects.domain.settings) implementation(projects.domain.tokens) implementation(projects.domain.tokens.models) - implementation(projects.domain.txhistory) - implementation(projects.domain.txhistory.models) implementation(projects.domain.wallets) implementation(projects.domain.wallets.models) implementation(projects.domain.appCurrency) diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/AddCustomTokenStateFactory.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/AddCustomTokenStateFactory.kt index 6e51737162..57ce303a3c 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/AddCustomTokenStateFactory.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/AddCustomTokenStateFactory.kt @@ -2,7 +2,7 @@ package com.tangem.managetokens.presentation.addcustomtoken.state.factory import com.tangem.core.ui.event.consumedEvent import com.tangem.core.ui.event.triggeredEvent -import com.tangem.domain.AddCustomTokenError +import com.tangem.domain.tokens.error.AddCustomTokenError import com.tangem.domain.tokens.model.Network import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId @@ -285,7 +285,7 @@ internal class AddCustomTokenStateFactory( fun handleAddressError(error: AddCustomTokenError): AddCustomTokenState { val uiState = currentStateProvider() return when (error) { - AddCustomTokenError.InvalidContractAddress -> { + AddCustomTokenError.INVALID_CONTRACT_ADDRESS -> { addTokenAddressFieldError(AddCustomTokenWarning.InvalidContractAddress) .copy( addTokenButton = uiState.addTokenButton.copy(isEnabled = false), @@ -294,7 +294,7 @@ internal class AddCustomTokenStateFactory( .toPersistentSet(), ) } - AddCustomTokenError.FieldIsEmpty -> + AddCustomTokenError.FIELD_IS_EMPTY -> removeTokenAddressError() .copy( addTokenButton = uiState.addTokenButton.copy( From 3affe7395c3b2cd160787e3d81875bd02181d8cb Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 9 Feb 2024 12:02:55 +0300 Subject: [PATCH 29/53] Updated on 2026-08-14 --- .../core/ui/utils/DateTimeFormatters.kt | 4 + .../BalancesAndLimitsBottomSheetConfig.kt | 5 +- .../model/VisaTxDetailsBottomSheetConfig.kt | 39 +++ .../BalancesAndLimitsBottomSheetConverter.kt | 17 +- .../VisaTxDetailsBottomSheetConverter.kt | 86 ++++++ .../presentation/wallet/ui/WalletScreen.kt | 2 + .../visa/BalancesAndLimitsBottomSheet.kt | 218 ++++++------- .../wallet/ui/components/visa/Blocks.kt | 83 +++++ .../visa/VisaTxDetailsBottomSheet.kt | 286 ++++++++++++++++++ .../viewmodels/intents/VisaWalletIntents.kt | 30 +- 10 files changed, 624 insertions(+), 146 deletions(-) create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/VisaTxDetailsBottomSheetConfig.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/VisaTxDetailsBottomSheetConverter.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/Blocks.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/VisaTxDetailsBottomSheet.kt diff --git a/core/ui/src/main/java/com/tangem/core/ui/utils/DateTimeFormatters.kt b/core/ui/src/main/java/com/tangem/core/ui/utils/DateTimeFormatters.kt index dc27c1641a..ce8b600e55 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/utils/DateTimeFormatters.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/utils/DateTimeFormatters.kt @@ -55,6 +55,10 @@ object DateTimeFormatters { .withLocale(Locale.getDefault()) } + val dateTimeFormatter: DateTimeFormatter by lazy { + DateTimeFormat.forPattern("dd.MM.yyyy HH:mm") + } + fun formatTime(formatter: DateTimeFormatter = timeFormatter, time: DateTime): String { return formatter.print(time) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/BalancesAndLimitsBottomSheetConfig.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/BalancesAndLimitsBottomSheetConfig.kt index 5945197384..bfabae7770 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/BalancesAndLimitsBottomSheetConfig.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/BalancesAndLimitsBottomSheetConfig.kt @@ -3,11 +3,8 @@ package com.tangem.feature.wallet.presentation.wallet.state.model import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent internal data class BalancesAndLimitsBottomSheetConfig( - val currency: String, val balance: Balance, val limit: Limit, - val onBalanceInfoClick: () -> Unit, - val onLimitInfoClick: () -> Unit, ) : TangemBottomSheetConfigContent { data class Balance( @@ -17,6 +14,7 @@ internal data class BalancesAndLimitsBottomSheetConfig( val debit: String, val pending: String, val amlVerified: String, + val onInfoClick: () -> Unit, ) data class Limit( @@ -24,5 +22,6 @@ internal data class BalancesAndLimitsBottomSheetConfig( val inStore: String, val other: String, val singleTransaction: String, + val onInfoClick: () -> Unit, ) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/VisaTxDetailsBottomSheetConfig.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/VisaTxDetailsBottomSheetConfig.kt new file mode 100644 index 0000000000..6389eb3a0f --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/VisaTxDetailsBottomSheetConfig.kt @@ -0,0 +1,39 @@ +package com.tangem.feature.wallet.presentation.wallet.state.model + +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent +import kotlinx.collections.immutable.ImmutableList + +internal data class VisaTxDetailsBottomSheetConfig( + val transaction: Transaction, + val requests: ImmutableList, +) : TangemBottomSheetConfigContent { + + data class Transaction( + val id: String, + val type: String, + val status: String, + val blockchainAmount: String, + val blockchainFee: String, + val transactionAmount: String, + val transactionCurrencyCode: String, + val merchantName: String, + val merchantCity: String, + val merchantCountryCode: String, + val merchantCategoryCode: String, + ) + + data class Request( + val id: String, + val type: String, + val status: String, + val blockchainAmount: String, + val blockchainFee: String, + val transactionAmount: String, + val currencyCode: String, + val errorCode: Int, + val date: String, + val txHash: String, + val txStatus: String, + val onExploreClick: (() -> Unit)?, + ) +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/BalancesAndLimitsBottomSheetConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/BalancesAndLimitsBottomSheetConverter.kt index 14f9493984..f43ec73629 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/BalancesAndLimitsBottomSheetConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/BalancesAndLimitsBottomSheetConverter.kt @@ -15,8 +15,13 @@ internal class BalancesAndLimitsBottomSheetConverter( ) : Converter { override fun convert(value: VisaCurrency): BalancesAndLimitsBottomSheetConfig { + fun formatAmount(amount: BigDecimal): String = BigDecimalFormatter.formatCryptoAmount( + amount, + cryptoCurrency = value.symbol, + decimals = value.decimals, + ) + return BalancesAndLimitsBottomSheetConfig( - currency = value.symbol, balance = BalancesAndLimitsBottomSheetConfig.Balance( totalBalance = value.balances.total.let(::formatAmount), availableBalance = value.balances.available.let(::formatAmount), @@ -24,24 +29,18 @@ internal class BalancesAndLimitsBottomSheetConverter( debit = value.balances.debt.let(::formatAmount), pending = value.balances.pendingRefund.let(::formatAmount), amlVerified = value.balances.verified.let(::formatAmount), + onInfoClick = this::showBalanceInfo, ), limit = BalancesAndLimitsBottomSheetConfig.Limit( availableBy = DateTimeFormatters.formatDate(date = value.limits.expirationDate), inStore = value.limits.remainingOtp.let(::formatAmount), other = value.limits.remainingNoOtp.let(::formatAmount), singleTransaction = value.limits.singleTransaction.let(::formatAmount), + onInfoClick = this::showLimitInfo, ), - onBalanceInfoClick = this::showBalanceInfo, - onLimitInfoClick = this::showLimitInfo, ) } - private fun formatAmount(amount: BigDecimal): String = BigDecimalFormatter.formatCryptoAmount( - amount, - cryptoCurrency = "", - decimals = 2, - ) - private fun showBalanceInfo() { eventSender.send(WalletEvent.ShowAlert(WalletAlertState.VisaBalancesInfo)) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/VisaTxDetailsBottomSheetConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/VisaTxDetailsBottomSheetConverter.kt new file mode 100644 index 0000000000..f431a74a5e --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/VisaTxDetailsBottomSheetConverter.kt @@ -0,0 +1,86 @@ +package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter + +import com.tangem.core.ui.utils.BigDecimalFormatter +import com.tangem.core.ui.utils.DateTimeFormatters +import com.tangem.domain.visa.model.VisaCurrency +import com.tangem.domain.visa.model.VisaTxDetails +import com.tangem.feature.wallet.presentation.wallet.state.model.VisaTxDetailsBottomSheetConfig +import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.VisaWalletIntents +import com.tangem.utils.converter.Converter +import kotlinx.collections.immutable.toImmutableList +import org.joda.time.DateTimeZone +import java.math.BigDecimal +import java.util.Currency + +internal class VisaTxDetailsBottomSheetConverter( + private val visaCurrency: VisaCurrency, + private val clickIntents: VisaWalletIntents, +) : Converter { + + override fun convert(value: VisaTxDetails): VisaTxDetailsBottomSheetConfig { + return VisaTxDetailsBottomSheetConfig( + transaction = createTransaction(value), + requests = value.requests.map(::createRequest).toImmutableList(), + ) + } + + private fun createTransaction(details: VisaTxDetails): VisaTxDetailsBottomSheetConfig.Transaction { + return VisaTxDetailsBottomSheetConfig.Transaction( + id = details.id, + type = details.type, + status = details.status, + blockchainAmount = formatNetworkAmount(details.blockchainAmount), + blockchainFee = formatNetworkAmount(details.blockchainFee), + transactionAmount = formatFiatAmount(details.transactionAmount, details.fiatCurrency), + transactionCurrencyCode = details.transactionCurrencyCode.toString(), + merchantName = details.merchantName ?: UNKNOWN, + merchantCity = details.merchantCity ?: UNKNOWN, + merchantCountryCode = details.merchantCountryCode ?: UNKNOWN, + merchantCategoryCode = details.merchantCategoryCode ?: UNKNOWN, + ) + } + + private fun createRequest(request: VisaTxDetails.Request): VisaTxDetailsBottomSheetConfig.Request { + val localDate = request.requestDate.withZone(DateTimeZone.getDefault()) + val exploreUrl = request.exploreUrl + + return VisaTxDetailsBottomSheetConfig.Request( + id = request.id, + type = request.requestType, + status = request.requestStatus, + blockchainAmount = formatNetworkAmount(request.blockchainAmount), + blockchainFee = formatNetworkAmount(request.blockchainFee), + transactionAmount = formatFiatAmount(request.transactionAmount, request.fiatCurrency), + currencyCode = request.billingCurrencyCode.toString(), + errorCode = request.errorCode, + date = DateTimeFormatters.formatDate(DateTimeFormatters.dateTimeFormatter, date = localDate), + txHash = request.txHash ?: UNKNOWN, + txStatus = request.txStatus ?: UNKNOWN, + onExploreClick = if (exploreUrl != null) { + { clickIntents.onExploreClick(exploreUrl) } + } else { + null + }, + ) + } + + private fun formatNetworkAmount(amount: BigDecimal): String { + return BigDecimalFormatter.formatCryptoAmount( + cryptoAmount = amount, + cryptoCurrency = visaCurrency.symbol, + decimals = visaCurrency.decimals, + ) + } + + private fun formatFiatAmount(amount: BigDecimal, fiatCurrency: Currency): String { + return BigDecimalFormatter.formatFiatAmount( + fiatAmount = amount, + fiatCurrencyCode = fiatCurrency.currencyCode, + fiatCurrencySymbol = fiatCurrency.symbol, + ) + } + + private companion object { + const val UNKNOWN = "Unknown" + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index 3dcd34d73e..e491dcb364 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -35,6 +35,7 @@ import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.controlButtons import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.marketPriceBlock import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.BalancesAndLimitsBottomSheet +import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.VisaTxDetailsBottomSheet import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.balancesAndLimitsBlock import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.depositButton import com.tangem.feature.wallet.presentation.wallet.ui.utils.changeWalletAnimator @@ -169,6 +170,7 @@ private fun WalletContent( is ActionsBottomSheetConfig -> TokenActionsBottomSheet(config = bottomSheetConfig) is ChooseAddressBottomSheetConfig -> ChooseAddressBottomSheet(config = bottomSheetConfig) is BalancesAndLimitsBottomSheetConfig -> BalancesAndLimitsBottomSheet(config = bottomSheetConfig) + is VisaTxDetailsBottomSheetConfig -> VisaTxDetailsBottomSheet(config = bottomSheetConfig) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/BalancesAndLimitsBottomSheet.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/BalancesAndLimitsBottomSheet.kt index fce6384945..c2e6a39b4d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/BalancesAndLimitsBottomSheet.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/BalancesAndLimitsBottomSheet.kt @@ -13,11 +13,8 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider import com.tangem.core.ui.components.SpacerH16 -import com.tangem.core.ui.components.SpacerWMax import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig -import com.tangem.core.ui.extensions.TextReference -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.wallet.impl.R @@ -45,136 +42,92 @@ private fun BalancesAndLimitsContent(config: BalancesAndLimitsBottomSheetConfig, ) }, firstBlock = { - BlockContent( - title = stringReference("Balance, ${config.currency}"), - content = { - BlockItem( - title = stringReference("Total"), - value = config.balance.totalBalance, - ) - BlockItem( - title = stringReference("AML Verified"), - value = config.balance.amlVerified, - ) - BlockItem( - title = stringReference("Available"), - value = config.balance.availableBalance, - ) - BlockItem( - title = stringReference("Blocked"), - value = config.balance.blockedBalance, - ) - BlockItem( - title = stringReference("Debit"), - value = config.balance.debit, - ) - BlockItem( - title = stringReference("Pending refund"), - value = config.balance.pending, - ) - }, - onInfoIconClick = config.onBalanceInfoClick, - ) + BalancesBlock(balances = config.balance) }, secondBlock = { - BlockContent( - title = stringReference("Limits, ${config.currency}"), - description = stringReference("Available by ${config.limit.availableBy}"), - content = { - BlockItem( - title = stringReference("In-store (otp)"), - value = config.limit.inStore, - ) - BlockItem( - title = stringReference("Other (no-otp)"), - value = config.limit.other, - ) - BlockItem( - title = stringReference("Single transaction"), - value = config.limit.singleTransaction, - ) - }, - onInfoIconClick = config.onBalanceInfoClick, - ) + LimitsBlock(limits = config.limit) }, ) } @Composable -private inline fun BlockContent( - title: TextReference, - content: @Composable ColumnScope.() -> Unit, - noinline onInfoIconClick: () -> Unit, - modifier: Modifier = Modifier, - description: TextReference? = null, -) { - Column( - modifier = modifier - .padding(horizontal = TangemTheme.dimens.spacing16) - .fillMaxWidth() - .background( - color = TangemTheme.colors.background.primary, - shape = TangemTheme.shapes.roundedCornersXMedium, +private fun BalancesBlock(balances: BalancesAndLimitsBottomSheetConfig.Balance, modifier: Modifier = Modifier) { + BlockContent( + modifier = modifier, + title = stringReference("Balance"), + content = { + BlockItem( + title = stringReference("Total"), + value = balances.totalBalance, ) - .padding(vertical = TangemTheme.dimens.spacing8), - ) { - Row( - modifier = Modifier - .padding( - start = TangemTheme.dimens.spacing12, - end = TangemTheme.dimens.spacing4, - ) - .fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - text = title.resolveReference(), - style = TangemTheme.typography.subtitle2, - color = TangemTheme.colors.text.tertiary, + BlockItem( + title = stringReference("AML Verified"), + value = balances.amlVerified, ) - SpacerWMax() - if (description != null) { - Text( - text = description.resolveReference(), - style = TangemTheme.typography.body2, - color = TangemTheme.colors.text.tertiary, - ) - } - IconButton( - modifier = Modifier.size(TangemTheme.dimens.size32), - onClick = onInfoIconClick, - ) { - Icon( - modifier = Modifier.size(TangemTheme.dimens.size16), - painter = painterResource(id = R.drawable.ic_information_24), - tint = TangemTheme.colors.icon.informative, - contentDescription = null, - ) - } - } - content() - } + BlockItem( + title = stringReference("Available"), + value = balances.availableBalance, + ) + BlockItem( + title = stringReference("Blocked"), + value = balances.blockedBalance, + ) + BlockItem( + title = stringReference("Debit"), + value = balances.debit, + ) + BlockItem( + title = stringReference("Pending refund"), + value = balances.pending, + ) + }, + description = { + InfoButton(onClick = balances.onInfoClick) + }, + ) } @Composable -private fun BlockItem(title: TextReference, value: String, modifier: Modifier = Modifier) { - Row( - modifier = modifier - .padding(horizontal = TangemTheme.dimens.spacing12) - .fillMaxWidth() - .heightIn(min = TangemTheme.dimens.size32), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, +private fun LimitsBlock(limits: BalancesAndLimitsBottomSheetConfig.Limit, modifier: Modifier = Modifier) { + BlockContent( + modifier = modifier, + title = stringReference("Limits"), + content = { + BlockItem( + title = stringReference("In-store (otp)"), + value = limits.inStore, + ) + BlockItem( + title = stringReference("Other (no-otp)"), + value = limits.other, + ) + BlockItem( + title = stringReference("Single transaction"), + value = limits.singleTransaction, + ) + }, + description = { + Text( + text = "Available till ${limits.availableBy}", + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.tertiary, + ) + InfoButton(onClick = limits.onInfoClick) + }, + ) +} + +@Composable +private fun InfoButton(onClick: () -> Unit, modifier: Modifier = Modifier) { + IconButton( + modifier = modifier.size(TangemTheme.dimens.size32), + onClick = onClick, ) { - Text( - text = title.resolveReference(), - style = TangemTheme.typography.caption2, - color = TangemTheme.colors.text.tertiary, - ) - Text( - text = value, - style = TangemTheme.typography.caption2, - color = TangemTheme.colors.text.primary1, + Icon( + modifier = Modifier.size(TangemTheme.dimens.size16), + painter = painterResource(id = R.drawable.ic_information_24), + tint = TangemTheme.colors.icon.informative, + contentDescription = null, ) } } @@ -229,23 +182,22 @@ private class BalancesAndLimitsBottomSheetParameterProvider : CollectionPreviewParameterProvider( collection = listOf( BalancesAndLimitsBottomSheetConfig( - currency = "USDT", balance = BalancesAndLimitsBottomSheetConfig.Balance( - totalBalance = "492.45", - availableBalance = "392.45", - blockedBalance = "36.00", - debit = "00.00", - pending = "20.99", - amlVerified = "356.45", + totalBalance = "492.45 USDT", + availableBalance = "392.45 USDT", + blockedBalance = "36.00 USDT", + debit = "00.00 USDT", + pending = "20.99 USDT", + amlVerified = "356.45 USDT", + onInfoClick = {}, ), limit = BalancesAndLimitsBottomSheetConfig.Limit( - availableBy = "Nov, 11", - inStore = "563.00", - other = "100.00", - singleTransaction = "100.00", + availableBy = "Nov, 11 USDT", + inStore = "563.00 USDT", + other = "100.00 USDT", + singleTransaction = "100.00 USDT", + onInfoClick = {}, ), - onBalanceInfoClick = {}, - onLimitInfoClick = {}, ), ), ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/Blocks.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/Blocks.kt new file mode 100644 index 0000000000..723fabf3bc --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/Blocks.kt @@ -0,0 +1,83 @@ +package com.tangem.feature.wallet.presentation.wallet.ui.components.visa + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import com.tangem.core.ui.components.SpacerH8 +import com.tangem.core.ui.components.SpacerWMax +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.res.TangemTheme + +private const val BLOCK_ITEM_NAME_WEIGHT = .45f +private const val BLOCK_ITEM_VALUE_WEIGHT = .55f + +@Composable +internal inline fun BlockContent( + title: TextReference, + content: @Composable ColumnScope.() -> Unit, + modifier: Modifier = Modifier, + description: @Composable RowScope.() -> Unit = {}, +) { + Column( + modifier = modifier + .padding(horizontal = TangemTheme.dimens.spacing16) + .fillMaxWidth() + .background( + color = TangemTheme.colors.background.primary, + shape = TangemTheme.shapes.roundedCornersXMedium, + ), + ) { + Row( + modifier = Modifier + .padding(start = TangemTheme.dimens.spacing12) + .fillMaxWidth() + .heightIn(min = TangemTheme.dimens.size42), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = title.resolveReference(), + style = TangemTheme.typography.subtitle2, + color = TangemTheme.colors.text.tertiary, + ) + SpacerWMax() + description() + } + content() + SpacerH8() + } +} + +@Composable +internal fun BlockItem(title: TextReference, value: String, modifier: Modifier = Modifier) { + Row( + modifier = modifier + .fillMaxWidth() + .heightIn(min = TangemTheme.dimens.size32) + .padding( + vertical = TangemTheme.dimens.spacing8, + horizontal = TangemTheme.dimens.spacing12, + ), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.Top, + ) { + Text( + modifier = Modifier.weight(BLOCK_ITEM_NAME_WEIGHT), + text = title.resolveReference(), + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + textAlign = TextAlign.Start, + ) + Text( + modifier = Modifier.weight(BLOCK_ITEM_VALUE_WEIGHT), + text = value, + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.primary1, + textAlign = TextAlign.End, + ) + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/VisaTxDetailsBottomSheet.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/VisaTxDetailsBottomSheet.kt new file mode 100644 index 0000000000..eafdaf3801 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/VisaTxDetailsBottomSheet.kt @@ -0,0 +1,286 @@ +package com.tangem.feature.wallet.presentation.wallet.ui.components.visa + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider +import com.tangem.core.ui.components.SpacerW12 +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.wallet.impl.R +import com.tangem.feature.wallet.presentation.wallet.state.model.VisaTxDetailsBottomSheetConfig +import kotlinx.collections.immutable.persistentListOf + +@Composable +internal fun VisaTxDetailsBottomSheet(config: TangemBottomSheetConfig) { + TangemBottomSheet( + config = config, + containerColor = TangemTheme.colors.background.secondary, + ) { content: VisaTxDetailsBottomSheetConfig -> + VisaTxDetailsBottomSheetContent(content) + } +} + +@Composable +private fun VisaTxDetailsBottomSheetContent(config: VisaTxDetailsBottomSheetConfig, modifier: Modifier = Modifier) { + ContentContainer( + modifier = modifier, + blocksCount = config.requests.size.inc(), + title = { + Text( + text = "Transaction Details", + style = TangemTheme.typography.subtitle1, + color = TangemTheme.colors.text.primary1, + ) + }, + block = { index -> + if (index == 0) { + TransactionBlock(config.transaction) + } else { + BlockchainRequestBlock(config.requests[index - 1]) + } + }, + ) +} + +@Composable +private fun TransactionBlock(transaction: VisaTxDetailsBottomSheetConfig.Transaction, modifier: Modifier = Modifier) { + BlockContent( + modifier = modifier, + title = stringReference(value = "Transaction"), + content = { + BlockItem( + title = stringReference(value = "Type"), + value = transaction.type, + ) + BlockItem( + title = stringReference(value = "Status"), + value = transaction.status, + ) + BlockItem( + title = stringReference(value = "Blockchain Amount"), + value = transaction.blockchainAmount, + ) + BlockItem( + title = stringReference(value = "Blockchain Fee"), + value = transaction.blockchainFee, + ) + BlockItem( + title = stringReference(value = "Transaction Amount"), + value = transaction.transactionAmount, + ) + BlockItem( + title = stringReference(value = "Currency Code"), + value = transaction.transactionCurrencyCode, + ) + BlockItem( + title = stringReference(value = "Merchant Name"), + value = transaction.merchantName, + ) + BlockItem( + title = stringReference(value = "Merchant City"), + value = transaction.merchantCity, + ) + BlockItem( + title = stringReference(value = "Merchant Country Code"), + value = transaction.merchantCountryCode, + ) + BlockItem( + title = stringReference(value = "Merchant Category Code"), + value = transaction.merchantCategoryCode, + ) + }, + ) +} + +@Composable +private fun BlockchainRequestBlock(request: VisaTxDetailsBottomSheetConfig.Request, modifier: Modifier = Modifier) { + BlockContent( + modifier = modifier, + title = stringReference(value = "Blockchain request"), + description = { + if (request.onExploreClick == null) return + + Row( + modifier = Modifier.clickable(onClick = request.onExploreClick), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing4), + ) { + Icon( + painter = painterResource(id = R.drawable.ic_compass_24), + contentDescription = null, + modifier = Modifier.size(size = TangemTheme.dimens.size18), + tint = TangemTheme.colors.icon.informative, + ) + Text( + text = "Explore", + color = TangemTheme.colors.text.tertiary, + style = TangemTheme.typography.caption1, + ) + } + SpacerW12() + }, + content = { + BlockItem( + title = stringReference(value = "Type"), + value = request.type, + ) + BlockItem( + title = stringReference(value = "Status"), + value = request.status, + ) + BlockItem( + title = stringReference(value = "Blockchain Amount"), + value = request.blockchainAmount, + ) + BlockItem( + title = stringReference(value = "Blockchain Fee"), + value = request.blockchainFee, + ) + BlockItem( + title = stringReference(value = "Transaction Amount"), + value = request.transactionAmount, + ) + BlockItem( + title = stringReference(value = "Currency Code"), + value = request.currencyCode, + ) + BlockItem( + title = stringReference(value = "Error Code"), + value = request.errorCode.toString(), + ) + BlockItem( + title = stringReference(value = "Date"), + value = request.date, + ) + BlockItem( + title = stringReference(value = "Tx Hash"), + value = request.txHash, + ) + BlockItem( + title = stringReference(value = "Tx Status"), + value = request.txStatus, + ) + }, + ) +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +private fun ContentContainer( + blocksCount: Int, + title: @Composable BoxScope.() -> Unit, + block: @Composable ColumnScope.(Int) -> Unit, + modifier: Modifier = Modifier, +) { + LazyColumn( + modifier = modifier.background(TangemTheme.colors.background.secondary), + contentPadding = PaddingValues( + bottom = TangemTheme.dimens.spacing16, + ), + verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing12), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + stickyHeader { + Box( + modifier = Modifier + .fillMaxWidth() + .heightIn(min = TangemTheme.dimens.size44) + .background(TangemTheme.colors.background.secondary), + contentAlignment = Alignment.Center, + content = title, + ) + } + items(blocksCount) { index -> + Column { + block(index) + } + } + } +} + +// region Preview +@Preview(showBackground = true, widthDp = 360) +@Composable +private fun VisaTxDetailsBottomSheetPreview_Light( + @PreviewParameter(VisaTxDetailsBottomSheetParameterProvider::class) state: VisaTxDetailsBottomSheetConfig, +) { + TangemTheme { + VisaTxDetailsBottomSheetContent(state) + } +} + +@Preview(showBackground = true, widthDp = 360) +@Composable +private fun VisaTxDetailsBottomSheetPreview_Dark( + @PreviewParameter(VisaTxDetailsBottomSheetParameterProvider::class) state: VisaTxDetailsBottomSheetConfig, +) { + TangemTheme(isDark = true) { + VisaTxDetailsBottomSheetContent(state) + } +} + +private class VisaTxDetailsBottomSheetParameterProvider : + CollectionPreviewParameterProvider( + collection = listOf( + VisaTxDetailsBottomSheetConfig( + transaction = VisaTxDetailsBottomSheetConfig.Transaction( + id = "518385816101345408", + type = "payment", + status = "authorized", + blockchainAmount = "1.0614 USDT", + blockchainFee = "0.12", + transactionAmount = "0.99 €", + transactionCurrencyCode = "978", + merchantName = "SQ *FORMATIVE", + merchantCity = "London", + merchantCountryCode = "GB", + merchantCategoryCode = "5814", + ), + requests = persistentListOf( + VisaTxDetailsBottomSheetConfig.Request( + id = "524582128501966718", + type = "authorize_payment", + status = "accepted", + blockchainAmount = "1.0593 USDT", + blockchainFee = "0.10", + transactionAmount = "0.99 €", + currencyCode = "978", + errorCode = 0, + date = "2023-12-01 14:20:09.230 +0300", + txHash = "0xc458f0204fe43b82c775004baabb38435b5595f4307d8c3ac74625c827be7c29", + txStatus = "confirmed", + onExploreClick = {}, + ), + VisaTxDetailsBottomSheetConfig.Request( + id = "524582128501966799", + type = "settlement", + status = "accepted", + blockchainAmount = "1.0614 USDT", + blockchainFee = "0.12", + transactionAmount = "0.99 €", + currencyCode = "978", + errorCode = 0, + date = "2023-12-01 00:01:00.000 +0300", + txHash = "0x635841d5fbdf1087cdd929019c863ee88a7165e4340bc17ddd0b1d04dfb11daa", + txStatus = "confirmed", + onExploreClick = {}, + ), + ), + ), + ), + ) + +// endregion Preview \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/VisaWalletIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/VisaWalletIntents.kt index f2f28ea189..b857c555b4 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/VisaWalletIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/VisaWalletIntents.kt @@ -7,9 +7,11 @@ import com.tangem.core.ui.components.bottomsheets.tokenreceive.mapToAddressModel import com.tangem.domain.tokens.GetCryptoCurrencyStatusSyncUseCase import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.visa.GetVisaCurrencyUseCase +import com.tangem.domain.visa.GetVisaTxDetailsUseCase import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.BalancesAndLimitsBottomSheetConverter +import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.VisaTxDetailsBottomSheetConverter import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.hilt.android.scopes.ViewModelScoped @@ -25,6 +27,8 @@ internal interface VisaWalletIntents { fun onBalancesAndLimitsClick() fun onVisaTransactionClick(id: String) + + fun onExploreClick(exploreUrl: String) } @ViewModelScoped @@ -33,6 +37,7 @@ internal class VisaWalletIntentsImplementor @Inject constructor( private val eventSender: WalletEventSender, private val getCurrencyStatusUseCase: GetCryptoCurrencyStatusSyncUseCase, private val getVisaCurrencyUseCase: GetVisaCurrencyUseCase, + private val getVisaTxDetailsUseCase: GetVisaTxDetailsUseCase, private val dispatchers: CoroutineDispatcherProvider, ) : BaseWalletClickIntents(), VisaWalletIntents { @@ -97,6 +102,29 @@ internal class VisaWalletIntentsImplementor @Inject constructor( } override fun onVisaTransactionClick(id: String) { - // TODO: Implement [REDACTED_JIRA] + viewModelScope.launch(dispatchers.main) { + val userWalletId = stateController.getSelectedWalletId() + val visaCurrency = getVisaCurrencyUseCase(userWalletId) + .getOrElse { + Timber.e(it, "Failed to get visa currency") + return@launch + } + val transactionDetails = getVisaTxDetailsUseCase(userWalletId, id) + .getOrElse { + Timber.e(it, "Failed to get transaction details") + return@launch + } + + val converter = VisaTxDetailsBottomSheetConverter( + visaCurrency, + clickIntents = this@VisaWalletIntentsImplementor, + ) + + stateController.showBottomSheet(content = converter.convert(transactionDetails)) + } + } + + override fun onExploreClick(exploreUrl: String) { + router.openUrl(exploreUrl) } } \ No newline at end of file From dec052b7c451e13c6a681b05d10a202c072a9604 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 12 Feb 2024 15:13:12 +0500 Subject: [PATCH 30/53] Updated on 2026-08-14 --- app/src/main/assets/tangem-app-config | 2 +- app/src/main/assets/testnet_tokens.json | 10 ++++++++++ .../datasource/config/ConfigManagerImpl.kt | 1 + .../datasource/config/models/JsonModels.kt | 1 + .../tangem/core/ui/extensions/BlockchainIcons.kt | 5 +++++ core/ui/src/main/res/drawable/ic_algorand_22.xml | 10 ++++++++++ .../ui/src/main/res/drawable/img_algorand_22.xml | 16 ++++++++++++++++ .../domain/common/extensions/Blockchain.kt | 14 ++++++++++---- .../walletmanager/DefaultWalletManagersFacade.kt | 12 ++++++++++-- .../utils/UpdateWalletManagerResultFactory.kt | 12 ++++++++---- gradle/dependencies.toml | 2 +- 11 files changed, 73 insertions(+), 12 deletions(-) create mode 100644 core/ui/src/main/res/drawable/ic_algorand_22.xml create mode 100644 core/ui/src/main/res/drawable/img_algorand_22.xml diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index 4a1be08512..a13406537e 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit 4a1be08512fe2b45504f2bdaa0e35f5c11e11319 +Subproject commit a13406537ec4b05f930131b151ea6ebb1c0b5ca3 diff --git a/app/src/main/assets/testnet_tokens.json b/app/src/main/assets/testnet_tokens.json index ada88c6bc7..6c19252e4f 100644 --- a/app/src/main/assets/testnet_tokens.json +++ b/app/src/main/assets/testnet_tokens.json @@ -434,6 +434,16 @@ } ] }, + { + "id" : "algorand", + "symbol" : "ALGO", + "name" : "Algorand", + "networks" : [ + { + "networkId" : "algorand/test" + } + ] + }, { "id" : "arbitrum-one", "symbol" : "ETH", diff --git a/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManagerImpl.kt b/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManagerImpl.kt index ed2d89b8d2..c09740d22f 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManagerImpl.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManagerImpl.kt @@ -145,6 +145,7 @@ internal class ConfigManagerImpl @Inject constructor() : ConfigManager { jsonRpc = accessTokens.bitcoin?.jsonRPC, blockBookRest = accessTokens.bitcoin?.blockBookRest, ), + algorand = GetBlockAccessToken(rest = accessTokens.algorand?.rest), ) } } diff --git a/core/datasource/src/main/java/com/tangem/datasource/config/models/JsonModels.kt b/core/datasource/src/main/java/com/tangem/datasource/config/models/JsonModels.kt index ec2d4845ad..f0b1403825 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/config/models/JsonModels.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/config/models/JsonModels.kt @@ -66,6 +66,7 @@ data class GetBlockAccessTokens( @Json(name = "litecoin") val litecoin: GetBlockToken?, @Json(name = "dash") val dash: GetBlockToken?, @Json(name = "bitcoin") val bitcoin: GetBlockToken?, + @Json(name = "algorand") val algorand: GetBlockToken?, ) @JsonClass(generateAdapter = true) diff --git a/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt b/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt index 33e08b6314..1f3cc0f7dc 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt @@ -50,6 +50,7 @@ fun getActiveIconRes(blockchainId: String): Int { "vechain", "vechain/test" -> R.drawable.img_vechain_22 "aptos", "aptos/test" -> R.drawable.img_aptos_22 "shibarium", "shibarium/test" -> R.drawable.img_shibarium_22 + "algorand", "algorand/test" -> R.drawable.img_algorand_22 else -> R.drawable.ic_alert_24 } } @@ -101,6 +102,7 @@ fun getActiveIconResByNetworkId(networkId: String): Int { "vechain", "vechain/test" -> R.drawable.img_vechain_22 "aptos", "aptos/test" -> R.drawable.img_aptos_22 "shibarium", "shibarium/test" -> R.drawable.img_shibarium_22 + "algorand", "algorand/test" -> R.drawable.img_algorand_22 else -> R.drawable.ic_alert_24 } } @@ -149,6 +151,7 @@ fun getActiveIconResByCoinId(coinId: String): Int { "vechain" -> R.drawable.img_vechain_22 "aptos" -> R.drawable.img_aptos_22 "shibarium" -> R.drawable.img_shibarium_22 + "algorand" -> R.drawable.img_algorand_22 else -> R.drawable.ic_alert_24 } } @@ -200,6 +203,7 @@ fun getGreyedOutIconRes(blockchainId: String): Int { "vechain", "vechain/test" -> R.drawable.ic_vechain_22 "aptos", "aptos/test" -> R.drawable.ic_aptos_22 "shibarium", "shibarium/test" -> R.drawable.ic_shibarium_22 + "algorand", "algorand/test" -> R.drawable.ic_algorand_22 else -> R.drawable.ic_alert_24 } } @@ -251,6 +255,7 @@ fun getGreyedOutIconResByNetworkId(networkId: String): Int { "vechain", "vechain/test" -> R.drawable.ic_vechain_22 "aptos", "aptos/test" -> R.drawable.ic_aptos_22 "shibarium", "shibarium/test" -> R.drawable.ic_shibarium_22 + "algorand", "algorand/test" -> R.drawable.ic_algorand_22 else -> R.drawable.ic_alert_24 } } \ No newline at end of file diff --git a/core/ui/src/main/res/drawable/ic_algorand_22.xml b/core/ui/src/main/res/drawable/ic_algorand_22.xml new file mode 100644 index 0000000000..770f3c6096 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_algorand_22.xml @@ -0,0 +1,10 @@ + + + diff --git a/core/ui/src/main/res/drawable/img_algorand_22.xml b/core/ui/src/main/res/drawable/img_algorand_22.xml new file mode 100644 index 0000000000..3298f10b19 --- /dev/null +++ b/core/ui/src/main/res/drawable/img_algorand_22.xml @@ -0,0 +1,16 @@ + + + + + + + diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt index 95ba582d02..2c273db0de 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt @@ -80,9 +80,11 @@ fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? { "vechain/test" -> Blockchain.VeChainTestnet "aptos" -> Blockchain.Aptos "aptos/test" -> Blockchain.AptosTestnet + "" -> Blockchain.Playa3ull // FIXME "shibarium" -> Blockchain.Shibarium "shibarium/test" -> Blockchain.ShibariumTestnet - "" -> Blockchain.Playa3ull // FIXME + "algorand" -> Blockchain.Algorand + "algorand/test" -> Blockchain.AlgorandTestnet else -> null } } @@ -164,9 +166,11 @@ fun Blockchain.toNetworkId(): String { Blockchain.VeChainTestnet -> "vechain/test" Blockchain.Aptos -> "aptos" Blockchain.AptosTestnet -> "aptos/test" + Blockchain.Playa3ull -> "" // FIXME Blockchain.Shibarium -> "shibarium" Blockchain.ShibariumTestnet -> "shibarium/test" - Blockchain.Playa3ull -> "" // FIXME + Blockchain.Algorand -> "algorand" + Blockchain.AlgorandTestnet -> "algorand/test" } } @@ -218,10 +222,12 @@ fun Blockchain.toCoinId(): String { Blockchain.VeChain, Blockchain.VeChainTestnet -> "vechain" Blockchain.Aptos -> "aptos" Blockchain.AptosTestnet -> "aptos/test" - Blockchain.Unknown -> "unknown" + Blockchain.Playa3ull -> "" // FIXME Blockchain.Shibarium -> "bone-shibaswap" Blockchain.ShibariumTestnet -> "bone-shibaswap/test" - Blockchain.Playa3ull -> "" // FIXME + Blockchain.Algorand -> "algorand" + Blockchain.AlgorandTestnet -> "algorand/test" + Blockchain.Unknown -> "unknown" } } diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt index a040c7db7d..c5792124c9 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt @@ -276,7 +276,11 @@ class DefaultWalletManagersFacade( resultFactory.getResult(walletManager) } catch (e: BlockchainSdkError.AccountNotFound) { - resultFactory.getNoAccountResult(walletManager = walletManager, customMessage = e.customMessage) + resultFactory.getNoAccountResult( + walletManager = walletManager, + customMessage = e.customMessage, + amountToCreateAccount = e.amountToCreateAccount, + ) } catch (e: Throwable) { Timber.w(e, "Unable to update a wallet manager for: ${walletManager.wallet.blockchain}") @@ -288,7 +292,11 @@ class DefaultWalletManagersFacade( return try { resultFactory.getResult(walletManager) } catch (e: BlockchainSdkError.AccountNotFound) { - resultFactory.getNoAccountResult(walletManager = walletManager, customMessage = e.customMessage) + resultFactory.getNoAccountResult( + walletManager = walletManager, + customMessage = e.customMessage, + amountToCreateAccount = e.amountToCreateAccount, + ) } catch (e: Throwable) { Timber.w(e, "Unable to update a wallet manager for: ${walletManager.wallet.blockchain}") diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/UpdateWalletManagerResultFactory.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/UpdateWalletManagerResultFactory.kt index dbcc2c7ff5..6b88794b4b 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/UpdateWalletManagerResultFactory.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/UpdateWalletManagerResultFactory.kt @@ -40,13 +40,17 @@ internal class UpdateWalletManagerResultFactory { ) } - fun getNoAccountResult(walletManager: WalletManager, customMessage: String): UpdateWalletManagerResult { + fun getNoAccountResult( + walletManager: WalletManager, + customMessage: String, + amountToCreateAccount: BigDecimal?, + ): UpdateWalletManagerResult { val wallet = walletManager.wallet val blockchain = wallet.blockchain val firstWalletToken = wallet.getTokens().firstOrNull() - val amountToCreateAccount = blockchain.amountToCreateAccount(firstWalletToken) + val amount = amountToCreateAccount ?: blockchain.amountToCreateAccount(firstWalletToken) - return if (amountToCreateAccount == null) { + return if (amount == null) { Timber.w("Unable to get required amount to create account for: $blockchain") UpdateWalletManagerResult.Unreachable( selectedAddress = wallet.address, @@ -56,7 +60,7 @@ internal class UpdateWalletManagerResultFactory { UpdateWalletManagerResult.NoAccount( selectedAddress = wallet.address, addresses = getAvailableAddresses(wallet.addresses), - amountToCreateAccount = amountToCreateAccount, + amountToCreateAccount = amount, errorMessage = customMessage, ) } diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 297f61b088..ac9b9b657c 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -85,7 +85,7 @@ web3j = "4.10.1" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-482" +tangemBlockchainSdk = "develop-483" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-324" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From 2d088ae4e2615ead07cb3082d2e14a48921fc945 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 12 Feb 2024 15:21:04 +0300 Subject: [PATCH 31/53] Updated on 2026-08-14 --- .../java/com/tangem/domain/common/extensions/Blockchain.kt | 1 - .../java/com/tangem/feature/swap/domain/SwapInteractorImpl.kt | 3 ++- gradle/dependencies.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt index 2c273db0de..1e14859635 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt @@ -255,6 +255,5 @@ private const val NODL_AMOUNT_TO_CREATE_ACCOUNT = 1.5 private val excludedBlockchains = listOf( Blockchain.Unknown, Blockchain.Ducatus, - Blockchain.Aptos, Blockchain.Playa3ull, ) \ 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 3be4224a4c..0bdde7ae48 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 @@ -14,9 +14,9 @@ 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.Quote +import com.tangem.domain.tokens.repository.CurrencyChecksRepository import com.tangem.domain.tokens.repository.QuotesRepository import com.tangem.domain.tokens.utils.convertToAmount -import com.tangem.domain.tokens.repository.CurrencyChecksRepository import com.tangem.domain.transaction.error.SendTransactionError import com.tangem.domain.transaction.usecase.EstimateFeeUseCase import com.tangem.domain.transaction.usecase.SendTransactionUseCase @@ -666,6 +666,7 @@ internal class SwapInteractorImpl @Inject constructor( Fee.Aptos( amount = feeAmount, gasUnitPrice = fee.feeValue.toLong() / fee.gasLimit, + gasLimit = fee.gasLimit.toLong(), ) } else -> Fee.Common(feeAmount) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index ac9b9b657c..e3b7fbf13e 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -85,7 +85,7 @@ web3j = "4.10.1" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-483" +tangemBlockchainSdk = "develop-484" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-324" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From 67a80fc0978aae2b30d09084ba48716b1bb2b6aa Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 12 Feb 2024 14:50:26 +0100 Subject: [PATCH 32/53] Updated on 2026-08-14 --- .../addcustomtoken/state/CustomTokenData.kt | 4 ++ .../addcustomtoken/state/TextFieldState.kt | 2 + .../factory/AddCustomTokenStateFactory.kt | 30 ++++++++++++- .../addcustomtoken/ui/AddCustomTokenScreen.kt | 44 ++++++++++++++++++- .../addcustomtoken/ui/TokenTextField.kt | 17 ++++++- .../managetokens/ui/ManageTokensScreen.kt | 17 ++++--- 6 files changed, 103 insertions(+), 11 deletions(-) diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/CustomTokenData.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/CustomTokenData.kt index ec93de1d4c..b481d6767a 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/CustomTokenData.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/CustomTokenData.kt @@ -11,4 +11,8 @@ internal data class CustomTokenData( return contractAddressTextField.isInputValid() && nameTextField.isInputValid() && symbolTextField.isInputValid() && decimalsTextField.isInputValid() } + + fun isNameSymbolDecimalsDisabled(): Boolean { + return nameTextField.isDisabled() && symbolTextField.isDisabled() && decimalsTextField.isDisabled() + } } \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/TextFieldState.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/TextFieldState.kt index 2e7f2b1168..3068a0b6e9 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/TextFieldState.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/TextFieldState.kt @@ -13,6 +13,8 @@ internal sealed class TextFieldState { fun isInputValid(): Boolean = this is Editable && value.isNotBlank() && error == null + fun isDisabled() = this is Editable && !this.isEnabled + fun copySealed( value: String = (this as? Editable)?.value ?: "", isEnabled: Boolean = (this as? Editable)?.isEnabled ?: true, diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/AddCustomTokenStateFactory.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/AddCustomTokenStateFactory.kt index 57ce303a3c..7fe3b1dc8c 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/AddCustomTokenStateFactory.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/AddCustomTokenStateFactory.kt @@ -176,6 +176,32 @@ internal class AddCustomTokenStateFactory( return currentStateProvider().copy(tokenData = tokenData.copy(contractAddressTextField = contractAddressField)) } + private fun unlockAndClearNameSymbolAndDecimals(state: AddCustomTokenState): AddCustomTokenState { + val currentTokenData = state.tokenData + return state.copy( + tokenData = currentTokenData?.copy( + nameTextField = TextFieldState.Editable( + value = "", + isEnabled = true, + onValueChange = clickIntents::onTokenNameChange, + onFocusExit = clickIntents::onTokenNameFocusExit, + ), + symbolTextField = TextFieldState.Editable( + value = "", + isEnabled = true, + onValueChange = clickIntents::onSymbolChange, + onFocusExit = clickIntents::onSymbolFocusExit, + ), + decimalsTextField = TextFieldState.Editable( + value = "", + isEnabled = true, + onValueChange = clickIntents::onDecimalsChange, + onFocusExit = clickIntents::onDecimalsFocusExit, + ), + ), + ) + } + fun getStateAndTriggerEvent( state: AddCustomTokenState, event: Event, @@ -286,7 +312,7 @@ internal class AddCustomTokenStateFactory( val uiState = currentStateProvider() return when (error) { AddCustomTokenError.INVALID_CONTRACT_ADDRESS -> { - addTokenAddressFieldError(AddCustomTokenWarning.InvalidContractAddress) + removeTokenAddressError().also { unlockAndClearNameSymbolAndDecimals(it) } .copy( addTokenButton = uiState.addTokenButton.copy(isEnabled = false), warnings = uiState.warnings @@ -295,7 +321,7 @@ internal class AddCustomTokenStateFactory( ) } AddCustomTokenError.FIELD_IS_EMPTY -> - removeTokenAddressError() + removeTokenAddressError().also { unlockAndClearNameSymbolAndDecimals(it) } .copy( addTokenButton = uiState.addTokenButton.copy( isEnabled = uiState.tokenData?.isRequiredInformationProvided() == true, diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/AddCustomTokenScreen.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/AddCustomTokenScreen.kt index d3291937f0..d335aaca71 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/AddCustomTokenScreen.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/AddCustomTokenScreen.kt @@ -4,11 +4,16 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.Text import androidx.compose.runtime.* +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.focus.FocusDirection +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -154,8 +159,11 @@ private fun ColumnScope.CustomTokenItemsList(state: AddCustomTokenState, modifie } } +@OptIn(ExperimentalComposeUiApi::class) @Composable private fun TokenFields(state: CustomTokenData, modifier: Modifier = Modifier) { + val focusManager = LocalFocusManager.current + Column( modifier = modifier .fillMaxWidth() @@ -166,21 +174,51 @@ private fun TokenFields(state: CustomTokenData, modifier: Modifier = Modifier) { textFieldState = state.contractAddressTextField, placeholder = "0x0000000000000000000000000000000", title = R.string.custom_token_contract_address_input_title, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Text, + imeAction = if (state.isNameSymbolDecimalsDisabled()) { + ImeAction.Done + } else { + ImeAction.Next + }, + ), + onImeAction = { + if (state.isNameSymbolDecimalsDisabled()) { + focusManager.moveFocus(FocusDirection.Exit) + } else { + focusManager.moveFocus(FocusDirection.Down) + } + }, ) TokenField( textFieldState = state.nameTextField, placeholder = stringResource(id = R.string.custom_token_name_input_placeholder), title = R.string.custom_token_name_input_title, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Text, + imeAction = ImeAction.Next, + ), + onImeAction = { focusManager.moveFocus(FocusDirection.Down) }, ) TokenField( textFieldState = state.symbolTextField, placeholder = stringResource(id = R.string.custom_token_token_symbol_input_placeholder), title = R.string.custom_token_token_symbol_input_title, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Text, + imeAction = ImeAction.Next, + ), + onImeAction = { focusManager.moveFocus(FocusDirection.Down) }, ) TokenField( textFieldState = state.decimalsTextField, placeholder = "0", title = R.string.custom_token_decimals_input_title, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Done, + ), + onImeAction = { focusManager.moveFocus(FocusDirection.Exit) }, ) } } @@ -190,7 +228,8 @@ private fun TokenField( textFieldState: TextFieldState, placeholder: String, title: Int, - keyboardType: KeyboardType = KeyboardType.Text, + onImeAction: () -> Unit, + keyboardOptions: KeyboardOptions, ) { Column( modifier = Modifier @@ -205,7 +244,8 @@ private fun TokenField( is TextFieldState.Editable -> TokenTextField( state = textFieldState, placeholder = placeholder, - keyboardType = keyboardType, + onImeAction = onImeAction, + keyboardOptions = keyboardOptions, ) is TextFieldState.Loading -> TokenShimmer() } diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/TokenTextField.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/TokenTextField.kt index d794ab5890..63521c6775 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/TokenTextField.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/TokenTextField.kt @@ -12,6 +12,7 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.input.key.* import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType @@ -22,7 +23,11 @@ import com.tangem.managetokens.presentation.addcustomtoken.state.TextFieldState internal fun TokenTextField( state: TextFieldState.Editable, placeholder: String, - keyboardType: KeyboardType = KeyboardType.Text, + onImeAction: () -> Unit, + keyboardOptions: KeyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Text, + imeAction = ImeAction.Default, + ), ) { val isInitiallyComposed = remember { mutableStateOf(false) } LaunchedEffect(key1 = true) { @@ -32,7 +37,7 @@ internal fun TokenTextField( BasicTextField( value = state.value, onValueChange = state.onValueChange, - keyboardOptions = KeyboardOptions(keyboardType = keyboardType, imeAction = ImeAction.Default), + keyboardOptions = keyboardOptions, singleLine = true, maxLines = 1, textStyle = TangemTheme.typography.subtitle1.copy( @@ -42,6 +47,14 @@ internal fun TokenTextField( cursorBrush = SolidColor(TangemTheme.colors.icon.primary1), modifier = Modifier .fillMaxWidth() + .onKeyEvent { keyEvent -> + if (keyEvent.type == KeyEventType.KeyUp && keyEvent.key == Key.Enter) { + onImeAction() + true + } else { + false + } + } .onFocusChanged { if (!it.isFocused && isInitiallyComposed.value) { state.onFocusExit() diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ManageTokensScreen.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ManageTokensScreen.kt index 77c91a26e3..0f83676b10 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ManageTokensScreen.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ManageTokensScreen.kt @@ -14,7 +14,9 @@ import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameter import androidx.paging.LoadState import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems +import com.tangem.core.ui.components.Keyboard import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.components.keyboardAsState import com.tangem.core.ui.res.TangemTheme import com.tangem.managetokens.presentation.common.state.AlertState import com.tangem.managetokens.presentation.common.state.ChooseWalletState @@ -46,6 +48,7 @@ internal fun ManageTokensScreen(state: ManageTokensState) { @Composable private fun Content(state: ManageTokensState) { + val keyboard by keyboardAsState() Box( modifier = Modifier .fillMaxSize() @@ -88,13 +91,17 @@ private fun Content(state: ManageTokensState) { TokensList(tokens = tokens, addCustomTokenButton = state.addCustomTokenButton) } + state.derivationNotification?.let { - DerivationNotification( - config = it.config, - modifier = Modifier - .align(Alignment.BottomCenter), - ) + if (keyboard is Keyboard.Closed) { + DerivationNotification( + config = it.config, + modifier = Modifier + .align(Alignment.BottomCenter), + ) + } } + state.selectedToken?.let { selectedToken -> ManageTokensBottomSheet(selectedToken = selectedToken, state = state) } From 8ee78ad042b9dd16da6319fde7f17043b22974fa Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 12 Feb 2024 17:07:25 +0300 Subject: [PATCH 33/53] Updated on 2026-08-14 --- .../segmentedbutton/SegmentedButton.kt | 15 ++- .../components/inputrow/InputRowRecipient.kt | 78 ++++++++----- .../components/inputrow/inner/PasteButton.kt | 107 +++++++++++------- .../tangem/core/ui/utils/AnimationUtils.kt | 3 + .../SendAmountFieldMaxAmountConverter.kt | 3 + .../presentation/ui/SendNavigationButtons.kt | 5 + .../ui/amount/SendAmountContent.kt | 1 + .../ui/recipient/TextFieldWithPaste.kt | 57 +++++----- 8 files changed, 169 insertions(+), 100 deletions(-) create mode 100644 core/ui/src/main/java/com/tangem/core/ui/utils/AnimationUtils.kt diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/segmentedbutton/SegmentedButton.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/segmentedbutton/SegmentedButton.kt index df18f78ab0..44d622f9eb 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/segmentedbutton/SegmentedButton.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/segmentedbutton/SegmentedButton.kt @@ -1,10 +1,16 @@ package com.tangem.core.ui.components.buttons.segmentedbutton +import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.spring import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Text import androidx.compose.runtime.* @@ -58,11 +64,16 @@ inline fun SegmentedButtons( val leftRadius = if (index == 0) TangemTheme.dimens.radius26 else TangemTheme.dimens.radius0 val rightRadius = if (index == config.lastIndex) TangemTheme.dimens.radius26 else TangemTheme.dimens.radius0 + val animateColor by animateColorAsState( + targetValue = if (index == selected) selectedColor else color, + label = "Segmented Button Selected Color Animation", + animationSpec = spring(stiffness = Spring.StiffnessMedium), + ) Box( modifier = Modifier .weight(1f) .background( - color = if (index == selected) selectedColor else color, + color = animateColor, shape = RoundedCornerShape( topStart = leftRadius, topEnd = rightRadius, diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowRecipient.kt b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowRecipient.kt index e58a5bd0a8..c16d459a12 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowRecipient.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/InputRowRecipient.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment.Companion.CenterEnd import androidx.compose.ui.Alignment.Companion.CenterVertically import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -16,6 +17,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import com.tangem.core.ui.R import com.tangem.core.ui.components.fields.SimpleTextField import com.tangem.core.ui.components.icons.identicon.IdentIcon +import com.tangem.core.ui.components.inputrow.inner.CrossIcon import com.tangem.core.ui.components.inputrow.inner.DividerContainer import com.tangem.core.ui.components.inputrow.inner.PasteButton import com.tangem.core.ui.extensions.TextReference @@ -73,44 +75,37 @@ fun InputRowRecipient( color = color, ) } - Row( + Box( modifier = Modifier .padding(top = TangemTheme.dimens.spacing8), ) { - AnimatedContent( - targetState = isLoading, - label = "Indicator Show Change", - modifier = Modifier - .align(CenterVertically) - .clip(RoundedCornerShape(TangemTheme.dimens.radius18)) - .size(TangemTheme.dimens.size36) - .background(TangemTheme.colors.background.tertiary), - ) { showIndicator -> - if (showIndicator) { - CircularProgressIndicator( - color = TangemTheme.colors.icon.informative, - modifier = Modifier - .padding(TangemTheme.dimens.spacing8), - ) - } else { - IdentIcon(address = value) - } + Row { + InputIcon( + isLoading = isLoading, + value = value, + ) + SimpleTextField( + value = value, + placeholder = placeholder, + onValueChange = onValueChange, + singleLine = singleLine, + modifier = Modifier + .padding(start = TangemTheme.dimens.spacing12) + .weight(1f) + .align(CenterVertically), + ) + CrossIcon( + onClick = onPasteClick, + modifier = Modifier + .align(CenterVertically) + .padding(start = TangemTheme.dimens.spacing8), + ) } - SimpleTextField( - value = value, - placeholder = placeholder, - onValueChange = onValueChange, - singleLine = singleLine, - modifier = Modifier - .padding(start = TangemTheme.dimens.spacing12) - .weight(1f) - .align(CenterVertically), - ) PasteButton( isPasteButtonVisible = value.isBlank(), onClick = onPasteClick, modifier = Modifier - .align(CenterVertically) + .align(CenterEnd) .padding(start = TangemTheme.dimens.spacing8), ) } @@ -118,6 +113,29 @@ fun InputRowRecipient( } } +@Composable +private fun RowScope.InputIcon(isLoading: Boolean, value: String) { + AnimatedContent( + targetState = isLoading, + label = "Indicator Show Change", + modifier = Modifier + .align(CenterVertically) + .clip(RoundedCornerShape(TangemTheme.dimens.radius18)) + .size(TangemTheme.dimens.size36) + .background(TangemTheme.colors.background.tertiary), + ) { showIndicator -> + if (showIndicator) { + CircularProgressIndicator( + color = TangemTheme.colors.icon.informative, + modifier = Modifier + .padding(TangemTheme.dimens.spacing8), + ) + } else { + IdentIcon(address = value) + } + } +} + //region preview @Preview @Composable diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/inner/PasteButton.kt b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/inner/PasteButton.kt index 3e4642c061..1d7d0fab95 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/inner/PasteButton.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/inputrow/inner/PasteButton.kt @@ -1,24 +1,28 @@ package com.tangem.core.ui.components.inputrow.inner +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.ripple.rememberRipple import androidx.compose.material3.Icon import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember +import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview import com.tangem.core.ui.R import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.utils.DEFAULT_ANIMATION_DURATION /** * Paste button with cross icon. Retrieves text from clipboard. @@ -33,48 +37,67 @@ fun PasteButton(isPasteButtonVisible: Boolean, onClick: (String) -> Unit, modifi val clipboardManager = LocalClipboardManager.current val hapticFeedback = LocalHapticFeedback.current - if (isPasteButtonVisible) { - Box(modifier = modifier) { - Text( - text = "Paste", - style = TangemTheme.typography.button, - color = TangemTheme.colors.text.primary2, - modifier = Modifier - .background( - color = TangemTheme.colors.button.primary, - shape = TangemTheme.shapes.roundedCornersXMedium, - ) - .padding( - horizontal = TangemTheme.dimens.spacing10, - vertical = TangemTheme.dimens.spacing2, - ) - .clickable( - interactionSource = remember { MutableInteractionSource() }, - indication = rememberRipple(radius = TangemTheme.dimens.radius8), - onClick = { - hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) - onClick( - clipboardManager - .getText() - ?.toString() - .orEmpty(), - ) - }, - ), - ) - } - } else { - Icon( - painter = painterResource(id = R.drawable.ic_close_24), - tint = TangemTheme.colors.icon.informative, - contentDescription = stringResource(R.string.common_close), - modifier = modifier - .size(TangemTheme.dimens.size20) + AnimatedVisibility( + visible = isPasteButtonVisible, + label = "Paste Button Visibility Animation", + enter = fadeIn(), + exit = fadeOut(animationSpec = tween(DEFAULT_ANIMATION_DURATION)), + modifier = modifier, + ) { + Text( + text = "Paste", + style = TangemTheme.typography.button, + color = TangemTheme.colors.text.primary2, + modifier = Modifier + .background( + color = TangemTheme.colors.button.primary, + shape = TangemTheme.shapes.roundedCornersXMedium, + ) + .padding( + horizontal = TangemTheme.dimens.spacing10, + vertical = TangemTheme.dimens.spacing2, + ) .clickable( interactionSource = remember { MutableInteractionSource() }, - indication = rememberRipple(radius = TangemTheme.dimens.radius10), - onClick = { onClick("") }, + indication = rememberRipple(radius = TangemTheme.dimens.radius8), + onClick = { + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + onClick( + clipboardManager + .getText() + ?.toString() + .orEmpty(), + ) + }, ), ) } +} + +@Composable +fun CrossIcon(onClick: (String) -> Unit, modifier: Modifier = Modifier) { + Icon( + painter = painterResource(id = R.drawable.ic_close_24), + tint = TangemTheme.colors.icon.informative, + contentDescription = stringResource(R.string.common_close), + modifier = modifier + .size(TangemTheme.dimens.size20) + .clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = rememberRipple(radius = TangemTheme.dimens.radius10), + onClick = { onClick("") }, + ), + ) +} + +@Preview +@Composable +private fun PasteButtonPreview() { + var isVisible by remember { mutableStateOf(true) } + TangemTheme { + PasteButton( + isPasteButtonVisible = isVisible, + onClick = { isVisible = !isVisible }, + ) + } } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/utils/AnimationUtils.kt b/core/ui/src/main/java/com/tangem/core/ui/utils/AnimationUtils.kt new file mode 100644 index 0000000000..e43855563b --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/utils/AnimationUtils.kt @@ -0,0 +1,3 @@ +package com.tangem.core.ui.utils + +const val DEFAULT_ANIMATION_DURATION = 300 \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldMaxAmountConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldMaxAmountConverter.kt index 6ca3cb3d93..1a9a4d6def 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldMaxAmountConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldMaxAmountConverter.kt @@ -5,6 +5,7 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.send.impl.presentation.state.SendUiState import com.tangem.utils.Provider import com.tangem.utils.converter.Converter +import com.tangem.utils.isNullOrZero internal class SendAmountFieldMaxAmountConverter( private val currentStateProvider: Provider, @@ -23,6 +24,8 @@ internal class SendAmountFieldMaxAmountConverter( val decimalCryptoValue = cryptoCurrencyStatus.value.amount val decimalFiatValue = cryptoCurrencyStatus.value.fiatAmount + if (decimalCryptoValue.isNullOrZero()) return state + val cryptoValue = decimalCryptoValue?.parseBigDecimal(cryptoDecimals).orEmpty() val fiatValue = decimalFiatValue?.parseBigDecimal(fiatDecimals).orEmpty() return state.copy( diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt index fb4fa79a9f..b0462ca18f 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt @@ -3,6 +3,8 @@ package com.tangem.features.send.impl.presentation.ui import androidx.annotation.StringRes import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.expandHorizontally +import androidx.compose.animation.shrinkHorizontally import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column @@ -14,6 +16,7 @@ import androidx.compose.material3.Icon import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.hapticfeedback.HapticFeedbackType @@ -54,6 +57,8 @@ private fun SendSecondaryNavigationButton(uiState: SendUiState) { val isCorrectScreen = currentState.value == SendUiStateType.Amount || currentState.value == SendUiStateType.Fee AnimatedVisibility( visible = !isEditingDisabled && isCorrectScreen, + enter = expandHorizontally(expandFrom = Alignment.End), + exit = shrinkHorizontally(shrinkTowards = Alignment.End), ) { Icon( modifier = Modifier diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/SendAmountContent.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/SendAmountContent.kt index 39e83215dc..fa0a7445eb 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/SendAmountContent.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/amount/SendAmountContent.kt @@ -46,6 +46,7 @@ internal fun SendAmountContent( .weight(1f) .height(TangemTheme.dimens.size40), config = amountState.segmentedButtonConfig, + showIndication = false, onClick = { clickIntents.onCurrencyChangeClick(it.isFiat) }, ) { SendAmountCurrencyButton(it) diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/recipient/TextFieldWithPaste.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/recipient/TextFieldWithPaste.kt index c3a49b8da6..6109c5ff50 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/recipient/TextFieldWithPaste.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/recipient/TextFieldWithPaste.kt @@ -1,15 +1,14 @@ package com.tangem.features.send.impl.presentation.ui.recipient import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment.Companion.CenterEnd import androidx.compose.ui.Alignment.Companion.CenterVertically import androidx.compose.ui.Modifier import com.tangem.core.ui.components.fields.SimpleTextField +import com.tangem.core.ui.components.inputrow.inner.CrossIcon import com.tangem.core.ui.components.inputrow.inner.PasteButton import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveReference @@ -35,40 +34,46 @@ internal fun TextFieldWithPaste( else -> label to TangemTheme.colors.text.secondary } FooterContainer(modifier, footer) { - Row( + Box( modifier = Modifier .background( color = TangemTheme.colors.background.action, shape = TangemTheme.shapes.roundedCornersXMedium, - ), - ) { - Column( - modifier = Modifier - .weight(1f) - .padding(TangemTheme.dimens.spacing12), - ) { - Text( - text = title.resolveReference(), - style = TangemTheme.typography.body2, - color = color, ) - SimpleTextField( - value = value, - placeholder = placeholder, - onValueChange = onValueChange, - readOnly = isReadOnly, + .padding(end = TangemTheme.dimens.spacing16), + ) { + Row { + Column( modifier = Modifier - .fillMaxWidth() - .padding(top = TangemTheme.dimens.spacing6), + .weight(1f) + .padding(TangemTheme.dimens.spacing12), + ) { + Text( + text = title.resolveReference(), + style = TangemTheme.typography.body2, + color = color, + ) + SimpleTextField( + value = value, + placeholder = placeholder, + onValueChange = onValueChange, + readOnly = isReadOnly, + modifier = Modifier + .fillMaxWidth() + .padding(top = TangemTheme.dimens.spacing6), + ) + } + CrossIcon( + onClick = onPasteClick, + modifier = Modifier + .align(CenterVertically), ) } if (!isReadOnly) { PasteButton( isPasteButtonVisible = value.isBlank(), onClick = onPasteClick, - modifier = Modifier - .align(CenterVertically) - .padding(end = TangemTheme.dimens.spacing16), + modifier = Modifier.align(CenterEnd), ) } } From b97ddcb3f2c64e4554246bcfcddac4345393049b Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 13 Feb 2024 18:00:34 +0300 Subject: [PATCH 34/53] Updated on 2026-08-14 --- .../buttons/HorizontalActionChips.kt | 66 ++++--- .../ui/components/buttons/actions/Actions.kt | 2 - .../marketprice/MarketPriceBlock.kt | 78 +++++--- .../ui/components/transactions/Transaction.kt | 36 ++-- .../transactions/TxHistoryGroupTitle.kt | 28 +-- .../components/transactions/TxHistoryTitle.kt | 14 +- .../empty/EmptyTransactionBlock.kt | 11 +- .../core/ui/decorations/RoundedDecorations.kt | 4 +- .../com/tangem/core/ui/res/TangemDimens.kt | 1 + .../main/res/drawable/ic_empty_token_64.xml | 16 +- .../utils/CryptoCurrencyFormatExtensions.kt | 4 +- .../tokendetails/TokenDetailsPreviewData.kt | 172 +++++++++++++++++- .../tokendetails/ui/TokenDetailsScreen.kt | 41 +++-- .../ui/components/TokenDetailsBalanceBlock.kt | 30 +-- .../ui/components/TokenInfoBlock.kt | 13 +- .../common/component/TokenItem.kt | 6 +- .../organizetokens/OrganizeTokensScreen.kt | 4 +- 17 files changed, 379 insertions(+), 147 deletions(-) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/HorizontalActionChips.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/HorizontalActionChips.kt index 3a5bbafce5..a6c3ee8402 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/HorizontalActionChips.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/HorizontalActionChips.kt @@ -39,47 +39,57 @@ fun HorizontalActionChips( } } -@Preview +// region Preview +@Preview(widthDp = 360) @Composable private fun Preview_HorizontalActionChips_Light( - @PreviewParameter(ActionButtonConfigProvider::class) buttons: ImmutableList, + @PreviewParameter(ActionButtonConfigProvider::class) buttons: HorizontalActionChips, ) { TangemTheme(isDark = false) { - HorizontalActionChips(buttons = buttons) + HorizontalActionChips(buttons = buttons.buttons) } } -@Preview +@Preview(widthDp = 360) @Composable private fun Preview_HorizontalActionChips_Dark( - @PreviewParameter(ActionButtonConfigProvider::class) buttons: ImmutableList, + @PreviewParameter(ActionButtonConfigProvider::class) buttons: HorizontalActionChips, ) { TangemTheme(isDark = true) { - HorizontalActionChips(buttons = buttons) + HorizontalActionChips(buttons = buttons.buttons) } } -private class ActionButtonConfigProvider : CollectionPreviewParameterProvider( - collection = persistentListOf( - ActionButtonConfig( - text = TextReference.Str(value = "Buy"), - iconResId = R.drawable.ic_plus_24, - onClick = {}, - ), - ActionButtonConfig( - text = TextReference.Str(value = "Send"), - iconResId = R.drawable.ic_arrow_up_24, - onClick = {}, - ), - ActionButtonConfig( - text = TextReference.Str(value = "Receive"), - iconResId = R.drawable.ic_arrow_down_24, - onClick = {}, - ), - ActionButtonConfig( - text = TextReference.Str(value = "Exchange"), - iconResId = R.drawable.ic_exchange_vertical_24, - onClick = {}, +private class ActionButtonConfigProvider : CollectionPreviewParameterProvider( + collection = listOf( + HorizontalActionChips( + buttons = persistentListOf( + ActionButtonConfig( + text = TextReference.Str(value = "Buy"), + iconResId = R.drawable.ic_plus_24, + onClick = {}, + ), + ActionButtonConfig( + text = TextReference.Str(value = "Send"), + iconResId = R.drawable.ic_arrow_up_24, + onClick = {}, + ), + ActionButtonConfig( + text = TextReference.Str(value = "Receive"), + iconResId = R.drawable.ic_arrow_down_24, + onClick = {}, + ), + ActionButtonConfig( + text = TextReference.Str(value = "Exchange"), + iconResId = R.drawable.ic_exchange_vertical_24, + onClick = {}, + ), + ), ), ), -) \ No newline at end of file +) + +private data class HorizontalActionChips( + val buttons: ImmutableList, +) +// endregion Preview \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/actions/Actions.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/actions/Actions.kt index 02cf8f05ba..9b56a22f57 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/actions/Actions.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/actions/Actions.kt @@ -11,7 +11,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow @@ -87,7 +86,6 @@ private fun Button( Row( modifier = modifier .heightIn(min = TangemTheme.dimens.size36) - .clip(shape) .background(color = backgroundColor, shape = shape) .clickable(enabled = config.enabled, onClick = config.onClick) .padding(start = TangemTheme.dimens.spacing16, end = TangemTheme.dimens.spacing24) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/marketprice/MarketPriceBlock.kt b/core/ui/src/main/java/com/tangem/core/ui/components/marketprice/MarketPriceBlock.kt index 3ff8fbb6fd..1626a42bce 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/marketprice/MarketPriceBlock.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/marketprice/MarketPriceBlock.kt @@ -3,7 +3,6 @@ package com.tangem.core.ui.components.marketprice import androidx.compose.animation.AnimatedContent import androidx.compose.foundation.background import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Icon import androidx.compose.material.Text import androidx.compose.runtime.* @@ -35,39 +34,60 @@ import com.tangem.core.ui.utils.BigDecimalFormatter */ @Composable fun MarketPriceBlock(state: MarketPriceBlockState, modifier: Modifier = Modifier) { - var rootWidth by remember { mutableIntStateOf(value = 0) } - - Column( + Row( modifier = modifier .background( color = TangemTheme.colors.background.primary, - shape = RoundedCornerShape(TangemTheme.dimens.radius14), + shape = TangemTheme.shapes.roundedCornersXMedium, ) - .heightIn(min = TangemTheme.dimens.size70) .fillMaxWidth() - .padding(all = TangemTheme.dimens.spacing14) - .onSizeChanged { rootWidth = it.width }, - verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing6), - horizontalAlignment = Alignment.Start, + .heightIn(min = TangemTheme.dimens.size72) + .padding(all = TangemTheme.dimens.spacing12), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, ) { - Title(currencyName = state.currencySymbol) + var rootWidth by remember { mutableIntStateOf(value = 0) } - Content(state = state, rootWidth = rootWidth) + Column( + modifier = Modifier.onSizeChanged { rootWidth = it.width }, + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4), + horizontalAlignment = Alignment.Start, + ) { + Title(currencyName = state.currencySymbol) + Content(state = state, rootWidth = rootWidth) + } + + Icon( + modifier = Modifier.size(TangemTheme.dimens.size20), + painter = painterResource(id = R.drawable.ic_chevron_right_24), + tint = TangemTheme.colors.icon.informative, + contentDescription = null, + ) } } @Composable -private fun Title(currencyName: String) { - Text( - text = stringResource(id = R.string.wallet_marketplace_block_title, currencyName), - color = TangemTheme.colors.text.tertiary, - style = TangemTheme.typography.subtitle2, - ) +private fun Title(currencyName: String, modifier: Modifier = Modifier) { + Box( + modifier = modifier.heightIn(min = TangemTheme.dimens.size24), + contentAlignment = Alignment.CenterStart, + ) { + Text( + text = stringResource(id = R.string.wallet_marketplace_block_title, currencyName), + color = TangemTheme.colors.text.tertiary, + style = TangemTheme.typography.subtitle2, + ) + } } @Composable -private fun Content(state: MarketPriceBlockState, rootWidth: Int) { - AnimatedContent(targetState = state, label = "Update the content") { marketPriceBlockState -> +private fun Content(state: MarketPriceBlockState, rootWidth: Int, modifier: Modifier = Modifier) { + AnimatedContent( + modifier = modifier.heightIn(min = TangemTheme.dimens.size20), + targetState = state, + contentAlignment = Alignment.CenterStart, + label = "Update the content", + ) { marketPriceBlockState -> when (marketPriceBlockState) { is MarketPriceBlockState.Content, is MarketPriceBlockState.Error, @@ -97,6 +117,7 @@ private fun PriceContent(state: MarketPriceBlockState, priceWidthDp: Dp) { @Composable private fun PriceBlock(state: MarketPriceBlockState, priceWidthDp: Dp) { val priceModifier = Modifier.widthIn(max = priceWidthDp) + AnimatedContent(targetState = state, label = "Update the price block") { marketPriceBlockState -> if (marketPriceBlockState is MarketPriceBlockState.Content) { Row( @@ -127,12 +148,17 @@ private fun Price(price: String, modifier: Modifier = Modifier) { @Composable private fun PriceChangeInPercent(config: PriceChangeState.Content) { - AnimatedContent(targetState = config.type, label = "Update price change") { type -> + AnimatedContent( + targetState = config.type, + contentAlignment = Alignment.CenterStart, + label = "Update price change", + ) { type -> Row( verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing4), + horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing2), ) { Icon( + modifier = Modifier.size(TangemTheme.dimens.size8), painter = painterResource( id = when (type) { PriceChangeType.UP -> R.drawable.ic_arrow_up_8 @@ -184,7 +210,8 @@ private fun QuoteTimeStatus() { ) } -@Preview +// region Preview +@Preview(showBackground = true, widthDp = 360) @Composable private fun Preview_MarketPriceBlock_Light( @PreviewParameter(WalletMarketPriceBlockStateProvider::class) @@ -195,7 +222,7 @@ private fun Preview_MarketPriceBlock_Light( } } -@Preview +@Preview(showBackground = true, widthDp = 360) @Composable private fun Preview_MarketPriceBlock_Dark( @PreviewParameter(WalletMarketPriceBlockStateProvider::class) @@ -227,4 +254,5 @@ private class WalletMarketPriceBlockStateProvider : CollectionPreviewParameterPr MarketPriceBlockState.Loading(currencySymbol = "BTC"), MarketPriceBlockState.Error(currencySymbol = "BTC"), ), -) \ No newline at end of file +) +// endregion Preview \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt index 180acd8a31..271e85f311 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt @@ -53,12 +53,16 @@ fun Transaction(state: TransactionState, isBalanceHidden: Boolean, modifier: Mod Surface( modifier = modifier .background(TangemTheme.colors.background.primary) - .defaultMinSize(minHeight = TangemTheme.dimens.size56) .clickable( enabled = state is TransactionState.Content, onClick = (state as? TransactionState.Content)?.onClick ?: {}, ) - .padding(horizontal = TangemTheme.dimens.spacing12, vertical = TangemTheme.dimens.spacing10), + .fillMaxWidth() + .heightIn(min = TangemTheme.dimens.size56) + .padding( + vertical = TangemTheme.dimens.spacing8, + horizontal = TangemTheme.dimens.spacing12, + ), color = TangemTheme.colors.background.primary, ) { @Suppress("DestructuringDeclarationWithTooManyEntries") @@ -81,12 +85,13 @@ fun Transaction(state: TransactionState, isBalanceHidden: Boolean, modifier: Mod modifier = Modifier .padding( start = TangemTheme.dimens.spacing12, - end = TangemTheme.dimens.spacing6, + end = TangemTheme.dimens.spacing4, ) .constrainAs(titleItem) { + top.linkTo(parent.top) + bottom.linkTo(subtitleItem.top) start.linkTo(iconItem.end) end.linkTo(amountItem.start) - top.linkTo(iconItem.top) width = Dimension.fillToConstraints }, ) @@ -94,11 +99,14 @@ fun Transaction(state: TransactionState, isBalanceHidden: Boolean, modifier: Mod Subtitle( state = state, modifier = Modifier - .padding(top = TangemTheme.dimens.spacing6) - .padding(horizontal = TangemTheme.dimens.spacing12) + .padding( + start = TangemTheme.dimens.spacing12, + end = TangemTheme.dimens.spacing4, + ) .constrainAs(subtitleItem) { + top.linkTo(titleItem.bottom) + bottom.linkTo(parent.bottom) start.linkTo(iconItem.end) - top.linkTo(amountItem.bottom) end.linkTo(timestampItem.start) width = Dimension.fillToConstraints }, @@ -108,8 +116,9 @@ fun Transaction(state: TransactionState, isBalanceHidden: Boolean, modifier: Mod state = state, isBalanceHidden = isBalanceHidden, modifier = Modifier.constrainAs(amountItem) { + top.linkTo(parent.top) + bottom.linkTo(timestampItem.top) start.linkTo(titleItem.end) - top.linkTo(titleItem.top) end.linkTo(parent.end) width = Dimension.fillToConstraints }, @@ -117,12 +126,11 @@ fun Transaction(state: TransactionState, isBalanceHidden: Boolean, modifier: Mod Timestamp( state = state, - modifier = Modifier - .padding(top = TangemTheme.dimens.spacing6) - .constrainAs(timestampItem) { - top.linkTo(amountItem.bottom) - end.linkTo(parent.end) - }, + modifier = Modifier.constrainAs(timestampItem) { + top.linkTo(amountItem.bottom) + bottom.linkTo(parent.bottom) + end.linkTo(parent.end) + }, ) } } diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryGroupTitle.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryGroupTitle.kt index 4676fd5c49..b2b02d9e1a 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryGroupTitle.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryGroupTitle.kt @@ -1,12 +1,14 @@ package com.tangem.core.ui.components.transactions import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import com.tangem.core.ui.components.transactions.state.TxHistoryState.TxHistoryItemState import com.tangem.core.ui.res.TangemTheme @@ -20,19 +22,23 @@ import java.util.UUID */ @Composable internal fun TxHistoryGroupTitle(config: TxHistoryItemState.GroupTitle, modifier: Modifier = Modifier) { - Text( - text = config.title, + Box( modifier = modifier .background(TangemTheme.colors.background.primary) - .fillMaxWidth() .padding( - horizontal = TangemTheme.dimens.spacing16, - vertical = TangemTheme.dimens.spacing14, - ), - color = TangemTheme.colors.text.tertiary, - textAlign = TextAlign.Start, - style = TangemTheme.typography.body2, - ) + vertical = TangemTheme.dimens.spacing8, + horizontal = TangemTheme.dimens.spacing12, + ) + .fillMaxWidth() + .heightIn(min = TangemTheme.dimens.size24), + contentAlignment = Alignment.CenterStart, + ) { + Text( + text = config.title, + color = TangemTheme.colors.text.tertiary, + style = TangemTheme.typography.body2, + ) + } } @Preview diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryTitle.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryTitle.kt index 89a804b588..352c19d181 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryTitle.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryTitle.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.* import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -24,9 +25,11 @@ internal fun TxHistoryTitle(onExploreClick: () -> Unit, modifier: Modifier = Mod Row( modifier = modifier .background(TangemTheme.colors.background.primary) - .fillMaxWidth() .padding(top = TangemTheme.dimens.spacing12) - .padding(horizontal = TangemTheme.dimens.spacing16), + .padding(horizontal = TangemTheme.dimens.spacing12) + .fillMaxWidth() + .heightIn(min = TangemTheme.dimens.size24), + verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, ) { Text( @@ -37,13 +40,14 @@ internal fun TxHistoryTitle(onExploreClick: () -> Unit, modifier: Modifier = Mod Row( modifier = Modifier.clickable(onClick = onExploreClick), - horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing4), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing2), ) { Icon( + modifier = Modifier.size(size = TangemTheme.dimens.size20), painter = painterResource(id = R.drawable.ic_compass_24), - contentDescription = null, - modifier = Modifier.size(size = TangemTheme.dimens.size18), tint = TangemTheme.colors.icon.informative, + contentDescription = null, ) Text( text = stringResource(id = R.string.common_explorer), diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/empty/EmptyTransactionBlock.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/empty/EmptyTransactionBlock.kt index 7f85f98d19..c2b7368924 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/empty/EmptyTransactionBlock.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/empty/EmptyTransactionBlock.kt @@ -1,8 +1,8 @@ package com.tangem.core.ui.components.transactions.empty -import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.* +import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -33,9 +33,10 @@ fun EmptyTransactionBlock(state: EmptyTransactionsBlockState, modifier: Modifier verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing24), horizontalAlignment = Alignment.CenterHorizontally, ) { - Image( + Icon( modifier = Modifier.size(TangemTheme.dimens.size64), painter = painterResource(id = state.iconRes), + tint = TangemTheme.colors.icon.inactive, contentDescription = null, ) @@ -44,7 +45,7 @@ fun EmptyTransactionBlock(state: EmptyTransactionsBlockState, modifier: Modifier textAlign = TextAlign.Center, text = state.text.resolveReference(), style = TangemTheme.typography.body2, - color = TangemTheme.colors.text.secondary, + color = TangemTheme.colors.text.tertiary, ) Buttons( @@ -84,8 +85,8 @@ private fun PairButtons(state: EmptyTransactionsBlockState.ButtonsState.PairButt } } -@Preview @Composable +@Preview(widthDp = 360, showBackground = true) private fun EmptyTransactionBlock_Light( @PreviewParameter(EmptyTransactionBlockStateProvider::class) state: EmptyTransactionsBlockState, ) { @@ -94,8 +95,8 @@ private fun EmptyTransactionBlock_Light( } } -@Preview @Composable +@Preview(widthDp = 360, showBackground = true) private fun EmptyTransactionBlock_Dark( @PreviewParameter(EmptyTransactionBlockStateProvider::class) state: EmptyTransactionsBlockState, ) { diff --git a/core/ui/src/main/java/com/tangem/core/ui/decorations/RoundedDecorations.kt b/core/ui/src/main/java/com/tangem/core/ui/decorations/RoundedDecorations.kt index 4a014cd824..78a23e7b11 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/decorations/RoundedDecorations.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/decorations/RoundedDecorations.kt @@ -19,7 +19,7 @@ fun Modifier.roundedShapeItemDecoration( modifier .then( if (addDefaultPadding) { - Modifier.padding(top = TangemTheme.dimens.spacing14) + Modifier.padding(top = TangemTheme.dimens.spacing12) } else { Modifier }, @@ -30,7 +30,7 @@ fun Modifier.roundedShapeItemDecoration( modifier .then( if (addDefaultPadding) { - Modifier.padding(top = TangemTheme.dimens.spacing14) + Modifier.padding(top = TangemTheme.dimens.spacing12) } else { Modifier }, diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt index e68dfd96a0..13ec4eb908 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt @@ -68,6 +68,7 @@ data class TangemDimens internal constructor( val size50: Dp = 50.dp, val size52: Dp = 52.dp, val size56: Dp = 56.dp, + val size60: Dp = 60.dp, val size62: Dp = 62.dp, val size64: Dp = 64.dp, val size68: Dp = 68.dp, diff --git a/core/ui/src/main/res/drawable/ic_empty_token_64.xml b/core/ui/src/main/res/drawable/ic_empty_token_64.xml index 51821ac760..fa38ac65a5 100644 --- a/core/ui/src/main/res/drawable/ic_empty_token_64.xml +++ b/core/ui/src/main/res/drawable/ic_empty_token_64.xml @@ -3,22 +3,8 @@ android:height="64dp" android:viewportWidth="64" android:viewportHeight="64"> - - - - - diff --git a/core/utils/src/main/java/com/tangem/utils/CryptoCurrencyFormatExtensions.kt b/core/utils/src/main/java/com/tangem/utils/CryptoCurrencyFormatExtensions.kt index a1995e1712..a2533147f7 100644 --- a/core/utils/src/main/java/com/tangem/utils/CryptoCurrencyFormatExtensions.kt +++ b/core/utils/src/main/java/com/tangem/utils/CryptoCurrencyFormatExtensions.kt @@ -4,7 +4,7 @@ import java.math.BigDecimal import java.math.RoundingMode import java.text.DecimalFormat import java.text.NumberFormat -import java.util.* +import java.util.Locale // todo determine where to place this extensions fun BigDecimal.toFormattedString( @@ -38,7 +38,7 @@ fun BigDecimal.toFormattedCurrencyString( decimals = decimalsForRounding, roundingMode = roundingMode, ) - val formattedCurrency = currency?.let { " $it " } ?: "" + val formattedCurrency = currency?.let { " $it" } ?: "" return "$formattedAmount$formattedCurrency" } diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt index 458e547586..77f27a4cac 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/TokenDetailsPreviewData.kt @@ -1,10 +1,15 @@ package com.tangem.feature.tokendetails.presentation.tokendetails import androidx.compose.ui.graphics.Color +import androidx.paging.PagingData import com.tangem.core.ui.components.marketprice.MarketPriceBlockState +import com.tangem.core.ui.components.marketprice.PriceChangeState +import com.tangem.core.ui.components.marketprice.PriceChangeType +import com.tangem.core.ui.components.transactions.state.TransactionState import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.event.consumedEvent import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.tokendetails.presentation.tokendetails.state.* import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsActionButton @@ -76,8 +81,8 @@ internal object TokenDetailsPreviewData { val balanceLoading = TokenDetailsBalanceBlockState.Loading(actionButtons = actionButtons) val balanceContent = TokenDetailsBalanceBlockState.Content( actionButtons = actionButtons, - fiatBalance = "123,00$", - cryptoBalance = "866,96 USDT", + fiatBalance = "91,50$", + cryptoBalance = "966,96 XLM", ) val balanceError = TokenDetailsBalanceBlockState.Error(actionButtons = actionButtons) @@ -88,7 +93,131 @@ internal object TokenDetailsPreviewData { onRefresh = {}, ) - val tokenDetailsState = TokenDetailsState( + private val txHistoryItems = listOf( + TxHistoryState.TxHistoryItemState.Title(onExploreClick = {}), + TxHistoryState.TxHistoryItemState.GroupTitle( + title = "Today", + itemKey = "Today", + ), + TxHistoryState.TxHistoryItemState.Transaction( + state = TransactionState.Content( + txHash = "1", + amount = "-0.500913 XLM", + time = "8:41", + status = TransactionState.Content.Status.Confirmed, + direction = TransactionState.Content.Direction.OUTGOING, + onClick = {}, + iconRes = R.drawable.ic_arrow_up_24, + title = stringReference(value = "Sending"), + subtitle = stringReference(value = "to: 33BddS...ga2B"), + timestamp = 0, + ), + ), + TxHistoryState.TxHistoryItemState.Transaction( + state = TransactionState.Content( + txHash = "2", + amount = "+0.500913 XLM", + time = "8:41", + status = TransactionState.Content.Status.Confirmed, + direction = TransactionState.Content.Direction.INCOMING, + onClick = {}, + iconRes = R.drawable.ic_arrow_down_24, + title = stringReference(value = "Receiving"), + subtitle = stringReference(value = "from: 33BddS...ga2B"), + timestamp = 0, + ), + ), + TxHistoryState.TxHistoryItemState.Transaction( + state = TransactionState.Content( + txHash = "3", + amount = "+0.500913 XLM", + time = "8:41", + status = TransactionState.Content.Status.Confirmed, + direction = TransactionState.Content.Direction.INCOMING, + onClick = {}, + iconRes = R.drawable.ic_doc_24, + title = stringReference(value = "Approving"), + subtitle = stringReference(value = "from: 33BddS...ga2B"), + timestamp = 0, + ), + ), + TxHistoryState.TxHistoryItemState.Transaction( + state = TransactionState.Content( + txHash = "4", + amount = "+0.500913 XLM", + time = "8:41", + status = TransactionState.Content.Status.Confirmed, + direction = TransactionState.Content.Direction.INCOMING, + onClick = {}, + iconRes = R.drawable.ic_exchange_vertical_24, + title = stringReference(value = "Swapping"), + subtitle = stringReference(value = "contract: 33BddS...ga2B"), + timestamp = 0, + ), + ), + TxHistoryState.TxHistoryItemState.GroupTitle( + title = "Yesterday", + itemKey = "Yesterday", + ), + TxHistoryState.TxHistoryItemState.Transaction( + state = TransactionState.Content( + txHash = "5", + amount = "-0.500913 XLM", + time = "8:41", + status = TransactionState.Content.Status.Confirmed, + direction = TransactionState.Content.Direction.OUTGOING, + onClick = {}, + iconRes = R.drawable.ic_arrow_up_24, + title = stringReference(value = "Sending"), + subtitle = stringReference(value = "to: 33BddS...ga2B"), + timestamp = 0, + ), + ), + TxHistoryState.TxHistoryItemState.Transaction( + state = TransactionState.Content( + txHash = "6", + amount = "+0.500913 XLM", + time = "8:41", + status = TransactionState.Content.Status.Confirmed, + direction = TransactionState.Content.Direction.INCOMING, + onClick = {}, + iconRes = R.drawable.ic_arrow_down_24, + title = stringReference(value = "Receiving"), + subtitle = stringReference(value = "from: 33BddS...ga2B"), + timestamp = 0, + ), + ), + TxHistoryState.TxHistoryItemState.Transaction( + state = TransactionState.Content( + txHash = "7", + amount = "+0.500913 XLM", + time = "8:41", + status = TransactionState.Content.Status.Confirmed, + direction = TransactionState.Content.Direction.INCOMING, + onClick = {}, + iconRes = R.drawable.ic_doc_24, + title = stringReference(value = "Approving"), + subtitle = stringReference(value = "from: 33BddS...ga2B"), + timestamp = 0, + ), + ), + TxHistoryState.TxHistoryItemState.Transaction( + state = TransactionState.Content( + txHash = "8", + amount = "+0.500913 XLM", + time = "8:41", + status = TransactionState.Content.Status.Confirmed, + direction = TransactionState.Content.Direction.INCOMING, + onClick = {}, + iconRes = R.drawable.ic_exchange_vertical_24, + title = stringReference(value = "Swapping"), + subtitle = stringReference(value = "contract: 33BddS...ga2B"), + timestamp = 0, + ), + ), + ) + + val tokenDetailsState_1 = TokenDetailsState( topAppBarConfig = tokenDetailsTopAppBarConfig, tokenInfoBlockState = tokenInfoBlockState, tokenBalanceBlockState = balanceLoading, @@ -108,4 +237,41 @@ internal object TokenDetailsPreviewData { isMarketPriceAvailable = false, event = consumedEvent(), ) + + val tokenDetailsState_2 = TokenDetailsState( + topAppBarConfig = tokenDetailsTopAppBarConfig, + tokenInfoBlockState = tokenInfoBlockState.copy( + name = "Stellar", + ), + tokenBalanceBlockState = balanceContent, + marketPriceBlockState = MarketPriceBlockState.Content( + currencySymbol = "XLM", + price = "0.11$", + priceChangeConfig = PriceChangeState.Content( + valueInPercent = "5,16%", + type = PriceChangeType.UP, + ), + ), + notifications = persistentListOf(), + txHistoryState = TxHistoryState.NotSupported( + onExploreClick = {}, + pendingTransactions = persistentListOf(), + ), + dialogConfig = null, + pendingTxs = persistentListOf(), + swapTxs = persistentListOf(), + pullToRefreshConfig = pullToRefreshConfig, + bottomSheetConfig = null, + isBalanceHidden = false, + isMarketPriceAvailable = true, + event = consumedEvent(), + ) + + val tokenDetailsState_3 = tokenDetailsState_2.copy( + txHistoryState = TxHistoryState.Content( + contentItems = MutableStateFlow( + value = PagingData.from(txHistoryItems), + ), + ), + ) } \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt index 1f1ae20a89..070d2b0065 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt @@ -3,6 +3,7 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.ui import androidx.activity.compose.BackHandler import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn @@ -20,6 +21,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider import androidx.paging.compose.collectAsLazyPagingItems import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheet import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheetConfig @@ -82,14 +85,12 @@ internal fun TokenDetailsScreen(state: TokenDetailsState) { .pullRefresh(pullRefreshState), ) { LazyColumn( - modifier = Modifier - .fillMaxSize(), + modifier = Modifier.fillMaxSize(), + contentPadding = PaddingValues(bottom = TangemTheme.dimens.spacing16), ) { item { TokenInfoBlock( - modifier = Modifier - .padding(top = TangemTheme.dimens.spacing4) - .padding(horizontal = horizontalPadding), + modifier = Modifier.padding(horizontal = horizontalPadding), state = state.tokenInfoBlockState, ) } @@ -181,18 +182,32 @@ internal fun TokenDetailsEventEffect(snackbarHostState: SnackbarHostState, event ) } -@Preview +// region Preview +@Preview(showBackground = true, widthDp = 360) @Composable -private fun Preview_TokenDetailsScreen_LightTheme() { - TangemTheme(isDark = false) { - TokenDetailsScreen(state = TokenDetailsPreviewData.tokenDetailsState) +private fun TokenDetailsScreenPreview_Light( + @PreviewParameter(TokenDetailsScreenParameterProvider::class) state: TokenDetailsState, +) { + TangemTheme { + TokenDetailsScreen(state) } } -@Preview +@Preview(showBackground = true, widthDp = 360) @Composable -private fun Preview_TokenDetailsScreen_DarkTheme() { +private fun TokenDetailsScreenPreview_Dark( + @PreviewParameter(TokenDetailsScreenParameterProvider::class) state: TokenDetailsState, +) { TangemTheme(isDark = true) { - TokenDetailsScreen(state = TokenDetailsPreviewData.tokenDetailsState) + TokenDetailsScreen(state) } -} \ No newline at end of file +} + +private class TokenDetailsScreenParameterProvider : CollectionPreviewParameterProvider( + collection = listOf( + TokenDetailsPreviewData.tokenDetailsState_1, + TokenDetailsPreviewData.tokenDetailsState_2, + TokenDetailsPreviewData.tokenDetailsState_3, + ), +) +// endregion Preview \ No newline at end of file diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsBalanceBlock.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsBalanceBlock.kt index f9911ae521..e8fd329b15 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsBalanceBlock.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsBalanceBlock.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.* import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -32,18 +33,21 @@ internal fun TokenDetailsBalanceBlock( color = TangemTheme.colors.background.primary, ) { Column { - Text( + Box( modifier = Modifier - .padding( - top = TangemTheme.dimens.spacing12, - start = TangemTheme.dimens.spacing12, - end = TangemTheme.dimens.spacing12, - ), - text = stringResource(id = R.string.common_balance_title), - color = TangemTheme.colors.text.tertiary, - style = TangemTheme.typography.subtitle2, - maxLines = 1, - ) + .padding(top = TangemTheme.dimens.spacing12) + .padding(horizontal = TangemTheme.dimens.spacing12) + .fillMaxWidth() + .heightIn(min = TangemTheme.dimens.spacing24), + contentAlignment = Alignment.CenterStart, + ) { + Text( + text = stringResource(id = R.string.common_balance_title), + color = TangemTheme.colors.text.tertiary, + style = TangemTheme.typography.subtitle2, + maxLines = 1, + ) + } FiatBalance( state = state, isBalanceHidden = isBalanceHidden, @@ -124,7 +128,7 @@ private fun CryptoBalance( } } -@Preview +@Preview(widthDp = 328, heightDp = 152) @Composable private fun Preview_TokenDetailsBalanceBlock_LightTheme( @PreviewParameter(TokenDetailsBalanceBlockStateProvider::class) state: TokenDetailsBalanceBlockState, @@ -134,7 +138,7 @@ private fun Preview_TokenDetailsBalanceBlock_LightTheme( } } -@Preview +@Preview(widthDp = 328, heightDp = 152) @Composable private fun Preview_TokenDetailsBalanceBlock_DarkTheme( @PreviewParameter(TokenDetailsBalanceBlockStateProvider::class) state: TokenDetailsBalanceBlockState, diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenInfoBlock.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenInfoBlock.kt index 8b48d54da4..3254286ed2 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenInfoBlock.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenInfoBlock.kt @@ -14,7 +14,6 @@ import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorMatrix import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider @@ -29,9 +28,15 @@ private const val NORMAL_ALPHA = 1f @Composable internal fun TokenInfoBlock(state: TokenInfoBlockState, modifier: Modifier = Modifier) { - Row(modifier = modifier.fillMaxWidth()) { + Row( + modifier = modifier + .fillMaxWidth() + .heightIn(min = TangemTheme.dimens.size60), + ) { Column( - modifier = Modifier.weight(1F), + modifier = Modifier + .align(Alignment.CenterVertically) + .weight(1F), ) { Text( text = state.name, @@ -88,7 +93,7 @@ private fun NetworkInfoText(currency: TokenInfoBlockState.Currency) { ) Text( text = state.boldText, - style = TangemTheme.typography.caption2.copy(fontWeight = FontWeight.Medium), + style = TangemTheme.typography.caption1, color = TangemTheme.colors.text.primary1, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt index 9c6177c05e..0b20c2a43a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt @@ -126,7 +126,7 @@ private fun CustomContainer(state: TokenItemState, modifier: Modifier = Modifier Layout(content = content, modifier = modifier) { measurables, constraints -> val layoutWidth = constraints.maxWidth - val horizontalPadding = with(density) { dimens.size14.roundToPx() } + val horizontalPadding = with(density) { dimens.size12.roundToPx() } val verticalPadding = with(density) { dimens.size16.roundToPx() } val layoutWidthWithoutPaddings = layoutWidth - 2 * horizontalPadding @@ -137,7 +137,7 @@ private fun CustomContainer(state: TokenItemState, modifier: Modifier = Modifier /* * Title width take the whole REMAINING space. - * If FiatAmount took the whole free space, then Title will has min width. + * If FiatAmount took the whole free space, then Title will have min width. */ val title: Placeable @@ -149,7 +149,7 @@ private fun CustomContainer(state: TokenItemState, modifier: Modifier = Modifier /* * PriceChange width take the whole REMAINING space. - * If CryptoAmount took the whole free space, then PriceChange will has min width. + * If CryptoAmount took the whole free space, then PriceChange will have min width. */ val priceChange: Placeable? diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt index 81cfb1f7a8..294fe204cd 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt @@ -103,7 +103,7 @@ private fun TokenList( ) val listContentPadding = PaddingValues( - top = TangemTheme.dimens.spacing12, + top = TangemTheme.dimens.spacing4, bottom = TangemTheme.dimens.spacing92, start = TangemTheme.dimens.spacing16, end = TangemTheme.dimens.spacing16, @@ -245,7 +245,7 @@ private fun TopBar( Row( modifier = Modifier .fillMaxWidth() - .heightIn(min = TangemTheme.dimens.size56), + .padding(bottom = TangemTheme.dimens.spacing8), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8), ) { From c6ddab601beeccd94e67b4ef1b644fa94ee9bb55 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 13 Feb 2024 09:56:02 +0300 Subject: [PATCH 35/53] Updated on 2026-08-14 --- .../tap/common/extensions/Navigation.kt | 13 +- core/ui/build.gradle.kts | 3 + .../com/tangem/core/ui/components/Spacers.kt | 5 + .../tangem/core/ui/components/atoms/Hand.kt | 17 +- .../ui/extensions/ComposeNavigationExt.kt | 49 ++++++ features/manage-tokens/api/build.gradle.kts | 3 + .../navigation/ManageTokensRouter.kt | 8 +- features/manage-tokens/impl/build.gradle.kts | 1 + .../managetokens/ManageTokensFragment.kt | 37 ----- .../managetokens/ui/ManageTokensScreen.kt | 61 ++++--- .../managetokens/ui/components/TokensList.kt | 8 +- .../router/DefaultManageTokensRouter.kt | 110 +++++++------ .../router/InnerManageTokensRouter.kt | 9 - features/wallet/impl/build.gradle.kts | 3 +- .../wallet/presentation/WalletFragment.kt | 9 +- .../router/DefaultWalletRouter.kt | 29 +++- .../presentation/router/InnerWalletRouter.kt | 3 +- .../wallet/state/WalletStateController.kt | 7 +- .../wallet/state/model/WalletScreenState.kt | 1 + .../presentation/wallet/ui/WalletScreen.kt | 154 +++++++++++++++++- 20 files changed, 370 insertions(+), 160 deletions(-) create mode 100644 core/ui/src/main/java/com/tangem/core/ui/extensions/ComposeNavigationExt.kt delete mode 100644 features/manage-tokens/impl/src/main/java/com/tangem/managetokens/ManageTokensFragment.kt diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt index 6dc414e945..fa0d174bf9 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt @@ -151,18 +151,7 @@ private fun fragmentFactory(screen: AppScreen): Fragment { AppScreen.ResetToFactory -> ResetCardFragment() AppScreen.AccessCodeRecovery -> AccessCodeRecoveryFragment() AppScreen.Disclaimer -> DisclaimerFragment() - AppScreen.ManageTokens -> { - val featureToggles = store.state.daggerGraphState.get( - getDependency = DaggerGraphState::manageTokensFeatureToggles, - ) - if (featureToggles.isRedesignedScreenEnabled) { - store.state.daggerGraphState - .get(getDependency = DaggerGraphState::manageTokensRouter) - .getEntryFragment() - } else { - TokensListFragment() - } - } + AppScreen.ManageTokens -> TokensListFragment() AppScreen.AddCustomToken -> AddCustomTokenFragment() AppScreen.WalletDetails -> { store.state.daggerGraphState diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index ab9b42cd1f..62f9551fa1 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -30,6 +30,8 @@ dependencies { implementation(deps.compose.ui.tooling) implementation(deps.compose.ui.utils) implementation(deps.compose.coil) + implementation(deps.compose.navigation) + implementation(deps.compose.navigation.hilt) /** Other libraries */ implementation(deps.compose.accompanist.systemUiController) @@ -38,4 +40,5 @@ dependencies { implementation(deps.kotlin.immutable.collections) implementation(deps.zxing.qrCore) implementation(deps.jodatime) + implementation(deps.timber) } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/Spacers.kt b/core/ui/src/main/java/com/tangem/core/ui/components/Spacers.kt index ad4fc53b0a..99230c1853 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/Spacers.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/Spacers.kt @@ -39,6 +39,11 @@ fun SpacerH16(modifier: Modifier = Modifier) { SpacerH(16.dp, modifier) } +@Composable +fun SpacerH18(modifier: Modifier = Modifier) { + SpacerH(18.dp, modifier) +} + @Composable fun SpacerH24(modifier: Modifier = Modifier) { SpacerH(24.dp, modifier) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/atoms/Hand.kt b/core/ui/src/main/java/com/tangem/core/ui/components/atoms/Hand.kt index cbb97ce8e1..f3d155f5a2 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/atoms/Hand.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/atoms/Hand.kt @@ -1,17 +1,13 @@ package com.tangem.core.ui.components.atoms import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.* import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp import com.tangem.core.ui.res.TangemTheme /** @@ -21,7 +17,7 @@ import com.tangem.core.ui.res.TangemTheme fun Hand(modifier: Modifier = Modifier) { Box( modifier = modifier - .padding(vertical = TangemTheme.dimens.spacing8) + .height(handComposableComponentHeight) .fillMaxWidth(), contentAlignment = Alignment.Center, ) { @@ -37,6 +33,11 @@ fun Hand(modifier: Modifier = Modifier) { } } +val handComposableComponentHeight: Dp + @Composable + @ReadOnlyComposable + get() = TangemTheme.dimens.size4 + TangemTheme.dimens.spacing16 + // region Preview @Composable private fun HandSample(modifier: Modifier = Modifier) { diff --git a/core/ui/src/main/java/com/tangem/core/ui/extensions/ComposeNavigationExt.kt b/core/ui/src/main/java/com/tangem/core/ui/extensions/ComposeNavigationExt.kt new file mode 100644 index 0000000000..2dc5bcd17e --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/extensions/ComposeNavigationExt.kt @@ -0,0 +1,49 @@ +package com.tangem.core.ui.extensions + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.ViewModel +import androidx.navigation.NavBackStackEntry +import androidx.navigation.NavController +import timber.log.Timber + +/** + * The ViewModel is scoped to the parent route Navigation graph + * and is provided using the Hilt-generated ViewModel factory + * + * ``` + * val navController = rememberNavController() + * + * navigation( + * route = "parent", + * startDestination = "parent/1" + * ) { + * composable("route/1") { entry -> + * val viewModel = entry.parentHiltViewModel(navController) + * } + * composable("route/2") { entry -> + * val viewModel = entry.parentHiltViewModel(navController) + * } + * composable("route/3") { entry -> + * val viewModel = entry.parentHiltViewModel(navController) + * } + * } + * ``` + * + * @param navController NavController within the common NavGraph + * @throws Exception if there is no parent route + */ +@Composable +inline fun NavBackStackEntry.parentHiltViewModel(navController: NavController): T { + val viewModelStoreOwner = remember(this) { + try { + navController.getBackStackEntry(this.destination.parent!!.id) + } catch (e: Exception) { + Timber.tag("scopedViewModel").e(e, "There is no parent route'") + throw e + } + } + + return hiltViewModel(viewModelStoreOwner) +} \ No newline at end of file diff --git a/features/manage-tokens/api/build.gradle.kts b/features/manage-tokens/api/build.gradle.kts index aa4ad2bd26..1195231904 100644 --- a/features/manage-tokens/api/build.gradle.kts +++ b/features/manage-tokens/api/build.gradle.kts @@ -11,4 +11,7 @@ android { dependencies { /** AndroidX */ implementation(deps.androidx.fragment.ktx) + + implementation(deps.compose.foundation) + implementation(deps.compose.navigation) } \ No newline at end of file diff --git a/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/navigation/ManageTokensRouter.kt b/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/navigation/ManageTokensRouter.kt index 7dd58d2a5b..b6310d4e11 100644 --- a/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/navigation/ManageTokensRouter.kt +++ b/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/navigation/ManageTokensRouter.kt @@ -1,8 +1,12 @@ package com.tangem.features.managetokens.navigation -import androidx.fragment.app.Fragment +import androidx.compose.ui.unit.Dp +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController interface ManageTokensRouter { - fun getEntryFragment(): Fragment + val startDestination: String + + fun NavGraphBuilder.initialize(navController: NavHostController, onHeaderSizeChange: (Dp) -> Unit) } \ No newline at end of file diff --git a/features/manage-tokens/impl/build.gradle.kts b/features/manage-tokens/impl/build.gradle.kts index 1be0e9701e..8fb983d6d7 100644 --- a/features/manage-tokens/impl/build.gradle.kts +++ b/features/manage-tokens/impl/build.gradle.kts @@ -35,6 +35,7 @@ dependencies { implementation(deps.jodatime) implementation(deps.kotlin.immutable.collections) implementation(deps.tangem.card.core) + implementation(deps.timber) /** DI */ implementation(deps.hilt.android) diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/ManageTokensFragment.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/ManageTokensFragment.kt deleted file mode 100644 index 87b216af73..0000000000 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/ManageTokensFragment.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.tangem.managetokens - -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import com.tangem.core.ui.components.SystemBarsEffect -import com.tangem.core.ui.res.TangemTheme -import com.tangem.core.ui.screen.ComposeFragment -import com.tangem.core.ui.theme.AppThemeModeHolder -import com.tangem.features.managetokens.navigation.ManageTokensRouter -import com.tangem.managetokens.presentation.router.InnerManageTokensRouter -import dagger.hilt.android.AndroidEntryPoint -import javax.inject.Inject - -@AndroidEntryPoint -internal class ManageTokensFragment : ComposeFragment() { - - @Inject - override lateinit var appThemeModeHolder: AppThemeModeHolder - - @Inject - lateinit var manageTokensRouter: ManageTokensRouter - - private val innerManageTokensRouter: InnerManageTokensRouter - get() = requireNotNull(manageTokensRouter as? InnerManageTokensRouter) { - "internalManageTokensRouter should be instance of InnerManageTokensRouter" - } - - @Composable - override fun ScreenContent(modifier: Modifier) { - val systemBarsColor = TangemTheme.colors.background.secondary - SystemBarsEffect { - setSystemBarsColor(systemBarsColor) - } - - innerManageTokensRouter.Initialize(viewModelStoreOwner = this) - } -} \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ManageTokensScreen.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ManageTokensScreen.kt index 0f83676b10..28dd482aeb 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ManageTokensScreen.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ManageTokensScreen.kt @@ -8,13 +8,18 @@ import androidx.compose.material.Surface import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.onGloballyPositioned +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp import androidx.paging.LoadState import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems import com.tangem.core.ui.components.Keyboard +import com.tangem.core.ui.components.SpacerH18 import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.keyboardAsState import com.tangem.core.ui.res.TangemTheme @@ -32,7 +37,7 @@ import com.tangem.managetokens.presentation.managetokens.ui.components.TokensLis import com.tangem.managetokens.presentation.managetokens.ui.components.TokensSearchBar @Composable -internal fun ManageTokensScreen(state: ManageTokensState) { +internal fun ManageTokensScreen(state: ManageTokensState, onHeaderSizeChange: (Dp) -> Unit) { var alertState by remember { mutableStateOf(value = null) } EventEffect( @@ -43,43 +48,50 @@ internal fun ManageTokensScreen(state: ManageTokensState) { Alert(state = it, onDismiss = { alertState = null }) } - Content(state) + Content(state = state, onHeaderSizeChange = onHeaderSizeChange) } @Composable -private fun Content(state: ManageTokensState) { +private fun Content(state: ManageTokensState, onHeaderSizeChange: (Dp) -> Unit) { val keyboard by keyboardAsState() + val density = LocalDensity.current + var tokenListAlertBottomPadding by remember(keyboard is Keyboard.Opened) { mutableStateOf(0.dp) } + Box( modifier = Modifier .fillMaxSize() .navigationBarsPadding() .imePadding() - .background(color = TangemTheme.colors.background.primary) - .padding(top = TangemTheme.dimens.spacing32), + .background(color = TangemTheme.colors.background.primary), ) { Column { val listState = rememberLazyListState() val raiseSearchBar by remember { derivedStateOf { listState.firstVisibleItemIndex > 0 } } - val elevation by animateDpAsState( - targetValue = if (raiseSearchBar) { - TangemTheme.dimens.elevation8 - } else { - TangemTheme.dimens.elevation0 - }, + targetValue = if (raiseSearchBar) TangemTheme.dimens.elevation8 else TangemTheme.dimens.elevation0, label = "top_bar_elevation", ) + Surface( elevation = elevation, - modifier = Modifier, + modifier = Modifier.onGloballyPositioned { + with(density) { onHeaderSizeChange(it.size.height.toDp()) } + }, ) { TokensSearchBar( state = state.searchBarState, modifier = Modifier .background(color = TangemTheme.colors.background.primary) - .padding(horizontal = TangemTheme.dimens.spacing16, vertical = TangemTheme.dimens.spacing20), + .padding( + start = TangemTheme.dimens.spacing16, + end = TangemTheme.dimens.spacing16, + bottom = TangemTheme.dimens.spacing4, + ), ) } + + SpacerH18() + val tokens = state.tokens.collectAsLazyPagingItems() val query = state.searchBarState.query @@ -89,7 +101,15 @@ private fun Content(state: ManageTokensState) { onEmptySearchResult = state.onEmptySearchResult, ) - TokensList(tokens = tokens, addCustomTokenButton = state.addCustomTokenButton) + TokensList( + modifier = Modifier.padding(bottom = tokenListAlertBottomPadding), + tokens = tokens, + addCustomTokenButton = state.addCustomTokenButton, + ) + } + + state.selectedToken?.let { selectedToken -> + ManageTokensBottomSheet(selectedToken = selectedToken, state = state) } state.derivationNotification?.let { @@ -97,14 +117,13 @@ private fun Content(state: ManageTokensState) { DerivationNotification( config = it.config, modifier = Modifier - .align(Alignment.BottomCenter), + .align(Alignment.BottomCenter) + .onGloballyPositioned { + with(density) { tokenListAlertBottomPadding = it.size.height.toDp() } + }, ) } } - - state.selectedToken?.let { selectedToken -> - ManageTokensBottomSheet(selectedToken = selectedToken, state = state) - } } } @@ -158,7 +177,7 @@ private fun Preview_ManageTokensScreen_LightTheme( state: ManageTokensState, ) { TangemTheme(isDark = false) { - ManageTokensScreen(state) + ManageTokensScreen(state) {} } } @@ -169,7 +188,7 @@ private fun Preview_ManageTokensScreen_DarkTheme( state: ManageTokensState, ) { TangemTheme(isDark = true) { - ManageTokensScreen(state) + ManageTokensScreen(state) {} } } diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/components/TokensList.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/components/TokensList.kt index 475f9a8687..d6c2b505ea 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/components/TokensList.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/components/TokensList.kt @@ -17,8 +17,12 @@ import com.tangem.managetokens.presentation.managetokens.state.TokenItemState private const val PLACEHOLDER_ITEMS_COUNT = 50 @Composable -internal fun TokensList(tokens: LazyPagingItems, addCustomTokenButton: AddCustomTokenButton) { - LazyColumn { +internal fun TokensList( + tokens: LazyPagingItems, + addCustomTokenButton: AddCustomTokenButton, + modifier: Modifier = Modifier, +) { + LazyColumn(modifier = modifier) { item { Text( text = stringResource(id = R.string.manage_tokens_title), diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/DefaultManageTokensRouter.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/DefaultManageTokensRouter.kt index 5a4ec5b232..5ecebe79fe 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/DefaultManageTokensRouter.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/DefaultManageTokensRouter.kt @@ -1,25 +1,22 @@ package com.tangem.managetokens.presentation.router -import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalLifecycleOwner -import androidx.fragment.app.Fragment +import androidx.compose.ui.unit.Dp import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.ViewModelStoreOwner +import androidx.navigation.NavGraphBuilder import androidx.navigation.NavHostController -import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable -import androidx.navigation.compose.rememberNavController import androidx.navigation.navigation import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction import com.tangem.core.navigation.ReduxNavController -import com.tangem.managetokens.ManageTokensFragment -import com.tangem.managetokens.presentation.common.state.ChooseWalletState +import com.tangem.core.ui.extensions.parentHiltViewModel +import com.tangem.managetokens.presentation.addcustomtoken.ui.AddCustomTokenScreen import com.tangem.managetokens.presentation.addcustomtoken.ui.ChooseDerivationScreen import com.tangem.managetokens.presentation.addcustomtoken.ui.ChooseNetworkCustomScreen import com.tangem.managetokens.presentation.addcustomtoken.ui.CustomTokensChooseWalletScreen -import com.tangem.managetokens.presentation.addcustomtoken.ui.AddCustomTokenScreen import com.tangem.managetokens.presentation.addcustomtoken.viewmodels.AddCustomTokenViewModel +import com.tangem.managetokens.presentation.common.state.ChooseWalletState import com.tangem.managetokens.presentation.managetokens.ui.ManageTokensScreen import com.tangem.managetokens.presentation.managetokens.viewmodels.ManageTokensViewModel import kotlin.properties.Delegates @@ -30,64 +27,69 @@ internal class DefaultManageTokensRouter( private var navController: NavHostController by Delegates.notNull() - override fun getEntryFragment(): Fragment = ManageTokensFragment() + override val startDestination: String = ManageTokensRoute.ManageTokens.route - @Composable - override fun Initialize(viewModelStoreOwner: ViewModelStoreOwner) { - NavHost( - navController = rememberNavController().apply { navController = this }, - startDestination = ManageTokensRoute.ManageTokens.route, - ) { - composable(ManageTokensRoute.ManageTokens.route) { - val viewModel = hiltViewModel().apply { router = this@DefaultManageTokensRouter } - LocalLifecycleOwner.current.lifecycle.addObserver(viewModel) + override fun NavGraphBuilder.initialize(navController: NavHostController, onHeaderSizeChange: (Dp) -> Unit) { + this@DefaultManageTokensRouter.navController = navController - ManageTokensScreen(state = viewModel.uiState) + composable(ManageTokensRoute.ManageTokens.route) { + val viewModel = hiltViewModel().apply { + router = this@DefaultManageTokensRouter } + LocalLifecycleOwner.current.lifecycle.addObserver(viewModel) - navigation( - startDestination = ManageTokensRoute.AddCustomToken.Main.route, - route = ManageTokensRoute.AddCustomToken.route, - ) { - composable( - ManageTokensRoute.AddCustomToken.Main.route, - ) { - val viewModel = hiltViewModel(viewModelStoreOwner).apply { + ManageTokensScreen( + state = viewModel.uiState, + onHeaderSizeChange = onHeaderSizeChange, + ) + } + + navigation( + route = ManageTokensRoute.AddCustomToken.route, + startDestination = ManageTokensRoute.AddCustomToken.Main.route, + ) { + composable( + ManageTokensRoute.AddCustomToken.Main.route, + ) { backStackEntry -> + val viewModel = + backStackEntry.parentHiltViewModel(navController = navController).apply { router = this@DefaultManageTokensRouter } - LocalLifecycleOwner.current.lifecycle.addObserver(viewModel) - AddCustomTokenScreen(state = viewModel.uiState) - } - composable( - ManageTokensRoute.AddCustomToken.ChooseNetwork.route, - ) { - val viewModel = hiltViewModel(viewModelStoreOwner).apply { + LocalLifecycleOwner.current.lifecycle.addObserver(viewModel) + AddCustomTokenScreen(state = viewModel.uiState) + } + composable( + ManageTokensRoute.AddCustomToken.ChooseNetwork.route, + ) { backStackEntry -> + val viewModel = + backStackEntry.parentHiltViewModel(navController = navController).apply { router = this@DefaultManageTokensRouter } - ChooseNetworkCustomScreen( - state = viewModel.uiState.chooseNetworkState, - ) - } - composable( - ManageTokensRoute.AddCustomToken.ChooseDerivation.route, - ) { - val viewModel = hiltViewModel(viewModelStoreOwner).apply { + ChooseNetworkCustomScreen( + state = viewModel.uiState.chooseNetworkState, + ) + } + composable( + ManageTokensRoute.AddCustomToken.ChooseDerivation.route, + ) { backStackEntry -> + val viewModel = + backStackEntry.parentHiltViewModel(navController = navController).apply { router = this@DefaultManageTokensRouter } - ChooseDerivationScreen( - state = requireNotNull(viewModel.uiState.chooseDerivationState), - ) - } - composable( - ManageTokensRoute.AddCustomToken.ChooseWallet.route, - ) { - val viewModel = hiltViewModel(viewModelStoreOwner).apply { + ChooseDerivationScreen( + state = requireNotNull(viewModel.uiState.chooseDerivationState), + ) + } + composable( + ManageTokensRoute.AddCustomToken.ChooseWallet.route, + ) { backStackEntry -> + val viewModel = + backStackEntry.parentHiltViewModel(navController = navController).apply { router = this@DefaultManageTokensRouter } - CustomTokensChooseWalletScreen( - state = viewModel.uiState.chooseWalletState as ChooseWalletState.Choose, - ) - } + CustomTokensChooseWalletScreen( + state = viewModel.uiState.chooseWalletState as ChooseWalletState.Choose, + ) } } } diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/InnerManageTokensRouter.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/InnerManageTokensRouter.kt index 4c45b39d4a..999ec29252 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/InnerManageTokensRouter.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/InnerManageTokensRouter.kt @@ -1,19 +1,10 @@ package com.tangem.managetokens.presentation.router -import androidx.compose.runtime.Composable -import androidx.lifecycle.ViewModelStoreOwner import com.tangem.core.navigation.AppScreen import com.tangem.features.managetokens.navigation.ManageTokensRouter internal interface InnerManageTokensRouter : ManageTokensRouter { - /** - * Initialize router - **/ - @Suppress("TopLevelComposableFunctions") - @Composable - fun Initialize(viewModelStoreOwner: ViewModelStoreOwner) - /** Pop back stack */ fun popBackStack(screen: AppScreen? = null) diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts index a24c30a994..e2d5cb6d0f 100644 --- a/features/wallet/impl/build.gradle.kts +++ b/features/wallet/impl/build.gradle.kts @@ -73,10 +73,11 @@ dependencies { //TODO: Create api/impl modules for onboarding [REDACTED_JIRA] implementation(projects.features.onboarding) - implementation(projects.features.tester.api) /** Feature Apis */ implementation(projects.features.wallet.api) implementation(projects.features.tokendetails.api) implementation(projects.features.send.api) + implementation(projects.features.tester.api) + implementation(projects.features.manageTokens.api) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt index e8c0881cfe..c13cfa5e90 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt @@ -7,6 +7,7 @@ import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.screen.ComposeFragment import com.tangem.core.ui.theme.AppThemeModeHolder import com.tangem.feature.wallet.presentation.router.InnerWalletRouter +import com.tangem.features.managetokens.navigation.ManageTokensRouter import com.tangem.features.wallet.navigation.WalletRouter import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject @@ -22,6 +23,9 @@ internal class WalletFragment : ComposeFragment() { @Inject override lateinit var appThemeModeHolder: AppThemeModeHolder + @Inject + internal lateinit var manageTokensRouter: ManageTokensRouter + /** Feature router */ @Inject internal lateinit var walletRouter: WalletRouter @@ -38,7 +42,10 @@ internal class WalletFragment : ComposeFragment() { setSystemBarsColor(systemBarsColor) } - _walletRouter.Initialize(onFinish = requireActivity()::finish) + _walletRouter.Initialize( + onFinish = requireActivity()::finish, + manageTokensRouter = manageTokensRouter, + ) } companion object { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index 5a7e3675da..ecf64015f4 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -1,10 +1,10 @@ package com.tangem.feature.wallet.presentation.router import androidx.compose.foundation.layout.statusBarsPadding -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue +import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.compose.ui.unit.dp import androidx.core.os.bundleOf import androidx.fragment.app.Fragment import androidx.hilt.navigation.compose.hiltViewModel @@ -27,6 +27,7 @@ import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensScree import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensViewModel import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletViewModel +import com.tangem.features.managetokens.navigation.ManageTokensRouter import com.tangem.features.tokendetails.navigation.TokenDetailsRouter import kotlin.properties.Delegates @@ -41,7 +42,7 @@ internal class DefaultWalletRouter( override fun getEntryFragment(): Fragment = WalletFragment.create() @Composable - override fun Initialize(onFinish: () -> Unit) { + override fun Initialize(onFinish: () -> Unit, manageTokensRouter: ManageTokensRouter) { this.onFinish = onFinish NavHost( @@ -54,7 +55,27 @@ internal class DefaultWalletRouter( subscribeToLifecycle(LocalLifecycleOwner.current) } - WalletScreen(state = viewModel.uiState.collectAsStateWithLifecycle().value) + var bottomSheetHeaderHeight by remember { mutableStateOf(0.dp) } + val innerController = rememberNavController() + + WalletScreen( + state = viewModel.uiState.collectAsStateWithLifecycle().value, + bottomSheetHeaderHeightProvider = { bottomSheetHeaderHeight }, + bottomSheetContent = { + NavHost( + navController = innerController, + startDestination = manageTokensRouter.startDestination, + ) { + // Manage Tokens + with(manageTokensRouter) { + initialize( + navController = innerController, + onHeaderSizeChange = { bottomSheetHeaderHeight = it }, + ) + } + } + }, + ) } composable( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt index c8f7b955ce..d5d53d879f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt @@ -5,6 +5,7 @@ import androidx.compose.runtime.Stable import com.tangem.core.navigation.AppScreen import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.features.managetokens.navigation.ManageTokensRouter import com.tangem.features.wallet.navigation.WalletRouter /** @@ -25,7 +26,7 @@ internal interface InnerWalletRouter : WalletRouter { */ @Suppress("TopLevelComposableFunctions") @Composable - fun Initialize(onFinish: () -> Unit) + fun Initialize(onFinish: () -> Unit, manageTokensRouter: ManageTokensRouter) /** Pop back stack */ fun popBackStack(screen: AppScreen? = null) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateController.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateController.kt index 6f86d37b02..f9e1705b63 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateController.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateController.kt @@ -10,6 +10,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.model.WalletTopBarCon import com.tangem.feature.wallet.presentation.wallet.state.transformers.CloseBottomSheetTransformer import com.tangem.feature.wallet.presentation.wallet.state.transformers.OpenBottomSheetTransformer import com.tangem.feature.wallet.presentation.wallet.state.transformers.WalletScreenStateTransformer +import com.tangem.features.managetokens.featuretoggles.ManageTokensFeatureToggles import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -23,9 +24,12 @@ import javax.inject.Singleton [REDACTED_AUTHOR] */ @Singleton -internal class WalletStateController @Inject constructor() { +internal class WalletStateController @Inject constructor( + private val manageTokensFeatureToggles: ManageTokensFeatureToggles, +) { val uiState: StateFlow get() = mutableUiState + val value: WalletScreenState get() = uiState.value private val mutableUiState: MutableStateFlow = MutableStateFlow(value = getInitialState()) @@ -81,6 +85,7 @@ internal class WalletStateController @Inject constructor() { onWalletChange = {}, event = consumedEvent(), isHidingMode = false, + manageTokenRedesignToggle = manageTokensFeatureToggles.isRedesignedScreenEnabled, ) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletScreenState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletScreenState.kt index 9285575d9e..b4a5a8dc25 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletScreenState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletScreenState.kt @@ -11,4 +11,5 @@ internal data class WalletScreenState( val onWalletChange: (Int) -> Unit, val event: StateEvent, val isHidingMode: Boolean, + val manageTokenRedesignToggle: Boolean, ) \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index e491dcb364..8341a0792c 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -1,6 +1,7 @@ package com.tangem.feature.wallet.presentation.wallet.ui import androidx.activity.compose.BackHandler +import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListScope @@ -9,20 +10,26 @@ import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.pullrefresh.pullRefresh import androidx.compose.material.pullrefresh.rememberPullRefreshState -import androidx.compose.material3.FabPosition -import androidx.compose.material3.Scaffold -import androidx.compose.material3.SnackbarHost -import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.Dp import androidx.paging.compose.collectAsLazyPagingItems +import com.google.accompanist.systemuicontroller.rememberSystemUiController +import com.tangem.core.ui.components.Keyboard import com.tangem.core.ui.components.PrimaryButton +import com.tangem.core.ui.components.atoms.Hand +import com.tangem.core.ui.components.atoms.handComposableComponentHeight import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheet import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheetConfig import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheet import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheetConfig +import com.tangem.core.ui.components.keyboardAsState import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.impl.R @@ -40,9 +47,14 @@ import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.balances import com.tangem.feature.wallet.presentation.wallet.ui.components.visa.depositButton import com.tangem.feature.wallet.presentation.wallet.ui.utils.changeWalletAnimator import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.launch @Composable -internal fun WalletScreen(state: WalletScreenState) { +internal fun WalletScreen( + state: WalletScreenState, + bottomSheetHeaderHeightProvider: () -> Dp, + bottomSheetContent: @Composable () -> Unit, +) { BackHandler(onBack = state.onBackClick) // It means that screen is still initializing @@ -58,6 +70,8 @@ internal fun WalletScreen(state: WalletScreenState) { snackbarHostState = snackbarHostState, isAutoScroll = isAutoScroll, onAutoScrollReset = { isAutoScroll.value = false }, + bottomSheetHeaderHeightProvider = bottomSheetHeaderHeightProvider, + bottomSheetContent = bottomSheetContent, ) var alertConfig by remember { mutableStateOf(value = null) } @@ -76,19 +90,21 @@ internal fun WalletScreen(state: WalletScreenState) { ) } -@Suppress("LongMethod") +@Suppress("LongMethod", "LongParameterList") @Composable private fun WalletContent( state: WalletScreenState, walletsListState: LazyListState, snackbarHostState: SnackbarHostState, isAutoScroll: State, + bottomSheetHeaderHeightProvider: () -> Dp, onAutoScrollReset: () -> Unit, + bottomSheetContent: @Composable () -> Unit, ) { var selectedWalletIndex by remember { mutableIntStateOf(state.selectedWalletIndex) } val selectedWallet = state.wallets[selectedWalletIndex] - BaseScaffold(state = state, selectedWallet = selectedWallet, snackbarHostState = snackbarHostState) { + val scaffoldContent: @Composable () -> Unit = { val movableItemModifier = Modifier.changeWalletAnimator(walletsListState) val lazyTxHistoryItems = (selectedWallet as? TxHistoryStateHolder)?.let { walletState -> @@ -183,6 +199,130 @@ private fun WalletContent( onAutoScrollReset = onAutoScrollReset, ) } + + if (state.manageTokenRedesignToggle) { + BaseScaffoldManageTokenRedesign( + state = state, + selectedWallet = selectedWallet, + snackbarHostState = snackbarHostState, + bottomSheetHeaderHeightProvider = bottomSheetHeaderHeightProvider, + bottomSheetContent = bottomSheetContent, + ) { + scaffoldContent() + } + } else { + BaseScaffold( + state = state, + selectedWallet = selectedWallet, + snackbarHostState = snackbarHostState, + ) { + scaffoldContent() + } + } +} + +@Suppress("LongParameterList", "LongMethod") +@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class) +@Composable +private fun BaseScaffoldManageTokenRedesign( + state: WalletScreenState, + selectedWallet: WalletState, + snackbarHostState: SnackbarHostState, + bottomSheetHeaderHeightProvider: () -> Dp, + bottomSheetContent: @Composable () -> Unit, + content: @Composable () -> Unit, +) { + val scaffoldState = rememberBottomSheetScaffoldState() + val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() } + val statusBarHeight = with(LocalDensity.current) { WindowInsets.statusBars.getTop(this).toDp() } + val systemUiController = rememberSystemUiController() + val navigationBarColor = TangemTheme.colors.background.primary + val navigationBarColorWithout = TangemTheme.colors.background.secondary + + DisposableEffect( + navigationBarColor, + navigationBarColorWithout, + ) { + systemUiController.setNavigationBarColor(navigationBarColor) + onDispose { + systemUiController.setNavigationBarColor(navigationBarColorWithout) + } + } + + val keyboardShown by keyboardAsState() + // expand bottom sheet when keyboard appears + LaunchedEffect(keyboardShown is Keyboard.Opened) { + if (keyboardShown is Keyboard.Opened) { + scaffoldState.bottomSheetState.expand() + } + } + + val keyboardController = LocalSoftwareKeyboardController.current + val sheetHasBeenHidden = scaffoldState.bottomSheetState.targetValue == SheetValue.PartiallyExpanded + // hide keyboard when bottom sheet is about to be hidden + LaunchedEffect(sheetHasBeenHidden) { + if (sheetHasBeenHidden) { + keyboardController?.hide() + } + } + + val peekHeight = bottomSheetHeaderHeightProvider() + handComposableComponentHeight + bottomBarHeight + val coroutineScope = rememberCoroutineScope() + + BottomSheetScaffold( + topBar = { + WalletTopBar(config = state.topBarConfig) + }, + snackbarHost = { + SnackbarHost(hostState = snackbarHostState) + }, + containerColor = TangemTheme.colors.background.secondary, + sheetContainerColor = TangemTheme.colors.background.primary, + scaffoldState = scaffoldState, + sheetPeekHeight = peekHeight, + sheetDragHandle = { + Hand(modifier = Modifier.background(color = TangemTheme.colors.background.primary)) + }, + sheetContent = { + BoxWithConstraints { + Box( + modifier = Modifier + .sizeIn(maxHeight = maxHeight - statusBarHeight) + .align(Alignment.BottomCenter), + ) { + bottomSheetContent() + } + } + + // hide bottom sheet when back pressed + BackHandler( + keyboardShown is Keyboard.Closed && + scaffoldState.bottomSheetState.currentValue == SheetValue.Expanded, + ) { + coroutineScope.launch { scaffoldState.bottomSheetState.partialExpand() } + } + }, + content = { paddingValues -> + val pullRefreshState = rememberPullRefreshState( + refreshing = selectedWallet.pullToRefreshConfig.isRefreshing, + onRefresh = selectedWallet.pullToRefreshConfig.onRefresh, + ) + + Box( + modifier = Modifier + .pullRefresh(pullRefreshState) + .padding(paddingValues), + ) { + content() + + WalletPullToRefreshIndicator( + isRefreshing = selectedWallet.pullToRefreshConfig.isRefreshing, + state = pullRefreshState, + modifier = Modifier.align(Alignment.TopCenter), + ) + } + }, + ) } @OptIn(ExperimentalMaterialApi::class) From 2e4d60e72c07ccd4331250fe35d610593b602eb0 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 13 Feb 2024 18:53:29 +0300 Subject: [PATCH 36/53] Updated on 2026-08-14 --- app/src/main/java/com/tangem/tap/common/text/Truncate.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/common/text/Truncate.kt b/app/src/main/java/com/tangem/tap/common/text/Truncate.kt index 64aeea2d59..9131ec1023 100644 --- a/app/src/main/java/com/tangem/tap/common/text/Truncate.kt +++ b/app/src/main/java/com/tangem/tap/common/text/Truncate.kt @@ -77,7 +77,7 @@ class TruncateStart : BaseTruncate() { class TruncateMiddle : BaseTruncate() { override fun roughTruncate(text: String, residualLength: Int): String { - if (text.length <= residualLength) return text + if (text.length <= residualLength || residualLength < 0) return text hasBeenTruncated = true val halfOfResidualLength = residualLength / 2 From 8da3529141ca77c2735201fd05141d59b4cf3f88 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 13 Feb 2024 18:56:17 +0300 Subject: [PATCH 37/53] Updated on 2026-08-14 --- .../wallet/domain/WalletWithFundsChecker.kt | 6 +-- .../implementors/MultiWalletContentLoader.kt | 3 ++ .../MultiWalletContentLoaderFactory.kt | 3 ++ .../subscribers/BasicTokenListSubscriber.kt | 44 ++++++++++++------- .../MultiWalletTokenListSubscriber.kt | 28 ++++++++++++ 5 files changed, 64 insertions(+), 20 deletions(-) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/WalletWithFundsChecker.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/WalletWithFundsChecker.kt index 3426a96ae1..bec1af9947 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/WalletWithFundsChecker.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/WalletWithFundsChecker.kt @@ -1,9 +1,7 @@ package com.tangem.feature.wallet.presentation.wallet.domain -import arrow.core.Either import com.tangem.common.extensions.isZero import com.tangem.domain.settings.SetWalletWithFundsFoundUseCase -import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.NetworkGroup import com.tangem.domain.tokens.model.TokenList @@ -13,9 +11,7 @@ internal class WalletWithFundsChecker @Inject constructor( private val setWalletWithFundsFoundUseCase: SetWalletWithFundsFoundUseCase, ) { - suspend fun check(maybeTokenList: Either) { - val tokenList = (maybeTokenList as? Either.Right)?.value ?: return - + suspend fun check(tokenList: TokenList) { val hasNonZeroWallets = when (tokenList) { is TokenList.GroupedByNetwork -> { tokenList.groups diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoader.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoader.kt index 91b6a97137..b12083df95 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoader.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoader.kt @@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.redux.ReduxStateHolder +import com.tangem.domain.tokens.ApplyTokenListSortingUseCase import com.tangem.domain.tokens.GetTokenListUseCase import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender @@ -25,6 +26,7 @@ internal class MultiWalletContentLoader( private val walletWithFundsChecker: WalletWithFundsChecker, private val getTokenListUseCase: GetTokenListUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, + private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase, private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory, private val reduxStateHolder: ReduxStateHolder, ) : WalletContentLoader(id = userWallet.walletId) { @@ -39,6 +41,7 @@ internal class MultiWalletContentLoader( walletWithFundsChecker = walletWithFundsChecker, getTokenListUseCase = getTokenListUseCase, getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase, + applyTokenListSortingUseCase = applyTokenListSortingUseCase, ), MultiWalletWarningsSubscriber( userWalletId = userWallet.walletId, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoaderFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoaderFactory.kt index a434e73c58..794574309a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoaderFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoaderFactory.kt @@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.redux.ReduxStateHolder +import com.tangem.domain.tokens.ApplyTokenListSortingUseCase import com.tangem.domain.tokens.GetTokenListUseCase import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender @@ -22,6 +23,7 @@ internal class MultiWalletContentLoaderFactory @Inject constructor( private val getMultiWalletWarningsFactory: GetMultiWalletWarningsFactory, private val getTokenListUseCase: GetTokenListUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, + private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase, private val reduxStateHolder: ReduxStateHolder, private val walletWarningsAnalyticsSender: WalletWarningsAnalyticsSender, ) { @@ -38,6 +40,7 @@ internal class MultiWalletContentLoaderFactory @Inject constructor( getMultiWalletWarningsFactory = getMultiWalletWarningsFactory, reduxStateHolder = reduxStateHolder, walletWarningsAnalyticsSender = walletWarningsAnalyticsSender, + applyTokenListSortingUseCase = applyTokenListSortingUseCase, ) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt index a1f88b718d..d9cc56d227 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/BasicTokenListSubscriber.kt @@ -18,6 +18,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.onEach +import timber.log.Timber internal typealias MaybeTokenListFlow = Flow> @@ -33,35 +34,48 @@ internal abstract class BasicTokenListSubscriber( protected abstract fun tokenListFlow(): MaybeTokenListFlow + protected open suspend fun onTokenListReceived(tokenList: TokenList) { /* no-op */ + } + override fun create(coroutineScope: CoroutineScope): Flow<*> { return combine( flow = tokenListFlow() - .onEach { + .onEach { maybeTokenList -> val displayedState = stateHolder.getWalletStateIfSelected(userWallet.walletId) - tokenListAnalyticsSender.send(displayedState, userWallet, it.getOrElse { return@onEach }) + tokenListAnalyticsSender.send( + displayedUiState = displayedState, + userWallet = userWallet, + tokenList = maybeTokenList.getOrElse { return@onEach }, + ) } .distinctUntilChanged(), flow2 = getSelectedAppCurrencyUseCase().distinctUntilChanged(), transform = { maybeTokenList, maybeAppCurrency -> - updateContent(maybeTokenList, maybeAppCurrency.getOrElse { AppCurrency.Default }) - walletWithFundsChecker.check(maybeTokenList) + val tokenList = maybeTokenList.getOrElse { e -> + Timber.e("Failed to load token list: $e") + SetTokenListErrorTransformer(userWallet.walletId, e) + return@combine + } + val appCurrency = maybeAppCurrency.getOrElse { e -> + Timber.e("Failed to load app currency: $e") + AppCurrency.Default + } + + updateContent(tokenList, appCurrency) + walletWithFundsChecker.check(tokenList) + onTokenListReceived(tokenList) }, ) } - private fun updateContent(maybeTokenList: Either, appCurrency: AppCurrency) { + private fun updateContent(tokenList: TokenList, appCurrency: AppCurrency) { stateHolder.update( - maybeTokenList.fold( - ifLeft = { SetTokenListErrorTransformer(userWalletId = userWallet.walletId, error = it) }, - ifRight = { - SetTokenListTransformer( - tokenList = it, - userWallet = userWallet, - appCurrency = appCurrency, - clickIntents = clickIntents, - ) - }, + SetTokenListTransformer( + tokenList = tokenList, + userWallet = userWallet, + appCurrency = appCurrency, + clickIntents = clickIntents, ), ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletTokenListSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletTokenListSubscriber.kt index cb5fdbc166..8eb0af9ab2 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletTokenListSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/MultiWalletTokenListSubscriber.kt @@ -1,7 +1,9 @@ package com.tangem.feature.wallet.presentation.wallet.subscribers import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase +import com.tangem.domain.tokens.ApplyTokenListSortingUseCase import com.tangem.domain.tokens.GetTokenListUseCase +import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender import com.tangem.feature.wallet.presentation.wallet.domain.WalletWithFundsChecker @@ -12,6 +14,7 @@ import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletCl internal class MultiWalletTokenListSubscriber( private val userWallet: UserWallet, private val getTokenListUseCase: GetTokenListUseCase, + private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase, stateHolder: WalletStateController, clickIntents: WalletClickIntents, tokenListAnalyticsSender: TokenListAnalyticsSender, @@ -27,4 +30,29 @@ internal class MultiWalletTokenListSubscriber( ) { override fun tokenListFlow(): MaybeTokenListFlow = getTokenListUseCase(userWallet.walletId) + + override suspend fun onTokenListReceived(tokenList: TokenList) { + updateSortingIfNeeded(tokenList) + } + + private suspend fun updateSortingIfNeeded(tokenList: TokenList) { + if (tokenList.totalFiatBalance is TokenList.FiatBalance.Loading || + tokenList.sortedBy == TokenList.SortType.NONE + ) { + return + } + + applyTokenListSortingUseCase( + userWalletId = userWallet.walletId, + sortedTokensIds = when (tokenList) { + is TokenList.GroupedByNetwork -> tokenList.groups.flatMap { group -> + group.currencies.map { it.currency.id } + } + is TokenList.Ungrouped -> tokenList.currencies.map { it.currency.id } + is TokenList.Empty -> return + }, + isGroupedByNetwork = tokenList is TokenList.GroupedByNetwork, + isSortedByBalance = true, + ) + } } \ No newline at end of file From ac9e0ac47bdcae00027d04584ad8ff81bf4a88fb Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 14 Feb 2024 13:18:44 +0300 Subject: [PATCH 38/53] Updated on 2026-08-14 --- .../transaction/error/SendTransactionError.kt | 2 + .../usecase/SendTransactionUseCase.kt | 6 +++ .../impl/presentation/state/SendAlertState.kt | 7 +++ .../state/SendNotificationFactory.kt | 43 +++++++++---------- .../state/SendTransactionAlertConverter.kt | 1 + .../fields/SendAmountFieldChangeConverter.kt | 8 ++-- 6 files changed, 42 insertions(+), 25 deletions(-) diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/error/SendTransactionError.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/error/SendTransactionError.kt index 2d5536cefb..753f45e4b4 100644 --- a/domain/transaction/src/main/java/com/tangem/domain/transaction/error/SendTransactionError.kt +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/error/SendTransactionError.kt @@ -14,6 +14,8 @@ sealed class SendTransactionError { object UserCancelledError : SendTransactionError() + data class CreateAccountUnderfunded(val amount: String) : SendTransactionError() + data class TangemSdkError(val code: Int, val messageReference: TextReference) : SendTransactionError() data class UnknownError(val ex: Exception? = null) : SendTransactionError() diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/SendTransactionUseCase.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/SendTransactionUseCase.kt index c8ed1fe905..059a672c70 100644 --- a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/SendTransactionUseCase.kt +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/SendTransactionUseCase.kt @@ -20,6 +20,7 @@ import com.tangem.domain.transaction.error.SendTransactionError import com.tangem.domain.transaction.error.SendTransactionError.Companion.USER_CANCELLED_ERROR_CODE import com.tangem.domain.wallets.models.UserWallet import com.tangem.sdk.extensions.localizedDescriptionRes +import com.tangem.utils.toFormattedString class SendTransactionUseCase( private val isDemoCardUseCase: IsDemoCardUseCase, @@ -85,6 +86,11 @@ class SendTransactionUseCase( } } } + is BlockchainSdkError.CreateAccountUnderfunded -> { + val minAmount = error.minReserve + val minValue = minAmount.value?.toFormattedString(minAmount.decimals).orEmpty() + SendTransactionError.CreateAccountUnderfunded(minValue) + } else -> { SendTransactionError.BlockchainSdkError( code = error.code, diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendAlertState.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendAlertState.kt index 54eb2f7868..672ceb39d5 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendAlertState.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendAlertState.kt @@ -49,4 +49,11 @@ internal sealed class SendAlertState { override val title: TextReference? = null override val message: TextReference = resourceReference(id = R.string.send_notification_high_fee_title) } + + data class ReserveAmount(val amount: String) : SendAlertState() { + override val title: TextReference = + resourceReference(id = R.string.send_notification_invalid_reserve_amount_title, wrappedList(amount)) + override val message: TextReference = + resourceReference(id = R.string.send_notification_invalid_reserve_amount_text) + } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendNotificationFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendNotificationFactory.kt index 8b3051e761..05d56ca70b 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendNotificationFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendNotificationFactory.kt @@ -31,7 +31,6 @@ internal class SendNotificationFactory( .map { val state = currentStateProvider() val feeState = state.feeState ?: return@map persistentListOf() - val recipientState = state.recipientState ?: return@map persistentListOf() val feeAmount = feeState.fee?.amount?.value ?: BigDecimal.ZERO val amountValue = state.amountState?.amountTextField?.value?.toBigDecimalOrNull() ?: BigDecimal.ZERO val sendAmount = if (feeState.isSubtract) feeState.receivedAmountValue else amountValue @@ -41,7 +40,6 @@ internal class SendNotificationFactory( addInvalidAmountNotification(feeState.isSubtract, sendAmount) addMinimumAmountErrorNotification(feeAmount, sendAmount) addDustWarningNotification(feeAmount, sendAmount) - addReserveAmountErrorNotification(recipientState.addressTextField.value) addTransactionLimitErrorNotification(feeAmount, sendAmount) // warnings addExistentialWarningNotification(feeAmount, sendAmount) @@ -116,26 +114,27 @@ internal class SendNotificationFactory( } } - private suspend fun MutableList.addReserveAmountErrorNotification(recipientAddress: String) { - val userWalletId = userWalletProvider().walletId - val cryptoCurrency = cryptoCurrencyStatusProvider().currency - val isAccountFunded = currencyChecksRepository.checkIfAccountFunded( - userWalletId, - cryptoCurrency.network, - recipientAddress, - ) - val minimumAmount = currencyChecksRepository.getReserveAmount(userWalletId, cryptoCurrency.network) - if (!isAccountFunded && minimumAmount != null && minimumAmount > BigDecimal.ZERO) { - add( - SendNotification.Error.ReserveAmountError( - BigDecimalFormatter.formatCryptoAmount( - cryptoAmount = minimumAmount, - cryptoCurrency = cryptoCurrency, - ), - ), - ) - } - } + // todo temporarily disabling notification + // private suspend fun MutableList.addReserveAmountErrorNotification(recipientAddress: String) { + // val userWalletId = userWalletProvider().walletId + // val cryptoCurrency = cryptoCurrencyStatusProvider().currency + // val isAccountFunded = currencyChecksRepository.checkIfAccountFunded( + // userWalletId, + // cryptoCurrency.network, + // recipientAddress, + // ) + // val minimumAmount = currencyChecksRepository.getReserveAmount(userWalletId, cryptoCurrency.network) + // if (!isAccountFunded && minimumAmount != null && minimumAmount > BigDecimal.ZERO) { + // add( + // SendNotification.Error.ReserveAmountError( + // BigDecimalFormatter.formatCryptoAmount( + // cryptoAmount = minimumAmount, + // cryptoCurrency = cryptoCurrency, + // ), + // ), + // ) + // } + // } private suspend fun MutableList.addTransactionLimitErrorNotification( feeAmount: BigDecimal, diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendTransactionAlertConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendTransactionAlertConverter.kt index 14f9cc265d..a35b421a16 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendTransactionAlertConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendTransactionAlertConverter.kt @@ -38,6 +38,7 @@ internal class SendTransactionAlertConverter( cause = value.ex?.localizedMessage, onConfirmClick = { clickIntents.onFailedTxEmailClick(value.ex?.localizedMessage.orEmpty()) }, ) + is SendTransactionError.CreateAccountUnderfunded -> SendAlertState.ReserveAmount(value.amount) else -> null } } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldChangeConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldChangeConverter.kt index 7a6c696c1a..b9bfa076bd 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldChangeConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldChangeConverter.kt @@ -1,5 +1,6 @@ package com.tangem.features.send.impl.presentation.state.fields +import com.tangem.common.extensions.isZero import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.core.ui.utils.parseToBigDecimal import com.tangem.domain.tokens.model.CryptoCurrency @@ -21,7 +22,6 @@ internal class SendAmountFieldChangeConverter( val feeState = state.feeState ?: return state if (value.isEmpty()) return state.emptyState() - val cryptoDecimals = amountTextField.cryptoAmount.decimals val fiatDecimals = amountTextField.fiatAmount.decimals @@ -89,10 +89,12 @@ internal class SendAmountFieldChangeConverter( val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() val currencyCryptoAmount = cryptoCurrencyStatus.value.amount ?: BigDecimal.ZERO val currencyFiatAmount = cryptoCurrencyStatus.value.fiatAmount ?: BigDecimal.ZERO + val fiatDecimal = parseToBigDecimal(amountTextField.fiatAmount.decimals) + val cryptoDecimal = parseToBigDecimal(amountTextField.cryptoAmount.decimals) return if (amountTextField.isFiatValue) { - parseToBigDecimal(amountTextField.fiatAmount.decimals) > currencyFiatAmount + fiatDecimal > currencyFiatAmount || fiatDecimal.isZero() } else { - parseToBigDecimal(amountTextField.cryptoAmount.decimals) > currencyCryptoAmount + cryptoDecimal > currencyCryptoAmount || cryptoDecimal.isZero() } } From 472e490b33595c0d7324279936ddb0077b6accc2 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 14 Feb 2024 13:24:05 +0300 Subject: [PATCH 39/53] Updated on 2026-08-14 --- gradle/dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index e291150114..60cac6a5d7 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -85,7 +85,7 @@ web3j = "4.10.1" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-485" +tangemBlockchainSdk = "develop-487" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-324" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From 3e7825b2e38348a09e68ce4a462234165ed30726 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 15 Feb 2024 12:28:34 +0500 Subject: [PATCH 40/53] Updated on 2026-08-14 --- .../txhistory/DefaultTxHistoryItemsStore.kt | 19 ++----- .../local/txhistory/TxHistoryItemsStore.kt | 5 +- .../paging/TxHistoryPagingSource.kt | 49 +++++++------------ .../DefaultWalletManagersFacade.kt | 12 +++-- .../walletmanager/WalletManagersFacade.kt | 3 +- .../walletmanager/utils/SdkPageConverter.kt | 23 +++++++++ .../tangem/domain/txhistory/models/Page.kt | 7 +++ .../txhistory/models/PaginationWrapper.kt | 5 +- gradle/dependencies.toml | 2 +- 9 files changed, 66 insertions(+), 59 deletions(-) create mode 100644 domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkPageConverter.kt create mode 100644 domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/Page.kt diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/txhistory/DefaultTxHistoryItemsStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/txhistory/DefaultTxHistoryItemsStore.kt index 7cbeb3f317..c992faf5bd 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/txhistory/DefaultTxHistoryItemsStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/txhistory/DefaultTxHistoryItemsStore.kt @@ -2,6 +2,7 @@ package com.tangem.datasource.local.txhistory import com.tangem.datasource.local.datastore.core.StringKeyDataStore import com.tangem.datasource.local.datastore.core.StringKeyDataStoreDecorator +import com.tangem.domain.txhistory.models.Page import com.tangem.domain.txhistory.models.PaginationWrapper import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.utils.extensions.addOrReplace @@ -13,28 +14,16 @@ internal class DefaultTxHistoryItemsStore( override fun provideStringKey(key: TxHistoryItemsStore.Key): String = key.toString() - override suspend fun getNextPageSyncOrNull(key: TxHistoryItemsStore.Key): Int? { - val storedValue = getSyncOrNull(key) ?: return null - val lastWrappedItems = storedValue.maxBy(PaginationWrapper<*>::page) - val lastPage = lastWrappedItems.page - - return if (lastPage <= lastWrappedItems.totalPages) { - lastPage - } else { - null - } - } - - override suspend fun getSyncOrNull(key: TxHistoryItemsStore.Key, page: Int): PaginationWrapper? { + override suspend fun getSyncOrNull(key: TxHistoryItemsStore.Key, page: Page): PaginationWrapper? { val storedValue = getSyncOrNull(key) - return storedValue?.firstOrNull { it.page == page } + return storedValue?.firstOrNull { it.currentPage == page } } override suspend fun store(key: TxHistoryItemsStore.Key, value: PaginationWrapper) { val oldValue = getSyncOrNull(key).orEmpty() val newValue = oldValue.addOrReplace(value) { - it.page == value.page + it.currentPage == value.currentPage } store(key, newValue) diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/txhistory/TxHistoryItemsStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/txhistory/TxHistoryItemsStore.kt index e2f288e235..b49bc4fdd4 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/txhistory/TxHistoryItemsStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/txhistory/TxHistoryItemsStore.kt @@ -1,15 +1,14 @@ package com.tangem.datasource.local.txhistory import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.txhistory.models.Page import com.tangem.domain.txhistory.models.PaginationWrapper import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.wallets.models.UserWalletId interface TxHistoryItemsStore { - suspend fun getNextPageSyncOrNull(key: Key): Int? - - suspend fun getSyncOrNull(key: Key, page: Int): PaginationWrapper? + suspend fun getSyncOrNull(key: Key, page: Page): PaginationWrapper? suspend fun remove(key: Key) diff --git a/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/paging/TxHistoryPagingSource.kt b/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/paging/TxHistoryPagingSource.kt index 5452c7c385..2709c26dfc 100644 --- a/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/paging/TxHistoryPagingSource.kt +++ b/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/paging/TxHistoryPagingSource.kt @@ -7,9 +7,11 @@ import com.tangem.data.common.cache.CacheRegistry import com.tangem.datasource.local.txhistory.TxHistoryItemsStore import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.txhistory.models.Page import com.tangem.domain.txhistory.models.PaginationWrapper import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.walletmanager.WalletManagersFacade +import com.tangem.domain.walletmanager.utils.SdkPageConverter import com.tangem.domain.wallets.models.UserWalletId import timber.log.Timber @@ -18,21 +20,19 @@ internal class TxHistoryPagingSource( private val txHistoryItemsStore: TxHistoryItemsStore, private val walletManagersFacade: WalletManagersFacade, private val cacheRegistry: CacheRegistry, -) : PagingSource() { +) : PagingSource() { private val storeKey = TxHistoryItemsStore.Key(sourceParams.userWalletId, sourceParams.currency) + private val sdkPageConverter by lazy { SdkPageConverter() } override val keyReuseSupported: Boolean get() = true - override fun getRefreshKey(state: PagingState): Int? { - return state.anchorPosition?.let { anchorPosition -> - val anchorPage = state.closestPageToPosition(anchorPosition) - anchorPage?.prevKey?.inc() ?: anchorPage?.nextKey?.dec() - } + override fun getRefreshKey(state: PagingState): Page? { + return null } - override suspend fun load(params: LoadParams): LoadResult { - val pageToLoad = params.key ?: INITIAL_PAGE + override suspend fun load(params: LoadParams): LoadResult { + val pageToLoad = params.key ?: Page.Initial return try { val wrappedItems = loadItems( @@ -40,20 +40,11 @@ internal class TxHistoryPagingSource( pageSize = sourceParams.pageSize, refresh = sourceParams.refresh && params is LoadParams.Refresh, ) - - val items = wrappedItems.items - val prevPage = when { - items.isEmpty() -> null - pageToLoad > INITIAL_PAGE -> pageToLoad.dec() - else -> null + val nextKey = when (wrappedItems.nextPage) { + Page.LastPage -> null + Page.Initial, is Page.Next -> wrappedItems.nextPage } - val nextPage = when { - items.isEmpty() -> INITIAL_PAGE - pageToLoad < wrappedItems.totalPages -> pageToLoad.inc() - else -> null - } - - LoadResult.Page(items, prevKey = prevPage, nextKey = nextPage) + LoadResult.Page(wrappedItems.items, prevKey = null, nextKey = nextKey) } catch (e: Throwable) { Timber.e(e, "Unable to load the transaction history for the requested page: $pageToLoad") @@ -61,7 +52,7 @@ internal class TxHistoryPagingSource( } } - private suspend fun loadItems(pageToLoad: Int, pageSize: Int, refresh: Boolean): PaginationWrapper { + private suspend fun loadItems(pageToLoad: Page, pageSize: Int, refresh: Boolean): PaginationWrapper { cacheRegistry.invokeOnExpire( key = getTxHistoryPageKey(pageToLoad), skipCache = refresh, @@ -71,23 +62,23 @@ internal class TxHistoryPagingSource( return txHistoryItemsStore.getSync(pageToLoad) } - private suspend fun fetch(pageToLoad: Int, pageSize: Int) { + private suspend fun fetch(pageToLoad: Page, pageSize: Int) { val wrappedItems = walletManagersFacade.getTxHistoryItems( userWalletId = sourceParams.userWalletId, currency = sourceParams.currency, - page = pageToLoad, + page = sdkPageConverter.convertBack(pageToLoad), pageSize = pageSize, ) txHistoryItemsStore.store(key = storeKey, value = wrappedItems) } - private suspend fun TxHistoryItemsStore.getSync(pageToLoad: Int): PaginationWrapper { + private suspend fun TxHistoryItemsStore.getSync(pageToLoad: Page): PaginationWrapper { val storedItems = requireNotNull(getSyncOrNull(storeKey, pageToLoad)) { "The transaction history page #$pageToLoad could not be retrieved" } - return if (pageToLoad == 1) storedItems.addRecentTransactions() else storedItems + return if (pageToLoad is Page.Initial) storedItems.addRecentTransactions() else storedItems } private suspend fun PaginationWrapper.addRecentTransactions(): PaginationWrapper { @@ -131,7 +122,7 @@ internal class TxHistoryPagingSource( return filter { item -> apiItems.none { it.txHash == item.txHash } } } - private fun getTxHistoryPageKey(page: Int): String { + private fun getTxHistoryPageKey(page: Page): String { return "tx_history_page_${sourceParams.currency}_${sourceParams.userWalletId}_$page" } @@ -141,8 +132,4 @@ internal class TxHistoryPagingSource( val pageSize: Int, val refresh: Boolean, ) - - private companion object { - private const val INITIAL_PAGE = 1 - } } \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt index c5792124c9..8895459bc1 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt @@ -11,6 +11,7 @@ import com.tangem.blockchain.common.address.Address import com.tangem.blockchain.common.address.AddressType import com.tangem.blockchain.common.address.EstimationFeeAddressFactory import com.tangem.blockchain.common.datastorage.BlockchainDataStorage +import com.tangem.blockchain.common.pagination.Page import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.blockchain.common.txhistory.TransactionHistoryRequest @@ -59,6 +60,7 @@ class DefaultWalletManagersFacade( private val sdkTokenConverter by lazy { SdkTokenConverter() } private val txHistoryStateConverter by lazy { SdkTransactionHistoryStateConverter() } private val txHistoryItemConverter by lazy { SdkTransactionHistoryItemConverter(assetReader, moshi) } + private val sdkPageConverter by lazy { SdkPageConverter() } private val estimationFeeAddressFactory by lazy { EstimationFeeAddressFactory(mnemonic) } override suspend fun update( @@ -187,7 +189,7 @@ class DefaultWalletManagersFacade( override suspend fun getTxHistoryItems( userWalletId: UserWalletId, currency: CryptoCurrency, - page: Int, + page: Page, pageSize: Int, ): PaginationWrapper { val walletManager = getOrCreateWalletManager( @@ -203,7 +205,8 @@ class DefaultWalletManagersFacade( request = TransactionHistoryRequest( address = walletManager.wallet.address, decimals = currency.decimals, - page = TransactionHistoryRequest.Page(number = page, size = pageSize), + page = page, + pageSize = pageSize, filterType = when (currency) { is CryptoCurrency.Coin -> TransactionHistoryRequest.FilterType.Coin is CryptoCurrency.Token -> TransactionHistoryRequest.FilterType.Contract(currency.contractAddress) @@ -213,9 +216,8 @@ class DefaultWalletManagersFacade( return when (itemsResult) { is Result.Success -> PaginationWrapper( - page = itemsResult.data.page, - totalPages = itemsResult.data.totalPages, - itemsOnPage = itemsResult.data.itemsOnPage, + currentPage = sdkPageConverter.convert(page), + nextPage = sdkPageConverter.convert(itemsResult.data.nextPage), items = txHistoryItemConverter.convertList(itemsResult.data.items), ) is Result.Failure -> error(itemsResult.error.message ?: itemsResult.error.customMessage) diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt index 684655b8a2..543ef9006e 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt @@ -5,6 +5,7 @@ import com.tangem.blockchain.blockchains.solana.RentProvider import com.tangem.blockchain.common.* import com.tangem.blockchain.common.address.Address import com.tangem.blockchain.common.address.AddressType +import com.tangem.blockchain.common.pagination.Page import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.blockchain.extensions.Result @@ -97,7 +98,7 @@ interface WalletManagersFacade { suspend fun getTxHistoryItems( userWalletId: UserWalletId, currency: CryptoCurrency, - page: Int, + page: Page, pageSize: Int, ): PaginationWrapper diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkPageConverter.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkPageConverter.kt new file mode 100644 index 0000000000..f39c3bfd79 --- /dev/null +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkPageConverter.kt @@ -0,0 +1,23 @@ +package com.tangem.domain.walletmanager.utils + +import com.tangem.domain.txhistory.models.Page +import com.tangem.utils.converter.TwoWayConverter +import com.tangem.blockchain.common.pagination.Page as SdkPage + +class SdkPageConverter : TwoWayConverter { + override fun convert(value: SdkPage): Page { + return when (value) { + is SdkPage.Initial -> Page.Initial + is SdkPage.LastPage -> Page.LastPage + is SdkPage.Next -> Page.Next(value = value.value) + } + } + + override fun convertBack(value: Page): SdkPage { + return when (value) { + is Page.Initial -> SdkPage.Initial + is Page.LastPage -> SdkPage.LastPage + is Page.Next -> SdkPage.Next(value = value.value) + } + } +} \ No newline at end of file diff --git a/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/Page.kt b/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/Page.kt new file mode 100644 index 0000000000..9e18e15403 --- /dev/null +++ b/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/Page.kt @@ -0,0 +1,7 @@ +package com.tangem.domain.txhistory.models + +sealed class Page { + object Initial : Page() + data class Next(val value: String) : Page() + object LastPage : Page() +} \ No newline at end of file diff --git a/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/PaginationWrapper.kt b/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/PaginationWrapper.kt index 92ea34de36..71cf94bb8b 100644 --- a/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/PaginationWrapper.kt +++ b/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/PaginationWrapper.kt @@ -1,8 +1,7 @@ package com.tangem.domain.txhistory.models data class PaginationWrapper( - val page: Int, - val totalPages: Int, - val itemsOnPage: Int, + val currentPage: Page, + val nextPage: Page, val items: List, ) \ No newline at end of file diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 60cac6a5d7..235009dbc4 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -85,7 +85,7 @@ web3j = "4.10.1" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-487" +tangemBlockchainSdk = "develop-488" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-324" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From eadc650d266cddf234fbc4efab52fad293a6f501 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 15 Feb 2024 10:08:56 +0100 Subject: [PATCH 41/53] Updated on 2026-08-14 --- core/res/src/main/res/values-ru/strings.xml | 3 + core/res/src/main/res/values/strings.xml | 3 + .../swap/domain/models/ui/SwapState.kt | 4 +- .../feature/swap/models/SwapStateHolder.kt | 3 +- .../tangem/feature/swap/ui/StateBuilder.kt | 24 +++- .../feature/swap/ui/SwapScreenContent.kt | 7 +- .../tangem/feature/swap/ui/TransactionCard.kt | 114 ++++++++++++++---- .../swap/viewmodels/SwapProcessDataState.kt | 2 +- .../feature/swap/viewmodels/SwapViewModel.kt | 6 +- 9 files changed, 133 insertions(+), 33 deletions(-) diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 007f6d05eb..e52b26c31c 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -536,6 +536,9 @@ Дать разрешение Обмен этой суммы выбранных токенов может вызвать значительные колебания цены и уменьшить получаемую сумму. Недостаточно средств + Комиссии + В сумму включено: \n• комиссия провайдера сервиса\n• комиссия сети за отправку %s от биржи обратно на адрес пользователя + В сумму включена комиссия провайдера сервиса. Недостаточно средств для оплаты комиссии на вашем %1$s кошельке для создания транзакции. Сначала пополните свой %2$s кошелек. Транзакция в процессе… Подождите diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 0040f9b3b0..80715ad6f9 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -549,6 +549,9 @@ Give Permission Swapping this amount of selected tokens will cause a significant price impact and reduce your outcome. Insufficient funds + Fees + The amount includes:\n• service provider\'s fee\n• network fee for sending %s from the exchange back to the user\'s address. + The amount includes the service provider\'s fee. Insufficient funds in your %1$s wallet to cover fees. Top up your %2$s wallet first. Transaction in progress... Waiting diff --git a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt index 264f39582b..1fae322ed1 100644 --- a/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt +++ b/features/swap/domain/models/src/main/java/com/tangem/feature/swap/domain/models/ui/SwapState.kt @@ -42,10 +42,12 @@ sealed class PriceImpact { abstract val value: Float - fun getIntPercentValue() = (value * HUNDRED_PERCENTS).toInt() data class Empty(override val value: Float = 0f) : PriceImpact() + data class Value(override val value: Float) : PriceImpact() + fun getIntPercentValue() = (value * HUNDRED_PERCENTS).toInt() + companion object { private const val HUNDRED_PERCENTS = 100 } diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt index 4e77ff7dc8..71df4eb5d3 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt @@ -84,6 +84,7 @@ sealed interface TransactionCardType { ) : TransactionCardType data class ReadOnly( + val showWarning: Boolean = false, val onWarningClick: (() -> Unit)? = null, @StringRes override val headerResId: Int = R.string.swapping_to_title, ) : TransactionCardType @@ -105,12 +106,12 @@ sealed interface SwapWarning { object InsufficientFunds : SwapWarning data class NoAvailableTokensToSwap(val notificationConfig: NotificationConfig) : SwapWarning data class GenericWarning( + val title: TextReference? = null, val message: TextReference? = null, val type: GenericWarningType = GenericWarningType.OTHER, val shouldWrapMessage: Boolean = false, val onClick: () -> Unit, ) : SwapWarning - // data class RateExpired(val onClick: () -> Unit) : SwapWarning data class GeneralError(val notificationConfig: NotificationConfig) : SwapWarning data class UnableToCoverFeeWarning(val notificationConfig: NotificationConfig) : SwapWarning data class GeneralWarning(val notificationConfig: NotificationConfig) : SwapWarning 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 d6a880f213..38aee5e9fb 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 @@ -233,7 +233,10 @@ internal class StateBuilder( isBalanceHidden = isBalanceHiddenProvider(), ), receiveCardData = SwapCardState.SwapCardData( - type = TransactionCardType.ReadOnly(actions.onReceiveCardWarningClick), + type = TransactionCardType.ReadOnly( + showWarning = true, + actions.onReceiveCardWarningClick, + ), amountTextFieldValue = TextFieldValue(quoteModel.toTokenInfo.tokenAmount.formatToUIRepresentation()), amountEquivalent = getFormattedFiatAmount(quoteModel.toTokenInfo.amountFiat), token = toCurrencyStatus, @@ -852,17 +855,30 @@ internal class StateBuilder( ) } - fun createImpactAlert( + fun createAlert( uiState: SwapStateHolder, + isPriceImpact: Boolean, + token: String, providerType: ExchangeProviderType, onAlertClick: () -> Unit, ): SwapStateHolder { val message = when (providerType) { - ExchangeProviderType.CEX -> resourceReference(R.string.express_cex_fee_explanation) - ExchangeProviderType.DEX -> resourceReference(R.string.swapping_high_price_impact_description) + ExchangeProviderType.CEX -> resourceReference(R.string.swapping_alert_cex_description, wrappedList(token)) + ExchangeProviderType.DEX -> { + val refs = buildList { + if (isPriceImpact) { + add(resourceReference(R.string.swapping_high_price_impact_description)) + add(stringReference("\n\n")) + } + add(resourceReference(R.string.swapping_alert_dex_description)) + } + + combinedReference(refs.toWrappedList()) + } } return uiState.copy( alert = SwapWarning.GenericWarning( + title = resourceReference(R.string.swapping_alert_title), message = message, onClick = onAlertClick, type = GenericWarningType.OTHER, diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt index f58b501863..4f2a3fefa1 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt @@ -111,8 +111,13 @@ internal fun SwapScreenContent(state: SwapStateHolder, modifier: Modifier = Modi } else { state.alert.message?.resolveReference() ?: stringResource(id = R.string.common_unknown_error) } - SimpleOkDialog( + BasicDialog( + title = state.alert.title?.resolveReference(), message = message, + confirmButton = DialogButton( + title = stringResource(id = R.string.common_ok), + onClick = state.alert.onClick, + ), onDismissDialog = state.alert.onClick, ) } diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt index 038465b6d3..9e7a5f8b2d 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt @@ -274,29 +274,49 @@ private fun Content( SpacerH8() if (amountEquivalent != null) { - if (type is TransactionCardType.ReadOnly && priceImpact !is PriceImpact.Empty) { + if (type is TransactionCardType.ReadOnly) { Row { - Text( - text = makePriceImpactBalanceWarning(amountEquivalent, priceImpact.getIntPercentValue()), - color = TangemTheme.colors.text.tertiary, - style = TangemTheme.typography.body2, - modifier = Modifier - .defaultMinSize(minHeight = TangemTheme.dimens.size20) - .align(Alignment.CenterVertically), - ) - SpacerW4() - IconButton( - onClick = { - type.onWarningClick?.invoke() - }, - modifier = Modifier.size(size = TangemTheme.dimens.size20), - ) { - Icon( - painter = painterResource(id = R.drawable.ic_alert_24), - contentDescription = null, - tint = TangemTheme.colors.icon.attention, - modifier = Modifier.align(Alignment.CenterVertically), + if (priceImpact is PriceImpact.Value) { + Text( + text = makePriceImpactBalanceWarning( + amountEquivalent, + priceImpact.getIntPercentValue(), + ), + color = TangemTheme.colors.text.tertiary, + style = TangemTheme.typography.body2, + modifier = Modifier + .defaultMinSize(minHeight = TangemTheme.dimens.size20) + .align(Alignment.CenterVertically), ) + } else { + AnimatedContent(targetState = amountEquivalent, label = "") { + Text( + text = it, + color = TangemTheme.colors.text.tertiary, + style = TangemTheme.typography.body2, + modifier = Modifier.defaultMinSize(minHeight = TangemTheme.dimens.size20), + ) + } + } + if (type.showWarning) { + SpacerW4() + IconButton( + onClick = { + type.onWarningClick?.invoke() + }, + modifier = Modifier.size(size = TangemTheme.dimens.size20), + ) { + Icon( + painter = painterResource(id = R.drawable.ic_alert_24), + contentDescription = null, + tint = if (priceImpact is PriceImpact.Value) { + TangemTheme.colors.text.attention + } else { + TangemTheme.colors.text.tertiary + }, + modifier = Modifier.align(Alignment.CenterVertically), + ) + } } } } else { @@ -471,22 +491,52 @@ private fun makePriceImpactBalanceWarning(value: String, priceImpactPercents: In @Preview(widthDp = 328, heightDp = 116, showBackground = true) @Composable -private fun Preview_SwapMainCard_InLightTheme() { +private fun Preview_TransactionCard_InLightTheme() { TangemTheme(isDark = false) { TransactionCardPreview() + } +} + +@Preview(widthDp = 328, heightDp = 116, showBackground = true) +@Composable +private fun Preview_TransactionCardWithPriceImpact_InLightTheme() { + TangemTheme(isDark = false) { TransactionCardPreviewWithPriceImpact() } } @Preview(widthDp = 328, heightDp = 116, showBackground = true) @Composable -private fun Preview_SwapMainCard_InDarkTheme() { - TangemTheme(isDark = true) { +private fun Preview_TransactionCardWithoutPriceImpact_InLightTheme() { + TangemTheme(isDark = false) { + TransactionCardPreviewWithoutPriceImpact() + } +} + +@Preview(widthDp = 328, heightDp = 116, showBackground = true) +@Composable +private fun Preview_TransactionCard_InDarkTheme() { + TangemTheme(isDark = false) { TransactionCardPreview() + } +} + +@Preview(widthDp = 328, heightDp = 116, showBackground = true) +@Composable +private fun Preview_TransactionCardWithPriceImpact_InDarkTheme() { + TangemTheme(isDark = false) { TransactionCardPreviewWithPriceImpact() } } +@Preview(widthDp = 328, heightDp = 116, showBackground = true) +@Composable +private fun Preview_TransactionCardWithoutPriceImpact_InDarkTheme() { + TangemTheme(isDark = false) { + TransactionCardPreviewWithoutPriceImpact() + } +} + @Composable private fun TransactionCardPreview() { TransactionCard( @@ -518,4 +568,20 @@ private fun TransactionCardPreviewWithPriceImpact() { ) } +@Composable +@Suppress("MagicNumber") +private fun TransactionCardPreviewWithoutPriceImpact() { + TransactionCard( + type = TransactionCardType.ReadOnly(), + amountEquivalent = "1 000 000", + tokenIconUrl = "", + tokenCurrency = "DAI", + networkIconRes = R.drawable.img_polygon_22, + onChangeTokenClick = {}, + balance = "123", + textFieldValue = TextFieldValue(), + priceImpact = PriceImpact.Empty(), + ) +} + // endregion preview \ No newline at end of file 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 7623970edf..aada4b7d2b 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 @@ -18,7 +18,7 @@ data class SwapProcessDataState( val approveDataModel: RequestApproveStateData? = null, val approveType: ApproveType? = null, val swapDataModel: SwapDataModel? = null, - val selectedFee: TxFee? = null, // todo + val selectedFee: TxFee? = null, val tokensDataState: TokensDataStateExpress? = null, val selectedProvider: SwapProvider? = null, val lastLoadedSwapStates: Map = emptyMap(), 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 ace4cb9006..52e89386a3 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 @@ -879,8 +879,12 @@ internal class SwapViewModel @Inject constructor( }, onReceiveCardWarningClick = { val selectedProvider = dataState.selectedProvider ?: return@UiActions - uiState = stateBuilder.createImpactAlert( + val currencySymbol = dataState.toCryptoCurrency?.currency?.symbol ?: return@UiActions + val isPriceImpact = uiState.priceImpact is PriceImpact.Value + uiState = stateBuilder.createAlert( uiState = uiState, + isPriceImpact = isPriceImpact, + token = currencySymbol, providerType = selectedProvider.type, ) { uiState = stateBuilder.clearAlert(uiState) From cad513c95b21a846a93dc189e5da332573f620b5 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 15 Feb 2024 10:09:13 +0100 Subject: [PATCH 42/53] Updated on 2026-08-14 --- core/res/src/main/res/values-ru/strings.xml | 3 +- core/res/src/main/res/values/strings.xml | 3 +- .../tangem/feature/swap/models/UiActions.kt | 1 + .../states/ChooseFeeBottomSheetConfig.kt | 4 ++ .../feature/swap/ui/ChooseFeeBottomSheet.kt | 63 +++++++++++++++---- .../tangem/feature/swap/ui/StateBuilder.kt | 24 +++++++ .../feature/swap/viewmodels/SwapViewModel.kt | 3 + 7 files changed, 87 insertions(+), 14 deletions(-) diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index e52b26c31c..ffb87108d1 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -91,7 +91,8 @@ Посмотреть историю транзакций Обозреватель Комиссия - Сетевые комиссии за транзакции используются для поддержки безопасности сети, поощрения валидаторов, выделения ресурсов и определения приоритета транзакции. + Сетевые комиссии – это плата пользователя за обработку и подтверждение транзакций. Размер комиссии зависит от нагрузка на сеть, объема транзакции и приоритета исполнения. %s + Подробнее Свое Быстро По рынку diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 80715ad6f9..052973065a 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -89,7 +89,8 @@ Explore transaction history Explorer Fee - Network transaction fees are used to support network security, incentivize validators, allocate resources, and determine transaction priority. + Network fees are charges users pay to process and confirm transactions. The fee amount can be affected by network congestion, transaction size, and execution priority. %s + Read more Custom Fast Market diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/UiActions.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/UiActions.kt index 52fffa4053..abeec20c38 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/UiActions.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/UiActions.kt @@ -24,4 +24,5 @@ data class UiActions( val onPolicyClick: (String) -> Unit, val onTosClick: (String) -> Unit, val onReceiveCardWarningClick: () -> Unit, + val onFeeReadMoreClick: (String) -> Unit, ) \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/states/ChooseFeeBottomSheetConfig.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/states/ChooseFeeBottomSheetConfig.kt index fddf5f557e..f54845cd51 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/states/ChooseFeeBottomSheetConfig.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/models/states/ChooseFeeBottomSheetConfig.kt @@ -1,6 +1,7 @@ package com.tangem.feature.swap.models.states import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent +import com.tangem.core.ui.extensions.TextReference import com.tangem.feature.swap.domain.models.ui.FeeType import kotlinx.collections.immutable.ImmutableList @@ -8,4 +9,7 @@ data class ChooseFeeBottomSheetConfig( val selectedFee: FeeType, val onSelectFeeType: (FeeType) -> Unit, val feeItems: ImmutableList, + val readMoreUrl: String, + val readMore: TextReference, + val onReadMoreClick: (String) -> Unit, ) : TangemBottomSheetConfigContent \ No newline at end of file diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ChooseFeeBottomSheet.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ChooseFeeBottomSheet.kt index d0f47a62a2..4cef8254c1 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ChooseFeeBottomSheet.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/ChooseFeeBottomSheet.kt @@ -3,18 +3,23 @@ package com.tangem.feature.swap.ui import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.ClickableText import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.withStyle import androidx.compose.ui.tooling.preview.Preview import com.tangem.core.ui.components.SpacerH24 import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.rows.SelectorRowItem import com.tangem.core.ui.extensions.TextReference +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.domain.models.ui.FeeType @@ -33,7 +38,9 @@ fun ChooseFeeBottomSheet(config: TangemBottomSheetConfig) { @Composable private fun ChooseFeeBottomSheetContent(content: ChooseFeeBottomSheetConfig) { Column( - modifier = Modifier.background(TangemTheme.colors.background.primary), + modifier = Modifier + .background(TangemTheme.colors.background.primary) + .padding(bottom = TangemTheme.dimens.spacing8), ) { Text( text = stringResource(R.string.common_fee_selector_title), @@ -53,21 +60,47 @@ private fun ChooseFeeBottomSheetContent(content: ChooseFeeBottomSheetConfig) { ) { FeeItemsBlock(content) } - Text( - text = stringResource(R.string.common_fee_selector_footer), - style = TangemTheme.typography.caption2, - color = TangemTheme.colors.text.secondary, - modifier = Modifier - .padding( - vertical = TangemTheme.dimens.spacing8, - horizontal = TangemTheme.dimens.spacing16, - ) - .align(Alignment.CenterHorizontally), - textAlign = TextAlign.Start, + FooterBlock( + readMore = content.readMore, + readMoreUrl = content.readMoreUrl, + onReadMoreClick = content.onReadMoreClick, ) } } +@Composable +private fun FooterBlock(readMore: TextReference, readMoreUrl: String, onReadMoreClick: (String) -> Unit) { + val linkText = readMore.resolveReference() + val fullString = stringResource(R.string.common_fee_selector_footer, linkText) + val linkTextPosition = fullString.length - linkText.length + val annotatedString = buildAnnotatedString { + withStyle(SpanStyle(color = TangemTheme.colors.text.tertiary)) { + append(fullString.substring(0, linkTextPosition)) + } + withStyle(SpanStyle(color = TangemTheme.colors.text.accent)) { + append(fullString.substring(linkTextPosition, fullString.length)) + } + } + + val click = { i: Int -> + val readMoreStyle = requireNotNull(annotatedString.spanStyles.getOrNull(1)) + if (i in readMoreStyle.start..readMoreStyle.end) { + onReadMoreClick(readMoreUrl) + } + } + + ClickableText( + text = annotatedString, + modifier = Modifier + .padding( + vertical = TangemTheme.dimens.spacing8, + horizontal = TangemTheme.dimens.spacing16, + ), + style = TangemTheme.typography.caption2.copy(textAlign = TextAlign.Start), + onClick = click, + ) +} + @Composable private fun FeeItemsBlock(content: ChooseFeeBottomSheetConfig) { content.feeItems.forEach { feeItem -> @@ -129,6 +162,9 @@ private fun ChooseFeeBottomSheetContent_Preview() { selectedFee = FeeType.NORMAL, onSelectFeeType = {}, feeItems = feeItems, + readMore = stringReference("Read more"), + readMoreUrl = "", + onReadMoreClick = {}, ), ) } @@ -141,6 +177,9 @@ private fun ChooseFeeBottomSheetContent_Preview() { selectedFee = FeeType.NORMAL, onSelectFeeType = {}, feeItems = feeItems, + readMore = stringReference("Read more"), + readMoreUrl = "", + onReadMoreClick = {}, ), ) } 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 38aee5e9fb..69289418b4 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 @@ -25,6 +25,7 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList import java.math.BigDecimal import java.math.RoundingMode +import java.util.Locale import kotlin.math.min /** @@ -1090,7 +1091,10 @@ internal class StateBuilder( } actions.onSelectFeeType.invoke(selectedItem) }, + readMoreUrl = buildReadMoreUrl(), feeItems = txFeeState.toFeeItemState(), + readMore = resourceReference(R.string.common_fee_selector_link_description), + onReadMoreClick = actions.onFeeReadMoreClick, ) return uiState.copy( bottomSheetConfig = TangemBottomSheetConfig( @@ -1101,6 +1105,14 @@ internal class StateBuilder( ) } + private fun buildReadMoreUrl(): String { + return buildString { + append(FEE_READ_MORE_URL_FIRST_PART) + append(getLocaleName()) + append(FEE_READ_MORE_URL_SECOND_PART) + } + } + fun updateSelectedFeeBottomSheet(uiState: SwapStateHolder, selectedFee: FeeType): SwapStateHolder { val config = uiState.bottomSheetConfig?.content as? ChooseFeeBottomSheetConfig return if (config != null) { @@ -1346,12 +1358,24 @@ internal class StateBuilder( return text.replace(",", ".").toBigDecimalOrNull() } + private fun getLocaleName(): String { + return if (Locale.getDefault().language == "ru") { + RU_LOCALE + } else { + EN_LOCALE + } + } + private companion object { + private const val RU_LOCALE = "ru" + private const val EN_LOCALE = "en" const val ADDRESS_MIN_LENGTH = 11 const val ADDRESS_FIRST_PART_LENGTH = 7 const val ADDRESS_SECOND_PART_LENGTH = 4 private const val PRICE_IMPACT_THRESHOLD = 0.1 private const val UNKNOWN_AMOUNT_SIGN = "—" private const val MAX_DECIMALS_TO_SHOW = 8 + private const val FEE_READ_MORE_URL_FIRST_PART = "https://tangem.com/" + private const val FEE_READ_MORE_URL_SECOND_PART = "/blog/post/what-is-a-transaction-fee-and-why-do-we-need-it/" } } \ 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 52e89386a3..d194494046 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 @@ -890,6 +890,9 @@ internal class SwapViewModel @Inject constructor( uiState = stateBuilder.clearAlert(uiState) } }, + onFeeReadMoreClick = { + swapRouter.openUrl(it) + }, ) } From f6ac7751208eed55ce852df4eea0542fd954eebc Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 15 Feb 2024 11:07:50 +0100 Subject: [PATCH 43/53] Updated on 2026-08-14 --- .../java/com/tangem/datasource/config/ConfigManagerImpl.kt | 3 ++- .../java/com/tangem/datasource/config/models/JsonModels.kt | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManagerImpl.kt b/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManagerImpl.kt index c09740d22f..580334eb2b 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManagerImpl.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManagerImpl.kt @@ -1,6 +1,7 @@ package com.tangem.datasource.config import com.tangem.blockchain.common.* +import com.tangem.datasource.BuildConfig import com.tangem.datasource.config.ConfigManager.Companion.IS_CREATING_TWIN_CARDS_ALLOWED import com.tangem.datasource.config.ConfigManager.Companion.IS_TOP_UP_ENABLED import com.tangem.datasource.config.models.Config @@ -105,7 +106,7 @@ internal class ConfigManagerImpl @Inject constructor() : ConfigManager { sprinklr = configValues.sprinklr, walletConnectProjectId = configValues.walletConnectProjectId, tangemComAuthorization = configValues.tangemComAuthorization, - express = configValues.express, + express = if (BuildConfig.ENVIRONMENT == "dev") configValues.devExpress else configValues.express, ) } diff --git a/core/datasource/src/main/java/com/tangem/datasource/config/models/JsonModels.kt b/core/datasource/src/main/java/com/tangem/datasource/config/models/JsonModels.kt index f0b1403825..eaedb32084 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/config/models/JsonModels.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/config/models/JsonModels.kt @@ -40,6 +40,7 @@ class ConfigValueModel( val tangemComAuthorization: String?, val chiaFireAcademyApiKey: String?, val chiaTangemApiKey: String?, + val devExpress: ExpressModel?, val express: ExpressModel?, ) From fc1591532ce54fa73b95ce18840c8e25c36843cb Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 13 Feb 2024 13:39:55 +0300 Subject: [PATCH 44/53] Updated on 2026-08-14 --- app/src/main/assets/testnet_tokens.json | 10 ++++++ .../java/com/tangem/tap/TapApplication.kt | 5 +++ .../di/domain/WalletManagersFacadeModule.kt | 3 ++ .../com/tangem/tap/domain/TapWalletManager.kt | 1 + .../tap/proxy/redux/DaggerGraphState.kt | 2 ++ .../api/tangemTech/TangemTechApi.kt | 7 ++++ .../models/CreateUserNetworkAccountBody.kt | 10 ++++++ .../CreateUserNetworkAccountResponse.kt | 17 ++++++++++ .../datasource/di/AccountCreatorModule.kt | 22 ++++++++++++ .../local/blockchain/DefaultAccountCreator.kt | 31 +++++++++++++++++ .../core/ui/extensions/BlockchainIcons.kt | 5 +++ .../ui/src/main/res/drawable/ic_hedera_22.xml | 16 +++++++++ .../src/main/res/drawable/img_hedera_22.xml | 19 +++++++++++ .../domain/common/extensions/Blockchain.kt | 6 ++++ .../DefaultWalletManagersFacade.kt | 9 ++++- .../utils/WalletManagerFactory.kt | 9 +++-- domain/tokens/models/build.gradle.kts | 1 + .../domain/tokens/model/NetworkAddress.kt | 6 +++- .../tokens/GetCryptoCurrencyActionsUseCase.kt | 34 +++++++++++++------ .../router/DefaultWalletRouter.kt | 23 +++++++------ .../presentation/router/InnerWalletRouter.kt | 4 +-- .../converter/TokenItemStateConverter.kt | 4 +-- .../intents/WalletContentClickIntents.kt | 7 ++-- gradle/dependencies.toml | 2 +- 24 files changed, 219 insertions(+), 34 deletions(-) create mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/CreateUserNetworkAccountBody.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/CreateUserNetworkAccountResponse.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/di/AccountCreatorModule.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/blockchain/DefaultAccountCreator.kt create mode 100644 core/ui/src/main/res/drawable/ic_hedera_22.xml create mode 100644 core/ui/src/main/res/drawable/img_hedera_22.xml diff --git a/app/src/main/assets/testnet_tokens.json b/app/src/main/assets/testnet_tokens.json index 6c19252e4f..b09a1ffa91 100644 --- a/app/src/main/assets/testnet_tokens.json +++ b/app/src/main/assets/testnet_tokens.json @@ -598,6 +598,16 @@ "networkId": "aptos/test" } ] + }, + { + "id": "hedera-hashgraph", + "name": "Hedera", + "symbol": "HBAR", + "networks": [ + { + "networkId": "hedera-hashgraph/test" + } + ] } ] } diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 11145469dc..e4337c01f6 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -10,6 +10,7 @@ import com.orhanobut.logger.AndroidLogAdapter import com.orhanobut.logger.Logger import com.tangem.Log import com.tangem.LogFormat +import com.tangem.blockchain.common.AccountCreator import com.tangem.blockchain.common.datastorage.BlockchainDataStorage import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder import com.tangem.core.analytics.Analytics @@ -151,6 +152,9 @@ internal class TapApplication : Application(), ImageLoaderFactory { @Inject lateinit var blockchainDataStorage: BlockchainDataStorage + + @Inject + lateinit var accountCreator: AccountCreator // endregion Injected override fun onCreate() { @@ -227,6 +231,7 @@ internal class TapApplication : Application(), ImageLoaderFactory { walletsRepository = walletsRepository, sendFeatureToggles = sendFeatureToggles, blockchainDataStorage = blockchainDataStorage, + accountCreator = accountCreator, ), ), ) diff --git a/app/src/main/java/com/tangem/tap/di/domain/WalletManagersFacadeModule.kt b/app/src/main/java/com/tangem/tap/di/domain/WalletManagersFacadeModule.kt index a78ad179cd..3fedeef4ca 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/WalletManagersFacadeModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/WalletManagersFacadeModule.kt @@ -1,6 +1,7 @@ package com.tangem.tap.di.domain import com.squareup.moshi.Moshi +import com.tangem.blockchain.common.AccountCreator import com.tangem.blockchain.common.datastorage.BlockchainDataStorage import com.tangem.datasource.asset.AssetReader import com.tangem.datasource.config.ConfigManager @@ -27,6 +28,7 @@ internal object WalletManagersFacadeModule { userWalletsStore: UserWalletsStore, configManager: ConfigManager, blockchainDataStorage: BlockchainDataStorage, + accountCreator: AccountCreator, mnemonicRepository: MnemonicRepository, assetReader: AssetReader, @SdkMoshi moshi: Moshi, @@ -39,6 +41,7 @@ internal object WalletManagersFacadeModule { assetReader = assetReader, moshi = moshi, mnemonic = mnemonicRepository.generateDefaultMnemonic(), + accountCreator = accountCreator, ) } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt index 41af6ffed9..24db8bcbc8 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -44,6 +44,7 @@ class TapWalletManager( val walletManagerFactory: WalletManagerFactory by lazy { WalletManagerFactory( config = blockchainSdkConfig, + accountCreator = store.state.daggerGraphState.get(DaggerGraphState::accountCreator), blockchainDataStorage = store.state.daggerGraphState.get(DaggerGraphState::blockchainDataStorage), ) } diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt index 3bfa07a2ce..febaf4d633 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt @@ -1,5 +1,6 @@ package com.tangem.tap.proxy.redux +import com.tangem.blockchain.common.AccountCreator import com.tangem.blockchain.common.datastorage.BlockchainDataStorage import com.tangem.datasource.connection.NetworkConnectionManager import com.tangem.domain.appcurrency.repository.AppCurrencyRepository @@ -53,6 +54,7 @@ data class DaggerGraphState( val qrScanningRouter: QrScanningRouter? = null, val currenciesRepository: CurrenciesRepository? = null, val blockchainDataStorage: BlockchainDataStorage? = null, + val accountCreator: AccountCreator? = null, ) : StateType { inline fun get(getDependency: DaggerGraphState.() -> T?): T { diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt index 43ff3af6e5..63e632438e 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt @@ -75,4 +75,11 @@ interface TangemTechApi { @GET("promotion") suspend fun getPromotionInfo(@Query("programName") name: String): ApiResponse + + @POST("user-network-account") + suspend fun createUserNetworkAccount( + @Header("card_public_key") cardPublicKey: String, + @Header("card_id") cardId: String, + @Body body: CreateUserNetworkAccountBody, + ): ApiResponse } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/CreateUserNetworkAccountBody.kt b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/CreateUserNetworkAccountBody.kt new file mode 100644 index 0000000000..3485e742a0 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/CreateUserNetworkAccountBody.kt @@ -0,0 +1,10 @@ +package com.tangem.datasource.api.tangemTech.models + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class CreateUserNetworkAccountBody( + @Json(name = "networkId") val networkId: String, + @Json(name = "publicWalletKey") val publicWalletKey: String, +) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/CreateUserNetworkAccountResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/CreateUserNetworkAccountResponse.kt new file mode 100644 index 0000000000..742694245b --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/CreateUserNetworkAccountResponse.kt @@ -0,0 +1,17 @@ +package com.tangem.datasource.api.tangemTech.models + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class CreateUserNetworkAccountResponse( + @Json(name = "status") val status: String, + @Json(name = "data") val data: AccountCreated, +) { + + @JsonClass(generateAdapter = true) + data class AccountCreated( + @Json(name = "accountId") val accountId: String, + @Json(name = "publicWalletKey") val publicWalletKey: String, + ) +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/AccountCreatorModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/AccountCreatorModule.kt new file mode 100644 index 0000000000..7abd08cf54 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/di/AccountCreatorModule.kt @@ -0,0 +1,22 @@ +package com.tangem.datasource.di + +import com.tangem.blockchain.common.AccountCreator +import com.tangem.datasource.api.tangemTech.TangemTechApi +import com.tangem.datasource.local.blockchain.DefaultAccountCreator +import com.tangem.lib.auth.AuthProvider +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal object AccountCreatorModule { + + @Provides + @Singleton + fun provideAccountCreator(authProvider: AuthProvider, @DevTangemApi tangemTechApi: TangemTechApi): AccountCreator { + return DefaultAccountCreator(authProvider, tangemTechApi) + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/blockchain/DefaultAccountCreator.kt b/core/datasource/src/main/java/com/tangem/datasource/local/blockchain/DefaultAccountCreator.kt new file mode 100644 index 0000000000..1501279d83 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/blockchain/DefaultAccountCreator.kt @@ -0,0 +1,31 @@ +package com.tangem.datasource.local.blockchain + +import com.tangem.blockchain.common.AccountCreator +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.BlockchainSdkError +import com.tangem.blockchain.extensions.Result +import com.tangem.common.extensions.toHexString +import com.tangem.datasource.api.common.response.getOrThrow +import com.tangem.datasource.api.tangemTech.TangemTechApi +import com.tangem.datasource.api.tangemTech.models.CreateUserNetworkAccountBody +import com.tangem.lib.auth.AuthProvider + +internal class DefaultAccountCreator( + private val authProvider: AuthProvider, + private val tangemTechApi: TangemTechApi, +) : AccountCreator { + + override suspend fun createAccount(blockchain: Blockchain, walletPublicKey: ByteArray): Result { + val request = CreateUserNetworkAccountBody(blockchain.id.removeSuffix("/test"), walletPublicKey.toHexString()) + return try { + val response = tangemTechApi.createUserNetworkAccount( + cardPublicKey = authProvider.getCardPublicKey(), + cardId = authProvider.getCardId(), + body = request, + ).getOrThrow() + Result.Success(response.data.accountId) + } catch (e: Exception) { + Result.Failure(BlockchainSdkError.FailedToCreateAccount) + } + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt b/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt index 1f3cc0f7dc..937b93c1d5 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt @@ -51,6 +51,7 @@ fun getActiveIconRes(blockchainId: String): Int { "aptos", "aptos/test" -> R.drawable.img_aptos_22 "shibarium", "shibarium/test" -> R.drawable.img_shibarium_22 "algorand", "algorand/test" -> R.drawable.img_algorand_22 + "hedera", "hedera/test" -> R.drawable.img_hedera_22 else -> R.drawable.ic_alert_24 } } @@ -103,6 +104,7 @@ fun getActiveIconResByNetworkId(networkId: String): Int { "aptos", "aptos/test" -> R.drawable.img_aptos_22 "shibarium", "shibarium/test" -> R.drawable.img_shibarium_22 "algorand", "algorand/test" -> R.drawable.img_algorand_22 + "hedera", "hedera/test" -> R.drawable.img_hedera_22 else -> R.drawable.ic_alert_24 } } @@ -152,6 +154,7 @@ fun getActiveIconResByCoinId(coinId: String): Int { "aptos" -> R.drawable.img_aptos_22 "shibarium" -> R.drawable.img_shibarium_22 "algorand" -> R.drawable.img_algorand_22 + "hedera" -> R.drawable.img_hedera_22 else -> R.drawable.ic_alert_24 } } @@ -204,6 +207,7 @@ fun getGreyedOutIconRes(blockchainId: String): Int { "aptos", "aptos/test" -> R.drawable.ic_aptos_22 "shibarium", "shibarium/test" -> R.drawable.ic_shibarium_22 "algorand", "algorand/test" -> R.drawable.ic_algorand_22 + "hedera", "hedera/test" -> R.drawable.ic_hedera_22 else -> R.drawable.ic_alert_24 } } @@ -256,6 +260,7 @@ fun getGreyedOutIconResByNetworkId(networkId: String): Int { "aptos", "aptos/test" -> R.drawable.ic_aptos_22 "shibarium", "shibarium/test" -> R.drawable.ic_shibarium_22 "algorand", "algorand/test" -> R.drawable.ic_algorand_22 + "hedera", "hedera/test" -> R.drawable.ic_hedera_22 else -> R.drawable.ic_alert_24 } } \ No newline at end of file diff --git a/core/ui/src/main/res/drawable/ic_hedera_22.xml b/core/ui/src/main/res/drawable/ic_hedera_22.xml new file mode 100644 index 0000000000..a8ead7c102 --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_hedera_22.xml @@ -0,0 +1,16 @@ + + + + + + + diff --git a/core/ui/src/main/res/drawable/img_hedera_22.xml b/core/ui/src/main/res/drawable/img_hedera_22.xml new file mode 100644 index 0000000000..f96e2befb9 --- /dev/null +++ b/core/ui/src/main/res/drawable/img_hedera_22.xml @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt index 1e14859635..a971c818c1 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt @@ -85,6 +85,8 @@ fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? { "shibarium/test" -> Blockchain.ShibariumTestnet "algorand" -> Blockchain.Algorand "algorand/test" -> Blockchain.AlgorandTestnet + "hedera-hashgraph" -> Blockchain.Hedera + "hedera-hashgraph/test" -> Blockchain.HederaTestnet else -> null } } @@ -171,6 +173,8 @@ fun Blockchain.toNetworkId(): String { Blockchain.ShibariumTestnet -> "shibarium/test" Blockchain.Algorand -> "algorand" Blockchain.AlgorandTestnet -> "algorand/test" + Blockchain.Hedera -> "hedera-hashgraph" + Blockchain.HederaTestnet -> "hedera-hashgraph/test" } } @@ -228,6 +232,8 @@ fun Blockchain.toCoinId(): String { Blockchain.Algorand -> "algorand" Blockchain.AlgorandTestnet -> "algorand/test" Blockchain.Unknown -> "unknown" + Blockchain.Hedera -> "hedera-hashgraph" + Blockchain.HederaTestnet -> "hedera-hashgraph/test" } } diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt index 8895459bc1..bb0cc5a3ad 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt @@ -52,11 +52,18 @@ class DefaultWalletManagersFacade( moshi: Moshi, configManager: ConfigManager, blockchainDataStorage: BlockchainDataStorage, + accountCreator: AccountCreator, ) : WalletManagersFacade { private val demoConfig by lazy { DemoConfig() } private val resultFactory by lazy { UpdateWalletManagerResultFactory() } - private val walletManagerFactory by lazy { WalletManagerFactory(configManager, blockchainDataStorage) } + private val walletManagerFactory by lazy { + WalletManagerFactory( + configManager, + accountCreator, + blockchainDataStorage, + ) + } private val sdkTokenConverter by lazy { SdkTokenConverter() } private val txHistoryStateConverter by lazy { SdkTransactionHistoryStateConverter() } private val txHistoryItemConverter by lazy { SdkTransactionHistoryItemConverter(assetReader, moshi) } diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/WalletManagerFactory.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/WalletManagerFactory.kt index 349d189f2e..e0377cf6a3 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/WalletManagerFactory.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/WalletManagerFactory.kt @@ -1,5 +1,6 @@ package com.tangem.domain.walletmanager.utils +import com.tangem.blockchain.common.AccountCreator import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.DerivationParams import com.tangem.blockchain.common.WalletManager @@ -13,10 +14,14 @@ import com.tangem.domain.models.scan.ScanResponse import timber.log.Timber import com.tangem.blockchain.common.WalletManagerFactory as BlockchainWalletManagerFactory -internal class WalletManagerFactory(configManager: ConfigManager, blockchainDataStorage: BlockchainDataStorage) { +internal class WalletManagerFactory( + configManager: ConfigManager, + accountCreator: AccountCreator, + blockchainDataStorage: BlockchainDataStorage, +) { private val sdkWalletManagerFactory by lazy { - BlockchainWalletManagerFactory(configManager.config.blockchainSdkConfig, blockchainDataStorage) + BlockchainWalletManagerFactory(configManager.config.blockchainSdkConfig, accountCreator, blockchainDataStorage) } fun createWalletManager( diff --git a/domain/tokens/models/build.gradle.kts b/domain/tokens/models/build.gradle.kts index f3bf4ce59c..948bad320b 100644 --- a/domain/tokens/models/build.gradle.kts +++ b/domain/tokens/models/build.gradle.kts @@ -16,4 +16,5 @@ dependencies { exclude(module = "joda-time") } implementation(deps.jodatime) + implementation(deps.timber) } \ No newline at end of file diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/NetworkAddress.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/NetworkAddress.kt index 70d50581d4..9b57fcfe15 100644 --- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/NetworkAddress.kt +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/NetworkAddress.kt @@ -1,5 +1,7 @@ package com.tangem.domain.tokens.model +import timber.log.Timber + /** * Represents a network address configuration. */ @@ -47,7 +49,9 @@ sealed class NetworkAddress { } init { - require(value.isNotBlank()) { "Address value must not be blank" } + if (value.isEmpty()) { + Timber.w("Address value is blank") + } } } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt index 794cf3a5f9..bbbc8c7071 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCryptoCurrencyActionsUseCase.kt @@ -2,10 +2,7 @@ package com.tangem.domain.tokens import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.exchange.RampStateManager -import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.tokens.model.FeePaidCurrency -import com.tangem.domain.tokens.model.TokenActionsState +import com.tangem.domain.tokens.model.* import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.MarketCryptoCurrencyRepository @@ -94,17 +91,21 @@ class GetCryptoCurrencyActionsUseCase( return listOf(TokenActionsState.ActionState.HideToken(true)) } if (cryptoCurrencyStatus.value is CryptoCurrencyStatus.Unreachable) { - return getActionsForUnreachableCurrency(cryptoCurrency) + return getActionsForUnreachableCurrency(cryptoCurrencyStatus) } val activeList = mutableListOf() val disabledList = mutableListOf() // copy address - activeList.add(TokenActionsState.ActionState.CopyAddress(true)) + if (isAddressAvailable(cryptoCurrencyStatus.value.networkAddress)) { + activeList.add(TokenActionsState.ActionState.CopyAddress(true)) + } // receive - activeList.add(TokenActionsState.ActionState.Receive(true)) + if (isAddressAvailable(cryptoCurrencyStatus.value.networkAddress)) { + activeList.add(TokenActionsState.ActionState.Receive(true)) + } // send if ( @@ -148,12 +149,16 @@ class GetCryptoCurrencyActionsUseCase( return activeList + disabledList } - private fun getActionsForUnreachableCurrency(cryptoCurrency: CryptoCurrency): List { + private fun getActionsForUnreachableCurrency( + cryptoCurrencyStatus: CryptoCurrencyStatus, + ): List { val activeList = mutableListOf() val disabledList = mutableListOf() - activeList.add(TokenActionsState.ActionState.CopyAddress(true)) - if (rampManager.availableForBuy(cryptoCurrency)) { + if (isAddressAvailable(cryptoCurrencyStatus.value.networkAddress)) { + activeList.add(TokenActionsState.ActionState.CopyAddress(true)) + } + if (rampManager.availableForBuy(cryptoCurrencyStatus.currency)) { activeList.add(TokenActionsState.ActionState.Buy(true)) } else { disabledList.add(TokenActionsState.ActionState.Buy(false)) @@ -161,7 +166,10 @@ class GetCryptoCurrencyActionsUseCase( disabledList.add(TokenActionsState.ActionState.Send(false)) disabledList.add(TokenActionsState.ActionState.Swap(false)) disabledList.add(TokenActionsState.ActionState.Sell(false)) - activeList.add(TokenActionsState.ActionState.Receive(true)) + + if (isAddressAvailable(cryptoCurrencyStatus.value.networkAddress)) { + activeList.add(TokenActionsState.ActionState.Receive(true)) + } activeList.add(TokenActionsState.ActionState.HideToken(true)) return activeList + disabledList } @@ -200,6 +208,10 @@ class GetCryptoCurrencyActionsUseCase( } } + private fun isAddressAvailable(networkAddress: NetworkAddress?): Boolean { + return networkAddress != null && networkAddress.defaultAddress.value.isNotEmpty() + } + private fun BigDecimal?.isZero(): Boolean { return this?.signum() == 0 } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index ecf64015f4..24c2270f96 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -19,7 +19,7 @@ import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction import com.tangem.core.navigation.ReduxNavController import com.tangem.core.navigation.StateDialog -import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.onboarding.navigation.OnboardingRouter import com.tangem.feature.wallet.presentation.WalletFragment @@ -136,16 +136,19 @@ internal class DefaultWalletRouter( reduxNavController.navigate(action = NavigationAction.OpenUrl(url)) } - override fun openTokenDetails(userWalletId: UserWalletId, currency: CryptoCurrency) { - reduxNavController.navigate( - action = NavigationAction.NavigateTo( - screen = AppScreen.WalletDetails, - bundle = bundleOf( - TokenDetailsRouter.USER_WALLET_ID_KEY to userWalletId.stringValue, - TokenDetailsRouter.CRYPTO_CURRENCY_KEY to currency, + override fun openTokenDetails(userWalletId: UserWalletId, currencyStatus: CryptoCurrencyStatus) { + val networkAddress = currencyStatus.value.networkAddress + if (networkAddress != null && networkAddress.defaultAddress.value.isNotEmpty()) { + reduxNavController.navigate( + action = NavigationAction.NavigateTo( + screen = AppScreen.WalletDetails, + bundle = bundleOf( + TokenDetailsRouter.USER_WALLET_ID_KEY to userWalletId.stringValue, + TokenDetailsRouter.CRYPTO_CURRENCY_KEY to currencyStatus.currency, + ), ), - ), - ) + ) + } } override fun openStoriesScreen() { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt index d5d53d879f..41abc741f6 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt @@ -3,7 +3,7 @@ package com.tangem.feature.wallet.presentation.router import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable import com.tangem.core.navigation.AppScreen -import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.wallets.models.UserWalletId import com.tangem.features.managetokens.navigation.ManageTokensRouter import com.tangem.features.wallet.navigation.WalletRouter @@ -44,7 +44,7 @@ internal interface InnerWalletRouter : WalletRouter { fun openUrl(url: String) /** Open token details screen */ - fun openTokenDetails(userWalletId: UserWalletId, currency: CryptoCurrency) + fun openTokenDetails(userWalletId: UserWalletId, currencyStatus: CryptoCurrencyStatus) /** Open stories screen */ fun openStoriesScreen() diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenItemStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenItemStateConverter.kt index 66311796de..77aa101de4 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenItemStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenItemStateConverter.kt @@ -54,7 +54,7 @@ internal class TokenItemStateConverter( ), cryptoAmountState = TokenItemState.CryptoAmountState.Content(text = getFormattedAmount()), cryptoPriceState = getCryptoPriceState(), - onItemClick = { clickIntents.onTokenItemClick(currency) }, + onItemClick = { clickIntents.onTokenItemClick(this) }, onItemLongClick = { clickIntents.onTokenItemLongClick(cryptoCurrencyStatus = this) }, ) } @@ -76,7 +76,7 @@ internal class TokenItemStateConverter( id = currency.id.value, iconState = iconStateConverter.convert(value = this), titleState = TokenItemState.TitleState.Content(text = currency.name), - onItemClick = { clickIntents.onTokenItemClick(currency) }, + onItemClick = { clickIntents.onTokenItemClick(this) }, onItemLongClick = { clickIntents.onTokenItemLongClick(cryptoCurrencyStatus = this) }, ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletContentClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletContentClickIntents.kt index 86e51c62e7..3ebb1b4830 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletContentClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletContentClickIntents.kt @@ -5,7 +5,6 @@ import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase import com.tangem.domain.tokens.TokensAction -import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TokenActionsState import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase @@ -33,7 +32,7 @@ internal interface WalletContentClickIntents { fun onOrganizeTokensClick() - fun onTokenItemClick(currency: CryptoCurrency) + fun onTokenItemClick(currencyStatus: CryptoCurrencyStatus) fun onTokenItemLongClick(cryptoCurrencyStatus: CryptoCurrencyStatus) @@ -69,9 +68,9 @@ internal class WalletContentClickIntentsImplementor @Inject constructor( router.openOrganizeTokensScreen(userWalletId = stateHolder.getSelectedWalletId()) } - override fun onTokenItemClick(currency: CryptoCurrency) { + override fun onTokenItemClick(currencyStatus: CryptoCurrencyStatus) { analyticsEventHandler.send(PortfolioEvent.TokenTapped) - router.openTokenDetails(stateHolder.getSelectedWalletId(), currency) + router.openTokenDetails(stateHolder.getSelectedWalletId(), currencyStatus) } override fun onTokenItemLongClick(cryptoCurrencyStatus: CryptoCurrencyStatus) { diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 235009dbc4..3e8fc614e9 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -85,7 +85,7 @@ web3j = "4.10.1" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-488" +tangemBlockchainSdk = "develop-489" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-324" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From 3a4ce4d24b572fef16aecb093f6d23665d521472 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 14 Feb 2024 15:03:07 +0300 Subject: [PATCH 45/53] Updated on 2026-08-14 --- .../features/send/redux/SendScreenAction.kt | 10 ++++++ .../reducers/TransactionExtrasReducer.kt | 17 ++++++++++ .../send/redux/states/AddressState.kt | 13 ++++++-- .../tap/features/send/ui/SendFragment.kt | 9 ++++++ .../stateSubscribers/SendStateSubscriber.kt | 14 +++++++- .../main/res/layout/layout_send_address.xml | 32 +++++++++++++++++++ .../CreateUserNetworkAccountResponse.kt | 2 +- .../core/ui/extensions/BlockchainIcons.kt | 6 ++-- .../DefaultTransactionRepository.kt | 2 ++ .../SendRecipientMemoFieldConverter.kt | 1 + 10 files changed, 99 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt index 08f0c090d5..00e279678b 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt @@ -59,26 +59,36 @@ sealed class TransactionExtrasAction : SendScreenActionUi { object Release : TransactionExtrasAction() + @Deprecated("Only in legacy send screen") sealed class XlmMemo : TransactionExtrasAction() { // data class ChangeSelectedMemo(val memoType: XlmMemoType) : XlmMemo() data class HandleUserInput(val data: String) : XlmMemo() } + @Deprecated("Only in legacy send screen") sealed class BinanceMemo : TransactionExtrasAction() { data class HandleUserInput(val data: String) : BinanceMemo() } + @Deprecated("Only in legacy send screen") sealed class XrpDestinationTag : TransactionExtrasAction() { data class HandleUserInput(val data: String) : XrpDestinationTag() } + @Deprecated("Only in legacy send screen") sealed class TonMemo : TransactionExtrasAction() { data class HandleUserInput(val data: String) : TonMemo() } + @Deprecated("Only in legacy send screen") sealed class CosmosMemo : TransactionExtrasAction() { data class HandleUserInput(val data: String) : CosmosMemo() } + + @Deprecated("Only in legacy send screen") + sealed class HederaMemo : TransactionExtrasAction() { + data class HandleUserInput(val data: String) : HederaMemo() + } } sealed class AddressVerifyAction : SendScreenAction { diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/TransactionExtrasReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/TransactionExtrasReducer.kt index cb0fbaa3aa..058a8ded6e 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/TransactionExtrasReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/TransactionExtrasReducer.kt @@ -19,6 +19,7 @@ class TransactionExtrasReducer : SendInternalReducer { is XrpDestinationTag -> handleXrpTag(action, sendState, sendState.transactionExtrasState) is TonMemo -> handleTonMemo(action, sendState, sendState.transactionExtrasState) is CosmosMemo -> handleCosmosMemo(action, sendState, sendState.transactionExtrasState) + is HederaMemo -> handleHederaMemo(action, sendState, sendState.transactionExtrasState) else -> sendState } } @@ -52,6 +53,7 @@ class TransactionExtrasReducer : SendInternalReducer { Blockchain.TerraV1, Blockchain.TerraV2, -> TransactionExtrasState(cosmosMemoState = CosmosMemoState()) + Blockchain.Hedera, Blockchain.HederaTestnet -> TransactionExtrasState(hederaMemoState = HederaMemoState()) else -> emptyResult } return updateLastState(sendState.copy(transactionExtrasState = result), result) @@ -162,4 +164,19 @@ class TransactionExtrasReducer : SendInternalReducer { } return updateLastState(sendState.copy(transactionExtrasState = result), result) } + + private fun handleHederaMemo( + action: HederaMemo, + sendState: SendState, + infoState: TransactionExtrasState, + ): SendState { + val result = when (action) { + is HederaMemo.HandleUserInput -> { + val memo = action.data + val input = InputViewValue(memo, true) + infoState.copy(hederaMemoState = HederaMemoState(input, memo)) + } + } + return updateLastState(sendState.copy(transactionExtrasState = result), result) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressState.kt index b6b65a595f..bda2324f45 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressState.kt @@ -23,18 +23,20 @@ data class AddressState( fun isReady(): Boolean = error == null && destinationWalletAddress?.isNotEmpty() ?: false } +@Deprecated("Legacy") data class TransactionExtrasState( val xlmMemo: XlmMemoState? = null, val binanceMemo: BinanceMemoState? = null, val xrpDestinationTag: XrpDestinationTagState? = null, val tonMemoState: TonMemoState? = null, val cosmosMemoState: CosmosMemoState? = null, + val hederaMemoState: HederaMemoState? = null, ) : IdStateHolder { override val stateId: StateId = StateId.TRANSACTION_EXTRAS fun isNull(): Boolean { return xlmMemo == null && binanceMemo == null && xrpDestinationTag == null && tonMemoState == null && - cosmosMemoState == null + cosmosMemoState == null && hederaMemoState == null } fun isEmpty(): Boolean { @@ -43,8 +45,9 @@ data class TransactionExtrasState( val isXrpEmpty = xrpDestinationTag?.viewFieldValue?.value?.isEmpty() ?: false val isTonEmpty = tonMemoState?.viewFieldValue?.value?.isEmpty() ?: false val isCosmosEmpty = cosmosMemoState?.viewFieldValue?.value?.isEmpty() ?: false + val isHederaEmpty = hederaMemoState?.viewFieldValue?.value?.isEmpty() ?: false - return isXlmEmpty || isBinanceEmpty || isXrpEmpty || isTonEmpty || isCosmosEmpty + return isXlmEmpty || isBinanceEmpty || isXrpEmpty || isTonEmpty || isCosmosEmpty || isHederaEmpty } } @@ -120,6 +123,12 @@ data class CosmosMemoState( val error: TransactionExtraError? = null, ) +data class HederaMemoState( + val viewFieldValue: InputViewValue = InputViewValue(""), + val memo: String? = null, + val error: TransactionExtraError? = null, +) + enum class TransactionExtraError { INVALID_DESTINATION_TAG, INVALID_XLM_MEMO, diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt index 3e8b23a5d3..4bf8730db7 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt @@ -234,6 +234,15 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { } .onEach { store.dispatch(TransactionExtrasAction.CosmosMemo.HandleUserInput(it)) } .launchIn(mainScope) + + etHederaMemo.inputtedTextAsFlow() + .debounce(EDIT_TEXT_INPUT_DEBOUNCE) + .filter { + val info = store.state.sendState.transactionExtrasState + info.hederaMemoState?.viewFieldValue?.value != it + } + .onEach { store.dispatch(TransactionExtrasAction.HederaMemo.HandleUserInput(it)) } + .launchIn(mainScope) } private fun onCodeScanned(scannedCode: String) { diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt index 927468b7ea..a2342fb5bf 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt @@ -62,7 +62,8 @@ internal class SendStateSubscriber( } } - @Suppress("ComplexMethod") + @Deprecated("Legacy") + @Suppress("ComplexMethod", "LongMethod") private fun handleTransactionExtrasState(fg: SendFragment, infoState: TransactionExtrasState) = with(fg.binding.lSendAddress) { fun showView(view: View, info: Any?) { @@ -73,6 +74,7 @@ internal class SendStateSubscriber( showView(binanceMemoContainer, infoState.binanceMemo) showView(tonMemoContainer, infoState.tonMemoState) showView(cosmosMemoContainer, infoState.cosmosMemoState) + showView(hederaMemoContainer, infoState.hederaMemoState) infoState.xlmMemo?.let { if (!it.viewFieldValue.isFromUserInput) etXlmMemo.setText(it.viewFieldValue.value) @@ -129,6 +131,16 @@ internal class SendStateSubscriber( etCosmosMemo.setText(it.viewFieldValue.value) } } + infoState.hederaMemoState?.let { + if (infoState.hederaMemoState.error != null) { + tilHederaMemo.error = fg.getText(R.string.send_extras_error_invalid_memo) + } else { + tilHederaMemo.error = null + } + if (!it.viewFieldValue.isFromUserInput) { + etHederaMemo.setText(it.viewFieldValue.value) + } + } } @Suppress("ComplexMethod") diff --git a/app/src/main/res/layout/layout_send_address.xml b/app/src/main/res/layout/layout_send_address.xml index 8367bf3524..dd4b1d9cc2 100644 --- a/app/src/main/res/layout/layout_send_address.xml +++ b/app/src/main/res/layout/layout_send_address.xml @@ -260,6 +260,38 @@ + + + + + + + + + + diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/CreateUserNetworkAccountResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/CreateUserNetworkAccountResponse.kt index 742694245b..2dcb8bfb8f 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/CreateUserNetworkAccountResponse.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/CreateUserNetworkAccountResponse.kt @@ -5,7 +5,7 @@ import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) data class CreateUserNetworkAccountResponse( - @Json(name = "status") val status: String, + @Json(name = "status") val status: Boolean, @Json(name = "data") val data: AccountCreated, ) { diff --git a/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt b/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt index 937b93c1d5..e38813089f 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt @@ -104,7 +104,7 @@ fun getActiveIconResByNetworkId(networkId: String): Int { "aptos", "aptos/test" -> R.drawable.img_aptos_22 "shibarium", "shibarium/test" -> R.drawable.img_shibarium_22 "algorand", "algorand/test" -> R.drawable.img_algorand_22 - "hedera", "hedera/test" -> R.drawable.img_hedera_22 + "hedera-hashgraph", "hedera/test" -> R.drawable.img_hedera_22 else -> R.drawable.ic_alert_24 } } @@ -154,7 +154,7 @@ fun getActiveIconResByCoinId(coinId: String): Int { "aptos" -> R.drawable.img_aptos_22 "shibarium" -> R.drawable.img_shibarium_22 "algorand" -> R.drawable.img_algorand_22 - "hedera" -> R.drawable.img_hedera_22 + "hedera-hashgraph" -> R.drawable.img_hedera_22 else -> R.drawable.ic_alert_24 } } @@ -260,7 +260,7 @@ fun getGreyedOutIconResByNetworkId(networkId: String): Int { "aptos", "aptos/test" -> R.drawable.ic_aptos_22 "shibarium", "shibarium/test" -> R.drawable.ic_shibarium_22 "algorand", "algorand/test" -> R.drawable.ic_algorand_22 - "hedera", "hedera/test" -> R.drawable.ic_hedera_22 + "hedera-hashgraph", "hedera/test" -> R.drawable.ic_hedera_22 else -> R.drawable.ic_alert_24 } } \ No newline at end of file diff --git a/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt index 7c515253c2..25704e1bf1 100644 --- a/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt +++ b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt @@ -3,6 +3,7 @@ package com.tangem.data.transaction import androidx.core.text.isDigitsOnly import com.tangem.blockchain.blockchains.binance.BinanceTransactionExtras import com.tangem.blockchain.blockchains.cosmos.CosmosTransactionExtras +import com.tangem.blockchain.blockchains.hedera.HederaTransactionBuilder import com.tangem.blockchain.blockchains.stellar.StellarMemo import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras import com.tangem.blockchain.blockchains.ton.TonTransactionExtras @@ -71,6 +72,7 @@ internal class DefaultTransactionRepository( Blockchain.XRP -> memo.toLongOrNull()?.let { XrpTransactionBuilder.XrpTransactionExtras(it) } Blockchain.Cosmos -> CosmosTransactionExtras(memo) Blockchain.TON -> TonTransactionExtras(memo) + Blockchain.Hedera -> HederaTransactionBuilder.HederaTransactionExtras(memo) else -> null } } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientMemoFieldConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientMemoFieldConverter.kt index 91321769f0..f3a0cefdd9 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientMemoFieldConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientMemoFieldConverter.kt @@ -28,6 +28,7 @@ internal class SendRecipientMemoFieldConverter( Blockchain.TerraV1.id, Blockchain.TerraV2.id, Blockchain.Stellar.id, + Blockchain.Hedera.id, -> convert(R.string.send_extras_hint_memo) else -> null } From 76c64ef543a77a5adaeeef9bf4aeb6ce2ce40f9e Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 15 Feb 2024 10:13:44 +0300 Subject: [PATCH 46/53] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/MainActivity.kt | 6 +- .../tap/proxy/redux/DaggerGraphAction.kt | 4 +- .../tap/proxy/redux/DaggerGraphReducer.kt | 2 +- .../tap/proxy/redux/DaggerGraphState.kt | 4 +- .../TangemBottomSheetConfigContent.kt | 4 +- features/manage-tokens/api/build.gradle.kts | 4 - .../navigation/ManageTokensRouter.kt | 12 -- .../managetokens/navigation/ManageTokensUi.kt | 10 ++ .../di/ManageTokensRouterModule.kt | 23 ++-- .../router/AddCustomTokenRoute.kt | 14 ++ .../router/AddCustomTokenRouter.kt | 29 ++++ .../ui/AddCustomTokenBottomSheet.kt | 115 ++++++++++++++++ .../viewmodels/AddCustomTokenViewModel.kt | 25 +++- .../managetokens/state/ManageTokensState.kt | 2 + .../state/factory/ManageTokensStateFactory.kt | 7 + .../ManageTokensStatePreviewData.kt | 3 + .../managetokens/ui/ManageTokensScreen.kt | 6 + .../viewmodels/ManageTokensUiEvents.kt | 2 + .../viewmodels/ManageTokensViewModel.kt | 12 +- .../router/DefaultManageTokensRouter.kt | 124 ------------------ .../router/InnerManageTokensRouter.kt | 25 ---- .../presentation/router/ManageTokensRoute.kt | 19 --- .../presentation/router/ManageTokensUiImpl.kt | 22 ++++ .../wallet/presentation/WalletFragment.kt | 6 +- .../router/DefaultWalletRouter.kt | 23 ++-- .../presentation/router/InnerWalletRouter.kt | 4 +- .../extension/BaseExtensionConfigurations.kt | 1 + 27 files changed, 270 insertions(+), 238 deletions(-) delete mode 100644 features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/navigation/ManageTokensRouter.kt create mode 100644 features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/navigation/ManageTokensUi.kt create mode 100644 features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/router/AddCustomTokenRoute.kt create mode 100644 features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/router/AddCustomTokenRouter.kt create mode 100644 features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/AddCustomTokenBottomSheet.kt delete mode 100644 features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/DefaultManageTokensRouter.kt delete mode 100644 features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/InnerManageTokensRouter.kt delete mode 100644 features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/ManageTokensRoute.kt create mode 100644 features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/ManageTokensUiImpl.kt diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index 4ef519edbd..04b3f17541 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -37,7 +37,7 @@ import com.tangem.domain.card.ScanCardUseCase import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.feature.qrscanning.QrScanningRouter -import com.tangem.features.managetokens.navigation.ManageTokensRouter +import com.tangem.features.managetokens.navigation.ManageTokensUi import com.tangem.features.send.api.navigation.SendRouter import com.tangem.features.tester.api.TesterRouter import com.tangem.features.tokendetails.navigation.TokenDetailsRouter @@ -126,7 +126,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac lateinit var tokenDetailsRouter: TokenDetailsRouter @Inject - lateinit var manageTokensRouter: ManageTokensRouter + lateinit var manageTokensUi: ManageTokensUi @Inject lateinit var walletConnectInteractor: WalletConnectInteractor @@ -213,7 +213,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac walletRouter = walletRouter, walletConnectInteractor = walletConnectInteractor, tokenDetailsRouter = tokenDetailsRouter, - manageTokensRouter = manageTokensRouter, + manageTokensUi = manageTokensUi, cardSdkConfigRepository = cardSdkConfigRepository, sendRouter = sendRouter, qrScanningRouter = qrScanningRouter, diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt index 9dab1b838d..995658a42c 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt @@ -3,7 +3,7 @@ package com.tangem.tap.proxy.redux import com.tangem.feature.qrscanning.QrScanningRouter import com.tangem.domain.card.ScanCardUseCase import com.tangem.domain.card.repository.CardSdkConfigRepository -import com.tangem.features.managetokens.navigation.ManageTokensRouter +import com.tangem.features.managetokens.navigation.ManageTokensUi import com.tangem.features.send.api.navigation.SendRouter import com.tangem.features.tester.api.TesterRouter import com.tangem.features.tokendetails.navigation.TokenDetailsRouter @@ -19,7 +19,7 @@ sealed interface DaggerGraphAction : Action { val walletRouter: WalletRouter, val walletConnectInteractor: WalletConnectInteractor, val tokenDetailsRouter: TokenDetailsRouter, - val manageTokensRouter: ManageTokensRouter, + val manageTokensUi: ManageTokensUi, val cardSdkConfigRepository: CardSdkConfigRepository, val sendRouter: SendRouter, val qrScanningRouter: QrScanningRouter, diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt index 5f2030f390..7074523df0 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt @@ -18,7 +18,7 @@ object DaggerGraphReducer { walletRouter = action.walletRouter, walletConnectInteractor = action.walletConnectInteractor, tokenDetailsRouter = action.tokenDetailsRouter, - manageTokensRouter = action.manageTokensRouter, + manageTokensUi = action.manageTokensUi, cardSdkConfigRepository = action.cardSdkConfigRepository, sendRouter = action.sendRouter, qrScanningRouter = action.qrScanningRouter, diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt index febaf4d633..4a543195d6 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt @@ -15,7 +15,7 @@ import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.feature.qrscanning.QrScanningRouter import com.tangem.features.managetokens.featuretoggles.ManageTokensFeatureToggles -import com.tangem.features.managetokens.navigation.ManageTokensRouter +import com.tangem.features.managetokens.navigation.ManageTokensUi import com.tangem.features.send.api.featuretoggles.SendFeatureToggles import com.tangem.features.send.api.navigation.SendRouter import com.tangem.features.tester.api.TesterRouter @@ -39,7 +39,7 @@ data class DaggerGraphState( val walletConnectInteractor: WalletConnectInteractor? = null, val tokenDetailsRouter: TokenDetailsRouter? = null, val manageTokensFeatureToggles: ManageTokensFeatureToggles? = null, - val manageTokensRouter: ManageTokensRouter? = null, + val manageTokensUi: ManageTokensUi? = null, val scanCardProcessor: ScanCardProcessor? = null, val cardSdkConfigRepository: CardSdkConfigRepository? = null, val appCurrencyRepository: AppCurrencyRepository? = null, diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetConfigContent.kt b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetConfigContent.kt index 409e1430d0..093d9cfce7 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetConfigContent.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetConfigContent.kt @@ -6,4 +6,6 @@ import androidx.compose.runtime.Immutable * General interface for bottom sheet config model */ @Immutable -interface TangemBottomSheetConfigContent \ No newline at end of file +interface TangemBottomSheetConfigContent { + object Empty : TangemBottomSheetConfigContent +} \ No newline at end of file diff --git a/features/manage-tokens/api/build.gradle.kts b/features/manage-tokens/api/build.gradle.kts index 1195231904..cbfb3f7b1f 100644 --- a/features/manage-tokens/api/build.gradle.kts +++ b/features/manage-tokens/api/build.gradle.kts @@ -9,9 +9,5 @@ android { } dependencies { - /** AndroidX */ - implementation(deps.androidx.fragment.ktx) - implementation(deps.compose.foundation) - implementation(deps.compose.navigation) } \ No newline at end of file diff --git a/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/navigation/ManageTokensRouter.kt b/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/navigation/ManageTokensRouter.kt deleted file mode 100644 index b6310d4e11..0000000000 --- a/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/navigation/ManageTokensRouter.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.tangem.features.managetokens.navigation - -import androidx.compose.ui.unit.Dp -import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController - -interface ManageTokensRouter { - - val startDestination: String - - fun NavGraphBuilder.initialize(navController: NavHostController, onHeaderSizeChange: (Dp) -> Unit) -} \ No newline at end of file diff --git a/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/navigation/ManageTokensUi.kt b/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/navigation/ManageTokensUi.kt new file mode 100644 index 0000000000..7e5c95da12 --- /dev/null +++ b/features/manage-tokens/api/src/main/kotlin/com/tangem/features/managetokens/navigation/ManageTokensUi.kt @@ -0,0 +1,10 @@ +package com.tangem.features.managetokens.navigation + +import androidx.compose.runtime.Composable +import androidx.compose.ui.unit.Dp + +interface ManageTokensUi { + @Suppress("TopLevelComposableFunctions") + @Composable + fun Content(onHeaderSizeChange: (Dp) -> Unit) +} \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/di/ManageTokensRouterModule.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/di/ManageTokensRouterModule.kt index 12c7db4c53..e0ca63c3b2 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/di/ManageTokensRouterModule.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/di/ManageTokensRouterModule.kt @@ -1,21 +1,18 @@ package com.tangem.managetokens.di -import com.tangem.core.navigation.ReduxNavController -import com.tangem.features.managetokens.navigation.ManageTokensRouter -import com.tangem.managetokens.presentation.router.DefaultManageTokensRouter +import com.tangem.features.managetokens.navigation.ManageTokensUi +import com.tangem.managetokens.presentation.router.ManageTokensUiImpl +import dagger.Binds import dagger.Module -import dagger.Provides import dagger.hilt.InstallIn -import dagger.hilt.android.components.ActivityComponent -import dagger.hilt.android.scopes.ActivityScoped +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton @Module -@InstallIn(ActivityComponent::class) -internal object ManageTokensRouterModule { +@InstallIn(SingletonComponent::class) +internal interface ManageTokensRouterModule { - @Provides - @ActivityScoped - fun provideManageTokensRouter(reduxNavController: ReduxNavController): ManageTokensRouter { - return DefaultManageTokensRouter(reduxNavController) - } + @Binds + @Singleton + fun provideManageTokensRouter(manageTokensUiImpl: ManageTokensUiImpl): ManageTokensUi } \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/router/AddCustomTokenRoute.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/router/AddCustomTokenRoute.kt new file mode 100644 index 0000000000..fad644416a --- /dev/null +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/router/AddCustomTokenRoute.kt @@ -0,0 +1,14 @@ +package com.tangem.managetokens.presentation.addcustomtoken.router + +/** + * Add Custom Tokens screens + * @property route route string representation + */ +internal sealed class AddCustomTokenRoute(val route: String) { + object Main : AddCustomTokenRoute("$BASE_ROUTE/main") + object ChooseNetwork : AddCustomTokenRoute("$BASE_ROUTE/choose_network") + object ChooseWallet : AddCustomTokenRoute("$BASE_ROUTE/choose_wallet") + object ChooseDerivation : AddCustomTokenRoute("$BASE_ROUTE/choose_derivation") +} + +private const val BASE_ROUTE = "manage_tokens/add_custom_token" \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/router/AddCustomTokenRouter.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/router/AddCustomTokenRouter.kt new file mode 100644 index 0000000000..4185ccddd4 --- /dev/null +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/router/AddCustomTokenRouter.kt @@ -0,0 +1,29 @@ +package com.tangem.managetokens.presentation.addcustomtoken.router + +import androidx.compose.runtime.Stable +import androidx.navigation.NavController + +@Stable +internal class AddCustomTokenRouter( + private val navController: NavController, +) { + /** Pop back stack */ + fun popBackStack() { + navController.popBackStack() + } + + /** Open custom token choose network screen */ + fun openCustomTokenChooseNetwork() { + navController.navigate(AddCustomTokenRoute.ChooseNetwork.route) + } + + /** Open custom token choose derivation screen */ + fun openCustomTokenChooseDerivation() { + navController.navigate(AddCustomTokenRoute.ChooseDerivation.route) + } + + /** Open custom token choose wallet screen */ + fun openCustomTokenChooseWallet() { + navController.navigate(AddCustomTokenRoute.ChooseWallet.route) + } +} \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/AddCustomTokenBottomSheet.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/AddCustomTokenBottomSheet.kt new file mode 100644 index 0000000000..499c2e6cb4 --- /dev/null +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/AddCustomTokenBottomSheet.kt @@ -0,0 +1,115 @@ +package com.tangem.managetokens.presentation.addcustomtoken.ui + +import android.annotation.SuppressLint +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.only +import androidx.compose.foundation.layout.systemBars +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetDraggableHeader +import com.tangem.core.ui.components.bottomsheets.collapse +import com.tangem.core.ui.res.TangemTheme +import com.tangem.managetokens.presentation.addcustomtoken.router.AddCustomTokenRoute +import com.tangem.managetokens.presentation.addcustomtoken.router.AddCustomTokenRouter +import com.tangem.managetokens.presentation.addcustomtoken.viewmodels.AddCustomTokenViewModel +import com.tangem.managetokens.presentation.common.state.ChooseWalletState + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun AddCustomTokenBottomSheet(config: TangemBottomSheetConfig) { + val viewModel = hiltViewModel() + + var isVisible by remember { mutableStateOf(value = config.isShow) } + val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) + + if (isVisible) { + // ViewModel cannot be scoped to ModalBottomSheet's lifecycle, + // so we have to manually initialize and dispose its state when bottom sheet enters and leaves a composition + DisposableEffect(viewModel) { + viewModel.onInitialize() + onDispose { viewModel.onDispose() } + } + + // FIXME: handle back presses after updating material3 to 1.2.0 + ModalBottomSheet( + onDismissRequest = config.onDismissRequest, + sheetState = sheetState, + containerColor = TangemTheme.colors.background.tertiary, + shape = TangemTheme.shapes.bottomSheetLarge, + windowInsets = WindowInsets.systemBars.only(WindowInsetsSides.Top), + dragHandle = { TangemBottomSheetDraggableHeader(color = TangemTheme.colors.background.tertiary) }, + ) { + Content(onDismissRequest = config.onDismissRequest, viewModel = viewModel) + } + } + + LaunchedEffect(key1 = config.isShow) { + if (config.isShow) { + isVisible = true + } else { + sheetState.collapse { isVisible = false } + } + } +} + +@SuppressLint("RestrictedApi") +@Composable +private fun Content(viewModel: AddCustomTokenViewModel, onDismissRequest: () -> Unit) { + val navController = rememberNavController() + + LaunchedEffect(navController) { + navController.currentBackStack + .collect { + if (it.isEmpty()) { + onDismissRequest() + } + } + } + + val router = remember(navController) { AddCustomTokenRouter(navController) } + + viewModel.router = router + + NavHost( + modifier = Modifier.fillMaxSize(), + navController = navController, + startDestination = AddCustomTokenRoute.Main.route, + ) { + composable( + route = AddCustomTokenRoute.Main.route, + ) { + AddCustomTokenScreen(state = viewModel.uiState) + } + composable( + route = AddCustomTokenRoute.ChooseNetwork.route, + ) { + ChooseNetworkCustomScreen(state = viewModel.uiState.chooseNetworkState) + } + composable( + route = AddCustomTokenRoute.ChooseDerivation.route, + ) { + ChooseDerivationScreen(state = requireNotNull(viewModel.uiState.chooseDerivationState)) + } + composable( + route = AddCustomTokenRoute.ChooseWallet.route, + ) { + CustomTokensChooseWalletScreen(state = viewModel.uiState.chooseWalletState as ChooseWalletState.Choose) + } + } +} \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/viewmodels/AddCustomTokenViewModel.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/viewmodels/AddCustomTokenViewModel.kt index 9211b1aaf1..1efdf2911b 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/viewmodels/AddCustomTokenViewModel.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/viewmodels/AddCustomTokenViewModel.kt @@ -1,5 +1,6 @@ package com.tangem.managetokens.presentation.addcustomtoken.viewmodels +import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue @@ -20,6 +21,7 @@ import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.domain.wallets.usecase.GetWalletsUseCase import com.tangem.domain.wallets.usecase.SelectWalletUseCase +import com.tangem.managetokens.presentation.addcustomtoken.router.AddCustomTokenRouter import com.tangem.managetokens.presentation.common.analytics.ManageTokens import com.tangem.managetokens.presentation.common.state.AlertState import com.tangem.managetokens.presentation.common.state.Event @@ -29,22 +31,20 @@ import com.tangem.managetokens.presentation.addcustomtoken.state.factory.AddCust import com.tangem.managetokens.presentation.addcustomtoken.state.factory.ContractAddressToCustomTokenDataConverter import com.tangem.managetokens.presentation.addcustomtoken.state.factory.AddCustomTokenStateFactory import com.tangem.managetokens.presentation.addcustomtoken.state.factory.FoundTokenToCustomTokenDataConverter -import com.tangem.managetokens.presentation.router.InnerManageTokensRouter import com.tangem.utils.Provider import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.Debouncer import com.tangem.utils.coroutines.Debouncer.Companion.DEFAULT_WAIT_TIME_MS import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.toPersistentSet +import kotlinx.coroutines.* import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.launch -import kotlinx.coroutines.plus -import kotlinx.coroutines.withContext import javax.inject.Inject import kotlin.properties.Delegates @Suppress("LongParameterList", "TooManyFunctions", "LargeClass") +@Stable @HiltViewModel internal class AddCustomTokenViewModel @Inject constructor( private val dispatchers: CoroutineDispatcherProvider, @@ -67,12 +67,15 @@ internal class AddCustomTokenViewModel @Inject constructor( clickIntents = this, ) - var router: InnerManageTokensRouter by Delegates.notNull() + var router: AddCustomTokenRouter by Delegates.notNull() var uiState: AddCustomTokenState by mutableStateOf(stateFactory.getInitialState()) private set - override fun onCreate(owner: LifecycleOwner) { + /** + * Called when the bottom sheet was opened + */ + fun onInitialize() { viewModelScope.launch(dispatchers.io) { getWalletsUseCase() .distinctUntilChanged() @@ -92,6 +95,16 @@ internal class AddCustomTokenViewModel @Inject constructor( } } + /** + * Called after the bottom sheet is closed + */ + fun onDispose() { + // We have to manually cancel viewModelScope's child jobs when bottom sheet is closed + viewModelScope.coroutineContext.cancelChildren() + // and reset state + uiState = stateFactory.getInitialState() + } + override fun onDestroy(owner: LifecycleOwner) { uiState = stateFactory.getInitialState() } diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/ManageTokensState.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/ManageTokensState.kt index 8b4c05217c..6738d56d3f 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/ManageTokensState.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/ManageTokensState.kt @@ -1,6 +1,7 @@ package com.tangem.managetokens.presentation.managetokens.state import androidx.paging.PagingData +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.event.StateEvent import com.tangem.managetokens.presentation.common.state.ChooseWalletState import com.tangem.managetokens.presentation.common.state.Event @@ -15,6 +16,7 @@ internal data class ManageTokensState( val derivationNotification: DerivationNotificationState? = null, val selectedToken: TokenItemState.Loaded? = null, val showChooseWalletScreen: Boolean = false, + val customTokenBottomSheetConfig: TangemBottomSheetConfig, val event: StateEvent, val onEmptySearchResult: (String) -> Unit, ) diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/factory/ManageTokensStateFactory.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/factory/ManageTokensStateFactory.kt index a32b887e26..d7531b2e85 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/factory/ManageTokensStateFactory.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/factory/ManageTokensStateFactory.kt @@ -1,6 +1,8 @@ package com.tangem.managetokens.presentation.managetokens.state.factory import androidx.paging.PagingData +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import com.tangem.core.ui.event.consumedEvent import com.tangem.core.ui.event.triggeredEvent import com.tangem.domain.tokens.CurrencyCompatibilityError @@ -39,6 +41,11 @@ internal class ManageTokensStateFactory( event = consumedEvent(), chooseWalletState = ChooseWalletState.NoSelection, onEmptySearchResult = uiIntents::onEmptySearchResult, + customTokenBottomSheetConfig = TangemBottomSheetConfig( + isShow = false, + onDismissRequest = uiIntents::onAddCustomTokenSheetDismissed, + content = TangemBottomSheetConfigContent.Empty, + ), ) } diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/previewdata/ManageTokensStatePreviewData.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/previewdata/ManageTokensStatePreviewData.kt index 5d141ddbaa..40d4334eb3 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/previewdata/ManageTokensStatePreviewData.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/state/previewdata/ManageTokensStatePreviewData.kt @@ -1,6 +1,8 @@ package com.tangem.managetokens.presentation.managetokens.state.previewdata import androidx.paging.PagingData +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import com.tangem.core.ui.event.consumedEvent import com.tangem.managetokens.presentation.common.state.previewdata.ChooseWalletStatePreviewData import com.tangem.managetokens.presentation.managetokens.state.AddCustomTokenButton @@ -20,6 +22,7 @@ internal object ManageTokensStatePreviewData { event = consumedEvent(), chooseWalletState = ChooseWalletStatePreviewData.state, onEmptySearchResult = {}, + customTokenBottomSheetConfig = TangemBottomSheetConfig(false, {}, TangemBottomSheetConfigContent.Empty), ) val loadingState: ManageTokensState diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ManageTokensScreen.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ManageTokensScreen.kt index 28dd482aeb..12f7a0a191 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ManageTokensScreen.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ManageTokensScreen.kt @@ -23,6 +23,7 @@ import com.tangem.core.ui.components.SpacerH18 import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.components.keyboardAsState import com.tangem.core.ui.res.TangemTheme +import com.tangem.managetokens.presentation.addcustomtoken.ui.AddCustomTokenBottomSheet import com.tangem.managetokens.presentation.common.state.AlertState import com.tangem.managetokens.presentation.common.state.ChooseWalletState import com.tangem.managetokens.presentation.common.ui.ChooseWalletBottomSheet @@ -49,6 +50,8 @@ internal fun ManageTokensScreen(state: ManageTokensState, onHeaderSizeChange: (D } Content(state = state, onHeaderSizeChange = onHeaderSizeChange) + + AddCustomTokenBottomSheet(state.customTokenBottomSheetConfig) } @Composable @@ -122,6 +125,9 @@ private fun Content(state: ManageTokensState, onHeaderSizeChange: (Dp) -> Unit) with(density) { tokenListAlertBottomPadding = it.size.height.toDp() } }, ) + DisposableEffect(Unit) { + onDispose { tokenListAlertBottomPadding = 0.dp } + } } } } diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensUiEvents.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensUiEvents.kt index a5ce66e8d5..663871cf2a 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensUiEvents.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensUiEvents.kt @@ -3,4 +3,6 @@ package com.tangem.managetokens.presentation.managetokens.viewmodels internal interface ManageTokensUiEvents { fun onEmptySearchResult(query: String) + + fun onAddCustomTokenSheetDismissed() } \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensViewModel.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensViewModel.kt index a2bb0805e8..ad42c3159e 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensViewModel.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/viewmodels/ManageTokensViewModel.kt @@ -3,7 +3,6 @@ package com.tangem.managetokens.presentation.managetokens.viewmodels import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue -import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.paging.PagingData @@ -30,7 +29,6 @@ import com.tangem.managetokens.presentation.managetokens.state.ManageTokensState import com.tangem.managetokens.presentation.managetokens.state.TokenButtonType import com.tangem.managetokens.presentation.managetokens.state.TokenItemState import com.tangem.managetokens.presentation.managetokens.state.factory.* -import com.tangem.managetokens.presentation.router.InnerManageTokensRouter import com.tangem.utils.Provider import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.Debouncer @@ -62,7 +60,7 @@ internal class ManageTokensViewModel @Inject constructor( private val checkCurrencyCompatibilityUseCase: CheckCurrencyCompatibilityUseCase, private val isCryptoCurrencyCoinCouldHide: IsCryptoCurrencyCoinCouldHideUseCase, private val analyticsEventHandler: AnalyticsEventHandler, -) : ViewModel(), ManageTokensClickIntents, ManageTokensUiEvents, DefaultLifecycleObserver { +) : ViewModel(), ManageTokensClickIntents, ManageTokensUiEvents { private val debouncer = Debouncer() @@ -72,8 +70,6 @@ internal class ManageTokensViewModel @Inject constructor( uiIntents = this, ) - var router: InnerManageTokensRouter by Delegates.notNull() - var uiState: ManageTokensState by mutableStateOf(stateFactory.getInitialState(flowOf(PagingData.from(emptyList())))) private set @@ -190,7 +186,7 @@ internal class ManageTokensViewModel @Inject constructor( override fun onAddCustomTokensButtonClick() { analyticsEventHandler.send(ManageTokens.ButtonCustomToken) - router.openAddCustomTokenScreen() + uiState = uiState.copy(customTokenBottomSheetConfig = uiState.customTokenBottomSheetConfig.copy(isShow = true)) } override fun onSearchQueryChange(query: String) { @@ -407,4 +403,8 @@ internal class ManageTokensViewModel @Inject constructor( override fun onEmptySearchResult(query: String) { analyticsEventHandler.send(ManageTokens.TokenIsNotFound(query)) } + + override fun onAddCustomTokenSheetDismissed() { + uiState = uiState.copy(customTokenBottomSheetConfig = uiState.customTokenBottomSheetConfig.copy(isShow = false)) + } } \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/DefaultManageTokensRouter.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/DefaultManageTokensRouter.kt deleted file mode 100644 index 5ecebe79fe..0000000000 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/DefaultManageTokensRouter.kt +++ /dev/null @@ -1,124 +0,0 @@ -package com.tangem.managetokens.presentation.router - -import androidx.compose.ui.platform.LocalLifecycleOwner -import androidx.compose.ui.unit.Dp -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavHostController -import androidx.navigation.compose.composable -import androidx.navigation.navigation -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction -import com.tangem.core.navigation.ReduxNavController -import com.tangem.core.ui.extensions.parentHiltViewModel -import com.tangem.managetokens.presentation.addcustomtoken.ui.AddCustomTokenScreen -import com.tangem.managetokens.presentation.addcustomtoken.ui.ChooseDerivationScreen -import com.tangem.managetokens.presentation.addcustomtoken.ui.ChooseNetworkCustomScreen -import com.tangem.managetokens.presentation.addcustomtoken.ui.CustomTokensChooseWalletScreen -import com.tangem.managetokens.presentation.addcustomtoken.viewmodels.AddCustomTokenViewModel -import com.tangem.managetokens.presentation.common.state.ChooseWalletState -import com.tangem.managetokens.presentation.managetokens.ui.ManageTokensScreen -import com.tangem.managetokens.presentation.managetokens.viewmodels.ManageTokensViewModel -import kotlin.properties.Delegates - -internal class DefaultManageTokensRouter( - private val reduxNavController: ReduxNavController, -) : InnerManageTokensRouter { - - private var navController: NavHostController by Delegates.notNull() - - override val startDestination: String = ManageTokensRoute.ManageTokens.route - - override fun NavGraphBuilder.initialize(navController: NavHostController, onHeaderSizeChange: (Dp) -> Unit) { - this@DefaultManageTokensRouter.navController = navController - - composable(ManageTokensRoute.ManageTokens.route) { - val viewModel = hiltViewModel().apply { - router = this@DefaultManageTokensRouter - } - LocalLifecycleOwner.current.lifecycle.addObserver(viewModel) - - ManageTokensScreen( - state = viewModel.uiState, - onHeaderSizeChange = onHeaderSizeChange, - ) - } - - navigation( - route = ManageTokensRoute.AddCustomToken.route, - startDestination = ManageTokensRoute.AddCustomToken.Main.route, - ) { - composable( - ManageTokensRoute.AddCustomToken.Main.route, - ) { backStackEntry -> - val viewModel = - backStackEntry.parentHiltViewModel(navController = navController).apply { - router = this@DefaultManageTokensRouter - } - LocalLifecycleOwner.current.lifecycle.addObserver(viewModel) - AddCustomTokenScreen(state = viewModel.uiState) - } - composable( - ManageTokensRoute.AddCustomToken.ChooseNetwork.route, - ) { backStackEntry -> - val viewModel = - backStackEntry.parentHiltViewModel(navController = navController).apply { - router = this@DefaultManageTokensRouter - } - ChooseNetworkCustomScreen( - state = viewModel.uiState.chooseNetworkState, - ) - } - composable( - ManageTokensRoute.AddCustomToken.ChooseDerivation.route, - ) { backStackEntry -> - val viewModel = - backStackEntry.parentHiltViewModel(navController = navController).apply { - router = this@DefaultManageTokensRouter - } - ChooseDerivationScreen( - state = requireNotNull(viewModel.uiState.chooseDerivationState), - ) - } - composable( - ManageTokensRoute.AddCustomToken.ChooseWallet.route, - ) { backStackEntry -> - val viewModel = - backStackEntry.parentHiltViewModel(navController = navController).apply { - router = this@DefaultManageTokensRouter - } - CustomTokensChooseWalletScreen( - state = viewModel.uiState.chooseWalletState as ChooseWalletState.Choose, - ) - } - } - } - - override fun popBackStack(screen: AppScreen?) { - if (screen != null) { - reduxNavController.navigate(action = NavigationAction.PopBackTo(screen)) - } else { - navController.popBackStack() - } - } - - override fun openManageTokensScreen() { - navController.navigate(ManageTokensRoute.ManageTokens.route) - } - - override fun openAddCustomTokenScreen() { - navController.navigate(ManageTokensRoute.AddCustomToken.route) - } - - override fun openCustomTokenChooseNetwork() { - navController.navigate(ManageTokensRoute.AddCustomToken.ChooseNetwork.route) - } - - override fun openCustomTokenChooseDerivation() { - navController.navigate(ManageTokensRoute.AddCustomToken.ChooseDerivation.route) - } - - override fun openCustomTokenChooseWallet() { - navController.navigate(ManageTokensRoute.AddCustomToken.ChooseWallet.route) - } -} \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/InnerManageTokensRouter.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/InnerManageTokensRouter.kt deleted file mode 100644 index 999ec29252..0000000000 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/InnerManageTokensRouter.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.tangem.managetokens.presentation.router - -import com.tangem.core.navigation.AppScreen -import com.tangem.features.managetokens.navigation.ManageTokensRouter - -internal interface InnerManageTokensRouter : ManageTokensRouter { - - /** Pop back stack */ - fun popBackStack(screen: AppScreen? = null) - - /** Open manage tokens screen */ - fun openManageTokensScreen() - - /** Open add custom token screen */ - fun openAddCustomTokenScreen() - - /** Open custom token choose network screen */ - fun openCustomTokenChooseNetwork() - - /** Open custom token choose derivation screen */ - fun openCustomTokenChooseDerivation() - - /** Open custom token choose wallet screen */ - fun openCustomTokenChooseWallet() -} \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/ManageTokensRoute.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/ManageTokensRoute.kt deleted file mode 100644 index 705769d5b0..0000000000 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/ManageTokensRoute.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.tangem.managetokens.presentation.router - -/** - * Manage Tokens screens - * - * @property route route string representation - * - */ -internal sealed class ManageTokensRoute(val route: String) { - - object ManageTokens : ManageTokensRoute(route = "manage_tokens") - - object AddCustomToken : ManageTokensRoute(route = "manage_tokens/add_custom_token") { - object Main : ManageTokensRoute(AddCustomToken.route + "/main") - object ChooseNetwork : ManageTokensRoute(AddCustomToken.route + "/choose_network") - object ChooseWallet : ManageTokensRoute(AddCustomToken.route + "/choose_wallet") - object ChooseDerivation : ManageTokensRoute(AddCustomToken.route + "/choose_derivation") - } -} \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/ManageTokensUiImpl.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/ManageTokensUiImpl.kt new file mode 100644 index 0000000000..3dd460d06f --- /dev/null +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/router/ManageTokensUiImpl.kt @@ -0,0 +1,22 @@ +package com.tangem.managetokens.presentation.router + +import androidx.compose.runtime.Composable +import androidx.compose.ui.unit.Dp +import androidx.hilt.navigation.compose.hiltViewModel +import com.tangem.features.managetokens.navigation.ManageTokensUi +import com.tangem.managetokens.presentation.managetokens.ui.ManageTokensScreen +import com.tangem.managetokens.presentation.managetokens.viewmodels.ManageTokensViewModel +import javax.inject.Inject + +internal class ManageTokensUiImpl @Inject constructor() : ManageTokensUi { + + @Composable + override fun Content(onHeaderSizeChange: (Dp) -> Unit) { + val viewModel = hiltViewModel() + + ManageTokensScreen( + state = viewModel.uiState, + onHeaderSizeChange = onHeaderSizeChange, + ) + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt index c13cfa5e90..74df363767 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/WalletFragment.kt @@ -7,7 +7,7 @@ import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.screen.ComposeFragment import com.tangem.core.ui.theme.AppThemeModeHolder import com.tangem.feature.wallet.presentation.router.InnerWalletRouter -import com.tangem.features.managetokens.navigation.ManageTokensRouter +import com.tangem.features.managetokens.navigation.ManageTokensUi import com.tangem.features.wallet.navigation.WalletRouter import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject @@ -24,7 +24,7 @@ internal class WalletFragment : ComposeFragment() { override lateinit var appThemeModeHolder: AppThemeModeHolder @Inject - internal lateinit var manageTokensRouter: ManageTokensRouter + internal lateinit var manageTokensUi: ManageTokensUi /** Feature router */ @Inject @@ -44,7 +44,7 @@ internal class WalletFragment : ComposeFragment() { _walletRouter.Initialize( onFinish = requireActivity()::finish, - manageTokensRouter = manageTokensRouter, + manageTokensUi = manageTokensUi, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index 24c2270f96..54ca84fe4d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -1,5 +1,6 @@ package com.tangem.feature.wallet.presentation.router +import android.annotation.SuppressLint import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.runtime.* import androidx.compose.ui.Modifier @@ -27,7 +28,7 @@ import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensScree import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensViewModel import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletViewModel -import com.tangem.features.managetokens.navigation.ManageTokensRouter +import com.tangem.features.managetokens.navigation.ManageTokensUi import com.tangem.features.tokendetails.navigation.TokenDetailsRouter import kotlin.properties.Delegates @@ -42,7 +43,7 @@ internal class DefaultWalletRouter( override fun getEntryFragment(): Fragment = WalletFragment.create() @Composable - override fun Initialize(onFinish: () -> Unit, manageTokensRouter: ManageTokensRouter) { + override fun Initialize(onFinish: () -> Unit, manageTokensUi: ManageTokensUi) { this.onFinish = onFinish NavHost( @@ -56,24 +57,15 @@ internal class DefaultWalletRouter( } var bottomSheetHeaderHeight by remember { mutableStateOf(0.dp) } - val innerController = rememberNavController() WalletScreen( state = viewModel.uiState.collectAsStateWithLifecycle().value, bottomSheetHeaderHeightProvider = { bottomSheetHeaderHeight }, bottomSheetContent = { - NavHost( - navController = innerController, - startDestination = manageTokensRouter.startDestination, - ) { - // Manage Tokens - with(manageTokensRouter) { - initialize( - navController = innerController, - onHeaderSizeChange = { bottomSheetHeaderHeight = it }, - ) - } - } + // Manage Tokens + manageTokensUi.Content( + onHeaderSizeChange = { bottomSheetHeaderHeight = it }, + ) }, ) } @@ -97,6 +89,7 @@ internal class DefaultWalletRouter( } } + @SuppressLint("RestrictedApi") override fun popBackStack(screen: AppScreen?) { /* * It's hack that avoid issue with closing the wallet screen. diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt index 41abc741f6..9cfd2fe858 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt @@ -5,7 +5,7 @@ import androidx.compose.runtime.Stable import com.tangem.core.navigation.AppScreen import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.features.managetokens.navigation.ManageTokensRouter +import com.tangem.features.managetokens.navigation.ManageTokensUi import com.tangem.features.wallet.navigation.WalletRouter /** @@ -26,7 +26,7 @@ internal interface InnerWalletRouter : WalletRouter { */ @Suppress("TopLevelComposableFunctions") @Composable - fun Initialize(onFinish: () -> Unit, manageTokensRouter: ManageTokensRouter) + fun Initialize(onFinish: () -> Unit, manageTokensUi: ManageTokensUi) /** Pop back stack */ fun popBackStack(screen: AppScreen? = null) diff --git a/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/configurations/extension/BaseExtensionConfigurations.kt b/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/configurations/extension/BaseExtensionConfigurations.kt index 3504fee112..142649ae2a 100644 --- a/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/configurations/extension/BaseExtensionConfigurations.kt +++ b/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/configurations/extension/BaseExtensionConfigurations.kt @@ -23,6 +23,7 @@ internal fun BaseExtension.configureCompose(project: Project) { contains(Regex(pattern = ":onboarding\$")) || // TODO: divide on api/impl after migrating all onboarding to module contains(Regex(pattern = ":presentation\$")) || contains(Regex(pattern = ":app\$")) || // TODO: [REDACTED_JIRA] + contains(Regex(pattern = ":features:manage-tokens:api\$")) || // provides Composable function contains(Regex(pattern = ":impl\$")) } From c570ff9ca678e3ffd2d678dac2d7faea57c60060 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 16 Feb 2024 19:35:41 +0400 Subject: [PATCH 47/53] Updated on 2026-08-14 --- .../tangem/core/ui/extensions/BlockchainIcons.kt | 5 +++++ .../ui/src/main/res/drawable/ic_playa3ull_22.xml | 12 ++++++++++++ .../src/main/res/drawable/img_playa3ull_22.xml | 16 ++++++++++++++++ .../domain/common/extensions/Blockchain.kt | 7 +++---- gradle/dependencies.toml | 2 +- 5 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 core/ui/src/main/res/drawable/ic_playa3ull_22.xml create mode 100644 core/ui/src/main/res/drawable/img_playa3ull_22.xml diff --git a/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt b/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt index e38813089f..c5a3195582 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt @@ -52,6 +52,7 @@ fun getActiveIconRes(blockchainId: String): Int { "shibarium", "shibarium/test" -> R.drawable.img_shibarium_22 "algorand", "algorand/test" -> R.drawable.img_algorand_22 "hedera", "hedera/test" -> R.drawable.img_hedera_22 + "playa3ull" -> R.drawable.img_playa3ull_22 else -> R.drawable.ic_alert_24 } } @@ -105,6 +106,7 @@ fun getActiveIconResByNetworkId(networkId: String): Int { "shibarium", "shibarium/test" -> R.drawable.img_shibarium_22 "algorand", "algorand/test" -> R.drawable.img_algorand_22 "hedera-hashgraph", "hedera/test" -> R.drawable.img_hedera_22 + "playa3ull-games" -> R.drawable.img_playa3ull_22 else -> R.drawable.ic_alert_24 } } @@ -155,6 +157,7 @@ fun getActiveIconResByCoinId(coinId: String): Int { "shibarium" -> R.drawable.img_shibarium_22 "algorand" -> R.drawable.img_algorand_22 "hedera-hashgraph" -> R.drawable.img_hedera_22 + "playa3ull-games-2" -> R.drawable.img_playa3ull_22 else -> R.drawable.ic_alert_24 } } @@ -208,6 +211,7 @@ fun getGreyedOutIconRes(blockchainId: String): Int { "shibarium", "shibarium/test" -> R.drawable.ic_shibarium_22 "algorand", "algorand/test" -> R.drawable.ic_algorand_22 "hedera", "hedera/test" -> R.drawable.ic_hedera_22 + "playa3ull" -> R.drawable.ic_playa3ull_22 else -> R.drawable.ic_alert_24 } } @@ -261,6 +265,7 @@ fun getGreyedOutIconResByNetworkId(networkId: String): Int { "shibarium", "shibarium/test" -> R.drawable.ic_shibarium_22 "algorand", "algorand/test" -> R.drawable.ic_algorand_22 "hedera-hashgraph", "hedera/test" -> R.drawable.ic_hedera_22 + "playa3ull-games" -> R.drawable.ic_playa3ull_22 else -> R.drawable.ic_alert_24 } } \ No newline at end of file diff --git a/core/ui/src/main/res/drawable/ic_playa3ull_22.xml b/core/ui/src/main/res/drawable/ic_playa3ull_22.xml new file mode 100644 index 0000000000..ad533a3abb --- /dev/null +++ b/core/ui/src/main/res/drawable/ic_playa3ull_22.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/core/ui/src/main/res/drawable/img_playa3ull_22.xml b/core/ui/src/main/res/drawable/img_playa3ull_22.xml new file mode 100644 index 0000000000..4a9a544f84 --- /dev/null +++ b/core/ui/src/main/res/drawable/img_playa3ull_22.xml @@ -0,0 +1,16 @@ + + + + + + + diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt index a971c818c1..86fc4fd0be 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt @@ -80,7 +80,7 @@ fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? { "vechain/test" -> Blockchain.VeChainTestnet "aptos" -> Blockchain.Aptos "aptos/test" -> Blockchain.AptosTestnet - "" -> Blockchain.Playa3ull // FIXME + "playa3ull-games" -> Blockchain.Playa3ull "shibarium" -> Blockchain.Shibarium "shibarium/test" -> Blockchain.ShibariumTestnet "algorand" -> Blockchain.Algorand @@ -168,7 +168,7 @@ fun Blockchain.toNetworkId(): String { Blockchain.VeChainTestnet -> "vechain/test" Blockchain.Aptos -> "aptos" Blockchain.AptosTestnet -> "aptos/test" - Blockchain.Playa3ull -> "" // FIXME + Blockchain.Playa3ull -> "playa3ull-games" Blockchain.Shibarium -> "shibarium" Blockchain.ShibariumTestnet -> "shibarium/test" Blockchain.Algorand -> "algorand" @@ -226,7 +226,7 @@ fun Blockchain.toCoinId(): String { Blockchain.VeChain, Blockchain.VeChainTestnet -> "vechain" Blockchain.Aptos -> "aptos" Blockchain.AptosTestnet -> "aptos/test" - Blockchain.Playa3ull -> "" // FIXME + Blockchain.Playa3ull -> "playa3ull-games-2" Blockchain.Shibarium -> "bone-shibaswap" Blockchain.ShibariumTestnet -> "bone-shibaswap/test" Blockchain.Algorand -> "algorand" @@ -261,5 +261,4 @@ private const val NODL_AMOUNT_TO_CREATE_ACCOUNT = 1.5 private val excludedBlockchains = listOf( Blockchain.Unknown, Blockchain.Ducatus, - Blockchain.Playa3ull, ) \ No newline at end of file diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 3e8fc614e9..ffbb71e675 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -85,7 +85,7 @@ web3j = "4.10.1" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-489" +tangemBlockchainSdk = "develop-490" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-324" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From c3fa13f53d79c9b330528a054ed64f4a6a20055b Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 15 Feb 2024 09:22:30 +0300 Subject: [PATCH 48/53] Updated on 2026-08-14 --- app/src/main/AndroidManifest.xml | 2 +- .../main/java/com/tangem/tap/common/CustomTabsManager.kt | 6 ++++++ .../wallet/redux/middlewares/TradeCryptoMiddleware.kt | 4 +--- .../tangem/tap/network/exchangeServices/ExchangeService.kt | 2 +- .../com/tangem/core/deeplink/global/BuyCurrencyDeepLink.kt | 2 +- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 32e44932c0..99e803e45b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -120,7 +120,7 @@ diff --git a/app/src/main/java/com/tangem/tap/common/CustomTabsManager.kt b/app/src/main/java/com/tangem/tap/common/CustomTabsManager.kt index a913befc98..08f62eee72 100644 --- a/app/src/main/java/com/tangem/tap/common/CustomTabsManager.kt +++ b/app/src/main/java/com/tangem/tap/common/CustomTabsManager.kt @@ -1,6 +1,8 @@ package com.tangem.tap.common import android.content.Context +import android.content.Intent.FLAG_ACTIVITY_NEW_TASK +import android.content.Intent.FLAG_ACTIVITY_NO_HISTORY import android.net.Uri import androidx.browser.customtabs.CustomTabColorSchemeParams import androidx.browser.customtabs.CustomTabsIntent @@ -22,6 +24,10 @@ class CustomTabsManager { if (MutableAppThemeModeHolder.isDarkThemeActive) COLOR_SCHEME_DARK else COLOR_SCHEME_LIGHT, ) .build() + + // Open CustomTabsActivity as new task without saving into the stack + customTabsIntent.intent.setFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_NO_HISTORY) + customTabsIntent.launchUrl(context, Uri.parse(url)) } } \ No newline at end of file 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 2432321a48..a3425aeb83 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 @@ -59,9 +59,7 @@ object TradeCryptoMiddleware { is TradeCryptoAction.FinishSelling -> openReceiptUrl(action.transactionId) is TradeCryptoAction.Buy -> proceedBuyAction(state, action) is TradeCryptoAction.Sell -> proceedSellAction(action) - is TradeCryptoAction.Swap -> openSwap( - currency = action.cryptoCurrency, - ) + is TradeCryptoAction.Swap -> openSwap(currency = action.cryptoCurrency) is TradeCryptoAction.SendToken -> { if (isSendRedesignedEnabled) { handleNewSendToken(action = action) diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/ExchangeService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/ExchangeService.kt index b139b14200..c3f3587c26 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/ExchangeService.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/ExchangeService.kt @@ -67,6 +67,6 @@ interface ExchangeUrlBuilder { companion object { const val SCHEME = "https" - const val SUCCESS_URL = "tangem://success.tangem.com" + const val SUCCESS_URL = "https://tangem.com/success" } } \ No newline at end of file diff --git a/core/deep-links/global/src/main/kotlin/com/tangem/core/deeplink/global/BuyCurrencyDeepLink.kt b/core/deep-links/global/src/main/kotlin/com/tangem/core/deeplink/global/BuyCurrencyDeepLink.kt index 286f8f8c73..3c31ec55a7 100644 --- a/core/deep-links/global/src/main/kotlin/com/tangem/core/deeplink/global/BuyCurrencyDeepLink.kt +++ b/core/deep-links/global/src/main/kotlin/com/tangem/core/deeplink/global/BuyCurrencyDeepLink.kt @@ -4,7 +4,7 @@ import com.tangem.core.deeplink.DeepLink class BuyCurrencyDeepLink(val onReceive: () -> Unit) : DeepLink { - override val uri: String = "tangem://success.tangem.com" + override val uri: String = "tangem://redirect?action=dismissBrowser" override fun onReceive(params: Map) { onReceive() From dbad1b430d649f04bad460941ca844f790b2ba9a Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 19 Feb 2024 15:03:35 +0000 Subject: [PATCH 49/53] Updated on 2026-08-14 --- app/src/main/assets/tangem-app-config | 2 +- .../tap/features/send/redux/middlewares/SendMiddleware.kt | 6 ++++++ .../api/tangemTech/models/CreateUserNetworkAccountBody.kt | 2 +- gradle/dependencies.toml | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index a13406537e..82f4a8aebc 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit a13406537ec4b05f930131b151ea6ebb1c0b5ca3 +Subproject commit 82f4a8aebcab924047705ad3ecd5f008f07ca0b5 diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt index 9fba907beb..dcdd0913bb 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt @@ -3,6 +3,7 @@ package com.tangem.tap.features.send.redux.middlewares import com.google.firebase.crashlytics.FirebaseCrashlytics import com.tangem.blockchain.blockchains.binance.BinanceTransactionExtras import com.tangem.blockchain.blockchains.cosmos.CosmosTransactionExtras +import com.tangem.blockchain.blockchains.hedera.HederaTransactionBuilder import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras import com.tangem.blockchain.blockchains.ton.TonTransactionExtras @@ -171,6 +172,11 @@ private fun sendTransaction( transactionExtras.xrpDestinationTag?.tag?.let { txData = txData.copy(extras = XrpTransactionExtras(it)) } transactionExtras.cosmosMemoState?.memo?.let { txData = txData.copy(extras = CosmosTransactionExtras(it)) } transactionExtras.tonMemoState?.memo?.let { txData = txData.copy(extras = TonTransactionExtras(it)) } + transactionExtras.hederaMemoState?.memo?.let { + txData = txData.copy( + extras = HederaTransactionBuilder.HederaTransactionExtras(it), + ) + } scope.launch { // TODO: Risky commented this part, unknown logic, need to test if removed diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/CreateUserNetworkAccountBody.kt b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/CreateUserNetworkAccountBody.kt index 3485e742a0..f359bbd961 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/CreateUserNetworkAccountBody.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/CreateUserNetworkAccountBody.kt @@ -6,5 +6,5 @@ import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) data class CreateUserNetworkAccountBody( @Json(name = "networkId") val networkId: String, - @Json(name = "publicWalletKey") val publicWalletKey: String, + @Json(name = "walletPublicKey") val publicWalletKey: String, ) \ No newline at end of file diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index ffbb71e675..198e563f67 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -85,7 +85,7 @@ web3j = "4.10.1" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-490" +tangemBlockchainSdk = "develop-493" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-324" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From 8417c09f971970b9890ac066984aae5e3a056b30 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 19 Feb 2024 20:31:58 +0500 Subject: [PATCH 50/53] Updated on 2026-08-14 --- .../tap/network/exchangeServices/moonpay/MoonPayApi.kt | 7 +++++++ .../tap/network/exchangeServices/moonpay/MoonPayService.kt | 5 ++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayApi.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayApi.kt index ce7c4c2a7f..e5948ce873 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayApi.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayApi.kt @@ -41,4 +41,11 @@ data class MoonPayCurrencies( val isSupportedInUS: Boolean = false, val isSellSupported: Boolean = false, val notAllowedUSStates: List = emptyList(), + val metadata: MoonPayCurrenciesMetadata? = null, +) + +@JsonClass(generateAdapter = true) +data class MoonPayCurrenciesMetadata( + val contractAddress: String?, + val networkCode: String?, ) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt index 664f31e958..328904eb0a 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt @@ -81,6 +81,7 @@ class MoonPayService( override fun availableForSell(currency: Currency): Boolean { val availableForSell = status?.availableForSell ?: return false + val metadata = status?.responseCurrencies?.map { it.metadata } if (!isSellAllowed()) return false return when (currency) { @@ -92,7 +93,9 @@ class MoonPayService( else -> availableForSell.contains(currency.currencySymbol) } } - is Currency.Token -> false + is Currency.Token -> { + metadata?.any { it?.contractAddress.equals(currency.token.contractAddress, ignoreCase = true) } ?: false + } } } From c49502dcd37e0f1ded883b393a2566d0fc18808e Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 19 Feb 2024 16:39:08 +0000 Subject: [PATCH 51/53] Updated on 2026-08-14 --- .../common/analytics/events/WalletConnect.kt | 26 ++++++++++------ .../data/WalletConnectRepositoryImpl.kt | 3 +- .../domain/models/WalletConnectError.kt | 30 +++++++++++++------ 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/WalletConnect.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/WalletConnect.kt index 13c04ec76f..ed47afa57c 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/WalletConnect.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/WalletConnect.kt @@ -1,7 +1,6 @@ package com.tangem.tap.common.analytics.events import com.tangem.core.analytics.models.AnalyticsEvent -import com.tangem.tap.common.extensions.filterNotNull /** [REDACTED_AUTHOR] @@ -42,17 +41,22 @@ sealed class WalletConnect( val methodName: String, val blockchain: String, val errorCode: String? = null, + val errorDescription: String? = null, ) { fun toParamsMap(): Map { val validation = if (errorCode == null) Validation.SUCCESS.param else Validation.FAIL.param - return mapOf( - AnalyticsParam.DAPP_NAME to dAppName, - AnalyticsParam.DAPP_URL to dAppUrl, - AnalyticsParam.METHOD_NAME to methodName, - AnalyticsParam.BLOCKCHAIN to blockchain, - AnalyticsParam.VALIDATION to validation, - if (errorCode != null) AnalyticsParam.ERROR_CODE to errorCode else null to null, - ).filterNotNull() + val code = errorCode ?: SUCCESS_CODE + return buildMap { + put(AnalyticsParam.DAPP_NAME, dAppName) + put(AnalyticsParam.DAPP_URL, dAppUrl) + put(AnalyticsParam.METHOD_NAME, methodName) + put(AnalyticsParam.BLOCKCHAIN, blockchain) + put(AnalyticsParam.VALIDATION, validation) + put(AnalyticsParam.ERROR_CODE, code) + if (errorDescription != null) { + put(AnalyticsParam.ERROR_DESCRIPTION, errorDescription) + } + } } } @@ -60,4 +64,8 @@ sealed class WalletConnect( SUCCESS("Success"), FAIL("Fail"), } + + companion object { + private const val SUCCESS_CODE = "0" + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/WalletConnectRepositoryImpl.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/WalletConnectRepositoryImpl.kt index ab1355fae2..4f37c03839 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/WalletConnectRepositoryImpl.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/WalletConnectRepositoryImpl.kt @@ -352,7 +352,8 @@ class WalletConnectRepositoryImpl @Inject constructor( dAppUrl = session?.url ?: "", methodName = requestData.method, blockchain = requestData.blockchain, - errorCode = WalletConnectError.ValidationError.toString(), + errorCode = WalletConnectError.ValidationError.error, + errorDescription = it.throwable.message, ) }, ) diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/WalletConnectError.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/WalletConnectError.kt index ca01b701a7..efb5215bd7 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/WalletConnectError.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/WalletConnectError.kt @@ -1,12 +1,24 @@ package com.tangem.tap.domain.walletconnect2.domain.models -sealed class WalletConnectError : Exception() { - data class ApprovalErrorMissingNetworks(val missingChains: List) : WalletConnectError() - data class ApprovalErrorAddNetwork(val networks: List) : WalletConnectError() - data class ApprovalErrorUnsupportedNetwork(val unsupportedNetworks: List) : WalletConnectError() - data class ExternalApprovalError(override val message: String?) : WalletConnectError() - object WrongUserWallet : WalletConnectError() - object UnsupportedMethod : WalletConnectError() - object SigningError : WalletConnectError() - object ValidationError : WalletConnectError() +sealed class WalletConnectError(val error: String) : Exception() { + data class ApprovalErrorMissingNetworks( + val missingChains: List, + ) : WalletConnectError("ApprovalErrorMissingNetworks") + + data class ApprovalErrorAddNetwork( + val networks: List, + ) : WalletConnectError("ApprovalErrorAddNetwork") + + data class ApprovalErrorUnsupportedNetwork( + val unsupportedNetworks: List, + ) : WalletConnectError("ApprovalErrorUnsupportedNetwork") + + data class ExternalApprovalError( + override val message: String?, + ) : WalletConnectError("ExternalApprovalError") + + object WrongUserWallet : WalletConnectError("WrongUserWallet") + object UnsupportedMethod : WalletConnectError("UnsupportedMethod") + object SigningError : WalletConnectError("SigningError") + object ValidationError : WalletConnectError("ValidationError") } \ No newline at end of file From a2d36ff27c647c97db746d21f77254df264fa34d Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 19 Feb 2024 22:18:19 +0500 Subject: [PATCH 52/53] Updated on 2026-08-14 --- .../features/send/redux/SendScreenAction.kt | 5 +++ .../send/redux/middlewares/SendMiddleware.kt | 2 ++ .../reducers/TransactionExtrasReducer.kt | 19 +++++++++++ .../send/redux/states/AddressState.kt | 18 +++++++++-- .../tap/features/send/ui/SendFragment.kt | 9 ++++++ .../stateSubscribers/SendStateSubscriber.kt | 11 +++++++ .../main/res/layout/layout_send_address.xml | 32 +++++++++++++++++++ .../DefaultTransactionRepository.kt | 2 ++ .../SendRecipientMemoFieldConverter.kt | 1 + gradle/dependencies.toml | 2 +- 10 files changed, 98 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt index 00e279678b..3c3409459e 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt @@ -89,6 +89,11 @@ sealed class TransactionExtrasAction : SendScreenActionUi { sealed class HederaMemo : TransactionExtrasAction() { data class HandleUserInput(val data: String) : HederaMemo() } + + @Deprecated("Only in legacy send screen") + sealed class AlgorandMemo : TransactionExtrasAction() { + data class HandleUserInput(val data: String) : AlgorandMemo() + } } sealed class AddressVerifyAction : SendScreenAction { diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt index dcdd0913bb..2c21ea333c 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt @@ -1,6 +1,7 @@ package com.tangem.tap.features.send.redux.middlewares import com.google.firebase.crashlytics.FirebaseCrashlytics +import com.tangem.blockchain.blockchains.algorand.AlgorandTransactionExtras import com.tangem.blockchain.blockchains.binance.BinanceTransactionExtras import com.tangem.blockchain.blockchains.cosmos.CosmosTransactionExtras import com.tangem.blockchain.blockchains.hedera.HederaTransactionBuilder @@ -177,6 +178,7 @@ private fun sendTransaction( extras = HederaTransactionBuilder.HederaTransactionExtras(it), ) } + transactionExtras.algorandMemoState?.memo?.let { txData = txData.copy(extras = AlgorandTransactionExtras(it)) } scope.launch { // TODO: Risky commented this part, unknown logic, need to test if removed diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/TransactionExtrasReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/TransactionExtrasReducer.kt index 058a8ded6e..c781547049 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/TransactionExtrasReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/TransactionExtrasReducer.kt @@ -20,6 +20,7 @@ class TransactionExtrasReducer : SendInternalReducer { is TonMemo -> handleTonMemo(action, sendState, sendState.transactionExtrasState) is CosmosMemo -> handleCosmosMemo(action, sendState, sendState.transactionExtrasState) is HederaMemo -> handleHederaMemo(action, sendState, sendState.transactionExtrasState) + is AlgorandMemo -> handleAlgorandMemo(action, sendState, sendState.transactionExtrasState) else -> sendState } } @@ -54,6 +55,9 @@ class TransactionExtrasReducer : SendInternalReducer { Blockchain.TerraV2, -> TransactionExtrasState(cosmosMemoState = CosmosMemoState()) Blockchain.Hedera, Blockchain.HederaTestnet -> TransactionExtrasState(hederaMemoState = HederaMemoState()) + Blockchain.Algorand, Blockchain.AlgorandTestnet -> TransactionExtrasState( + algorandMemoState = AlgorandMemoState(), + ) else -> emptyResult } return updateLastState(sendState.copy(transactionExtrasState = result), result) @@ -179,4 +183,19 @@ class TransactionExtrasReducer : SendInternalReducer { } return updateLastState(sendState.copy(transactionExtrasState = result), result) } + + private fun handleAlgorandMemo( + action: AlgorandMemo, + sendState: SendState, + infoState: TransactionExtrasState, + ): SendState { + val result = when (action) { + is AlgorandMemo.HandleUserInput -> { + val memo = action.data + val input = InputViewValue(memo, true) + infoState.copy(algorandMemoState = AlgorandMemoState(input, memo)) + } + } + return updateLastState(sendState.copy(transactionExtrasState = result), result) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressState.kt index bda2324f45..3310a772ce 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressState.kt @@ -31,12 +31,13 @@ data class TransactionExtrasState( val tonMemoState: TonMemoState? = null, val cosmosMemoState: CosmosMemoState? = null, val hederaMemoState: HederaMemoState? = null, + val algorandMemoState: AlgorandMemoState? = null, ) : IdStateHolder { override val stateId: StateId = StateId.TRANSACTION_EXTRAS fun isNull(): Boolean { return xlmMemo == null && binanceMemo == null && xrpDestinationTag == null && tonMemoState == null && - cosmosMemoState == null && hederaMemoState == null + cosmosMemoState == null && hederaMemoState == null && algorandMemoState == null } fun isEmpty(): Boolean { @@ -46,8 +47,15 @@ data class TransactionExtrasState( val isTonEmpty = tonMemoState?.viewFieldValue?.value?.isEmpty() ?: false val isCosmosEmpty = cosmosMemoState?.viewFieldValue?.value?.isEmpty() ?: false val isHederaEmpty = hederaMemoState?.viewFieldValue?.value?.isEmpty() ?: false + val isAlgorandEmpty = algorandMemoState?.viewFieldValue?.value?.isEmpty() ?: false - return isXlmEmpty || isBinanceEmpty || isXrpEmpty || isTonEmpty || isCosmosEmpty || isHederaEmpty + return isXlmEmpty || + isBinanceEmpty || + isXrpEmpty || + isTonEmpty || + isCosmosEmpty || + isHederaEmpty || + isAlgorandEmpty } } @@ -129,6 +137,12 @@ data class HederaMemoState( val error: TransactionExtraError? = null, ) +data class AlgorandMemoState( + val viewFieldValue: InputViewValue = InputViewValue(""), + val memo: String? = null, + val error: TransactionExtraError? = null, +) + enum class TransactionExtraError { INVALID_DESTINATION_TAG, INVALID_XLM_MEMO, diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt index 4bf8730db7..775a5cc578 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt @@ -243,6 +243,15 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { } .onEach { store.dispatch(TransactionExtrasAction.HederaMemo.HandleUserInput(it)) } .launchIn(mainScope) + + etAlgorandMemo.inputtedTextAsFlow() + .debounce(EDIT_TEXT_INPUT_DEBOUNCE) + .filter { + val info = store.state.sendState.transactionExtrasState + info.algorandMemoState?.viewFieldValue?.value != it + } + .onEach { store.dispatch(TransactionExtrasAction.AlgorandMemo.HandleUserInput(it)) } + .launchIn(mainScope) } private fun onCodeScanned(scannedCode: String) { diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt index a2342fb5bf..81899d8e65 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt @@ -75,6 +75,7 @@ internal class SendStateSubscriber( showView(tonMemoContainer, infoState.tonMemoState) showView(cosmosMemoContainer, infoState.cosmosMemoState) showView(hederaMemoContainer, infoState.hederaMemoState) + showView(algorandMemoContainer, infoState.algorandMemoState) infoState.xlmMemo?.let { if (!it.viewFieldValue.isFromUserInput) etXlmMemo.setText(it.viewFieldValue.value) @@ -141,6 +142,16 @@ internal class SendStateSubscriber( etHederaMemo.setText(it.viewFieldValue.value) } } + infoState.algorandMemoState?.let { + if (infoState.algorandMemoState.error != null) { + tilAlgorandMemo.error = fg.getText(R.string.send_extras_error_invalid_memo) + } else { + tilAlgorandMemo.error = null + } + if (!it.viewFieldValue.isFromUserInput) { + etAlgorandMemo.setText(it.viewFieldValue.value) + } + } } @Suppress("ComplexMethod") diff --git a/app/src/main/res/layout/layout_send_address.xml b/app/src/main/res/layout/layout_send_address.xml index dd4b1d9cc2..1256f33037 100644 --- a/app/src/main/res/layout/layout_send_address.xml +++ b/app/src/main/res/layout/layout_send_address.xml @@ -292,6 +292,38 @@ + + + + + + + + + + diff --git a/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt index 25704e1bf1..76b16b1409 100644 --- a/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt +++ b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultTransactionRepository.kt @@ -1,6 +1,7 @@ package com.tangem.data.transaction import androidx.core.text.isDigitsOnly +import com.tangem.blockchain.blockchains.algorand.AlgorandTransactionExtras import com.tangem.blockchain.blockchains.binance.BinanceTransactionExtras import com.tangem.blockchain.blockchains.cosmos.CosmosTransactionExtras import com.tangem.blockchain.blockchains.hedera.HederaTransactionBuilder @@ -73,6 +74,7 @@ internal class DefaultTransactionRepository( Blockchain.Cosmos -> CosmosTransactionExtras(memo) Blockchain.TON -> TonTransactionExtras(memo) Blockchain.Hedera -> HederaTransactionBuilder.HederaTransactionExtras(memo) + Blockchain.Algorand -> AlgorandTransactionExtras(memo) else -> null } } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientMemoFieldConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientMemoFieldConverter.kt index f3a0cefdd9..1eeffb3762 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientMemoFieldConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientMemoFieldConverter.kt @@ -29,6 +29,7 @@ internal class SendRecipientMemoFieldConverter( Blockchain.TerraV2.id, Blockchain.Stellar.id, Blockchain.Hedera.id, + Blockchain.Algorand.id, -> convert(R.string.send_extras_hint_memo) else -> null } diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 198e563f67..56e74e1713 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -85,7 +85,7 @@ web3j = "4.10.1" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-493" +tangemBlockchainSdk = "develop-495" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-324" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From 665c3f024d65bdaf79f3f61c3735c73a7fcddcc4 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 19 Feb 2024 17:20:46 +0000 Subject: [PATCH 53/53] Updated on 2026-08-14 --- gradle/dependencies.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 56e74e1713..b7b0ab03c0 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -85,9 +85,9 @@ web3j = "4.10.1" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-495" +tangemBlockchainSdk = "release-app_5.7-496" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "develop-324" +tangemCardSdk = "release-app_5.7-329" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ # endregion Tangem