diff --git a/Marathonfile b/Marathonfile
new file mode 100644
index 0000000000..91d15ba5fa
--- /dev/null
+++ b/Marathonfile
@@ -0,0 +1,41 @@
+name: "Tangem Android UI Tests"
+outputDir: "build/reports/marathon"
+
+testOutputTimeoutMillis: 180000 # 3 minutes
+testBatchTimeoutMillis: 600000 # 10 minutes
+
+vendorConfiguration:
+ type: "Android"
+ applicationApk: "app/build/outputs/apk/google/mocked/app-google-mocked.apk"
+ testApplicationApk: "app/build/outputs/apk/androidTest/google/mocked/app-google-mocked-androidTest.apk"
+ autoGrantPermission: true
+ applicationPmClear: true
+ testApplicationPmClear: true
+ # wiremockBaseUrl redirects API calls from wiremock.tests-d.com to local WireMock
+ # This is used on CI where each emulator has its own WireMock instance via adb reverse
+ instrumentationArgs:
+ wiremockBaseUrl: "http://localhost:8080"
+ allureConfiguration:
+ enabled: false # we collect allure via adb pull, not through marathon
+
+poolingStrategy:
+ type: "omni"
+
+batchingStrategy:
+ type: "isolate"
+
+sortingStrategy:
+ type: "no-sorting"
+
+retryStrategy:
+ type: "fixed-quota"
+ totalAllowedRetryQuota: 30
+ retryPerTestQuota: 1
+
+flakinessStrategy:
+ type: "ignore"
+
+analyticsConfiguration:
+ type: "disabled"
+
+debug: false
diff --git a/app/src/androidTest/kotlin/com/tangem/common/BaseTestCase.kt b/app/src/androidTest/kotlin/com/tangem/common/BaseTestCase.kt
index 96c353f84c..009af28e09 100644
--- a/app/src/androidTest/kotlin/com/tangem/common/BaseTestCase.kt
+++ b/app/src/androidTest/kotlin/com/tangem/common/BaseTestCase.kt
@@ -6,6 +6,7 @@ import androidx.compose.ui.test.junit4.createEmptyComposeRule
import androidx.compose.ui.test.printToLog
import androidx.test.core.app.ActivityScenario
import androidx.test.espresso.intent.Intents
+import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.GrantPermissionRule
import com.kaspersky.components.alluresupport.interceptors.step.ScreenshotStepInterceptor
import com.kaspersky.components.alluresupport.withForcedAllureSupport
@@ -19,6 +20,7 @@ import com.tangem.common.rules.ApiEnvironmentRule
import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
+import com.tangem.datasource.utils.WireMockRedirectInterceptor
import com.tangem.domain.promo.PromoRepository
import com.tangem.domain.promo.models.PromoId
import com.tangem.tap.MainActivity
@@ -86,6 +88,9 @@ abstract class BaseTestCase : TestCase(
additionalAfterSection: () -> Unit = {},
) = before {
Allure.label(ALLURE_LABEL_NAME, ALLURE_LABEL_VALUE)
+ // Setup WireMock redirect for CI with local WireMock instances
+ val wiremockUrl = InstrumentationRegistry.getArguments().getString(WIREMOCK_BASE_URL_ARG)
+ WireMockRedirectInterceptor.overriddenBaseUrl = wiremockUrl
hiltRule.inject()
runBlocking {
appPreferencesStore.editData { mutablePreferences ->
@@ -138,7 +143,6 @@ abstract class BaseTestCase : TestCase(
private fun applicationInjectionRule(): ApplicationInjectionExecutionRule {
return ApplicationInjectionExecutionRule(
toggleStates = mapOf(
- "NEW_TOKEN_RECEIVE_ENABLED" to true,
"SWAP_REDESIGN_ENABLED" to false,
"NEW_ONRAMP_MAIN_ENABLED" to true,
"HOT_WALLET_ENABLED" to true,
@@ -147,4 +151,8 @@ abstract class BaseTestCase : TestCase(
)
)
}
+
+ private companion object {
+ const val WIREMOCK_BASE_URL_ARG = "wiremockBaseUrl"
+ }
}
\ No newline at end of file
diff --git a/app/src/androidTest/kotlin/com/tangem/common/utils/WireMockUtils.kt b/app/src/androidTest/kotlin/com/tangem/common/utils/WireMockUtils.kt
index fe4c30d9a6..1095a7aa96 100644
--- a/app/src/androidTest/kotlin/com/tangem/common/utils/WireMockUtils.kt
+++ b/app/src/androidTest/kotlin/com/tangem/common/utils/WireMockUtils.kt
@@ -1,22 +1,31 @@
package com.tangem.common.utils
+import com.tangem.datasource.utils.WireMockRedirectInterceptor
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import timber.log.Timber
import java.io.IOException
+private const val DEFAULT_WIREMOCK_URL = "[REDACTED_ENV_URL]"
+
+/**
+ * Returns the WireMock base URL to use.
+ */
+private fun getWireMockBaseUrl(): String =
+ WireMockRedirectInterceptor.overriddenBaseUrl ?: DEFAULT_WIREMOCK_URL
+
/**
* Method uses to set WireMock scenario state
* @param scenarioName Name of the scenario to modify
* @param state The target state to set (must be one of the scenario's possibleStates)
- * @param baseUrl WireMock base URL
+ * @param baseUrl WireMock base URL (defaults to local override if set, otherwise remote)
* @return true if state was set successfully, false otherwise
*/
fun setWireMockScenarioState(
scenarioName: String,
state: String,
- baseUrl: String = "[REDACTED_ENV_URL]"
+ baseUrl: String = getWireMockBaseUrl()
): Boolean {
Timber.i("=== WireMock Scenario Set ===")
Timber.i("Setting scenario '$scenarioName' to state: $state")
@@ -46,8 +55,9 @@ fun setWireMockScenarioState(
/**
* Method checks accessibility of WireMock
+ * @param baseUrl WireMock base URL (defaults to local override if set, otherwise remote)
*/
-fun checkWireMockStatus(baseUrl: String = "[REDACTED_ENV_URL]"): Boolean {
+fun checkWireMockStatus(baseUrl: String = getWireMockBaseUrl()): Boolean {
val client = OkHttpClient()
val request = Request.Builder()
.url("$baseUrl/__admin/scenarios")
@@ -69,8 +79,9 @@ fun checkWireMockStatus(baseUrl: String = "[REDACTED_ENV_URL]"): Boolean {
/**
* Method to reset all WireMock scenarios
+ * @param baseUrl WireMock base URL (defaults to local override if set, otherwise remote)
*/
-fun resetWireMockScenarios(baseUrl: String = "[REDACTED_ENV_URL]"): Boolean {
+fun resetWireMockScenarios(baseUrl: String = getWireMockBaseUrl()): Boolean {
Timber.i("=== WireMock Scenarios Reset ===")
Timber.i("Base URL: $baseUrl")
@@ -105,13 +116,13 @@ fun resetWireMockScenarios(baseUrl: String = "[REDACTED_ENV_URL]"): Boolean {
* Method to reset a specific WireMock scenario to its initial state
* @param scenarioName Name of the scenario to reset
* @param initialState The target state to reset the scenario to (must be one of the scenario's possibleStates)
- * @param baseUrl WireMock base URL
+ * @param baseUrl WireMock base URL (defaults to local override if set, otherwise remote)
* @return true if reset was successful, false otherwise
*/
fun resetWireMockScenarioState(
scenarioName: String,
initialState: String = "Started",
- baseUrl: String = "[REDACTED_ENV_URL]"
+ baseUrl: String = getWireMockBaseUrl()
): Boolean {
Timber.i("=== WireMock Scenario Reset ===")
Timber.i("Resetting scenario '$scenarioName' to initial state: $initialState")
diff --git a/app/src/main/java/com/tangem/tap/TangemApplication.kt b/app/src/main/java/com/tangem/tap/TangemApplication.kt
index 4e83789e83..6b59b1b8b2 100644
--- a/app/src/main/java/com/tangem/tap/TangemApplication.kt
+++ b/app/src/main/java/com/tangem/tap/TangemApplication.kt
@@ -42,6 +42,7 @@ import com.tangem.datasource.local.logs.AppLogsStore
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.token.UserTokensResponseStore
import com.tangem.datasource.utils.NetworkLogsSaveInterceptor
+import com.tangem.datasource.utils.WireMockRedirectInterceptor
import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
import com.tangem.domain.apptheme.GetAppThemeModeUseCase
import com.tangem.domain.apptheme.repository.AppThemeModeRepository
@@ -334,15 +335,23 @@ open class TangemApplication : Application(), ImageLoaderFactory, Configuration.
ExceptionHandler.append(blockchainExceptionHandler)
if (LogConfig.network.isBlockchainSdkNetworkLogEnabled) {
- BlockchainSdkRetrofitBuilder.interceptors = listOf(
- createNetworkLoggingInterceptor(),
- ChuckerInterceptor(this),
- )
+ BlockchainSdkRetrofitBuilder.interceptors = buildList {
+ if (BuildConfig.MOCK_DATA_SOURCE) {
+ add(WireMockRedirectInterceptor())
+ }
+ add(createNetworkLoggingInterceptor())
+ add(ChuckerInterceptor(this@TangemApplication))
+ }
TangemApiServiceSettings.addInterceptors(
- createNetworkLoggingInterceptor(),
- ChuckerInterceptor(this),
- NetworkLogsSaveInterceptor(appLogsStore),
+ *buildList {
+ if (BuildConfig.MOCK_DATA_SOURCE) {
+ add(WireMockRedirectInterceptor())
+ }
+ add(createNetworkLoggingInterceptor())
+ add(ChuckerInterceptor(this@TangemApplication))
+ add(NetworkLogsSaveInterceptor(appLogsStore))
+ }.toTypedArray(),
)
}
diff --git a/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt
index ff1b6bb97a..55dab8c3b9 100644
--- a/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt
+++ b/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt
@@ -393,4 +393,18 @@ internal object TransactionDomainModule {
currencyChecksRepository = currencyChecksRepository,
)
}
+
+ @Provides
+ @Singleton
+ fun provideSignCloreMessageUseCase(
+ walletManagersFacade: WalletManagersFacade,
+ cardSdkConfigRepository: CardSdkConfigRepository,
+ tangemHotWalletSignerFactory: TangemHotWalletSigner.Factory,
+ ): SignCloreMessageUseCase {
+ return SignCloreMessageUseCase(
+ walletManagersFacade = walletManagersFacade,
+ cardSdkConfigRepository = cardSdkConfigRepository,
+ getHotWalletSigner = tangemHotWalletSignerFactory::create,
+ )
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt
index 52533057f0..53c7fa8abd 100644
--- a/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt
+++ b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt
@@ -1,5 +1,6 @@
package com.tangem.tap.di.domain
+import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.tokens.repository.CurrenciesRepository
@@ -129,12 +130,14 @@ internal object WalletsDomainModule {
userWalletsListRepository: UserWalletsListRepository,
hotWalletFeatureToggles: HotWalletFeatureToggles,
walletsRepository: WalletsRepository,
+ analyticsEventHandler: AnalyticsEventHandler,
): SaveWalletUseCase {
return SaveWalletUseCase(
userWalletsListManager = userWalletsListManager,
userWalletsListRepository = userWalletsListRepository,
walletsRepository = walletsRepository,
useNewRepository = hotWalletFeatureToggles.isHotWalletEnabled,
+ analyticsEventHandler = analyticsEventHandler,
)
}
diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DefaultUserWalletsListRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DefaultUserWalletsListRepository.kt
index 80dce033a7..414ea8a0b3 100644
--- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DefaultUserWalletsListRepository.kt
+++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DefaultUserWalletsListRepository.kt
@@ -21,6 +21,7 @@ import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isLocked
import com.tangem.domain.wallets.R
+import com.tangem.domain.wallets.analytics.WalletSettingsAnalyticEvents
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.domain.wallets.hot.HotWalletAccessCodeAttemptsRepository
import com.tangem.domain.wallets.hot.HotWalletPasswordRequester
@@ -390,6 +391,7 @@ internal class DefaultUserWalletsListRepository(
if (newUserWallet.walletId == oldUserWallet.walletId &&
oldUserWallet is UserWallet.Hot && newUserWallet is UserWallet.Cold
) {
+ trackWalletUpgradeEvent()
removeHotWalletsFromSDKAndRepos(walletIds = listOf(oldUserWallet.walletId))
// When upgrading from Hot to Cold, if biometric lock is available, set it
if (hasBiometry()) {
@@ -536,4 +538,8 @@ internal class DefaultUserWalletsListRepository(
),
)
}
+
+ private fun trackWalletUpgradeEvent() {
+ analyticsEventHandler.send(event = WalletSettingsAnalyticEvents.WalletUpgraded())
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/proxy/UserWalletManagerImpl.kt b/app/src/main/java/com/tangem/tap/proxy/UserWalletManagerImpl.kt
deleted file mode 100644
index f8350e77a9..0000000000
--- a/app/src/main/java/com/tangem/tap/proxy/UserWalletManagerImpl.kt
+++ /dev/null
@@ -1,79 +0,0 @@
-package com.tangem.tap.proxy
-
-import com.tangem.blockchain.common.AmountType
-import com.tangem.blockchain.common.Blockchain
-import com.tangem.blockchain.common.WalletManager
-import com.tangem.blockchainsdk.utils.fromNetworkId
-import com.tangem.domain.walletmanager.WalletManagersFacade
-import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
-import com.tangem.lib.crypto.UserWalletManager
-import com.tangem.lib.crypto.models.ProxyAmount
-import com.tangem.utils.coroutines.CoroutineDispatcherProvider
-import kotlinx.coroutines.withContext
-import timber.log.Timber
-import java.math.BigDecimal
-
-class UserWalletManagerImpl(
- private val walletManagersFacade: WalletManagersFacade,
- private val getSelectedWalletUseCase: GetSelectedWalletUseCase,
- private val dispatchers: CoroutineDispatcherProvider,
-) : UserWalletManager {
-
- override fun getWalletId(): String {
- val selectedUserWallet = requireNotNull(
- getSelectedWalletUseCase.sync().getOrNull(),
- ) { "selectedUserWallet shouldn't be null" }
- return selectedUserWallet.walletId.stringValue
- }
-
- override suspend fun hideAllTokens() {
- // FIXME: Used only in Tester Actions
- Timber.w("Not implemented")
- }
-
- override suspend fun getWalletAddress(networkId: String, derivationPath: String?): String {
- val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
- val walletManager = getActualWalletManager(blockchain, derivationPath)
- return walletManager.wallet.address
- }
-
- override suspend fun getLastTransactionHash(networkId: String, derivationPath: String?): String? {
- val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
- val walletManager = getActualWalletManager(blockchain, derivationPath)
- return walletManager.wallet.recentTransactions
- .lastOrNull { it.hash?.isNotEmpty() == true }
- ?.hash
- }
-
- override suspend fun getNativeTokenBalance(networkId: String, derivationPath: String?): ProxyAmount? {
- val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
- val walletManager = getActualWalletManager(blockchain, derivationPath)
- return walletManager.wallet.amounts.firstNotNullOfOrNull { amountEntry ->
- amountEntry.takeIf { amountEntry.key is AmountType.Coin }
- }?.value?.let { amount ->
- ProxyAmount(
- amount.currencySymbol,
- amount.value ?: BigDecimal.ZERO,
- amount.decimals,
- )
- }
- }
-
- @Throws(IllegalArgumentException::class)
- private suspend fun getActualWalletManager(blockchain: Blockchain, derivationPath: String?): WalletManager {
- val selectedUserWallet = requireNotNull(
- getSelectedWalletUseCase.sync().getOrNull(),
- ) { "userWallet or userWalletsListManager is null" }
- val walletManager = withContext(dispatchers.io) {
- walletManagersFacade.getOrCreateWalletManager(
- selectedUserWallet.walletId,
- blockchain,
- derivationPath,
- )
- }
-
- return requireNotNull(walletManager) {
- "No wallet manager found"
- }
- }
-}
\ 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 677d7e5ebd..5c943d917b 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
@@ -1,11 +1,6 @@
package com.tangem.tap.proxy.di
-import com.tangem.domain.walletmanager.WalletManagersFacade
-import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
-import com.tangem.lib.crypto.UserWalletManager
import com.tangem.tap.proxy.AppStateHolder
-import com.tangem.tap.proxy.UserWalletManagerImpl
-import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@@ -21,18 +16,4 @@ internal object ProxyModule {
fun provideAppStateHolder(): AppStateHolder {
return AppStateHolder()
}
-
- @Provides
- @Singleton
- fun provideUserWalletManager(
- walletManagersFacade: WalletManagersFacade,
- getSelectedWalletUseCase: GetSelectedWalletUseCase,
- dispatchers: CoroutineDispatcherProvider,
- ): UserWalletManager {
- return UserWalletManagerImpl(
- walletManagersFacade = walletManagersFacade,
- getSelectedWalletUseCase = getSelectedWalletUseCase,
- dispatchers = dispatchers,
- )
- }
}
\ No newline at end of file
diff --git a/app/src/mocked/res/xml/network_security_config.xml b/app/src/mocked/res/xml/network_security_config.xml
index 52c44ac992..a4929411f1 100644
--- a/app/src/mocked/res/xml/network_security_config.xml
+++ b/app/src/mocked/res/xml/network_security_config.xml
@@ -6,4 +6,9 @@
+
+
+ 10.0.2.2
+ localhost
+
diff --git a/common/ui/src/main/java/com/tangem/common/ui/account/AccountIcon.kt b/common/ui/src/main/java/com/tangem/common/ui/account/AccountIcon.kt
index 188fcb1709..ba746abcc9 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/account/AccountIcon.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/account/AccountIcon.kt
@@ -6,6 +6,7 @@ import com.tangem.common.ui.R
import com.tangem.core.ui.components.account.AccountCharIcon
import com.tangem.core.ui.components.account.AccountIconSize
import com.tangem.core.ui.components.account.AccountResIcon
+import com.tangem.core.ui.components.account.PaymentAccountIcon
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.domain.models.account.CryptoPortfolioIcon
@@ -16,7 +17,7 @@ import com.tangem.domain.models.account.CryptoPortfolioIcon.Color
* - a single character (derived from the [name]) using [AccountCharIcon], or
* - a predefined vector resource (from [icon]) using [AccountResIcon].
*
- * The background color is taken from [CryptoPortfolioIconUM.color],
+ * The background color is taken from [AccountIconUM.CryptoPortfolio.color],
* and the icon size, text style, and container shape are adapted based on the given [size].
*
* @param name A [TextReference] used to resolve and display the first letter
@@ -31,29 +32,43 @@ import com.tangem.domain.models.account.CryptoPortfolioIcon.Color
@Composable
fun AccountIcon(
name: TextReference,
- icon: CryptoPortfolioIconUM,
+ icon: AccountIconUM.CryptoPortfolio,
size: AccountIconSize,
modifier: Modifier = Modifier,
) {
val letter = name.resolveReference().firstOrNull()
+ val iconColor = icon.color.getUiColor()
when {
icon.value == CryptoPortfolioIcon.Icon.Letter && letter == null -> {
AccountResIcon(
resId = R.drawable.ic_tangem_24,
- color = icon.color.getUiColor(),
+ color = iconColor,
size = size,
modifier = modifier,
)
}
icon.value == CryptoPortfolioIcon.Icon.Letter -> AccountCharIcon(
char = letter ?: 'N',
- color = icon.color.getUiColor(),
+ color = iconColor,
size = size,
modifier = modifier,
)
else -> AccountResIcon(
resId = icon.value.getResId(),
- color = icon.color.getUiColor(),
+ color = iconColor,
+ size = size,
+ modifier = modifier,
+ )
+ }
+}
+
+@Composable
+fun AccountIcon(name: TextReference, icon: AccountIconUM, size: AccountIconSize, modifier: Modifier = Modifier) {
+ when (icon) {
+ is AccountIconUM.Payment -> PaymentAccountIcon(size = size, modifier = modifier)
+ is AccountIconUM.CryptoPortfolio -> AccountIcon(
+ name = name,
+ icon = icon,
size = size,
modifier = modifier,
)
@@ -62,7 +77,7 @@ fun AccountIcon(
object AccountIconPreviewData {
- fun randomAccountIcon(letter: Boolean = false) = CryptoPortfolioIconUM(
+ fun randomAccountIcon(letter: Boolean = false) = AccountIconUM.CryptoPortfolio(
value = if (letter) CryptoPortfolioIcon.Icon.Letter else CryptoPortfolioIcon.Icon.entries.random(),
color = Color.entries.random(),
)
diff --git a/common/ui/src/main/java/com/tangem/common/ui/account/AccountIconUM.kt b/common/ui/src/main/java/com/tangem/common/ui/account/AccountIconUM.kt
new file mode 100644
index 0000000000..ca79e78402
--- /dev/null
+++ b/common/ui/src/main/java/com/tangem/common/ui/account/AccountIconUM.kt
@@ -0,0 +1,10 @@
+package com.tangem.common.ui.account
+
+import com.tangem.domain.models.account.CryptoPortfolioIcon.Color
+import com.tangem.domain.models.account.CryptoPortfolioIcon.Icon
+
+sealed class AccountIconUM {
+ data class CryptoPortfolio(val value: Icon, val color: Color) : AccountIconUM()
+
+ data object Payment : AccountIconUM()
+}
\ No newline at end of file
diff --git a/common/ui/src/main/java/com/tangem/common/ui/account/AccountLabel.kt b/common/ui/src/main/java/com/tangem/common/ui/account/AccountLabel.kt
index 51f13b848f..4938e46e43 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/account/AccountLabel.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/account/AccountLabel.kt
@@ -27,7 +27,7 @@ import com.tangem.core.ui.res.TangemTheme
@Composable
fun AccountLabel(
name: TextReference,
- icon: CryptoPortfolioIconUM,
+ icon: AccountIconUM,
iconSize: AccountIconSize,
modifier: Modifier = Modifier,
nameStyle: TextStyle = TangemTheme.typography.subtitle2,
diff --git a/common/ui/src/main/java/com/tangem/common/ui/account/AccountPortfolioItemUMConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/account/AccountPortfolioItemUMConverter.kt
index baed1af4d7..6bb24f24e3 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/account/AccountPortfolioItemUMConverter.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/account/AccountPortfolioItemUMConverter.kt
@@ -48,13 +48,15 @@ class AccountPortfolioItemUMConverter(
)
UserWalletItemUM.Information.Loaded(text)
}
+ is Account.Payment -> TODO("[REDACTED_JIRA]")
}
private fun getImageState(account: Account.CryptoPortfolio) = when (account) {
is Account.CryptoPortfolio -> UserWalletItemUM.ImageState.Account(
name = account.accountName.toUM().value,
- icon = account.icon.toUM(),
+ icon = CryptoPortfolioIconConverter.convert(account.icon),
)
+ is Account.Payment -> TODO("[REDACTED_JIRA]")
}
private fun getBalanceInfo(): UserWalletItemUM.Balance {
diff --git a/common/ui/src/main/java/com/tangem/common/ui/account/AccountRow.kt b/common/ui/src/main/java/com/tangem/common/ui/account/AccountRow.kt
index 0b51e38465..8e755eb8bc 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/account/AccountRow.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/account/AccountRow.kt
@@ -41,7 +41,7 @@ import com.tangem.core.ui.res.TangemThemePreview
fun AccountRow(
title: TextReference,
subtitle: TextReference,
- icon: CryptoPortfolioIconUM,
+ icon: AccountIconUM.CryptoPortfolio,
modifier: Modifier = Modifier,
isReverse: Boolean = false,
) {
diff --git a/common/ui/src/main/java/com/tangem/common/ui/account/AccountTitle.kt b/common/ui/src/main/java/com/tangem/common/ui/account/AccountTitle.kt
index d21a35276b..a77848f899 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/account/AccountTitle.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/account/AccountTitle.kt
@@ -1,7 +1,9 @@
package com.tangem.common.ui.account
import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -9,11 +11,16 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.account.AccountIconSize
import com.tangem.core.ui.extensions.resolveReference
+import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.TangemTheme
+import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.core.ui.test.SendScreenTestTags
+import com.tangem.domain.models.account.CryptoPortfolioIcon
+import com.tangem.utils.StringsSigns
/**
* A composable function that displays an account label (icon + name) with an optional prefix.
@@ -61,4 +68,28 @@ fun AccountTitle(
)
}
}
+}
+
+@Preview
+@Composable
+private fun PreviewAccountTitle() {
+ TangemThemePreview {
+ Column {
+ AccountTitle(
+ accountTitleUM = AccountTitleUM.Account(
+ prefixText = stringReference(StringsSigns.DOT),
+ name = stringReference("Main Wallet"),
+ icon = AccountIconUM.CryptoPortfolio(
+ value = CryptoPortfolioIcon.Icon.Bookmark,
+ color = CryptoPortfolioIcon.Color.Pattypan,
+ ),
+ ),
+ modifier = Modifier.padding(4.dp),
+ )
+ AccountTitle(
+ accountTitleUM = AccountTitleUM.Account.payment(prefixText = stringReference(StringsSigns.DOT)),
+ modifier = Modifier.padding(4.dp),
+ )
+ }
+ }
}
\ No newline at end of file
diff --git a/common/ui/src/main/java/com/tangem/common/ui/account/AccountTitleUM.kt b/common/ui/src/main/java/com/tangem/common/ui/account/AccountTitleUM.kt
index 5b9537de75..f5cdef91d2 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/account/AccountTitleUM.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/account/AccountTitleUM.kt
@@ -1,7 +1,9 @@
package com.tangem.common.ui.account
import androidx.compose.runtime.Immutable
+import com.tangem.common.ui.R
import com.tangem.core.ui.extensions.TextReference
+import com.tangem.core.ui.extensions.resourceReference
/**
* A sealed interface representing the title of an account, which can be either a simple text
@@ -19,6 +21,16 @@ sealed interface AccountTitleUM {
data class Account(
val prefixText: TextReference,
val name: TextReference,
- val icon: CryptoPortfolioIconUM,
- ) : AccountTitleUM
+ val icon: AccountIconUM,
+ ) : AccountTitleUM {
+ companion object {
+ fun payment(prefixText: TextReference = TextReference.EMPTY): Account {
+ return Account(
+ prefixText = prefixText,
+ name = resourceReference(R.string.tangempay_payment_account),
+ icon = AccountIconUM.Payment,
+ )
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/common/ui/src/main/java/com/tangem/common/ui/account/CryptoPortfolioIconConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/account/CryptoPortfolioIconConverter.kt
new file mode 100644
index 0000000000..cfb11a33be
--- /dev/null
+++ b/common/ui/src/main/java/com/tangem/common/ui/account/CryptoPortfolioIconConverter.kt
@@ -0,0 +1,14 @@
+package com.tangem.common.ui.account
+
+import com.tangem.domain.models.account.CryptoPortfolioIcon
+import com.tangem.utils.converter.TwoWayConverter
+
+object CryptoPortfolioIconConverter : TwoWayConverter {
+ override fun convert(value: CryptoPortfolioIcon): AccountIconUM.CryptoPortfolio {
+ return AccountIconUM.CryptoPortfolio(value = value.value, color = value.color)
+ }
+
+ override fun convertBack(value: AccountIconUM.CryptoPortfolio): CryptoPortfolioIcon {
+ return CryptoPortfolioIcon.ofCustomAccount(value = value.value, color = value.color)
+ }
+}
\ No newline at end of file
diff --git a/common/ui/src/main/java/com/tangem/common/ui/account/CryptoPortfolioIconExt.kt b/common/ui/src/main/java/com/tangem/common/ui/account/CryptoPortfolioIconExt.kt
index d97401c0a3..14015c86b2 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/account/CryptoPortfolioIconExt.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/account/CryptoPortfolioIconExt.kt
@@ -47,7 +47,4 @@ fun CryptoPortfolioIcon.Icon.getResId(): Int {
CryptoPortfolioIcon.Icon.Package -> R.drawable.ic_package_24
CryptoPortfolioIcon.Icon.Gift -> R.drawable.ic_gift_24
}
-}
-
-fun CryptoPortfolioIcon.toUM() = CryptoPortfolioIconUM(domainModel = this)
-fun CryptoPortfolioIconUM.toDomain() = CryptoPortfolioIcon.ofCustomAccount(value = this.value, color = this.color)
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/common/ui/src/main/java/com/tangem/common/ui/account/CryptoPortfolioIconUM.kt b/common/ui/src/main/java/com/tangem/common/ui/account/CryptoPortfolioIconUM.kt
deleted file mode 100644
index cb8b2d22af..0000000000
--- a/common/ui/src/main/java/com/tangem/common/ui/account/CryptoPortfolioIconUM.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.tangem.common.ui.account
-
-import com.tangem.domain.models.account.CryptoPortfolioIcon
-import com.tangem.domain.models.account.CryptoPortfolioIcon.Color
-import com.tangem.domain.models.account.CryptoPortfolioIcon.Icon
-
-data class CryptoPortfolioIconUM(
- val value: Icon,
- val color: Color,
-) {
- constructor(domainModel: CryptoPortfolioIcon) : this(
- value = domainModel.value,
- color = domainModel.color,
- )
-}
\ No newline at end of file
diff --git a/common/ui/src/main/java/com/tangem/common/ui/account/PortfolioSelectRow.kt b/common/ui/src/main/java/com/tangem/common/ui/account/PortfolioSelectRow.kt
index caa4ec4ce0..e4575a197c 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/account/PortfolioSelectRow.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/account/PortfolioSelectRow.kt
@@ -82,7 +82,7 @@ fun PortfolioSelectRow(
@Immutable
data class PortfolioSelectUM(
- val icon: CryptoPortfolioIconUM?,
+ val icon: AccountIconUM.CryptoPortfolio?,
val name: TextReference,
val isAccountMode: Boolean,
val isMultiChoice: Boolean,
diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountAccountConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountAccountConverter.kt
index b859d383d1..ea09230bd7 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountAccountConverter.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/converters/AmountAccountConverter.kt
@@ -1,6 +1,7 @@
package com.tangem.common.ui.amountScreen.converters
import com.tangem.common.ui.account.AccountTitleUM
+import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.common.ui.account.toUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.models.account.Account
@@ -15,7 +16,7 @@ class AmountAccountConverter(
return if (value != null && isAccountsMode) {
AccountTitleUM.Account(
name = value.accountName.toUM().value,
- icon = value.icon.toUM(),
+ icon = CryptoPortfolioIconConverter.convert(value.icon),
prefixText = prefixText,
)
} else {
diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/preview/AmountStatePreviewData.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/preview/AmountStatePreviewData.kt
index d5eafdf94d..d5a6605ccd 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/preview/AmountStatePreviewData.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/preview/AmountStatePreviewData.kt
@@ -5,7 +5,7 @@ import androidx.compose.foundation.text.KeyboardOptions
import com.tangem.common.ui.R
import com.tangem.common.ui.account.AccountNameUM
import com.tangem.common.ui.account.AccountTitleUM
-import com.tangem.common.ui.account.toUM
+import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.common.ui.amountScreen.models.AmountFieldModel
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
@@ -88,7 +88,7 @@ object AmountStatePreviewData {
val amountStateV2Accounts = amountState.copy(
accountTitleUM = AccountTitleUM.Account(
name = AccountNameUM.DefaultMain.value,
- icon = CryptoPortfolioIcon.ofDefaultCustomAccount().toUM(),
+ icon = CryptoPortfolioIconConverter.convert(CryptoPortfolioIcon.ofDefaultCustomAccount()),
prefixText = resourceReference(R.string.common_from),
),
availableBalanceCrypto = stringReference("2 130,81231238 USDT"),
diff --git a/common/ui/src/main/java/com/tangem/common/ui/bottomsheet/chooseaddress/ChooseAddressBottomSheet.kt b/common/ui/src/main/java/com/tangem/common/ui/bottomsheet/chooseaddress/ChooseAddressBottomSheet.kt
index 4054fddac7..2bb9af138c 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/bottomsheet/chooseaddress/ChooseAddressBottomSheet.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/bottomsheet/chooseaddress/ChooseAddressBottomSheet.kt
@@ -1,15 +1,23 @@
package com.tangem.common.ui.bottomsheet.chooseaddress
+import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
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.PreviewParameterProvider
+import com.tangem.common.ui.bottomsheet.receive.AddressModel
import com.tangem.core.ui.R
import com.tangem.core.ui.components.SimpleSettingsRow
-import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
+import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
import com.tangem.core.ui.extensions.resolveReference
+import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.TangemTheme
+import com.tangem.core.ui.res.TangemThemePreview
+import kotlinx.collections.immutable.persistentListOf
@Composable
fun ChooseAddressBottomSheet(config: TangemBottomSheetConfig) {
@@ -31,4 +39,40 @@ private fun ChooseAddressBottomSheetContent(content: ChooseAddressBottomSheetCon
)
}
}
-}
\ No newline at end of file
+}
+
+// region Preview
+@Composable
+@Preview(showBackground = true, widthDp = 360)
+@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
+private fun ChooseAddressBottomSheet_Preview(
+ @PreviewParameter(ChooseAddressBottomSheetPreviewProvider::class) params: ChooseAddressBottomSheetConfig,
+) {
+ TangemThemePreview {
+ ChooseAddressBottomSheetContent(params)
+ }
+}
+
+private class ChooseAddressBottomSheetPreviewProvider : PreviewParameterProvider {
+ override val values: Sequence
+ get() = sequenceOf(
+ ChooseAddressBottomSheetConfig(
+ addressModels = persistentListOf(
+ AddressModel(
+ displayName = stringReference("Main address"),
+ fullName = stringReference("Bitcoin"),
+ value = "0x1234...abcd",
+ type = AddressModel.Type.Default,
+ ),
+ AddressModel(
+ displayName = stringReference("Legacy address"),
+ fullName = stringReference("Bitcoin"),
+ value = "0x1234...abcd",
+ type = AddressModel.Type.Default,
+ ),
+ ),
+ onClick = {},
+ ),
+ )
+}
+// endregion
\ No newline at end of file
diff --git a/common/ui/src/main/java/com/tangem/common/ui/bottomsheet/chooseaddress/ChooseAddressBottomSheetConfig.kt b/common/ui/src/main/java/com/tangem/common/ui/bottomsheet/chooseaddress/ChooseAddressBottomSheetConfig.kt
index 70a3642e18..a02e20a5c1 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/bottomsheet/chooseaddress/ChooseAddressBottomSheetConfig.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/bottomsheet/chooseaddress/ChooseAddressBottomSheetConfig.kt
@@ -1,10 +1,9 @@
package com.tangem.common.ui.bottomsheet.chooseaddress
import com.tangem.common.ui.bottomsheet.receive.AddressModel
-import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheetConfig
import com.tangem.common.ui.bottomsheet.receive.mapToAddressModels
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
-import com.tangem.domain.models.network.Network
+import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.NetworkAddress
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
@@ -14,13 +13,12 @@ class ChooseAddressBottomSheetConfig(
val onClick: (AddressModel) -> Unit,
) : TangemBottomSheetConfigContent {
constructor(
- asset: TokenReceiveBottomSheetConfig.Asset,
- network: Network,
+ currency: CryptoCurrency,
networkAddress: NetworkAddress,
onClick: (AddressModel) -> Unit,
) : this(
addressModels = networkAddress.availableAddresses
- .mapToAddressModels(asset, network)
+ .mapToAddressModels(cryptoCurrency = currency)
.toImmutableList(),
onClick = onClick,
)
diff --git a/common/ui/src/main/java/com/tangem/common/ui/bottomsheet/receive/AddressMappers.kt b/common/ui/src/main/java/com/tangem/common/ui/bottomsheet/receive/AddressMappers.kt
index f1c9f86b9d..2ad5ab5c2a 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/bottomsheet/receive/AddressMappers.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/bottomsheet/receive/AddressMappers.kt
@@ -1,9 +1,7 @@
package com.tangem.common.ui.bottomsheet.receive
-import com.tangem.core.ui.R
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.combinedReference
-import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
@@ -20,20 +18,6 @@ fun Set.mapToAddressModels(cryptoCurrency: CryptoCurrenc
cryptoCurrency.network,
)
-fun Set.mapToAddressModels(
- asset: TokenReceiveBottomSheetConfig.Asset,
- network: Network,
-): List = when (asset) {
- is TokenReceiveBottomSheetConfig.Asset.Currency -> mapToAddressModels(
- stringReference("${asset.name} (${asset.symbol})"),
- network,
- )
- is TokenReceiveBottomSheetConfig.Asset.NFT -> mapToAddressModels(
- resourceReference(R.string.common_nft),
- network,
- )
-}
-
private fun Set.mapToAddressModels(name: TextReference, network: Network): List =
this
.sortedBy { it.type.ordinal }
diff --git a/common/ui/src/main/java/com/tangem/common/ui/bottomsheet/receive/TokenReceiveBottomSheet.kt b/common/ui/src/main/java/com/tangem/common/ui/bottomsheet/receive/TokenReceiveBottomSheet.kt
deleted file mode 100644
index 9093a606aa..0000000000
--- a/common/ui/src/main/java/com/tangem/common/ui/bottomsheet/receive/TokenReceiveBottomSheet.kt
+++ /dev/null
@@ -1,330 +0,0 @@
-package com.tangem.common.ui.bottomsheet.receive
-
-import android.content.res.Configuration
-import androidx.compose.animation.animateColorAsState
-import androidx.compose.foundation.Image
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.*
-import androidx.compose.foundation.lazy.LazyRow
-import androidx.compose.foundation.lazy.rememberLazyListState
-import androidx.compose.foundation.pager.HorizontalPager
-import androidx.compose.foundation.pager.rememberPagerState
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.foundation.verticalScroll
-import androidx.compose.material3.SnackbarHostState
-import androidx.compose.material3.Text
-import androidx.compose.runtime.*
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.painter.Painter
-import androidx.compose.ui.hapticfeedback.HapticFeedbackType
-import androidx.compose.ui.layout.ContentScale
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalHapticFeedback
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.tooling.preview.PreviewParameter
-import androidx.compose.ui.tooling.preview.PreviewParameterProvider
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.util.fastForEach
-import com.tangem.core.res.getStringSafe
-import com.tangem.core.ui.R
-import com.tangem.core.ui.components.SecondaryButtonIconStart
-import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
-import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
-import com.tangem.core.ui.components.notifications.Notification
-import com.tangem.core.ui.components.notifications.NotificationConfig
-import com.tangem.core.ui.components.rememberQrPainters
-import com.tangem.core.ui.components.snackbar.CopiedTextSnackbarHost
-import com.tangem.core.ui.extensions.*
-import com.tangem.core.ui.res.TangemTheme
-import com.tangem.core.ui.res.TangemThemePreview
-import kotlinx.collections.immutable.ImmutableList
-import kotlinx.collections.immutable.persistentListOf
-import kotlinx.coroutines.launch
-
-@Composable
-fun TokenReceiveBottomSheet(config: TangemBottomSheetConfig) {
- TangemBottomSheet(config) { content: TokenReceiveBottomSheetConfig ->
- TokenReceiveBottomSheetContent(content = content)
- }
-}
-
-@Composable
-private fun TokenReceiveBottomSheetContent(content: TokenReceiveBottomSheetConfig) {
- var selectedAddress by remember { mutableStateOf(content.addresses.first()) }
-
- val snackbarHostState = remember(::SnackbarHostState)
-
- ContainerWithSnackbarHost(snackbarHostState = snackbarHostState) {
- Column(
- modifier = Modifier
- .verticalScroll(state = rememberScrollState())
- .padding(top = 24.dp, bottom = 16.dp)
- .padding(horizontal = 24.dp),
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.spacedBy(space = 24.dp),
- ) {
- QrCodeContent(content = content, onAddressChange = { selectedAddress = it })
-
- Info(
- showMemoDisclaimer = content.showMemoDisclaimer,
- notifications = content.notifications,
- )
-
- Buttons(
- snackbarHostState = snackbarHostState,
- onShareClick = { content.onShareClick(selectedAddress.value) },
- onCopyClick = { content.onCopyClick(selectedAddress.value) },
- )
- }
- }
-}
-
-@Composable
-private fun Info(
- showMemoDisclaimer: Boolean,
- notifications: ImmutableList,
- modifier: Modifier = Modifier,
-) {
- Column(
- modifier = modifier,
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.spacedBy(space = 16.dp),
- ) {
- if (showMemoDisclaimer) {
- Text(
- modifier = Modifier
- .padding(horizontal = 18.dp)
- .fillMaxWidth(),
- text = stringResourceSafe(R.string.receive_bottom_sheet_no_memo_required_message),
- style = TangemTheme.typography.caption1,
- color = TangemTheme.colors.text.tertiary,
- textAlign = TextAlign.Center,
- )
- }
-
- notifications.fastForEach { notificationConfig ->
- key(notificationConfig.hashCode()) {
- Notification(config = notificationConfig)
- }
- }
- }
-}
-
-@Composable
-private fun ContainerWithSnackbarHost(snackbarHostState: SnackbarHostState, content: @Composable () -> Unit) {
- Box {
- content()
-
- CopiedTextSnackbarHost(
- hostState = snackbarHostState,
- modifier = Modifier
- .align(Alignment.BottomCenter)
- .padding(bottom = 80.dp),
- )
- }
-}
-
-@Composable
-private fun QrCodeContent(content: TokenReceiveBottomSheetConfig, onAddressChange: (AddressModel) -> Unit) {
- val qrCodes = rememberQrPainters(content.addresses.map(AddressModel::value))
-
- val pagerState = rememberPagerState(
- initialPage = 0,
- initialPageOffsetFraction = 0f,
- pageCount = content.addresses::count,
- )
-
- LaunchedEffect(key1 = pagerState.currentPage) {
- onAddressChange.invoke(content.addresses[pagerState.currentPage])
- }
-
- HorizontalPager(state = pagerState) { currentPage ->
- QrCodePage(
- content = content,
- qrCodePainter = qrCodes[currentPage],
- currentIndex = currentPage,
- )
- }
-
- if (pagerState.pageCount > 1) {
- val indicatorState = rememberLazyListState()
- val selectedColor = TangemTheme.colors.icon.primary1
- val unselectedColor = TangemTheme.colors.icon.informative
-
- LazyRow(
- modifier = Modifier.height(20.dp),
- state = indicatorState,
- horizontalArrangement = Arrangement.Center,
- verticalAlignment = Alignment.CenterVertically,
- ) {
- repeat(pagerState.pageCount) { iteration ->
- item(key = iteration) {
- val color by animateColorAsState(
- targetValue = if (pagerState.currentPage == iteration) selectedColor else unselectedColor,
- label = "",
- )
-
- Box(
- modifier = Modifier
- .padding(horizontal = 4.dp, vertical = 6.dp)
- .background(color = color, shape = CircleShape)
- .size(7.dp),
- )
- }
- }
- }
- }
-}
-
-@Composable
-private fun QrCodePage(content: TokenReceiveBottomSheetConfig, qrCodePainter: Painter, currentIndex: Int) {
- Column(
- modifier = Modifier.fillMaxWidth(),
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.spacedBy(24.dp),
- ) {
- Text(
- text = stringResourceSafe(
- R.string.receive_bottom_sheet_warning_message_compact,
- content.addresses[currentIndex].fullName.resolveReference(),
- content.network,
- ),
- color = TangemTheme.colors.text.primary1,
- textAlign = TextAlign.Center,
- style = TangemTheme.typography.h3,
- )
-
- Image(
- painter = qrCodePainter,
- contentDescription = null,
- contentScale = ContentScale.Fit,
- modifier = Modifier.size(248.dp),
- )
-
- Text(
- text = content.addresses[currentIndex].value,
- color = TangemTheme.colors.text.primary1,
- textAlign = TextAlign.Center,
- style = TangemTheme.typography.subtitle1,
- )
- }
-}
-
-@Composable
-private fun Buttons(
- snackbarHostState: SnackbarHostState,
- onShareClick: () -> Unit,
- onCopyClick: () -> Unit,
- modifier: Modifier = Modifier,
-) {
- val hapticFeedback = LocalHapticFeedback.current
- val coroutineScope = rememberCoroutineScope()
- val context = LocalContext.current
- val resources = context.resources
-
- Row(
- modifier = modifier,
- horizontalArrangement = Arrangement.spacedBy(16.dp),
- ) {
- SecondaryButtonIconStart(
- modifier = Modifier.weight(1f),
- text = stringResourceSafe(id = R.string.common_copy),
- iconResId = R.drawable.ic_copy_24,
- onClick = {
- onCopyClick()
-
- hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
-
- coroutineScope.launch {
- snackbarHostState.showSnackbar(
- message = resources.getStringSafe(R.string.wallet_notification_address_copied),
- )
- }
- },
- )
-
- SecondaryButtonIconStart(
- modifier = Modifier.weight(1f),
- text = stringResourceSafe(id = R.string.common_share),
- iconResId = R.drawable.ic_share_24,
- onClick = {
- hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
-
- onShareClick()
- },
- )
- }
-}
-
-// region Preview
-@Composable
-@Preview(showBackground = true, widthDp = 360)
-@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
-private fun Preview_TokenReceiveBottomSheet(
- @PreviewParameter(TokenReceiveBottomSheetConfigPreviewProvider::class) params: TokenReceiveBottomSheetConfig,
-) {
- TangemThemePreview {
- val config = TangemBottomSheetConfig(
- isShown = true,
- content = params,
- onDismissRequest = {},
- )
-
- TokenReceiveBottomSheet(config)
- }
-}
-
-private class TokenReceiveBottomSheetConfigPreviewProvider : PreviewParameterProvider {
- val address = AddressModel(
- displayName = stringReference("Address 1"),
- fullName = combinedReference(
- stringReference("Address 1"),
- stringReference(" Stellar"),
- ),
- value = "0xe5178c7d4d0e861ed2e9414e045b501226b0de8d",
- type = AddressModel.Type.Default,
- )
- private val baseConfig = TokenReceiveBottomSheetConfig(
- asset = TokenReceiveBottomSheetConfig.Asset.Currency(
- name = "Stellar",
- symbol = "XLM",
- ),
- network = "Ethereum",
- notifications = persistentListOf(
- NotificationConfig(
- title = stringReference("Send only XLM on the Ethereum network"),
- subtitle = resourceReference(R.string.receive_bottom_sheet_warning_message_description),
- iconResId = R.drawable.ic_alert_circle_24,
- ),
- ),
- addresses = persistentListOf(address),
- showMemoDisclaimer = false,
- onCopyClick = {},
- onShareClick = {},
- )
-
- override val values: Sequence
- get() = sequenceOf(
- baseConfig,
- baseConfig.copy(
- addresses = persistentListOf(
- address,
- address.copy(displayName = stringReference("Address 2")),
- ),
- ),
- baseConfig.copy(
- showMemoDisclaimer = true,
- ),
- baseConfig.copy(
- showMemoDisclaimer = true,
- addresses = persistentListOf(
- address,
- address.copy(displayName = stringReference("Address 2")),
- ),
- ),
- )
-}
-// endregion Preview
\ No newline at end of file
diff --git a/common/ui/src/main/java/com/tangem/common/ui/bottomsheet/receive/TokenReceiveBottomSheetConfig.kt b/common/ui/src/main/java/com/tangem/common/ui/bottomsheet/receive/TokenReceiveBottomSheetConfig.kt
deleted file mode 100644
index 5d29be17ec..0000000000
--- a/common/ui/src/main/java/com/tangem/common/ui/bottomsheet/receive/TokenReceiveBottomSheetConfig.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-package com.tangem.common.ui.bottomsheet.receive
-
-import androidx.compose.runtime.Immutable
-import com.tangem.core.ui.R
-import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
-import com.tangem.core.ui.components.notifications.NotificationConfig
-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.domain.models.network.Network
-import com.tangem.domain.models.network.NetworkAddress
-import kotlinx.collections.immutable.ImmutableList
-import kotlinx.collections.immutable.persistentListOf
-import kotlinx.collections.immutable.toImmutableList
-
-data class TokenReceiveBottomSheetConfig(
- val asset: Asset,
- val network: String,
- val addresses: ImmutableList,
- val showMemoDisclaimer: Boolean,
- val notifications: ImmutableList,
- val onCopyClick: (String) -> Unit,
- val onShareClick: (String) -> Unit,
-) : TangemBottomSheetConfigContent {
-
- constructor(
- asset: Asset,
- network: Network,
- networkAddress: NetworkAddress,
- showMemoDisclaimer: Boolean,
- customNotifications: ImmutableList = persistentListOf(),
- onCopyClick: (String) -> Unit,
- onShareClick: (String) -> Unit,
- ) : this(
- asset = asset,
- network = network.name,
- addresses = networkAddress.availableAddresses
- .mapToAddressModels(asset, network)
- .toImmutableList(),
- showMemoDisclaimer = showMemoDisclaimer,
- notifications = persistentListOf(defaultAssetNotificationConfig(asset, network)).addAll(0, customNotifications),
- onCopyClick = onCopyClick,
- onShareClick = onShareClick,
- )
-
- companion object {
- private fun defaultAssetNotificationConfig(asset: Asset, network: Network): NotificationConfig =
- NotificationConfig(
- title = resourceReference(
- R.string.receive_bottom_sheet_warning_title,
- wrappedList(asset.displaySymbol, network.name),
- ),
- subtitle = resourceReference(R.string.receive_bottom_sheet_warning_message_description),
- iconResId = R.drawable.ic_alert_circle_24,
- iconTint = NotificationConfig.IconTint.Accent,
- )
- }
-
- @Immutable
- sealed class Asset {
- abstract val displaySymbol: TextReference
-
- data class Currency(val name: String, val symbol: String) : Asset() {
- override val displaySymbol = stringReference(symbol)
- }
-
- data object NFT : Asset() {
- override val displaySymbol = resourceReference(R.string.common_nft)
- }
- }
-}
\ No newline at end of file
diff --git a/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationButtonsBlock.kt b/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationButtonsBlock.kt
index dde935dbec..639f70a3ec 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationButtonsBlock.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationButtonsBlock.kt
@@ -24,6 +24,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import com.tangem.common.ui.navigationButtons.preview.NavigationButtonsPreview
+import com.tangem.core.ui.components.HoldToConfirmButton
import com.tangem.core.ui.components.Keyboard
import com.tangem.core.ui.components.SecondaryButtonIconStart
import com.tangem.core.ui.components.buttons.common.TangemButton
@@ -98,34 +99,48 @@ fun NavigationPrimaryButton(primaryButton: NavigationButton?, modifier: Modifier
modifier = modifier.fillMaxWidth(),
) { button ->
if (button != null && button.textReference != TextReference.EMPTY) {
- val icon = if (button.iconRes != null && button.isIconVisible) {
- TangemButtonIconPosition.End(iconResId = button.iconRes)
- } else {
- TangemButtonIconPosition.None
- }
- val color = if (button.isDimmed) {
- TangemButtonsDefaults.secondaryButtonColors
- .copy(contentColor = TangemTheme.colors.text.tertiary)
- } else {
- TangemButtonsDefaults.primaryButtonColors
- }
- TangemButton(
- text = button.textReference.resolveReference(),
- enabled = button.isEnabled,
- onClick = {
- GlobalMultipleClickPreventer.processEvent {
- if (button.isHapticClick) {
- hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
+ if (button.isHoldToConfirm) {
+ HoldToConfirmButton(
+ text = button.textReference.resolveReference(),
+ enabled = button.isEnabled,
+ isLoading = button.shouldShowProgress,
+ onConfirm = {
+ GlobalMultipleClickPreventer.processEvent {
+ button.onClick()
}
- button.onClick()
- }
- },
- showProgress = button.shouldShowProgress,
- colors = color,
- textStyle = TangemTheme.typography.subtitle1,
- icon = icon,
- modifier = Modifier.fillMaxWidth(),
- )
+ },
+ modifier = Modifier.fillMaxWidth(),
+ )
+ } else {
+ val icon = if (button.iconRes != null && button.isIconVisible) {
+ TangemButtonIconPosition.End(iconResId = button.iconRes)
+ } else {
+ TangemButtonIconPosition.None
+ }
+ val color = if (button.isDimmed) {
+ TangemButtonsDefaults.secondaryButtonColors
+ .copy(contentColor = TangemTheme.colors.text.tertiary)
+ } else {
+ TangemButtonsDefaults.primaryButtonColors
+ }
+ TangemButton(
+ text = button.textReference.resolveReference(),
+ enabled = button.isEnabled,
+ onClick = {
+ GlobalMultipleClickPreventer.processEvent {
+ if (button.isHapticClick) {
+ hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
+ }
+ button.onClick()
+ }
+ },
+ showProgress = button.shouldShowProgress,
+ colors = color,
+ textStyle = TangemTheme.typography.subtitle1,
+ icon = icon,
+ modifier = Modifier.fillMaxWidth(),
+ )
+ }
} else {
Spacer(modifier = Modifier.fillMaxWidth())
}
diff --git a/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationButtonsState.kt b/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationButtonsState.kt
index 6191b4de9c..8b1bebbcd4 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationButtonsState.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/navigationButtons/NavigationButtonsState.kt
@@ -34,5 +34,6 @@ data class NavigationButton(
val isEnabled: Boolean = true,
val isDimmed: Boolean = false,
val isHapticClick: Boolean = false,
+ val isHoldToConfirm: Boolean = false,
val onClick: () -> Unit,
)
\ No newline at end of file
diff --git a/common/ui/src/main/java/com/tangem/common/ui/userwallet/state/UserWalletItemUM.kt b/common/ui/src/main/java/com/tangem/common/ui/userwallet/state/UserWalletItemUM.kt
index 3c59142510..75632fb64a 100644
--- a/common/ui/src/main/java/com/tangem/common/ui/userwallet/state/UserWalletItemUM.kt
+++ b/common/ui/src/main/java/com/tangem/common/ui/userwallet/state/UserWalletItemUM.kt
@@ -1,6 +1,6 @@
package com.tangem.common.ui.userwallet.state
-import com.tangem.common.ui.account.CryptoPortfolioIconUM
+import com.tangem.common.ui.account.AccountIconUM
import com.tangem.core.ui.components.artwork.ArtworkUM
import com.tangem.core.ui.extensions.TextReference
import javax.annotation.concurrent.Immutable
@@ -64,7 +64,7 @@ data class UserWalletItemUM(
data class Account(
val name: TextReference,
- val icon: CryptoPortfolioIconUM,
+ val icon: AccountIconUM.CryptoPortfolio,
) : ImageState()
data class Image(
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 837e466433..8fead6f8ae 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
@@ -89,6 +89,7 @@ sealed class AnalyticsParam {
data object ImportWallet : ScreensSources("Import Wallet")
data object CreateWalletIntro : ScreensSources("Create Wallet Intro")
data object AddNewWallet : ScreensSources("Add New Wallet")
+ data object AddNew : ScreensSources("Add New")
data object CreateWallet : ScreensSources("Create Wallet")
data object NewsList : ScreensSources("News List")
data object NewsLink : ScreensSources("News Link")
diff --git a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json
index 5dc9541240..299c0abe09 100644
--- a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json
+++ b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json
@@ -47,10 +47,6 @@
"name": "TANGEM_PAY_ENABLED",
"version": "5.31.0"
},
- {
- "name": "NEW_TOKEN_RECEIVE_ENABLED",
- "version": "5.28.0"
- },
{
"name": "YIELD_SUPPLY_FEATURE_ENABLED",
"version": "5.30.0"
@@ -82,5 +78,9 @@
{
"name": "SWAP_MARKET_LIST_ENABLED",
"version": "undefined"
+ },
+ {
+ "name": "EARN_BLOCK_ENABLED",
+ "version": "undefined"
}
]
diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/PromoBannerV2Response.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/PromoBannerV2Response.kt
new file mode 100644
index 0000000000..23b62757cb
--- /dev/null
+++ b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/PromoBannerV2Response.kt
@@ -0,0 +1,10 @@
+package com.tangem.datasource.api.promotion.models
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+data class PromoBannerV2Response(
+ @Json(name = "promotions")
+ val promotions: List,
+)
\ No newline at end of file
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 a3b38cb1f0..f9bdbc2aa6 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
@@ -2,6 +2,7 @@ package com.tangem.datasource.api.tangemTech
import com.tangem.datasource.api.common.response.ApiResponse
import com.tangem.datasource.api.promotion.models.PromoBannerResponse
+import com.tangem.datasource.api.promotion.models.PromoBannerV2Response
import com.tangem.datasource.api.promotion.models.StoryContentResponse
import com.tangem.datasource.api.tangemTech.models.*
import com.tangem.datasource.api.tangemTech.models.account.GetWalletAccountsResponse
@@ -188,6 +189,12 @@ interface TangemTechApi {
@Query("programName") name: String,
@Header("Cache-Control") cacheControl: String = "max-age=600",
): ApiResponse
+
+ @GET("/v2/promotion")
+ suspend fun getPromoBannersV2(
+ @Query("walletId") walletId: String,
+ @Header("Cache-Control") cacheControl: String = "max-age=600",
+ ): ApiResponse
// endregion
/**
diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/utils/RetrofitApiBuilder.kt b/core/datasource/src/main/java/com/tangem/datasource/di/utils/RetrofitApiBuilder.kt
index 2c3b8604a0..f1f6854fce 100644
--- a/core/datasource/src/main/java/com/tangem/datasource/di/utils/RetrofitApiBuilder.kt
+++ b/core/datasource/src/main/java/com/tangem/datasource/di/utils/RetrofitApiBuilder.kt
@@ -18,6 +18,7 @@ import com.tangem.datasource.api.utils.WriteTimeout
import com.tangem.datasource.di.NetworkMoshi
import com.tangem.datasource.local.logs.AppLogsStore
import com.tangem.datasource.utils.NetworkLogsSaveInterceptor
+import com.tangem.datasource.utils.WireMockRedirectInterceptor
import com.tangem.datasource.utils.addHeaders
import dagger.hilt.android.qualifiers.ApplicationContext
import okhttp3.Interceptor
@@ -77,6 +78,7 @@ internal class RetrofitApiBuilder @Inject constructor(
.client(
OkHttpClient.Builder()
.applyApiConfig(apiConfigId = apiConfigId, environmentConfig = environmentConfig)
+ .applyWireMockRedirect()
.let {
if (applyTimeoutAnnotations) it.applyTimeoutAnnotations() else it
}
@@ -181,6 +183,14 @@ internal class RetrofitApiBuilder @Inject constructor(
)
}
+ private fun OkHttpClient.Builder.applyWireMockRedirect(): OkHttpClient.Builder {
+ return if (BuildConfig.MOCK_DATA_SOURCE) {
+ addInterceptor(interceptor = WireMockRedirectInterceptor())
+ } else {
+ this
+ }
+ }
+
private fun OkHttpClient.Builder.addLoggers(apiConfigId: ApiConfig.ID, context: Context): OkHttpClient.Builder {
if (apiConfigId in excludedApiForLogging) return this
diff --git a/core/datasource/src/main/java/com/tangem/datasource/utils/WireMockRedirectInterceptor.kt b/core/datasource/src/main/java/com/tangem/datasource/utils/WireMockRedirectInterceptor.kt
new file mode 100644
index 0000000000..b246a027cb
--- /dev/null
+++ b/core/datasource/src/main/java/com/tangem/datasource/utils/WireMockRedirectInterceptor.kt
@@ -0,0 +1,40 @@
+package com.tangem.datasource.utils
+
+import okhttp3.Interceptor
+import okhttp3.Response
+import timber.log.Timber
+
+/**
+ * OkHttp interceptor that redirects requests from wiremock.tests-d.com to a local WireMock instance.
+ */
+class WireMockRedirectInterceptor : Interceptor {
+
+ override fun intercept(chain: Interceptor.Chain): Response {
+ val override = overriddenBaseUrl ?: return chain.proceed(chain.request())
+
+ val request = chain.request()
+ val url = request.url.toString()
+
+ if (url.contains(WIREMOCK_REMOTE_URL)) {
+ val newUrl = url.replace(WIREMOCK_REMOTE_URL, override.trimEnd('/'))
+ Timber.d("WireMockRedirect: $url -> $newUrl")
+ val newRequest = request.newBuilder()
+ .url(newUrl)
+ .build()
+ return chain.proceed(newRequest)
+ }
+
+ return chain.proceed(request)
+ }
+
+ companion object {
+ private const val WIREMOCK_REMOTE_URL = "[REDACTED_ENV_URL]"
+
+ /**
+ * Override base URL for WireMock requests.
+ * When null (default), requests go to wiremock.tests-d.com.
+ * When set (e.g., "http://localhost:8080"), requests are redirected to local WireMock instance.
+ */
+ var overriddenBaseUrl: String? = null
+ }
+}
\ No newline at end of file
diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml
index 2a9bd1b3eb..4175f06e1c 100644
--- a/core/res/src/main/res/values-de/strings.xml
+++ b/core/res/src/main/res/values-de/strings.xml
@@ -899,6 +899,9 @@
Krypto mit SEPA kaufen
Trage dich in die Warteliste ein und erhalte eine Zahlungskarte, die es so noch nie gab.
Tangem Visa Card
+ Bedingungen
+ Zahlen Sie mindestens $100 ein, halten Sie den Betrag 30 Tage und erhalten Sie $10.
+ Yield-Mode-Kampagne
Du musst einen einzigen Zugangscode einrichten, um alle deine Geräte zu schützen
Schützen
Du kannst später auf jeder Karte oder Ring einen individuellen Zugangscode einrichten
diff --git a/core/res/src/main/res/values-es/strings.xml b/core/res/src/main/res/values-es/strings.xml
index 901dbaefc1..c3b8bb8a6a 100644
--- a/core/res/src/main/res/values-es/strings.xml
+++ b/core/res/src/main/res/values-es/strings.xml
@@ -299,6 +299,7 @@
Ir al token
Entendido
Ocultar
+ Mantener para %s
hora
Importe
En progreso
@@ -323,6 +324,7 @@
No
Ninguna dirección
No agregadas
+ No disponible
Ahora no
Ahora
OK
@@ -373,6 +375,7 @@
Intercambiar
Tangem
Tangem Wallet
+ Toque y mantenga presionado
términos y condiciones
Condiciones de uso
A
@@ -462,6 +465,11 @@
Enviar activos a otras redes resultará en una pérdida permanente.
Red de %s
Envíe fondos utilizando solo
+ Las mejores oportunidades
+ Todas las redes
+ Todos los tipos
+ Mayormente usado
+ Ganar
Hola equipo de soporte, he encontrado un error con el código: %s
Error de WalletConnect
Ha usado una tarjeta de otra billetera. Toque la tarjeta asociada con esta billetera
@@ -561,6 +569,8 @@
Comentarios en Tangem
No se puede completar una transacción
Error de descripción de moneda
+ Fondos insuficientes
+ Tarifa de transacción
Ocurrió un error
Ocurrió un error. Código: %s
Requiere memo
@@ -610,6 +620,7 @@
Finalizar la copia de seguridad primero
Incompleto
Otros métodos
+ Guarde su frase de recuperación en un lugar seguro y manténgala privada para proteger sus fondos y configure un código de acceso para mayor seguridad.
Guarde su frase de recuperación en un lugar seguro y manténgala en privado para proteger sus fondos.
Frase de recuperación
Para proteger su billetera con un código de acceso, complete el proceso de copia de seguridad.
@@ -896,6 +907,9 @@
Comprar criptomonedas con SEPA
Únete a la lista de espera y consigue una tarjeta de pago como ninguna otra.
Tangem Visa Card
+ Términos y condiciones
+ Deposite $100 o más, manténgalo durante 30 días y reciba $10.
+ ¡Únase a la campaña Yield Mode!
Configure un código de acceso único para proteger todos sus dispositivos.
Proteger
Puede configurar un código de acceso individual en cada tarjeta más adelante
@@ -995,6 +1009,7 @@
Restaurar código de acceso
Tarjetas idénticas
Código de acceso
+ Solo puede tener una billetera móvil. Actualícela a la billetera de hardware Tangem o añada una nueva billetera de hardware.
Todas las ofertas
Disponible con %s
La compra de criptomonedas en Tangem se lleva a cabo utilizando proveedores externos.
@@ -1266,6 +1281,7 @@
La cantidad de staking se redondeará a %1$s TRX debido a las reglas de la red.
La cantidad de unstaking se redondeará a %1$s TRX debido a las reglas de la red.
APR %1$s%%
+ Sus recompensas de staking comenzarán después de 5 épocas (~25 días) mientras su delegación esté registrada y contabilizada por la red.
Reclamar lo que no está en staking
Tarifa de staking de la cuenta
Una cuenta de staking es una cuenta especial donde se almacenan los tokens SOL de staking. Se crea cuando delegas sus tokens a un validador para participar en la validación de transacciones y ganar recompensas. Se cobra una pequeña tarifa por crear la cuenta de staking, que se devuelve una vez que se completa el staking.
@@ -1276,6 +1292,7 @@
APR
APY
Las recompensas se acumulan automáticamente en su saldo de staking diariamente.
+ Las recompensas se acumulan en su saldo de staking. Fondos ganados: %s
Disponible
Tasa de recompensa promedio
¿Cómo funciona el staking?
@@ -1420,6 +1437,7 @@
Sencillo e intuitivo, permite cambiar tokens con solo unos cuantos toques
Simplemente cómodo
Intercambio a través del proveedor
+ Sus activos
La cantidad incluye:\n• Tarifas del proveedor de servicios\n• Tarifas de red por enviar %s desde el intercambio a la dirección del usuario.
El importe incluye:\n- comisión del proveedor de servicios\n- comisión de red por el envío de %1$s desde el exchange de vuelta a la dirección del usuario. \n\nEl deslizamiento (slippage) del proveedor es de hasta %2$s
El importe incluye los honorarios del proveedor de servicios.
@@ -1434,6 +1452,7 @@
Fondos insuficientes
Dar autorización
Intercambiar
+ Intercambiando...
Usted recibe
Elige token
no disponible
@@ -1452,6 +1471,9 @@
Mantén tu dinero seguro. Puedes desbloquear en cualquier momento.
Tu tarjeta está congelada.
Obtener ayuda
+ Razón: %s
+ %s · %s
+ MCC %s
Otro
No se puede usar en dispositivos rooteados
Completado
@@ -1528,10 +1550,15 @@
¿Seguro que desea detener el proceso KYC? Puede retomarlo en cualquier momento.
No pudimos verificar tu perfil. Si tienes alguna pregunta, contacta con el soporte.
Lamentablemente, no pudimos verificar tu identidad
+ El proceso KYC ha fallado
KYC en curso
Ver estado
KYC en progreso para Tangem Pay
Los documentos suelen verificarse automáticamente en menos de 5 minutos. En casos excepcionales que requieran revisión manual, el proceso puede tardar hasta 48 horas.
+ KYC rechazado
+ Ocultar el bloque KYC
+ Lo sentimos, no pudimos verificarle
+ su perfil.
Obtén tu tarjeta virtual Tangem Visa gratuita
Usa USDC para pagos cotidianos
Obtener tarjeta
@@ -1818,7 +1845,17 @@
Entendido
¡Realmente genial!
Actualizar
+ Iniciar migración
+ Copiar
+ La firma de mensajes no es compatible con esta red
+ No se puede firmar el mensaje. Por favor, inténtelo de nuevo.
Según la documentación oficial de Clore, todas las monedas recibidas antes del 21 de diciembre serán migradas a Clore (token ERC-20); las monedas recibidas después de esa fecha no lo serán. Se está desarrollando una solución de transferencia — mantente atento.
+ Mensaje
+ Abrir el portal de reclamaciones
+ Para mantener el acceso a sus fondos, comience la migración de acuerdo con las pautas oficiales de Clore.
+ Migración de la red Clore
+ Firmar
+ Firma
Migración de la red Clore
Actualmente estás en el modo Demo
Modo demo activo
diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml
index f0253cda22..d88cdd4e57 100644
--- a/core/res/src/main/res/values-fr/strings.xml
+++ b/core/res/src/main/res/values-fr/strings.xml
@@ -43,6 +43,7 @@
Ajouter un compte
Enregistrer
Nom du compte
+ Un compte portant ce nom existe déjà. Veuillez choisir un autre nom.
Nom de compte déjà utilisé
Compte
Nouveau compte
@@ -298,6 +299,7 @@
Aller au jeton
Compris
Cacher
+ Maintenir contre %s
heure
Importez
En cours
@@ -322,6 +324,7 @@
Non
Aucune adresse
Non ajouté
+ Indisponible
Pas maintenant
Maintenant
OK
@@ -372,6 +375,7 @@
Échanger
Tangem
Tangem Wallet
+ Appuyez et maintenez enfoncé
termes et conditions
Conditions d\'utilisation
À
@@ -399,6 +403,8 @@
Mode Rendement
Adresse du contrat copiée !
Réseaux disponibles
+ La dérivation de votre token correspond à la dérivation de %1$s. Votre token sera ajouté à ce compte.
+ La dérivation appartient à un autre compte.
Jeton ajouté au compte %1$s
Adresse du contrat
L\'adresse du contrat n\'est pas valide
@@ -459,6 +465,11 @@
L’envoi d’actifs sur d’autres réseaux entraînera une perte définitive.
%s réseau
Envoyez des fonds en utilisant uniquement
+ Meilleures opportunités
+ Tous les réseaux
+ Tous les types
+ Principalement utilisé
+ Gagner
Bonjour équipe de support, j’ai rencontré une erreur avec le code : %s
Erreur WalletConnect
Vous avez utilisé une carte d\'un autre portefeuille. Appuyez sur la carte associée à ce portefeuille
@@ -558,6 +569,8 @@
Commentaires sur Tangem
Impossible d\'effectuer une transaction
Erreur de description de la pièce
+ Fonds insuffisants
+ Frais de transaction
Une erreur s\'est produite
Une erreur s\'est produite. Code:%s
Un mémo est requis
@@ -607,6 +620,7 @@
Finalisez d\'abord la sauvegarde.
Incomplet
Autres méthodes
+ Conservez votre seed phrase dans un endroit sûr et gardez-la confidentielle afin de protéger vos fonds, puis configurez un code d\'accès pour plus de sécurité.
Conservez votre seed phrase dans un endroit sûr et gardez-la confidentielle afin de protéger vos fonds.
Seed phrase
Pour sécuriser votre wallet avec un code d\'accès, effectuez la procédure de sauvegarde.
@@ -644,7 +658,7 @@
Une erreur s\'est produite pendant l\'opération.
Vos fonds restent en sécurité et entièrement accessibles pendant toute la durée du processus.
Accès aux fonds
- Après la mise à niveau, votre portefeuille mobile sera supprimé de l\'application et stocké sur votre hardware wallet; votre seedphrase restera en votre possession.
+ Après la mise à niveau, votre portefeuille mobile sera supprimé de l\'application et stocké sur votre hardware wallet; votre seed phrase restera en votre possession.
Sécurité générale
Les clés privées seront transférées de l\'application vers votre hardware wallet Tangem.
Migration des clés
@@ -893,6 +907,9 @@
Acheter des cryptomonnaies avec SEPA
Rejoignez la liste d’attente et obtenez une carte de paiement pas comme les autres.
Tangem Visa Card
+ Conditions générales
+ Déposez 100 $ ou plus, conservez le dépôt pendant 30 jours et recevez 10 $.
+ Rejoignez la campagne Yield Mode !
Vous devez définir un seul code d\'accès pour protéger tous vos appareils.
Protéger
Vous pourrez définir un code d\'accès individuel sur chaque carte plus tard
@@ -992,6 +1009,7 @@
Restauration du code d\'accès
Cartes identiques
Code d\'accès
+ Vous ne pouvez avoir qu\'un seul portefeuille mobile. Passez au portefeuille matériel Tangem ou ajoutez un nouveau portefeuille matériel.
Toutes les offres
Disponible avec %s
L\'achat de crypto-monnaies sur Tangem est assuré par des fournisseurs tiers selon leurs conditions.
@@ -1036,6 +1054,7 @@
Disponible auprès de
Disponible jusqu\'à
Vous obtenez
+ Le service est fourni par un prestataire externe. Tangem n\'est pas responsable.
Vous pouvez vérifier l\'état de la transaction à partir de la page du jeton
Jusqu\'à
Via
@@ -1248,18 +1267,21 @@
Cela supprimera le portefeuille de l\'application. Le portefeuille lui-même peut être ajouté à nouveau.
Facile à utiliser
Gardez vos cryptomonnaies en sécurité et hors ligne. Aussi fin qu\'une carte de crédit, plus sûr qu\'un coffre-fort bancaire.
- Pas de seedphrase
+ Pas de seed phrase
Le meilleur hardware wallet
Tangem Hardware Wallet
Nom
Faites travailler votre jeton pour vous
+ Les frais de réseau sont une petite somme à payer pour traiter et confirmer ta transaction sur la blockchain.
Pour commencer à staker, votre compte TON doit être activé avec une transaction de 1 TON. Les fonds restent sur votre compte, car cette étape sert uniquement à l\'activer pour le staking.
+ Activation du compte
Les frais de réseau ont changé. Veuillez vérifier le nouveau montant avant de continuer.
Frais de réseau mis à jour
Le montant à staker doit être au moins %s
Le montant du staking sera arrondi à %1$s TRX en raison des règles du réseau.
Le montant d\'annulation du staking sera arrondi à %1$s TRX en raison des règles du réseau.
TAEG %1$s%%
+ Vos récompenses de staking commenceront après 5 époques (~25 jours), une fois que votre délégation aura été enregistrée et comptabilisée par le réseau.
Réclamation déstakée
Frais de staking du compte
Un compte de staking est un compte spécial où sont stockés les jetons SOL stakés. Il est créé lorsque vous déléguez vos jetons à un validateur pour participer à la validation des transactions et gagner des récompenses. Des frais minimes sont facturés pour la création du compte de staking, qui sont restitués une fois le staking terminé.
@@ -1270,6 +1292,7 @@
APR
APY
Les récompenses s\'accumulent automatiquement sur votre solde de staking quotidiennement.
+ Les récompenses sont ajoutées à votre solde de staking. Fonds gagnés : %s
Disponible
Taux de récompense moyen
Qu\'est-ce que le Staking ?
@@ -1325,6 +1348,7 @@
L\'option de restaker vous permet de déplacer vos fonds d\'un validateur à un autre sans avoir besoin de les déstaker.
Vous êtes sur le point de staker l\'intégralité de votre solde. Nous vous recommandons de laisser un petit montant pour couvrir les frais de réseau pour unstaking ou la réclamation des récompenses.
Pour commencer le staking, vous devez d’abord activer votre compte TON
+ Activation du compte
Pour commencer à staker TON, effectuez d\'abord une transaction sortante de n\'importe quel montant — cela activera votre portefeuille.
Jusqu\'à 0,2 TON peuvent être requis en plus des frais de réseau pour terminer la transaction. Tout montant non utilisé sera remboursé.
0,2 TON requis pour effectuer cette opération, en plus des frais de réseau. Veuillez recharger votre solde.
@@ -1413,6 +1437,7 @@
Simple et intuitif, vous permettant d\'échanger des jetons en quelques clics
Plus Simple Que Jamais
Échange via le fournisseur
+ Vos actifs
Le montant comprend :\n• les frais du fournisseur de services\n• les frais de réseau pour l\'envoi de %s depuis l\'échange vers l\'adresse de l\'utilisateur.
Le montant comprend :\n• les frais du fournisseur de services\n• les frais de réseau pour l\'envoi de %1$s depuis l\'échange vers l\'adresse de l\'utilisateur. \n\nLe slippage du fournisseur peut atteindre %2$s
Le montant comprend les frais du fournisseur de services.
@@ -1427,6 +1452,7 @@
Fonds insuffisants
Donner l\'autorisation
Échanger
+ Échange...
Vous recevez
Choisir le jeton
non disponible
@@ -1445,6 +1471,9 @@
Geler
Votre carte est gelée.
Obtenir de l\'aide
+ Raison: %s
+ %s · %s
+ MCC %s
Autre
Impossible à utiliser sur les appareils rootés
Terminé
@@ -1521,10 +1550,15 @@
Êtes-vous sûr de vouloir interrompre le processus KYC ? Vous pouvez y revenir à tout moment.
Nous n\'avons pas pu vérifier votre profil. Pour toute question, veuillez contacter le support.
Malheureusement, nous n\'avons pas pu vérifier votre identité
+ KYC a échoué
KYC en cours
Voir le statut
KYC en cours pour Tangem Pay
Les documents sont généralement vérifiés automatiquement en moins de 5 minutes. Dans de rares cas nécessitant une vérification manuelle, le processus peut prendre jusqu’à 48 heures.
+ KYC rejeté
+ Masquer le bloc KYC
+ Désolé, nous n\'avons pas pu vérifier.
+ votre profil.
Obtenez votre carte virtuelle Tangem Visa gratuite
Utilisez USDC pour les paiements quotidiens
Obtenir la carte
@@ -1579,6 +1613,7 @@
Jeton dans le %%image%% %1$s réseau
Le jeton %1$s (%2$s) est la principale devise du réseau %3$s et ne peut pas être masqué tant que vous avez d\'autres jetons de ce réseau dans la liste
Impossible de masquer %s
+ Afficher le code QR
Échangez ce jeton contre un autre moyennant des frais de service de %1$s du %2$s au %3$s février.
Échangez avec Changelly, %s frais
Échangez maintenant
@@ -1710,6 +1745,12 @@
Scannez votre carte pour déverrouiller l\'accès
Déverrouillage nécessaire
Choisissez comment ajouter votre portefeuille
+ Scannez votre carte ou votre bague Tangem pour la restaurer ou l\'importer depuis un autre portefeuille.
+ Créer un Hardware Wallet
+ Vous souhaitez acheter un portefeuille Tangem ?
+ Importer la seed phrase
+ Restaurez votre portefeuille sur votre téléphone ou importez-le depuis une autre application — pratique, mais moins sécurisé qu\'une carte Tangem.
+ Que choisir ?
La blockchain n\'est pas accessible. Réessayez plus tard
Scanner la carte ou la bague
Ce portefeuille a déjà été activé auparavant.\nSi cela n\'a pas été fait par vous, veuillez contacter le support.\nTangem ne vend jamais de portefeuilles avec le code d\'accès pré-généré.
@@ -1766,9 +1807,9 @@
Ajouter un portefeuille existant
Dispositifs physiques qui stockent votre clé privée hors ligne en toute sécurité.
Scanner un Tangem Wallet
- Importez un portefeuille existant à l\'aide de votre seedphrase.
+ Importez un portefeuille existant à l\'aide de votre seed phrase.
Importer un wallet
- Entrez la seedphrase
+ Entrez la seed phrase
Vous avez importé votre portefeuille avec succès.
Importer un wallet
Importation terminée
@@ -1984,7 +2025,7 @@
Livraison rapide
Commencez en un seul clic
Sans faille et sécurisé
- Pas de seedphrase
+ Pas de seed phrase
Facile à utiliser
Créez un harware wallet avec Tangem. Aussi fin qu\'une carte bancaire, aussi sûr qu\'un coffre-fort.
Créer ou importer un portefeuille logiciel
@@ -2105,4 +2146,5 @@
Impossible de couvrir les frais %s
Le mode rendement n\'est pas disponible pour le moment. Veuillez réessayer plus tard.
Le mode Rendement est indisponible
+ Impossible de charger le graphique...
diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml
index cae90804ea..770eb98276 100644
--- a/core/res/src/main/res/values-ja/strings.xml
+++ b/core/res/src/main/res/values-ja/strings.xml
@@ -294,6 +294,7 @@
トークンへ移動
わかりました
非表示
+ %sまで長押し
時間
インポート
進行中
@@ -368,6 +369,7 @@
スワップ
Tangem
Tangem Wallet
+ タップして長押し
利用規約
利用規約
宛先
@@ -456,6 +458,11 @@
他のネットワークで資産を送金すると、永久に失われます。
%sネットワーク
下記のみを使用して資金を送金する
+ おすすめ
+ すべてのネットワーク
+ すべての種類
+ よく使われています
+ 運用
こんにちは、サポートチームの皆さん、コード %s のエラーが発生しました。
WalletConnectエラー
別のウォレットのカードまたはリングを使用しました。このウォレットにリンクしているカードまたはリングをタップしてください。
@@ -887,6 +894,9 @@
SEPAで暗号資産を購入
事前申し込み受付中。他にない特別なカードを、いち早く体験しよう。
Tangem Visaカード
+ 利用規約
+ 100ドル以上を入金し、30日間保持すると、10ドルを受け取れます。
+ Yield Mode キャンペーンに参加しよう!
すべてのデバイスを保護するには、単一のアクセスコードを設定してください。
保護する
後で各カードおよびリングに個別のアクセスコードを設定できます。
@@ -935,11 +945,11 @@
生体認証
シードフレーズについてもっと読む
- - これらの %d 単語を以下の順番通りに書き留め、安全かつ秘密の場所に保管してください。
+ - 以下に表示される%d個の単語を順番どおりに書き留め、安全で他人に知られない場所に保管してください。
あなたのシードフレーズ
- - %d 単語
+ - %d個の単語
ウォレットをインポートするには、下のフィールドにシードフレーズを入力してください。
シードフレーズを生成する
@@ -1252,6 +1262,7 @@
ネットワークルールにより、ステーキング金額は%1$s TRX に切り上げられます。
ネットワークルールにより、ステーキング解除の量は%1$s TRX に切り上げられます。
APR %1$s%%
+ デリゲーションがネットワークに登録され、集計されるまでの間、ステーキング報酬は5エポック後(約25日後)から開始されます。
ステーキング解除分を請求する
ステーキングアカウント手数料
ステーキングアカウントとは、ステーキングされたSOLが保管される特別なアカウントです。トークンをバリデーターに委任し、取引の検証に参加して報酬を受け取る際に作成されます。ステーキングアカウントの作成には少額の手数料がかかりますが、ステーキング完了後に返金されます。
@@ -1441,6 +1452,9 @@
一時停止
カードが凍結されています
サポートを受ける
+ 理由:%s
+ %s・%s
+ MCC %s
その他
Root化された端末では使用できません
完了
@@ -1643,25 +1657,69 @@
ウォレット名の変更
すべてのロックを解除
%sですべてをロック解除
+ AML認証済み
+ 利用可能
+ ブロック中
+ 負債
+ 利用上限
+ その他(OTPなし)
+ 単一の取引
+ 合計
- %d日間利用可能
+ 利用可能残高は、保留中の取引、ブロック中の金額、デビット残高を考慮した、実際に利用できる資金を示します。
+ %1$sまで利用可能
残高と限度額
+ 支払いアカウントの情報が見つかりません。サポートにお問い合わせください。
+ トークンの情報を読み込めません。
+ 情報を読み込み中です。しばらくお待ちください。
+ コストの管理、セキュリティの向上、リスク管理のために利用上限が設けられています。店舗でのカード決済では期間内に%1$s、サブスクリプションや支払いなどのその他の取引では%2$sまで利用できます。
アクセスコードは、支払いアカウントの管理および不正アクセスからの保護に使用されます。
アクセスコード
+ アカウント有効化
+ 登録手続きを開始したウォレットを選択し、ブロックチェーン上でアカウントを作成するための取引に署名してください。
+ 有効化をキャンセル
本当に終了してもよろしいですか?中断したところから後で続行できます。
長くはかかりません。アカウントを設定しています。
長くはかかりません。アクティベーションを完了しています。
準備完了です!
+ その他のウォレット
4桁のコードを設定します。 \nお支払いの際に使用されます。
+ PINコード
PINコードを作成する
PINの認証に失敗しました。もう一度お試しいただくか、別のコードを使用してください。
無効な暗証番号:連続や繰り返しを避けてください
+ 準備完了です!
+ Tangemカードを用意し、タップして承認してください。
+ Tangemウォレットを準備してください。
+ \nサードパーティのWebサイト\nで接続手続きを完了し、その後Tangemアプリに戻ってください。
ウェブサイトに移動
+ ウォレット接続
+ ウォレットを選択
+ 有効化を続行
アカウントの設定を続けましょう。
お帰りなさい!
+ 有効化を開始
手順に従ってアカウントを設定してください。
ようこそ!
+ ブロックチェーン上の金額
+ 通貨コード
+ 日付
+ エラーコード
+ 取引詳細
+ 加盟店カテゴリーコード
+ 加盟店の所在地
+ 加盟店の国コード
+ 加盟店名
+ リクエストID
+ ステータス
+ 取引
+ 取引金額
+ トランザクションハッシュ
+ 取引リクエスト
+ 取引ステータス
+ 種類
この取引に異議を唱える
ロック解除
カードをスキャンしてアクセスロックを解除する
diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml
index 45f190c03a..b0e42bdf47 100644
--- a/core/res/src/main/res/values-ru/strings.xml
+++ b/core/res/src/main/res/values-ru/strings.xml
@@ -925,6 +925,9 @@
Покупайте крипту через SEPA
Присоединяйтесь к списку ожидания и получите уникальную платёжную карту, не похожую ни на одну другую.
Tangem Visa Card
+ Условия программы
+ Внесите депозит от $100, удерживайте его 30 дней и получите бонус $10
+ Подключите Режим доходности!
Установите единый код доступа для защиты всех ваших карт или колец
Защита
Установите индивидуальный код доступа для каждой карты или кольца позже.
@@ -1800,7 +1803,17 @@
Понятно!
Очень круто!
Обновить
+ Начать миграцию
+ Копировать
+ Подписание сообщений не поддерживается в этой сети
+ Невозможно подписать сообщение. Пожалуйста, попробуй позже.
Согласно официальной документации Clore, все монеты, полученные до 21 декабря, будут мигрированы в токен Clore (ERC-20); монеты, полученные после этой даты, — нет. Решение для перевода находится в разработке — следите за обновлениями.
+ Сообщение
+ Открыть портал клейма
+ Чтобы сохранить доступ к своим средствам, начните миграцию в соответствии с официальной инструкцией Clore.
+ Миграция сети Clore
+ Подписать
+ Подпись
Миграция сети Clore
Вы находитесь в режиме демо
Демо режим включен
diff --git a/core/res/src/main/res/values-uk-rUA/strings.xml b/core/res/src/main/res/values-uk-rUA/strings.xml
index 61ee270e13..67360c1ce3 100644
--- a/core/res/src/main/res/values-uk-rUA/strings.xml
+++ b/core/res/src/main/res/values-uk-rUA/strings.xml
@@ -44,6 +44,7 @@
Зберегти
Назва акаунту
Акаунт з такою назвою вже існує. Будь ласка, виберіть іншу назву.
+ Обліковий запис з таким ім’ям уже існує
Акаунт
Новий акаунт
Додати акаунт
@@ -908,6 +909,9 @@
Купуйте криптовалюту через SEPA
Приєднуйтесь до списку очікування та отримайте унікальну платіжну картку, якої ще не було.
Tangem Visa Card
+ Умови та положення
+ Внесіть депозит від $100, утримуйте його 30 днів та отримайте $10.
+ Кампанія Yield Mode!
Налаштуйте єдиний код доступу для захисту всіх ваших карток або кілець
Захист
Встановіть індивідуальний код доступу для кожної картки або кільця пізніше.
diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index f583c64401..ed8c132108 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -86,6 +86,8 @@
Send only %1$s (%2$s) from %3$s network to this address. Using other tokens and networks may result in loss of funds.
Default
Legacy
+ Try to reset biometrics on your device or contact support
+ Authentication error
How to scan
Request support
Try again
@@ -908,6 +910,9 @@
Buy Crypto with SEPA
Join the waitlist and get a payment card unlike any other
Tangem Visa Card
+ Terms and conditions
+ Deposit $100+, hold for 30 days, get $10
+ Join the Yield Mode Campaign!
Set up a single access code to protect all your devices.
Protect
Set an individual access code for each card or ring later.
@@ -1843,7 +1848,18 @@
Ok, Got it!
Really cool!
Refresh
+ Start migration
+ Copy
+ To keep access to your funds, start the migration according to the official Clore guidelines.
+ Message signing is not supported for this network
+ Unable to sign message. Please try again.
According to Clore’s official documentation, all coins received before December 21 will be migrated to Clore (ERC-20 token); coins received after that date will not. A transfer solution is coming — stay tuned.
+ Message
+ Open Claim portal
+ To continue using your Clore tokens, you must complete the token migration according to the information on the Claim Portal.
+ Clore Network Migration
+ Sign
+ Signature
Clore Network Migration
You are currently in the Demo mode
Demo mode active
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/Buttons.kt b/core/ui/src/main/java/com/tangem/core/ui/components/Buttons.kt
index d1cf7a2a93..52caca9648 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/Buttons.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/Buttons.kt
@@ -15,6 +15,7 @@ import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.R
import com.tangem.core.ui.components.buttons.common.*
+import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
@@ -320,9 +321,9 @@ fun SelectorButton(text: String, onClick: () -> Unit, modifier: Modifier = Modif
@Composable
fun HoldToConfirmButton(
text: String,
- hintText: String,
onConfirm: () -> Unit,
modifier: Modifier = Modifier,
+ hintText: String = stringResourceSafe(R.string.common_tap_and_hold_hint),
enabled: Boolean = true,
isLoading: Boolean = false,
) {
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/account/AccountIcon.kt b/core/ui/src/main/java/com/tangem/core/ui/components/account/AccountIcon.kt
index dd5ea8c8a6..939eac7407 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/account/AccountIcon.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/account/AccountIcon.kt
@@ -6,22 +6,20 @@ import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
+import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
+import androidx.compose.runtime.*
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.graphics.vector.ImageVector
+import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
@@ -76,6 +74,33 @@ fun AccountResIcon(@DrawableRes resId: Int, color: Color, size: AccountIconSize,
}
}
+/**
+ * Displays a PaymentAccount icon using a predefined painter resource.
+ *
+ * The icon size is adapted based on the provided [size].
+ *
+ * @param size The size of the icon, defined by [AccountIconSize].
+ */
+@Composable
+fun PaymentAccountIcon(size: AccountIconSize, modifier: Modifier = Modifier) {
+ val boxSize by animateDpAsState(
+ targetValue = size.boxSizeInDp(),
+ animationSpec = animation(),
+ )
+ val boxShapeCornerSize by animateDpAsState(
+ targetValue = size.boxShapeSizeInDp(),
+ animationSpec = animation(),
+ )
+ Box(
+ contentAlignment = Alignment.Center,
+ modifier = modifier
+ .size(boxSize)
+ .clip(RoundedCornerShape(boxShapeCornerSize)),
+ ) {
+ Image(painter = painterResource(id = R.drawable.img_visa_36), contentDescription = null)
+ }
+}
+
/**
* Displays a portfolio icon using a single character.
*
@@ -186,6 +211,7 @@ private fun Sample() {
AccountResIcon(resId = R.drawable.ic_user_24, color = Color.Magenta, size = AccountIconSize.Medium)
AccountResIcon(resId = R.drawable.ic_family_24, color = Color.DarkGray, size = AccountIconSize.Small)
AccountResIcon(resId = R.drawable.ic_wallet_24, color = Color.Green, size = AccountIconSize.ExtraSmall)
+ PaymentAccountIcon(size = AccountIconSize.Default)
}
Column(verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) {
AccountCharIcon(char = 'D', color = Color.Red, size = sizeState)
diff --git a/data/account/src/main/kotlin/com/tangem/data/account/fetcher/DefaultWalletAccountsFetcher.kt b/data/account/src/main/kotlin/com/tangem/data/account/fetcher/DefaultWalletAccountsFetcher.kt
index 12c40f608d..68bce0a025 100644
--- a/data/account/src/main/kotlin/com/tangem/data/account/fetcher/DefaultWalletAccountsFetcher.kt
+++ b/data/account/src/main/kotlin/com/tangem/data/account/fetcher/DefaultWalletAccountsFetcher.kt
@@ -87,6 +87,15 @@ internal class DefaultWalletAccountsFetcher @Inject constructor(
store.updateData { response }
}
+ override suspend fun update(
+ userWalletId: UserWalletId,
+ transform: (GetWalletAccountsResponse?) -> GetWalletAccountsResponse?,
+ ) {
+ val store = getAccountsResponseStore(userWalletId = userWalletId)
+
+ store.updateData { transform(it) }
+ }
+
override suspend fun push(
userWalletId: UserWalletId,
accounts: List,
diff --git a/data/common/src/main/kotlin/com/tangem/data/common/account/WalletAccountsSaver.kt b/data/common/src/main/kotlin/com/tangem/data/common/account/WalletAccountsSaver.kt
index de6c29ccc4..09c0ea5fa7 100644
--- a/data/common/src/main/kotlin/com/tangem/data/common/account/WalletAccountsSaver.kt
+++ b/data/common/src/main/kotlin/com/tangem/data/common/account/WalletAccountsSaver.kt
@@ -15,6 +15,11 @@ interface WalletAccountsSaver {
/** Store wallet accounts [response] by [userWalletId] */
suspend fun store(userWalletId: UserWalletId, response: GetWalletAccountsResponse)
+ suspend fun update(
+ userWalletId: UserWalletId,
+ transform: (GetWalletAccountsResponse?) -> GetWalletAccountsResponse?,
+ )
+
/** Push wallet accounts [body] by [userWalletId] */
@Throws
suspend fun push(userWalletId: UserWalletId, body: SaveWalletAccountsResponse): GetWalletAccountsResponse?
diff --git a/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt b/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt
index 31133bb96f..625fdc2535 100644
--- a/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt
+++ b/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt
@@ -66,6 +66,11 @@ internal class DefaultPromoRepository(
PromoId.OnePlusOne -> {
val isActive = getOnePlusOnePromoBanner()?.isActive == true
+ isActive && shouldShow
+ }
+ PromoId.YieldPromo -> {
+ val isActive = getYieldPromoBanner(userWalletId)?.isActive == true
+
isActive && shouldShow
}
}
@@ -79,6 +84,7 @@ internal class DefaultPromoRepository(
PromoId.VisaPresale -> flowOf(false)
PromoId.BlackFriday -> flowOf(false)
PromoId.OnePlusOne -> flowOf(false)
+ PromoId.YieldPromo -> flowOf(false)
}
}
@@ -190,12 +196,22 @@ internal class DefaultPromoRepository(
}.getOrNull()
}
+ private suspend fun getYieldPromoBanner(userWalletId: UserWalletId): PromoBanner? {
+ return runCatching(dispatchers.io) {
+ val response = tangemApi.getPromoBannersV2(userWalletId.stringValue).getOrThrow()
+ val yieldPromotion = response.promotions.find { it.name == YIELD_PROMO_NAME }
+ ?: return@runCatching null
+ promoBannerConverter.convert(yieldPromotion)
+ }.getOrNull()
+ }
+
private companion object {
const val SEPA_NAME = "sepa"
const val VISA_NAME = "visa-waitlist"
const val BLACK_FRIDAY_NAME = "black-friday"
const val MOONPAY_NAME = "moonpay"
const val ONE_PLUS_ONE_NAME = "one-plus-one"
+ const val YIELD_PROMO_NAME = "promo-yield"
const val STORIES_LOAD_DELAY = 1000L
}
}
\ No newline at end of file
diff --git a/data/wallet-manager/src/main/java/com/tangem/data/walletmanager/DefaultWalletManagersFacade.kt b/data/wallet-manager/src/main/java/com/tangem/data/walletmanager/DefaultWalletManagersFacade.kt
index 788165be89..b2b04e77fa 100644
--- a/data/wallet-manager/src/main/java/com/tangem/data/walletmanager/DefaultWalletManagersFacade.kt
+++ b/data/wallet-manager/src/main/java/com/tangem/data/walletmanager/DefaultWalletManagersFacade.kt
@@ -22,6 +22,7 @@ import com.tangem.blockchain.transactionhistory.models.TransactionHistoryRequest
import com.tangem.blockchain.yieldsupply.YieldSupplyContractCallDataProviderFactory
import com.tangem.blockchainsdk.BlockchainSDKFactory
import com.tangem.blockchainsdk.models.UpdateWalletManagerResult
+import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.blockchainsdk.utils.toBlockchain
import com.tangem.blockchainsdk.utils.toNetworkId
import com.tangem.crypto.hdWallet.DerivationPath
@@ -629,6 +630,22 @@ internal class DefaultWalletManagersFacade @Inject constructor(
)
}
+ override suspend fun getNativeTokenBalance(
+ userWalletId: UserWalletId,
+ networkId: String,
+ derivationPath: String?,
+ ): BigDecimal {
+ val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" }
+ val walletManager = getOrCreateWalletManager(userWalletId, blockchain, derivationPath)
+
+ return walletManager?.wallet?.amounts
+ ?.firstNotNullOfOrNull { amountEntry ->
+ amountEntry.takeIf { amountEntry.key is AmountType.Coin }
+ }
+ ?.value?.value
+ ?: BigDecimal.ZERO
+ }
+
override suspend fun getAssetRequirements(
userWalletId: UserWalletId,
currency: CryptoCurrency,
diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/wallet/UserWallet.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/wallet/UserWallet.kt
index 8f2b0572ff..5df91b67b1 100644
--- a/domain/models/src/main/kotlin/com/tangem/domain/models/wallet/UserWallet.kt
+++ b/domain/models/src/main/kotlin/com/tangem/domain/models/wallet/UserWallet.kt
@@ -108,4 +108,6 @@ val UserWallet.isLocked
get() = when (this) {
is UserWallet.Cold -> isLocked
is UserWallet.Hot -> isLocked
- }
\ No newline at end of file
+ }
+
+inline val UserWallet.isHotWallet get() = this is UserWallet.Hot
\ No newline at end of file
diff --git a/domain/promo/models/src/main/java/com/tangem/domain/promo/models/PromoBanner.kt b/domain/promo/models/src/main/java/com/tangem/domain/promo/models/PromoBanner.kt
index 9f8055dc60..ee31586b29 100644
--- a/domain/promo/models/src/main/java/com/tangem/domain/promo/models/PromoBanner.kt
+++ b/domain/promo/models/src/main/java/com/tangem/domain/promo/models/PromoBanner.kt
@@ -31,4 +31,5 @@ enum class PromoId {
VisaPresale,
BlackFriday,
OnePlusOne,
+ YieldPromo,
}
\ No newline at end of file
diff --git a/domain/promo/src/main/java/com/tangem/domain/promo/ShouldShowPromoWalletUseCase.kt b/domain/promo/src/main/java/com/tangem/domain/promo/ShouldShowPromoWalletUseCase.kt
index 5cab5b1cde..7f6f41114d 100644
--- a/domain/promo/src/main/java/com/tangem/domain/promo/ShouldShowPromoWalletUseCase.kt
+++ b/domain/promo/src/main/java/com/tangem/domain/promo/ShouldShowPromoWalletUseCase.kt
@@ -33,6 +33,7 @@ class ShouldShowPromoWalletUseCase(
PromoId.VisaPresale,
PromoId.BlackFriday,
PromoId.OnePlusOne,
+ PromoId.YieldPromo,
-> true
PromoId.Sepa -> {
val walletFirstUsageDate = settingsRepository.getWalletFirstUsageDate()
diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/PromoAnalyticsEvent.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/PromoAnalyticsEvent.kt
index 3d3f0fbdb1..df7fde5ad2 100644
--- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/PromoAnalyticsEvent.kt
+++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/analytics/PromoAnalyticsEvent.kt
@@ -60,5 +60,6 @@ sealed class PromoAnalyticsEvent(
Sepa("Sepa"),
BlackFriday("Black Friday"),
OnePlusOne("One-Plus-One"),
+ YieldPromo("Yield Promo"),
}
}
\ No newline at end of file
diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/details/NavigationAction.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/details/NavigationAction.kt
index e3343ec90b..ca8dbfd9f9 100644
--- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/details/NavigationAction.kt
+++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/details/NavigationAction.kt
@@ -6,4 +6,5 @@ import kotlinx.serialization.Serializable
sealed class NavigationAction {
data object Staking : NavigationAction()
data class YieldSupply(val isActive: Boolean) : NavigationAction()
+ data object CloreMigration : NavigationAction()
}
\ No newline at end of file
diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/error/SignCloreMessageError.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/error/SignCloreMessageError.kt
new file mode 100644
index 0000000000..a87418223c
--- /dev/null
+++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/error/SignCloreMessageError.kt
@@ -0,0 +1,10 @@
+package com.tangem.domain.transaction.error
+
+sealed class SignCloreMessageError {
+
+ data object WalletManagerNotFound : SignCloreMessageError()
+
+ data object MessageSigningNotSupported : SignCloreMessageError()
+
+ data class SigningFailed(val message: String) : SignCloreMessageError()
+}
\ No newline at end of file
diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/SignCloreMessageUseCase.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/SignCloreMessageUseCase.kt
new file mode 100644
index 0000000000..c4e4913822
--- /dev/null
+++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/SignCloreMessageUseCase.kt
@@ -0,0 +1,58 @@
+package com.tangem.domain.transaction.usecase
+
+import arrow.core.Either
+import arrow.core.left
+import arrow.core.right
+import com.tangem.blockchain.common.MessageSigner
+import com.tangem.blockchain.common.TransactionSigner
+import com.tangem.common.CompletionResult
+import com.tangem.domain.card.repository.CardSdkConfigRepository
+import com.tangem.domain.models.currency.CryptoCurrency
+import com.tangem.domain.models.wallet.UserWallet
+import com.tangem.domain.transaction.error.SignCloreMessageError
+import com.tangem.domain.walletmanager.WalletManagersFacade
+
+class SignCloreMessageUseCase(
+ private val walletManagersFacade: WalletManagersFacade,
+ private val cardSdkConfigRepository: CardSdkConfigRepository,
+ private val getHotWalletSigner: (UserWallet.Hot) -> TransactionSigner,
+) {
+
+ suspend operator fun invoke(
+ userWallet: UserWallet,
+ currency: CryptoCurrency,
+ message: String,
+ ): Either {
+ val walletManager = walletManagersFacade.getOrCreateWalletManager(
+ userWalletId = userWallet.walletId,
+ network = currency.network,
+ )
+
+ if (walletManager == null) {
+ return SignCloreMessageError.WalletManagerNotFound.left()
+ }
+
+ if (walletManager !is MessageSigner) {
+ return SignCloreMessageError.MessageSigningNotSupported.left()
+ }
+
+ val signer = when (userWallet) {
+ is UserWallet.Cold -> {
+ val card = userWallet.scanResponse.card
+ val isCardNotBackedUp = card.backupStatus?.isActive != true
+ cardSdkConfigRepository.getCommonSigner(
+ cardId = card.cardId.takeIf { isCardNotBackedUp },
+ twinKey = null,
+ )
+ }
+ is UserWallet.Hot -> getHotWalletSigner(userWallet)
+ }
+
+ return when (val result = walletManager.signMessage(message, signer)) {
+ is CompletionResult.Success -> result.data.right()
+ is CompletionResult.Failure -> SignCloreMessageError.SigningFailed(
+ message = result.error.message ?: "Unknown error",
+ ).left()
+ }
+ }
+}
\ No newline at end of file
diff --git a/domain/wallet-manager/src/main/kotlin/com/tangem/domain/walletmanager/WalletManagersFacade.kt b/domain/wallet-manager/src/main/kotlin/com/tangem/domain/walletmanager/WalletManagersFacade.kt
index 26f486dbb3..adf5c09070 100644
--- a/domain/wallet-manager/src/main/kotlin/com/tangem/domain/walletmanager/WalletManagersFacade.kt
+++ b/domain/wallet-manager/src/main/kotlin/com/tangem/domain/walletmanager/WalletManagersFacade.kt
@@ -228,6 +228,13 @@ interface WalletManagersFacade {
id: String? = null,
): BigDecimal
+ @Throws(IllegalStateException::class)
+ suspend fun getNativeTokenBalance(
+ userWalletId: UserWalletId,
+ networkId: String,
+ derivationPath: String?,
+ ): BigDecimal
+
/**
* Get requirements for asset(currency)
* @return null if there's no requirement, otherwise [AssetRequirementsCondition].
diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/analytics/Settings.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/analytics/Settings.kt
index 703dcb8cbb..7f8f5b9916 100644
--- a/domain/wallets/src/main/java/com/tangem/domain/wallets/analytics/Settings.kt
+++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/analytics/Settings.kt
@@ -1,6 +1,9 @@
package com.tangem.domain.wallets.analytics
import com.tangem.core.analytics.models.AnalyticsEvent
+import com.tangem.core.analytics.models.AnalyticsParam
+import com.tangem.core.analytics.models.AppsFlyerIncludedEvent
+import com.tangem.core.analytics.models.OneTimeAnalyticsEvent
sealed class Settings(
category: String = "Settings",
@@ -9,4 +12,13 @@ sealed class Settings(
) : AnalyticsEvent(category, event, params) {
class ButtonManageTokens : Settings(event = "Button - Manage Tokens")
+
+ class ColdWalletAdded(
+ source: AnalyticsParam.ScreensSources?,
+ ) : Settings(
+ event = "Cold Wallet Added",
+ params = mapOf(AnalyticsParam.SOURCE to (source?.value ?: "Unknown")),
+ ), OneTimeAnalyticsEvent, AppsFlyerIncludedEvent {
+ override val oneTimeEventId: String = id
+ }
}
\ No newline at end of file
diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/analytics/WalletSettingsAnalyticEvents.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/analytics/WalletSettingsAnalyticEvents.kt
index 1c23c85475..be4e913124 100644
--- a/domain/wallets/src/main/java/com/tangem/domain/wallets/analytics/WalletSettingsAnalyticEvents.kt
+++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/analytics/WalletSettingsAnalyticEvents.kt
@@ -4,6 +4,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.AnalyticsParam.Key.ACTION
import com.tangem.core.analytics.models.AnalyticsParam.Key.STATUS
+import com.tangem.core.analytics.models.AppsFlyerIncludedEvent
sealed class WalletSettingsAnalyticEvents(
category: String = "Settings / Wallet Settings",
@@ -166,6 +167,10 @@ sealed class WalletSettingsAnalyticEvents(
event = "Button - Start Upgrade",
)
+ class WalletUpgraded : WalletSettingsAnalyticEvents(
+ event = "Wallet Upgraded",
+ ), AppsFlyerIncludedEvent
+
enum class RecoveryPhraseScreenAction(val value: String) {
Upgrade("Upgrade"),
Backup("Backup"),
diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/SaveWalletUseCase.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/SaveWalletUseCase.kt
index 354d0b6bc8..75f322f635 100644
--- a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/SaveWalletUseCase.kt
+++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/SaveWalletUseCase.kt
@@ -6,9 +6,12 @@ import arrow.core.raise.either
import arrow.core.right
import com.tangem.common.doOnFailure
import com.tangem.common.doOnSuccess
+import com.tangem.core.analytics.api.AnalyticsEventHandler
+import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.common.wallets.error.SaveWalletError
import com.tangem.domain.models.wallet.UserWallet
+import com.tangem.domain.wallets.analytics.Settings
import com.tangem.domain.wallets.legacy.UserWalletsListError
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.repository.WalletsRepository
@@ -16,8 +19,6 @@ import com.tangem.domain.wallets.repository.WalletsRepository
/**
* Use case for saving user wallet
*
- * @property userWalletsListManager user wallets list manager
- *
[REDACTED_AUTHOR]
*/
class SaveWalletUseCase(
@@ -25,14 +26,21 @@ class SaveWalletUseCase(
private val userWalletsListRepository: UserWalletsListRepository,
private val walletsRepository: WalletsRepository,
private val useNewRepository: Boolean,
+ private val analyticsEventHandler: AnalyticsEventHandler,
) {
- suspend operator fun invoke(userWallet: UserWallet, canOverride: Boolean = false): Either {
+ suspend operator fun invoke(
+ userWallet: UserWallet,
+ canOverride: Boolean = false,
+ analyticsSource: AnalyticsParam.ScreensSources? = null,
+ ): Either {
return if (useNewRepository) {
either {
val newUserWallet =
userWalletsListRepository.userWalletsSync().none { it.walletId == userWallet.walletId }
- val userWallet = userWalletsListRepository.saveWithoutLock(userWallet, canOverride).bind()
+ val userWallet = userWalletsListRepository.saveWithoutLock(userWallet, canOverride)
+ .onRight { trackColdWalletAddedIfNeeded(analyticsSource, it) }
+ .bind()
if (newUserWallet) {
when (userWallet) {
@@ -76,4 +84,11 @@ class SaveWalletUseCase(
}
}
}
+
+ private suspend fun trackColdWalletAddedIfNeeded(source: AnalyticsParam.ScreensSources?, userWallet: UserWallet) {
+ val hasHotWallet = userWalletsListRepository.userWalletsSync().any { it is UserWallet.Hot }
+ if (hasHotWallet && userWallet is UserWallet.Cold) {
+ analyticsEventHandler.send(event = Settings.ColdWalletAdded(source))
+ }
+ }
}
\ No newline at end of file
diff --git a/features/account/impl/src/main/java/com/tangem/features/account/archived/entity/AccountArchivedUM.kt b/features/account/impl/src/main/java/com/tangem/features/account/archived/entity/AccountArchivedUM.kt
index 42c5faa452..a9166f1318 100644
--- a/features/account/impl/src/main/java/com/tangem/features/account/archived/entity/AccountArchivedUM.kt
+++ b/features/account/impl/src/main/java/com/tangem/features/account/archived/entity/AccountArchivedUM.kt
@@ -1,7 +1,7 @@
package com.tangem.features.account.archived.entity
import androidx.compose.runtime.Immutable
-import com.tangem.common.ui.account.CryptoPortfolioIconUM
+import com.tangem.common.ui.account.AccountIconUM
import com.tangem.core.ui.extensions.TextReference
import kotlinx.collections.immutable.ImmutableList
@@ -23,7 +23,7 @@ internal sealed interface AccountArchivedUM {
internal data class ArchivedAccountUM(
val accountId: String,
val accountName: TextReference,
- val accountIconUM: CryptoPortfolioIconUM,
+ val accountIconUM: AccountIconUM.CryptoPortfolio,
val tokensInfo: TextReference,
val networksInfo: TextReference,
val isLoading: Boolean,
diff --git a/features/account/impl/src/main/java/com/tangem/features/account/archived/entity/AccountArchivedUMBuilder.kt b/features/account/impl/src/main/java/com/tangem/features/account/archived/entity/AccountArchivedUMBuilder.kt
index 207e1f1a4a..737b5f6e86 100644
--- a/features/account/impl/src/main/java/com/tangem/features/account/archived/entity/AccountArchivedUMBuilder.kt
+++ b/features/account/impl/src/main/java/com/tangem/features/account/archived/entity/AccountArchivedUMBuilder.kt
@@ -1,5 +1,6 @@
package com.tangem.features.account.archived.entity
+import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.common.ui.account.toUM
import com.tangem.core.res.R
import com.tangem.core.ui.extensions.pluralReference
@@ -28,7 +29,7 @@ internal class AccountArchivedUMBuilder @Inject constructor() {
ArchivedAccountUM(
accountId = accountId.value,
accountName = name.toUM().value,
- accountIconUM = icon.toUM(),
+ accountIconUM = CryptoPortfolioIconConverter.convert(icon),
isLoading = false,
tokensInfo = pluralReference(
R.plurals.common_tokens_count,
diff --git a/features/account/impl/src/main/java/com/tangem/features/account/createedit/AccountCreateEditModel.kt b/features/account/impl/src/main/java/com/tangem/features/account/createedit/AccountCreateEditModel.kt
index e0783453da..ebb0f334f3 100644
--- a/features/account/impl/src/main/java/com/tangem/features/account/createedit/AccountCreateEditModel.kt
+++ b/features/account/impl/src/main/java/com/tangem/features/account/createedit/AccountCreateEditModel.kt
@@ -2,6 +2,7 @@ package com.tangem.features.account.createedit
import androidx.annotation.StringRes
import com.tangem.common.ui.account.AccountNameUM
+import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.common.ui.account.toDomain
import com.tangem.common.ui.account.toUM
import com.tangem.core.analytics.api.AnalyticsEventHandler
@@ -109,7 +110,7 @@ internal class AccountCreateEditModel @Inject constructor(
private suspend fun createNewCryptoPortfolio(params: AccountCreateEditComponent.Params.Create) {
val state = uiState.value
val name = state.account.name.toDomain().getOrNull() ?: return
- val icon = state.account.portfolioIcon.toDomain()
+ val icon = CryptoPortfolioIconConverter.convertBack(state.account.portfolioIcon)
val index = state.account.derivationInfo.index ?: return
val derivationIndex = DerivationIndex(value = index).getOrNull() ?: return
val event = AccountSettingsAnalyticEvents.ButtonAddNewAccount(
@@ -161,7 +162,7 @@ internal class AccountCreateEditModel @Inject constructor(
private suspend fun editCryptoPortfolio(params: AccountCreateEditComponent.Params.Edit) {
val state = uiState.value
val name = state.account.name.toDomain().getOrNull() ?: return
- val icon = state.account.portfolioIcon.toDomain()
+ val icon = CryptoPortfolioIconConverter.convertBack(state.account.portfolioIcon)
val isNewName = name != params.account.accountName
val isNewIcon = icon != params.account.portfolioIcon
analyticsEventHandler.send(AccountSettingsAnalyticEvents.ButtonSave(name, icon))
@@ -246,9 +247,10 @@ internal class AccountCreateEditModel @Inject constructor(
is AccountCreateEditComponent.Params.Create -> isValidName
is AccountCreateEditComponent.Params.Edit -> {
val oldName = params.account.accountName.toUM()
+ val oldIcon = CryptoPortfolioIconConverter.convert(params.account.portfolioIcon)
val isNewName = this.account.name.trim() != oldName
- val isNewIcon = this.account.portfolioIcon != params.account.portfolioIcon.toUM()
+ val isNewIcon = this.account.portfolioIcon != oldIcon
isValidName && (isNewName || isNewIcon)
}
}
diff --git a/features/account/impl/src/main/java/com/tangem/features/account/createedit/entity/AccountCreateEditUM.kt b/features/account/impl/src/main/java/com/tangem/features/account/createedit/entity/AccountCreateEditUM.kt
index da8220a1b5..01c83781ce 100644
--- a/features/account/impl/src/main/java/com/tangem/features/account/createedit/entity/AccountCreateEditUM.kt
+++ b/features/account/impl/src/main/java/com/tangem/features/account/createedit/entity/AccountCreateEditUM.kt
@@ -1,8 +1,8 @@
package com.tangem.features.account.createedit.entity
import androidx.compose.runtime.Immutable
+import com.tangem.common.ui.account.AccountIconUM
import com.tangem.common.ui.account.AccountNameUM
-import com.tangem.common.ui.account.CryptoPortfolioIconUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.models.account.CryptoPortfolioIcon
import kotlinx.collections.immutable.ImmutableList
@@ -18,7 +18,7 @@ internal data class AccountCreateEditUM(
data class Account(
val name: AccountNameUM,
- val portfolioIcon: CryptoPortfolioIconUM,
+ val portfolioIcon: AccountIconUM.CryptoPortfolio,
val derivationInfo: DerivationInfo,
val inputPlaceholder: TextReference,
val onNameChange: (AccountNameUM) -> Unit,
diff --git a/features/account/impl/src/main/java/com/tangem/features/account/createedit/entity/AccountCreateEditUMBuilder.kt b/features/account/impl/src/main/java/com/tangem/features/account/createedit/entity/AccountCreateEditUMBuilder.kt
index b9944a8996..b98da2e8b9 100644
--- a/features/account/impl/src/main/java/com/tangem/features/account/createedit/entity/AccountCreateEditUMBuilder.kt
+++ b/features/account/impl/src/main/java/com/tangem/features/account/createedit/entity/AccountCreateEditUMBuilder.kt
@@ -1,6 +1,7 @@
package com.tangem.features.account.createedit.entity
import com.tangem.common.ui.account.AccountNameUM
+import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.common.ui.account.toUM
import com.tangem.core.res.R
import com.tangem.core.ui.extensions.TextReference
@@ -17,7 +18,7 @@ internal class AccountCreateEditUMBuilder(
private val accountColors = CryptoPortfolioIcon.Color.entries.toImmutableList()
private val accountIcons = CryptoPortfolioIcon.Icon.entries.toImmutableList()
- private val createIcon = CryptoPortfolioIcon.ofDefaultCustomAccount().toUM()
+ private val createIcon = CryptoPortfolioIconConverter.convert(CryptoPortfolioIcon.ofDefaultCustomAccount())
val toolbarTitle: TextReference
get() = when (params) {
@@ -36,7 +37,7 @@ internal class AccountCreateEditUMBuilder(
)
is AccountCreateEditComponent.Params.Edit -> AccountCreateEditUM.Account(
name = params.account.accountName.toUM(),
- portfolioIcon = params.account.portfolioIcon.toUM(),
+ portfolioIcon = CryptoPortfolioIconConverter.convert(params.account.portfolioIcon),
derivationInfo = createAccountDerivationInfo(
index = (params.account as Account.CryptoPortfolio).derivationIndex.value,
),
diff --git a/features/account/impl/src/main/java/com/tangem/features/account/details/AccountDetailsModel.kt b/features/account/impl/src/main/java/com/tangem/features/account/details/AccountDetailsModel.kt
index fad8eeb69f..c4b4297c35 100644
--- a/features/account/impl/src/main/java/com/tangem/features/account/details/AccountDetailsModel.kt
+++ b/features/account/impl/src/main/java/com/tangem/features/account/details/AccountDetailsModel.kt
@@ -1,6 +1,7 @@
package com.tangem.features.account.details
import com.tangem.common.routing.AppRoute
+import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.common.ui.account.toUM
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
@@ -159,7 +160,7 @@ internal class AccountDetailsModel @Inject constructor(
?.isMultiCurrency == true
return AccountDetailsUM(
accountName = account.accountName.toUM().value,
- accountIcon = account.portfolioIcon.toUM(),
+ accountIcon = CryptoPortfolioIconConverter.convert(account.portfolioIcon),
onCloseClick = { router.pop() },
onAccountEditClick = { onEditAccountClick(account) },
onManageTokensClick = { onManageTokensClick(account) },
diff --git a/features/account/impl/src/main/java/com/tangem/features/account/details/entity/AccountDetailsUM.kt b/features/account/impl/src/main/java/com/tangem/features/account/details/entity/AccountDetailsUM.kt
index 15fe34025b..67e33ebcf6 100644
--- a/features/account/impl/src/main/java/com/tangem/features/account/details/entity/AccountDetailsUM.kt
+++ b/features/account/impl/src/main/java/com/tangem/features/account/details/entity/AccountDetailsUM.kt
@@ -1,12 +1,12 @@
package com.tangem.features.account.details.entity
import androidx.compose.runtime.Immutable
-import com.tangem.common.ui.account.CryptoPortfolioIconUM
+import com.tangem.common.ui.account.AccountIconUM
import com.tangem.core.ui.extensions.TextReference
internal data class AccountDetailsUM(
val accountName: TextReference,
- val accountIcon: CryptoPortfolioIconUM,
+ val accountIcon: AccountIconUM.CryptoPortfolio,
val archiveMode: ArchiveMode,
val isManageTokensAvailable: Boolean,
val onCloseClick: () -> Unit,
diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/UserWalletSaver.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/UserWalletSaver.kt
index 04d7e91736..59be5a0529 100644
--- a/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/UserWalletSaver.kt
+++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/UserWalletSaver.kt
@@ -75,7 +75,12 @@ internal class UserWalletSaver @Inject constructor(
private suspend fun Raise.saveWallet(userWallet: UserWallet) {
fold(
- block = { saveWalletUseCase(userWallet).bind() },
+ block = {
+ saveWalletUseCase(
+ userWallet = userWallet,
+ analyticsSource = AnalyticsParam.ScreensSources.Settings,
+ ).bind()
+ },
recover = { error ->
when (error) {
is SaveWalletError.WalletAlreadySaved -> {
diff --git a/features/feed/api/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/AddToPortfolioPreselectedDataComponent.kt b/features/feed/api/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/AddToPortfolioPreselectedDataComponent.kt
new file mode 100644
index 0000000000..713ce93cf1
--- /dev/null
+++ b/features/feed/api/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/AddToPortfolioPreselectedDataComponent.kt
@@ -0,0 +1,33 @@
+package com.tangem.features.feed.components.market.details.portfolio.add
+
+import com.tangem.core.decompose.factory.ComponentFactory
+import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
+import com.tangem.domain.markets.TokenMarketInfo
+import com.tangem.domain.models.currency.CryptoCurrency
+import com.tangem.domain.models.wallet.UserWalletId
+
+interface AddToPortfolioPreselectedDataComponent : ComposableBottomSheetComponent {
+
+ /**
+ * @param tokenToAdd token and preselected network (no network selector).
+ * @param callback callbacks for add-to-portfolio flow.
+ */
+ data class Params(
+ val tokenToAdd: TokenToAdd,
+ val callback: Callback,
+ )
+
+ interface Callback {
+ fun onDismiss()
+ fun onSuccess(addedToken: CryptoCurrency, walletId: UserWalletId)
+ }
+
+ data class TokenToAdd(
+ val network: TokenMarketInfo.Network,
+ val id: CryptoCurrency.RawID,
+ val name: String,
+ val symbol: String,
+ )
+
+ interface Factory : ComponentFactory
+}
\ No newline at end of file
diff --git a/features/feed/api/src/main/kotlin/com/tangem/features/feed/entry/featuretoggle/FeedFeatureToggle.kt b/features/feed/api/src/main/kotlin/com/tangem/features/feed/entry/featuretoggle/FeedFeatureToggle.kt
index c04c556450..72d5656bad 100644
--- a/features/feed/api/src/main/kotlin/com/tangem/features/feed/entry/featuretoggle/FeedFeatureToggle.kt
+++ b/features/feed/api/src/main/kotlin/com/tangem/features/feed/entry/featuretoggle/FeedFeatureToggle.kt
@@ -2,4 +2,5 @@ package com.tangem.features.feed.entry.featuretoggle
interface FeedFeatureToggle {
val isFeedEnabled: Boolean
+ val isEarnBlockEnabled: Boolean
}
\ No newline at end of file
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/AddToPortfolioBottomSheet.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/AddToPortfolioBottomSheet.kt
new file mode 100644
index 0000000000..8c6aba3001
--- /dev/null
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/AddToPortfolioBottomSheet.kt
@@ -0,0 +1,118 @@
+package com.tangem.features.feed.components.market.details.portfolio.add.impl
+
+import androidx.compose.animation.AnimatedContent
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.runtime.*
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.arkivanov.decompose.router.stack.ChildStack
+import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
+import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
+import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
+import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
+import com.tangem.core.ui.decompose.ComposableContentComponent
+import com.tangem.core.ui.extensions.TextReference
+import com.tangem.core.ui.extensions.resourceReference
+import com.tangem.core.ui.res.TangemTheme
+import com.tangem.features.account.PortfolioSelectorComponent
+import com.tangem.features.feed.components.market.details.portfolio.add.impl.model.AddToPortfolioRoutes
+import com.tangem.features.feed.impl.R
+
+@Composable
+internal fun AddToPortfolioBottomSheet(
+ childStack: State>,
+ onBack: () -> Unit,
+ onDismiss: () -> Unit,
+) {
+ val stack by childStack
+ val contentStack = remember { mutableStateOf(stack) }
+ val currentRoute = stack.active.configuration
+ val isNotEmpty = currentRoute != AddToPortfolioRoutes.Empty
+ if (isNotEmpty) {
+ contentStack.value = stack
+ }
+
+ TangemModalBottomSheet(
+ scrollableContent = false,
+ onBack = onBack,
+ config = TangemBottomSheetConfig(
+ isShown = isNotEmpty,
+ onDismissRequest = onDismiss,
+ content = TangemBottomSheetConfigContent.Empty,
+ ),
+ containerColor = TangemTheme.colors.background.tertiary,
+ title = {
+ AnimatedContent(targetState = contentStack.value, label = "Title Animation") { animatedStack ->
+ AddToPortfolioBottomSheetTitle(
+ stack = animatedStack,
+ onBackClick = onBack,
+ modifier = Modifier.fillMaxWidth(),
+ )
+ }
+ },
+ content = {
+ AnimatedContent(targetState = contentStack.value, label = "Content Animation") { animatedStack ->
+ val paddingModifier = Modifier.padding(
+ start = 16.dp,
+ end = 16.dp,
+ bottom = 16.dp,
+ )
+ val isScrollableContent = when (animatedStack.active.configuration) {
+ AddToPortfolioRoutes.PortfolioSelector -> false
+ AddToPortfolioRoutes.AddToken,
+ AddToPortfolioRoutes.Empty,
+ is AddToPortfolioRoutes.NetworkSelector,
+ AddToPortfolioRoutes.TokenActions,
+ -> true
+ }
+ if (isScrollableContent) {
+ Column(
+ modifier = paddingModifier.verticalScroll(rememberScrollState()),
+ ) {
+ animatedStack.active.instance.Content(modifier = Modifier)
+ }
+ } else {
+ animatedStack.active.instance.Content(modifier = paddingModifier)
+ }
+ }
+ },
+ )
+}
+
+@Composable
+private fun AddToPortfolioBottomSheetTitle(
+ stack: ChildStack,
+ onBackClick: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ val title: TextReference = when (stack.active.configuration) {
+ AddToPortfolioRoutes.AddToken -> resourceReference(R.string.common_add_token)
+ AddToPortfolioRoutes.Empty -> TextReference.EMPTY
+ is AddToPortfolioRoutes.NetworkSelector -> resourceReference(R.string.common_choose_network)
+ AddToPortfolioRoutes.TokenActions -> resourceReference(R.string.common_get_token)
+ AddToPortfolioRoutes.PortfolioSelector -> (stack.active.instance as PortfolioSelectorComponent)
+ .title.collectAsStateWithLifecycle().value
+ }
+ val startIconRes: Int?
+ val endIconRes: Int?
+ if (stack.backStack.isNotEmpty()) {
+ startIconRes = R.drawable.ic_back_24
+ endIconRes = null
+ } else {
+ startIconRes = null
+ endIconRes = R.drawable.ic_close_24
+ }
+ TangemModalBottomSheetTitle(
+ modifier = modifier,
+ title = title,
+ startIconRes = startIconRes,
+ endIconRes = endIconRes,
+ onStartClick = onBackClick,
+ onEndClick = onBackClick,
+ )
+}
\ No newline at end of file
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/DefaultAddToPortfolioComponent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/DefaultAddToPortfolioComponent.kt
index edd7f01ef5..8f7dfb79a1 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/DefaultAddToPortfolioComponent.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/DefaultAddToPortfolioComponent.kt
@@ -1,21 +1,8 @@
package com.tangem.features.feed.components.market.details.portfolio.add.impl
-import androidx.compose.animation.AnimatedContent
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.decompose.extensions.compose.subscribeAsState
-import com.arkivanov.decompose.router.stack.ChildStack
import com.arkivanov.decompose.router.stack.backStack
import com.arkivanov.decompose.router.stack.childStack
import com.arkivanov.decompose.router.stack.pop
@@ -23,19 +10,11 @@ import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.child
import com.tangem.core.decompose.context.childByContext
import com.tangem.core.decompose.model.getOrCreateModel
-import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
-import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
-import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
-import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
import com.tangem.core.ui.decompose.ComposableContentComponent
-import com.tangem.core.ui.extensions.TextReference
-import com.tangem.core.ui.extensions.resourceReference
-import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.account.PortfolioSelectorComponent
import com.tangem.features.feed.components.market.details.portfolio.add.AddToPortfolioComponent
import com.tangem.features.feed.components.market.details.portfolio.add.impl.model.AddToPortfolioModel
import com.tangem.features.feed.components.market.details.portfolio.add.impl.model.AddToPortfolioRoutes
-import com.tangem.features.feed.impl.R
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -97,91 +76,10 @@ internal class DefaultAddToPortfolioComponent @AssistedInject constructor(
@Composable
override fun BottomSheet() {
- val stack by childStack.subscribeAsState()
- val contentStack = remember { mutableStateOf(stack) }
- val currentRoute = stack.active.configuration
- val isNotEmpty = currentRoute != AddToPortfolioRoutes.Empty
- if (isNotEmpty) {
- contentStack.value = stack
- }
-
- TangemModalBottomSheet(
- scrollableContent = false,
+ AddToPortfolioBottomSheet(
+ childStack = childStack.subscribeAsState(),
onBack = ::onBack,
- config = TangemBottomSheetConfig(
- isShown = isNotEmpty,
- onDismissRequest = ::dismiss,
- content = TangemBottomSheetConfigContent.Empty,
- ),
- containerColor = TangemTheme.colors.background.tertiary,
- title = { state ->
- AnimatedContent(targetState = contentStack.value) { stack ->
- BottomSheetTitle(
- stack = stack,
- onBackClick = ::onBack,
- modifier = Modifier.fillMaxWidth(),
- )
- }
- },
- content = { state ->
- AnimatedContent(targetState = contentStack.value) { stack ->
- val paddingModifier = Modifier.padding(
- start = 16.dp,
- end = 16.dp,
- bottom = 16.dp,
- )
- val isScrollableContent = when (stack.active.configuration) {
- AddToPortfolioRoutes.PortfolioSelector -> false
- AddToPortfolioRoutes.AddToken,
- AddToPortfolioRoutes.Empty,
- is AddToPortfolioRoutes.NetworkSelector,
- AddToPortfolioRoutes.TokenActions,
- -> true
- }
- if (isScrollableContent) {
- Column(
- modifier = paddingModifier.verticalScroll(rememberScrollState()),
- ) {
- stack.active.instance.Content(modifier = Modifier)
- }
- } else {
- stack.active.instance.Content(modifier = paddingModifier)
- }
- }
- },
- )
- }
-
- @Composable
- private fun BottomSheetTitle(
- stack: ChildStack,
- onBackClick: (() -> Unit),
- modifier: Modifier = Modifier,
- ) {
- val title: TextReference = when (stack.active.configuration) {
- AddToPortfolioRoutes.AddToken -> resourceReference(R.string.common_add_token)
- AddToPortfolioRoutes.Empty -> TextReference.EMPTY
- is AddToPortfolioRoutes.NetworkSelector -> resourceReference(R.string.common_choose_network)
- AddToPortfolioRoutes.TokenActions -> resourceReference(R.string.common_get_token)
- AddToPortfolioRoutes.PortfolioSelector -> (stack.active.instance as PortfolioSelectorComponent)
- .title.collectAsStateWithLifecycle().value
- }
- val startIconRes: Int?
- val endIconRes: Int?
- if (stack.backStack.isNotEmpty()) {
- startIconRes = R.drawable.ic_back_24
- endIconRes = null
- } else {
- startIconRes = null
- endIconRes = R.drawable.ic_close_24
- }
- TangemModalBottomSheetTitle(
- modifier = modifier,
- title = title,
- startIconRes = startIconRes,
- endIconRes = endIconRes,
- onStartClick = onBackClick,
- onEndClick = onBackClick,
+ onDismiss = ::dismiss,
)
}
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/DefaultAddToPortfolioPreselectedDataComponent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/DefaultAddToPortfolioPreselectedDataComponent.kt
new file mode 100644
index 0000000000..95efde9298
--- /dev/null
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/DefaultAddToPortfolioPreselectedDataComponent.kt
@@ -0,0 +1,90 @@
+package com.tangem.features.feed.components.market.details.portfolio.add.impl
+
+import androidx.compose.runtime.Composable
+import com.arkivanov.decompose.extensions.compose.subscribeAsState
+import com.arkivanov.decompose.router.stack.backStack
+import com.arkivanov.decompose.router.stack.childStack
+import com.arkivanov.decompose.router.stack.pop
+import com.tangem.core.decompose.context.AppComponentContext
+import com.tangem.core.decompose.context.child
+import com.tangem.core.decompose.model.getOrCreateModel
+import com.tangem.core.ui.decompose.ComposableContentComponent
+import com.tangem.features.account.PortfolioSelectorComponent
+import com.tangem.features.feed.components.market.details.portfolio.add.AddToPortfolioPreselectedDataComponent
+import com.tangem.features.feed.components.market.details.portfolio.add.impl.model.AddToPortfolioPreselectedDataModel
+import com.tangem.features.feed.components.market.details.portfolio.add.impl.model.AddToPortfolioRoutes
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+internal class DefaultAddToPortfolioPreselectedDataComponent @AssistedInject constructor(
+ @Assisted context: AppComponentContext,
+ @Assisted private val params: AddToPortfolioPreselectedDataComponent.Params,
+ portfolioSelectorComponentFactory: PortfolioSelectorComponent.Factory,
+ addTokenComponentFactory: AddTokenComponent.Factory,
+) : AppComponentContext by context, AddToPortfolioPreselectedDataComponent {
+
+ private val model: AddToPortfolioPreselectedDataModel = getOrCreateModel(params)
+
+ private val portfolioSelectorComponent: PortfolioSelectorComponent = portfolioSelectorComponentFactory.create(
+ context = child("portfolioSelectorComponent"),
+ params = PortfolioSelectorComponent.Params(
+ portfolioFetcher = model.portfolioFetcher,
+ controller = model.portfolioSelectorController,
+ ),
+ )
+
+ private val addTokenComponent: AddTokenComponent = addTokenComponentFactory.create(
+ context = child("addTokenComponent"),
+ params = AddTokenComponent.Params(
+ eventBuilder = model.eventBuilder,
+ callbacks = model,
+ selectedPortfolio = model.selectedPortfolio,
+ selectedNetwork = model.selectedNetwork,
+ ),
+ )
+
+ private val childStack = childStack(
+ key = "addToPortfolioFromEarnStack",
+ handleBackButton = true,
+ source = model.navigation,
+ serializer = AddToPortfolioRoutes.serializer(),
+ initialStack = { model.currentStack },
+ childFactory = { configuration, _ ->
+ contentChild(configuration)
+ },
+ )
+
+ private fun onBack() {
+ if (childStack.backStack.isNotEmpty()) model.navigation.pop() else dismiss()
+ }
+
+ override fun dismiss() {
+ params.callback.onDismiss()
+ }
+
+ @Composable
+ override fun BottomSheet() {
+ AddToPortfolioBottomSheet(
+ childStack = childStack.subscribeAsState(),
+ onBack = ::onBack,
+ onDismiss = ::dismiss,
+ )
+ }
+
+ private fun contentChild(config: AddToPortfolioRoutes): ComposableContentComponent = when (config) {
+ AddToPortfolioRoutes.AddToken -> addTokenComponent
+ AddToPortfolioRoutes.PortfolioSelector -> portfolioSelectorComponent
+ AddToPortfolioRoutes.TokenActions -> ComposableContentComponent.EMPTY
+ AddToPortfolioRoutes.Empty -> ComposableContentComponent.EMPTY
+ is AddToPortfolioRoutes.NetworkSelector -> ComposableContentComponent.EMPTY
+ }
+
+ @AssistedFactory
+ interface Factory : AddToPortfolioPreselectedDataComponent.Factory {
+ override fun create(
+ context: AppComponentContext,
+ params: AddToPortfolioPreselectedDataComponent.Params,
+ ): DefaultAddToPortfolioPreselectedDataComponent
+ }
+}
\ No newline at end of file
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/di/AddToPortfolioModelModule.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/di/AddToPortfolioModelModule.kt
index b60d84f099..60b3f51d91 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/di/AddToPortfolioModelModule.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/di/AddToPortfolioModelModule.kt
@@ -2,10 +2,7 @@ package com.tangem.features.feed.components.market.details.portfolio.add.impl.di
import com.tangem.core.decompose.di.ModelComponent
import com.tangem.core.decompose.model.Model
-import com.tangem.features.feed.components.market.details.portfolio.add.impl.model.AddToPortfolioModel
-import com.tangem.features.feed.components.market.details.portfolio.add.impl.model.AddTokenModel
-import com.tangem.features.feed.components.market.details.portfolio.add.impl.model.ChooseNetworkModel
-import com.tangem.features.feed.components.market.details.portfolio.add.impl.model.TokenActionsModel
+import com.tangem.features.feed.components.market.details.portfolio.add.impl.model.*
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
@@ -26,6 +23,11 @@ internal interface AddToPortfolioModelModule {
@ClassKey(AddToPortfolioModel::class)
fun addToPortfolioModel(model: AddToPortfolioModel): Model
+ @Binds
+ @IntoMap
+ @ClassKey(AddToPortfolioPreselectedDataModel::class)
+ fun addToPortfolioPreselectedDataModel(model: AddToPortfolioPreselectedDataModel): Model
+
@Binds
@IntoMap
@ClassKey(TokenActionsModel::class)
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/model/AddToPortfolioModel.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/model/AddToPortfolioModel.kt
index 299f1d72ed..a06d46878d 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/model/AddToPortfolioModel.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/model/AddToPortfolioModel.kt
@@ -67,7 +67,7 @@ internal class AddToPortfolioModel @Inject constructor(
private val addToPortfolioManager = params.addToPortfolioManager
val portfolioFetcher = addToPortfolioManager.portfolioFetcher
val eventBuilder = PortfolioAnalyticsEvent.EventBuilder(
- token = addToPortfolioManager.token,
+ tokenSymbol = addToPortfolioManager.token.symbol,
source = addToPortfolioManager.analyticsParams?.source,
)
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/model/AddToPortfolioPreselectedDataModel.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/model/AddToPortfolioPreselectedDataModel.kt
new file mode 100644
index 0000000000..b673414488
--- /dev/null
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/model/AddToPortfolioPreselectedDataModel.kt
@@ -0,0 +1,280 @@
+package com.tangem.features.feed.components.market.details.portfolio.add.impl.model
+
+import com.arkivanov.decompose.router.stack.StackNavigation
+import com.arkivanov.decompose.router.stack.replaceAll
+import com.tangem.core.analytics.api.AnalyticsEventHandler
+import com.tangem.core.analytics.models.AnalyticsParam
+import com.tangem.core.decompose.di.ModelScoped
+import com.tangem.core.decompose.model.Model
+import com.tangem.core.decompose.model.ParamsContainer
+import com.tangem.core.decompose.ui.UiMessageSender
+import com.tangem.core.ui.extensions.resourceReference
+import com.tangem.core.ui.message.ToastMessage
+import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase
+import com.tangem.domain.markets.GetTokenMarketCryptoCurrency
+import com.tangem.domain.markets.TokenMarketInfo
+import com.tangem.domain.markets.TokenMarketParams
+import com.tangem.domain.models.account.AccountStatus
+import com.tangem.domain.models.currency.CryptoCurrency
+import com.tangem.domain.models.currency.CryptoCurrencyStatus
+import com.tangem.domain.models.wallet.UserWallet
+import com.tangem.domain.models.wallet.UserWalletId
+import com.tangem.features.account.PortfolioFetcher
+import com.tangem.features.account.PortfolioSelectorController
+import com.tangem.features.feed.components.market.details.portfolio.add.*
+import com.tangem.features.feed.components.market.details.portfolio.add.impl.AddTokenComponent
+import com.tangem.features.feed.components.market.details.portfolio.impl.analytics.PortfolioAnalyticsEvent
+import com.tangem.features.feed.impl.R
+import com.tangem.utils.coroutines.CoroutineDispatcherProvider
+import kotlinx.coroutines.channels.Channel
+import kotlinx.coroutines.flow.*
+import timber.log.Timber
+import java.math.BigDecimal
+import javax.inject.Inject
+
+@Suppress("LongParameterList")
+internal class AddToPortfolioPreselectedDataModel @Inject constructor(
+ paramsContainer: ParamsContainer,
+ portfolioFetcherFactory: PortfolioFetcher.Factory,
+ override val dispatchers: CoroutineDispatcherProvider,
+ val portfolioSelectorController: PortfolioSelectorController,
+ private val callbackDelegate: AddToPortfolioFromEarnCallbackDelegate,
+ private val messageSender: UiMessageSender,
+ private val analyticsEventHandler: AnalyticsEventHandler,
+ private val getTokenMarketCryptoCurrency: GetTokenMarketCryptoCurrency,
+ private val getAccountCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase,
+) : Model(), AddTokenComponent.Callbacks by callbackDelegate {
+
+ private val params = paramsContainer.require()
+
+ val portfolioFetcher: PortfolioFetcher = portfolioFetcherFactory.create(
+ mode = PortfolioFetcher.Mode.All(isOnlyMultiCurrency = true),
+ scope = modelScope,
+ )
+
+ val navigation = StackNavigation()
+ var currentStack = listOf(AddToPortfolioRoutes.Empty)
+
+ private val _selectedNetwork = MutableStateFlow(null)
+ val selectedNetwork: Flow = _selectedNetwork.asStateFlow().filterNotNull()
+
+ private val _selectedPortfolio = MutableStateFlow(null)
+ val selectedPortfolio: Flow = _selectedPortfolio.asStateFlow().filterNotNull()
+ val eventBuilder = PortfolioAnalyticsEvent.EventBuilder(
+ tokenSymbol = params.tokenToAdd.symbol,
+ source = AnalyticsParam.ScreensSources.Markets.value,
+ )
+
+ init {
+ navigation.subscribe { currentStack = it.transformer.invoke(currentStack) }
+ startAddToPortfolioFlow()
+ }
+
+ @Suppress("LongMethod")
+ private fun startAddToPortfolioFlow() {
+ channelFlow {
+ fun finishSuccessFlow(currency: CryptoCurrency, userWalletId: UserWalletId) {
+ params.callback.onSuccess(addedToken = currency, walletId = userWalletId)
+ channel.close()
+ }
+
+ val data = createAvailableToAddDataForPreselectedNetwork(params.tokenToAdd.network)
+ ?: return@channelFlow
+
+ val isAccountMode = portfolioSelectorController.isAccountModeSync()
+ val firstSelectedPortfolio = setupPortfolioFlow(data)
+ .onEach { _selectedPortfolio.value = it }
+
+ val firstSelectedNetwork = firstSelectedPortfolio
+ .map { portfolio -> createSelectedNetwork(network = params.tokenToAdd.network, portfolio = portfolio) }
+ .filterNotNull()
+ .onEach { _selectedNetwork.value = it }
+
+ val isSinglePortfolio = data.isSinglePortfolio
+ if (isSinglePortfolio) {
+ val accountId = data.availableToAddWallets.values.first()
+ .availableToAddAccounts.values.first()
+ .account.account.accountId
+ // force select a portfolio, triggers [selectedPortfolio]
+ portfolioSelectorController.selectAccount(accountId)
+ } else {
+ logAccountSelector(isAccountMode)
+ navigation.replaceAll(AddToPortfolioRoutes.PortfolioSelector)
+ }
+
+ // main flow that combine all require data
+ val allRequireForAdd = combine(
+ flow = firstSelectedNetwork,
+ flow2 = firstSelectedPortfolio,
+ transform = { a, b -> a to b },
+ )
+
+ // suspend until all required data is selected
+ val (selectedNetworkValue, selectedPortfolioValue) = allRequireForAdd.first()
+
+ val isTokenAlreadyAdded = selectedPortfolioValue.account.addedMarketNetworks
+ .any { it.networkId == selectedNetworkValue.selectedNetwork.networkId }
+
+ if (isTokenAlreadyAdded) {
+ finishSuccessFlow(
+ currency = selectedNetworkValue.cryptoCurrency,
+ userWalletId = selectedPortfolioValue.userWallet.walletId,
+ )
+ return@channelFlow
+ }
+
+ navigation.replaceAll(AddToPortfolioRoutes.AddToken)
+ val addedToken = callbackDelegate.onTokenAdded.receiveAsFlow().first()
+ messageSender.send(ToastMessage(message = resourceReference(R.string.markets_token_added)))
+ finishSuccessFlow(addedToken.currency, selectedPortfolioValue.userWallet.walletId)
+ }
+ .catch { throwable ->
+ Timber.e(throwable)
+ params.callback.onDismiss()
+ }
+ .launchIn(modelScope)
+ }
+
+ private fun logAccountSelector(isAccountMode: Boolean) {
+ if (isAccountMode) {
+ analyticsEventHandler.send(eventBuilder.popupToChooseAccount())
+ }
+ }
+
+ private fun minimalTokenMarketParams() = with(params.tokenToAdd) {
+ TokenMarketParams(
+ id = id,
+ name = name,
+ symbol = symbol,
+ tokenQuotes = TokenMarketParams.Quotes(
+ currentPrice = BigDecimal.ZERO,
+ h24Percent = null,
+ weekPercent = null,
+ monthPercent = null,
+ ),
+ imageUrl = null,
+ )
+ }
+
+ /**
+ * Creates [AvailableToAddData] for preselected network when token is already added in all networks.
+ * This allows user to select wallet/account and do smth after it with selected info.
+ */
+ private suspend fun createAvailableToAddDataForPreselectedNetwork(
+ preSelectedNetwork: TokenMarketInfo.Network,
+ ): AvailableToAddData? {
+ val portfolioData = portfolioFetcher.data.firstOrNull() ?: return null
+
+ val availableToOpenWallets = portfolioData.balances.mapNotNull { (walletId, balance) ->
+ val wallet = balance.userWallet
+ val accounts = balance.accountsBalance.accountStatuses
+
+ val availableToAddAccounts = accounts.mapNotNull { accountStatus ->
+ val accountIndex = when (accountStatus) {
+ is AccountStatus.CryptoPortfolio -> accountStatus.account.derivationIndex
+ }
+
+ val cryptoCurrency = getTokenMarketCryptoCurrency(
+ userWalletId = walletId,
+ tokenMarketParams = minimalTokenMarketParams(),
+ network = preSelectedNetwork,
+ accountIndex = accountIndex,
+ ) ?: return@mapNotNull null
+
+ val addedNetworks = getAccountCurrencyStatusUseCase.invokeSync(
+ userWalletId = walletId,
+ currency = cryptoCurrency,
+ ).fold(
+ ifEmpty = { emptySet() },
+ ifSome = { setOf(it.status.currency.network) },
+ )
+
+ AvailableToAddAccount(
+ account = accountStatus,
+ availableNetworks = setOf(preSelectedNetwork),
+ addedNetworks = addedNetworks,
+ )
+ }.associateBy { it.account.account.accountId }
+
+ if (availableToAddAccounts.isEmpty()) return@mapNotNull null
+
+ walletId to AvailableToAddWallet(
+ userWallet = wallet,
+ accounts = accounts,
+ availableNetworks = setOf(preSelectedNetwork),
+ availableToAddAccounts = availableToAddAccounts,
+ )
+ }.toMap()
+
+ if (availableToOpenWallets.isEmpty()) return null
+
+ return AvailableToAddData(availableToAddWallets = availableToOpenWallets)
+ }
+
+ private suspend fun createCryptoCurrency(
+ userWallet: UserWallet,
+ network: TokenMarketInfo.Network,
+ account: AvailableToAddAccount,
+ ): CryptoCurrency? {
+ val accountIndex = when (val accountStatus = account.account) {
+ is AccountStatus.CryptoPortfolio -> accountStatus.account.derivationIndex
+ }
+ return getTokenMarketCryptoCurrency(
+ userWalletId = userWallet.walletId,
+ tokenMarketParams = minimalTokenMarketParams(),
+ network = network,
+ accountIndex = accountIndex,
+ )
+ }
+
+ private suspend fun createSelectedNetwork(
+ network: TokenMarketInfo.Network,
+ portfolio: SelectedPortfolio,
+ ): SelectedNetwork? {
+ val cryptoCurrency = createCryptoCurrency(
+ userWallet = portfolio.userWallet,
+ network = network,
+ account = portfolio.account,
+ ) ?: return null
+
+ return SelectedNetwork(
+ cryptoCurrency = cryptoCurrency,
+ selectedNetwork = network,
+ isAvailableMoreNetwork = false,
+ )
+ }
+
+ private fun setupPortfolioFlow(data: AvailableToAddData): Flow = combine(
+ flow = portfolioSelectorController.isAccountMode,
+ flow2 = portfolioSelectorController.selectedAccount,
+ transform = { isAccountMode, selectedAccountId ->
+ selectedAccountId ?: return@combine null
+ val availableToAddWallets =
+ data.availableToAddWallets[selectedAccountId.userWalletId] ?: return@combine null
+ val availableToAddAccount =
+ availableToAddWallets.availableToAddAccounts[selectedAccountId] ?: return@combine null
+ if (!isAccountMode) analyticsEventHandler.send(eventBuilder.addToPortfolioWalletChanged())
+ SelectedPortfolio(
+ isAccountMode = isAccountMode,
+ userWallet = availableToAddWallets.userWallet,
+ account = availableToAddAccount,
+ isAvailableMorePortfolio = false,
+ )
+ },
+ )
+ .filterNotNull()
+}
+
+@ModelScoped
+internal class AddToPortfolioFromEarnCallbackDelegate @Inject constructor() :
+ AddTokenComponent.Callbacks {
+ val onTokenAdded = Channel()
+
+ override fun onChangeNetworkClick() = Unit
+
+ override fun onChangePortfolioClick() = Unit
+
+ override fun onTokenAdded(status: CryptoCurrencyStatus) {
+ onTokenAdded.trySend(status)
+ }
+}
\ No newline at end of file
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/model/AddTokenUiBuilder.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/model/AddTokenUiBuilder.kt
index b6ad346e0c..5d5c7e379e 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/model/AddTokenUiBuilder.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/model/AddTokenUiBuilder.kt
@@ -1,6 +1,7 @@
package com.tangem.features.feed.components.market.details.portfolio.add.impl.model
-import com.tangem.common.ui.account.CryptoPortfolioIconUM
+import com.tangem.common.ui.account.AccountIconUM
+import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.common.ui.account.PortfolioSelectUM
import com.tangem.common.ui.account.toUM
import com.tangem.common.ui.addtoken.AddTokenUM
@@ -35,7 +36,7 @@ internal class AddTokenUiBuilder @Inject constructor(
}
private fun createPortfolio(selectedPortfolio: SelectedPortfolio): PortfolioSelectUM {
- val accountIcon: CryptoPortfolioIconUM?
+ val accountIcon: AccountIconUM.CryptoPortfolio?
val portfolioName: TextReference
when (selectedPortfolio.isAccountMode) {
false -> {
@@ -46,7 +47,7 @@ internal class AddTokenUiBuilder @Inject constructor(
val accountStatus = selectedPortfolio.account.account
portfolioName = accountStatus.account.accountName.toUM().value
accountIcon = when (accountStatus) {
- is AccountStatus.CryptoPortfolio -> accountStatus.account.icon.toUM()
+ is AccountStatus.CryptoPortfolio -> CryptoPortfolioIconConverter.convert(accountStatus.account.icon)
}
}
}
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/model/TokenActionsModel.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/model/TokenActionsModel.kt
index 0bb9a30fd9..65e1cf28d1 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/model/TokenActionsModel.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/add/impl/model/TokenActionsModel.kt
@@ -47,7 +47,6 @@ internal class TokenActionsModel @Inject constructor(
private val tokenActionsHandler: TokenActionsHandler =
tokenActionsIntentsFactory.create(
currentAppCurrency = Provider { currentAppCurrency.value },
- updateTokenReceiveBSConfig = { },
onHandleQuickAction = { handledAction -> handledQuickAction(handledAction) },
)
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/analytics/PortfolioAnalyticsEvent.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/analytics/PortfolioAnalyticsEvent.kt
index 87ea980421..b75d67abf4 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/analytics/PortfolioAnalyticsEvent.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/analytics/PortfolioAnalyticsEvent.kt
@@ -1,7 +1,6 @@
package com.tangem.features.feed.components.market.details.portfolio.impl.analytics
import com.tangem.core.analytics.models.AnalyticsEvent
-import com.tangem.domain.markets.TokenMarketParams
import com.tangem.features.feed.components.market.details.portfolio.impl.ui.state.TokenActionsBSContentUM
internal class PortfolioAnalyticsEvent(
@@ -10,14 +9,14 @@ internal class PortfolioAnalyticsEvent(
) : AnalyticsEvent(category = "Markets / Chart", event = event, params = params) {
data class EventBuilder(
- val token: TokenMarketParams,
+ val tokenSymbol: String,
val source: String?,
) {
fun addToPortfolioClicked() = PortfolioAnalyticsEvent(
event = "Button - Add To Portfolio",
params = mapOf(
- "Token" to token.symbol,
+ "Token" to tokenSymbol,
),
)
@@ -35,7 +34,7 @@ internal class PortfolioAnalyticsEvent(
event = "Token Network Selected",
params = mapOf(
"Count" to blockchainNames.size.toString(),
- "Token" to token.symbol,
+ "Token" to tokenSymbol,
"blockchain" to blockchainNames.joinToString(separator = ", "),
),
)
@@ -51,7 +50,7 @@ internal class PortfolioAnalyticsEvent(
else -> "error"
},
params = buildMap {
- put("Token", token.symbol)
+ put("Token", tokenSymbol)
source?.let { put("Source", it) }
put("blockchain", blockchainName)
},
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/MarketsPortfolioModel.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/MarketsPortfolioModel.kt
index 1715e60e47..71d48270b1 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/MarketsPortfolioModel.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/MarketsPortfolioModel.kt
@@ -39,7 +39,6 @@ import com.tangem.features.feed.components.market.details.portfolio.impl.loader.
import com.tangem.features.feed.components.market.details.portfolio.impl.ui.state.MyPortfolioUM
import com.tangem.features.feed.components.market.details.portfolio.impl.ui.state.TokenActionsBSContentUM
import com.tangem.features.feed.impl.R
-import com.tangem.features.tokenreceive.TokenReceiveFeatureToggle
import com.tangem.features.wallet.utils.UserWalletImageFetcher
import com.tangem.lib.crypto.BlockchainUtils
import com.tangem.operations.attestation.ArtworkSize
@@ -68,7 +67,6 @@ internal class MarketsPortfolioModel @Inject constructor(
private val saveMarketTokensUseCase: SaveMarketTokensUseCase,
private val addToPortfolioManager: AddToPortfolioManager,
private val analyticsEventHandler: AnalyticsEventHandler,
- private val tokenReceiveFeatureToggle: TokenReceiveFeatureToggle,
private val userWalletImageFetcher: UserWalletImageFetcher,
private val receiveAddressesFactory: ReceiveAddressesFactory,
accountsFeatureToggles: AccountsFeatureToggles,
@@ -81,7 +79,7 @@ internal class MarketsPortfolioModel @Inject constructor(
private val params = paramsContainer.require()
private val analyticsEventBuilder = PortfolioAnalyticsEvent.EventBuilder(
- token = params.token,
+ tokenSymbol = params.token.symbol,
source = params.analyticsParams?.source,
)
@@ -111,13 +109,6 @@ internal class MarketsPortfolioModel @Inject constructor(
private val tokenActionsHandler = tokenActionsIntentsFactory.create(
currentAppCurrency = Provider { currentAppCurrency.value },
- updateTokenReceiveBSConfig = { updateBlock ->
- if (tokenReceiveFeatureToggle.isNewTokenReceiveEnabled.not()) {
- updateTokensState {
- it.copy(tokenReceiveBSConfig = updateBlock(it.tokenReceiveBSConfig))
- }
- }
- },
onHandleQuickAction = { handledAction ->
analyticsEventHandler.send(
analyticsEventBuilder.quickActionClick(
@@ -130,9 +121,7 @@ internal class MarketsPortfolioModel @Inject constructor(
.name,
),
)
- if (tokenReceiveFeatureToggle.isNewTokenReceiveEnabled) {
- configureReceiveAddresses(handledAction)
- }
+ configureReceiveAddresses(handledAction)
},
)
@@ -426,8 +415,7 @@ internal class MarketsPortfolioModel @Inject constructor(
}
private fun configureReceiveAddresses(quickAction: TokenActionsHandler.HandledQuickAction) {
- val isNewReceive = quickAction.action == TokenActionsBSContentUM.Action.Receive &&
- tokenReceiveFeatureToggle.isNewTokenReceiveEnabled
+ val isNewReceive = quickAction.action == TokenActionsBSContentUM.Action.Receive
if (isNewReceive) {
modelScope.launch {
val tokenConfig = receiveAddressesFactory.create(
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/NewMarketsPortfolioDelegate.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/NewMarketsPortfolioDelegate.kt
index 28c78b4d76..21a34d471c 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/NewMarketsPortfolioDelegate.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/NewMarketsPortfolioDelegate.kt
@@ -3,6 +3,7 @@ package com.tangem.features.feed.components.market.details.portfolio.impl.model
import arrow.core.getOrElse
import com.tangem.blockchainsdk.compatibility.getTokenIdIfL2Network
import com.tangem.common.ui.account.AccountTitleUM
+import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.common.ui.account.toUM
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.extensions.TextReference
@@ -285,7 +286,7 @@ internal class NewMarketsPortfolioDelegate @AssistedInject constructor(
prefixText = TextReference.EMPTY,
name = this.accountName.toUM().value,
icon = when (this) {
- is Account.CryptoPortfolio -> this.icon.toUM()
+ is Account.CryptoPortfolio -> CryptoPortfolioIconConverter.convert(this.icon)
is Account.Payment -> TODO("[REDACTED_JIRA]")
},
),
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/TokenActionsHandler.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/TokenActionsHandler.kt
index 55d52cea7d..7bfe5544bd 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/TokenActionsHandler.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/TokenActionsHandler.kt
@@ -1,20 +1,16 @@
package com.tangem.features.feed.components.market.details.portfolio.impl.model
import com.tangem.common.routing.AppRoute
-import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheetConfig
import com.tangem.common.ui.bottomsheet.receive.mapToAddressModels
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender
-import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.ui.clipboard.ClipboardManager
-import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.DialogMessage
import com.tangem.core.ui.message.SnackbarMessage
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.demo.IsDemoCardUseCase
-import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.onramp.model.OnrampSource
import com.tangem.domain.redux.ReduxStateHolder
@@ -36,11 +32,9 @@ internal class TokenActionsHandler @AssistedInject constructor(
private val uiMessageSender: UiMessageSender,
private val reduxStateHolder: ReduxStateHolder,
@Assisted private val currentAppCurrency: Provider,
- @Assisted private val updateTokenReceiveBSConfig: ((TangemBottomSheetConfig) -> TangemBottomSheetConfig) -> Unit,
@Assisted private val onHandleQuickAction: (HandledQuickAction) -> Unit,
private val isDemoCardUseCase: IsDemoCardUseCase,
private val messageSender: UiMessageSender,
- private val shareManager: ShareManager,
) {
private val disabledActionsInDemoMode = buildSet {
@@ -60,7 +54,7 @@ internal class TokenActionsHandler @AssistedInject constructor(
when (action) {
TokenActionsBSContentUM.Action.Buy -> onBuyClick(cryptoCurrencyData)
TokenActionsBSContentUM.Action.Exchange -> onExchangeClick(cryptoCurrencyData)
- TokenActionsBSContentUM.Action.Receive -> onReceiveClick(cryptoCurrencyData)
+ TokenActionsBSContentUM.Action.Receive -> Unit
TokenActionsBSContentUM.Action.CopyAddress -> onCopyAddress(cryptoCurrencyData)
TokenActionsBSContentUM.Action.Sell -> onSellClick(cryptoCurrencyData)
TokenActionsBSContentUM.Action.Send -> onSendClick(cryptoCurrencyData)
@@ -87,34 +81,6 @@ internal class TokenActionsHandler @AssistedInject constructor(
messageSender.send(message)
}
- private fun onReceiveClick(cryptoCurrencyData: PortfolioData.CryptoCurrencyData) {
- val cryptoCurrencyStatus = cryptoCurrencyData.status
- val currency = cryptoCurrencyStatus.currency
- val networkAddress = cryptoCurrencyStatus.value.networkAddress ?: return
-
- updateTokenReceiveBSConfig {
- TangemBottomSheetConfig(
- isShown = true,
- onDismissRequest = {
- updateTokenReceiveBSConfig {
- it.copy(isShown = false)
- }
- },
- content = TokenReceiveBottomSheetConfig(
- asset = TokenReceiveBottomSheetConfig.Asset.Currency(
- name = currency.name,
- symbol = currency.symbol,
- ),
- network = currency.network,
- networkAddress = networkAddress,
- showMemoDisclaimer = currency.network.transactionExtrasType != Network.TransactionExtrasType.NONE,
- onCopyClick = { clipboardManager.setText(networkAddress.defaultAddress.value, isSensitive = true) },
- onShareClick = { shareManager.shareText(networkAddress.defaultAddress.value) },
- ),
- )
- }
- }
-
private fun onCopyAddress(cryptoCurrencyData: PortfolioData.CryptoCurrencyData) {
val cryptoCurrencyStatus = cryptoCurrencyData.status
val networkAddress = cryptoCurrencyStatus.value.networkAddress ?: return
@@ -196,7 +162,6 @@ internal class TokenActionsHandler @AssistedInject constructor(
interface Factory {
fun create(
currentAppCurrency: Provider,
- updateTokenReceiveBSConfig: ((TangemBottomSheetConfig) -> TangemBottomSheetConfig) -> Unit,
onHandleQuickAction: (HandledQuickAction) -> Unit,
): TokenActionsHandler
}
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/TokensPortfolioUMConverter.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/TokensPortfolioUMConverter.kt
index 4444e6a82f..249415e21f 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/TokensPortfolioUMConverter.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/model/TokensPortfolioUMConverter.kt
@@ -12,7 +12,6 @@ import com.tangem.utils.converter.Converter
import com.tangem.utils.extensions.isZero
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
-import kotlin.collections.map
/**
* Converter from [Map] of [UserWallet] and [CryptoCurrencyStatus] to [MyPortfolioUM.Tokens]
@@ -50,9 +49,6 @@ internal class TokensPortfolioUMConverter(
buttonState = addButtonState,
addToPortfolioBSConfig = bsConfig,
onAddClick = onAddClick,
- tokenReceiveBSConfig = (currentState() as? MyPortfolioUM.Tokens)
- ?.tokenReceiveBSConfig
- ?: TangemBottomSheetConfig.Empty,
)
}
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/ui/MyPortfolio.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/ui/MyPortfolio.kt
index 7ff006cdb9..8d6911c448 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/ui/MyPortfolio.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/ui/MyPortfolio.kt
@@ -16,7 +16,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEachIndexed
import com.tangem.common.ui.account.AccountTitle
-import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheet
import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.SmallButtonShimmer
@@ -140,8 +139,6 @@ private fun TokenList(state: MyPortfolioUM.Tokens, modifier: Modifier = Modifier
}
}
}
-
- TokenReceiveBottomSheet(config = state.tokenReceiveBSConfig)
}
@Composable
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/ui/preview/PreviewMyPortfolioUMProvider.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/ui/preview/PreviewMyPortfolioUMProvider.kt
index 3a40be94b8..8c6bf7a7a1 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/ui/preview/PreviewMyPortfolioUMProvider.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/components/market/details/portfolio/impl/ui/preview/PreviewMyPortfolioUMProvider.kt
@@ -23,21 +23,18 @@ internal class PreviewMyPortfolioUMProvider : PreviewParameterProvider,
val buttonState: AddButtonState,
- val tokenReceiveBSConfig: TangemBottomSheetConfig,
val onAddClick: () -> Unit,
) : MyPortfolioUM() {
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/featuretoggle/DefaultFeedFeatureToggle.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/featuretoggle/DefaultFeedFeatureToggle.kt
index c15096b261..38b3458572 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/featuretoggle/DefaultFeedFeatureToggle.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/featuretoggle/DefaultFeedFeatureToggle.kt
@@ -9,4 +9,7 @@ internal class DefaultFeedFeatureToggle(
override val isFeedEnabled: Boolean
get() = featureTogglesManager.isFeatureEnabled("FEED_ENABLED")
+
+ override val isEarnBlockEnabled: Boolean
+ get() = featureTogglesManager.isFeatureEnabled("EARN_BLOCK_ENABLED")
}
\ No newline at end of file
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/FeedComponentModel.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/FeedComponentModel.kt
index 769c975519..8c1774fcf8 100644
--- a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/FeedComponentModel.kt
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/model/feed/FeedComponentModel.kt
@@ -47,10 +47,10 @@ internal class FeedComponentModel @Inject constructor(
private val fetchTrendingNewsUseCase: FetchTrendingNewsUseCase,
private val manageTrendingNewsUseCase: ManageTrendingNewsUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
+ private val stateController: FeedStateController,
getTopFiveMarketTokenUseCase: GetTopFiveMarketTokenUseCase,
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
paramsContainer: ParamsContainer,
- private val stateController: FeedStateController,
) : Model() {
private val params = paramsContainer.require()
@@ -80,11 +80,9 @@ internal class FeedComponentModel @Inject constructor(
init {
initializeState()
updateCallbacks()
-
- modelScope.launch(dispatchers.default) {
- fetchTrendingNewsUseCase()
- }
-
+ fetchTrendingNews()
+ subscribeOnCurrencyUpdate()
+ loadCharts()
modelScope.launch(dispatchers.default) {
combine(
flow = marketsBatchFlowManager.itemsByOrder,
@@ -144,13 +142,23 @@ internal class FeedComponentModel @Inject constructor(
}
}.collect()
}
+ }
+ private fun fetchTrendingNews() {
+ modelScope.launch(dispatchers.default) {
+ fetchTrendingNewsUseCase()
+ }
+ }
+
+ private fun subscribeOnCurrencyUpdate() {
modelScope.launch(dispatchers.default) {
currentAppCurrency.drop(1).collect {
marketsBatchFlowManager.reloadAll()
}
}
+ }
+ private fun loadCharts() {
modelScope.launch(dispatchers.default) {
TokenMarketListConfig.Order.entries.forEach { order ->
marketsBatchFlowManager.getOnLastBatchLoadedSuccessFlow(order)?.collect { batchKey ->
diff --git a/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/state/EarnListUM.kt b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/state/EarnListUM.kt
new file mode 100644
index 0000000000..f703b958c0
--- /dev/null
+++ b/features/feed/impl/src/main/kotlin/com/tangem/features/feed/ui/feed/state/EarnListUM.kt
@@ -0,0 +1,44 @@
+package com.tangem.features.feed.ui.feed.state
+
+import androidx.compose.runtime.Immutable
+import com.tangem.core.ui.components.currency.icon.CurrencyIconState
+import com.tangem.core.ui.extensions.TextReference
+import com.tangem.domain.models.serialization.SerializedBigDecimal
+import kotlinx.collections.immutable.ImmutableList
+
+internal data class EarnListUM(
+ val items: ImmutableList,
+ val contentState: EarnListContentState,
+)
+
+@Immutable
+internal data class EarnListItemUM(
+ val network: TextReference.Str,
+ val symbol: String,
+ val tokenName: String,
+ val currencyIconState: CurrencyIconState,
+ val earnValue: EarnValueUM,
+ val earnType: EarnType,
+ val onItemClick: () -> Unit,
+)
+
+@Immutable
+internal data class EarnValueUM(
+ val percent: SerializedBigDecimal,
+ val earnValueType: EarnValueType,
+)
+
+internal enum class EarnType {
+ Staking, Yield
+}
+
+internal enum class EarnValueType {
+ APR, APY
+}
+
+@Immutable
+internal sealed interface EarnListContentState {
+ data object Loading : EarnListContentState
+ data object Content : EarnListContentState
+ data class Error(val onRetryClicked: () -> Unit) : EarnListContentState
+}
\ No newline at end of file
diff --git a/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/model/HomeModel.kt b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/model/HomeModel.kt
index e650912dc6..b261bf588d 100644
--- a/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/model/HomeModel.kt
+++ b/features/home/impl/src/main/kotlin/com/tangem/features/home/impl/model/HomeModel.kt
@@ -190,7 +190,10 @@ internal class HomeModel @Inject constructor(
return
}
- saveWalletUseCase(userWallet).fold(
+ saveWalletUseCase(
+ userWallet = userWallet,
+ analyticsSource = AnalyticsParam.ScreensSources.Intro,
+ ).fold(
ifLeft = {
delay(HIDE_PROGRESS_DELAY)
setLoading(false)
diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createhardwarewallet/CreateHardwareWalletModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createhardwarewallet/CreateHardwareWalletModel.kt
index 39a2c8b242..5d32c8952b 100644
--- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createhardwarewallet/CreateHardwareWalletModel.kt
+++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createhardwarewallet/CreateHardwareWalletModel.kt
@@ -133,7 +133,10 @@ internal class CreateHardwareWalletModel @Inject constructor(
return
}
- saveWalletUseCase(userWallet = userWallet).fold(
+ saveWalletUseCase(
+ userWallet = userWallet,
+ analyticsSource = AnalyticsParam.ScreensSources.AddNew,
+ ).fold(
ifLeft = { saveWalletError ->
delay(HIDE_PROGRESS_DELAY)
setLoading(false)
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/model/AddTokenUiBuilder.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/model/AddTokenUiBuilder.kt
index 1d95ddde68..107c7a8c3b 100644
--- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/model/AddTokenUiBuilder.kt
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/model/AddTokenUiBuilder.kt
@@ -1,8 +1,10 @@
package com.tangem.features.markets.portfolio.add.impl.model
-import com.tangem.common.ui.account.CryptoPortfolioIconUM
+import com.tangem.common.ui.account.AccountIconUM
+import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.common.ui.account.PortfolioSelectUM
import com.tangem.common.ui.account.toUM
+import com.tangem.common.ui.addtoken.AddTokenUM
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
@@ -16,7 +18,6 @@ import com.tangem.features.markets.impl.R
import com.tangem.features.markets.portfolio.add.api.SelectedNetwork
import com.tangem.features.markets.portfolio.add.api.SelectedPortfolio
import com.tangem.features.markets.portfolio.add.impl.AddTokenComponent
-import com.tangem.common.ui.addtoken.AddTokenUM
import javax.inject.Inject
@ModelScoped
@@ -35,7 +36,7 @@ internal class AddTokenUiBuilder @Inject constructor(
}
private fun createPortfolio(selectedPortfolio: SelectedPortfolio): PortfolioSelectUM {
- val accountIcon: CryptoPortfolioIconUM?
+ val accountIcon: AccountIconUM.CryptoPortfolio?
val portfolioName: TextReference
when (selectedPortfolio.isAccountMode) {
false -> {
@@ -46,7 +47,7 @@ internal class AddTokenUiBuilder @Inject constructor(
val accountStatus = selectedPortfolio.account.account
portfolioName = accountStatus.account.accountName.toUM().value
accountIcon = when (accountStatus) {
- is AccountStatus.CryptoPortfolio -> accountStatus.account.icon.toUM()
+ is AccountStatus.CryptoPortfolio -> CryptoPortfolioIconConverter.convert(accountStatus.account.icon)
}
}
}
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/model/TokenActionsModel.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/model/TokenActionsModel.kt
index d3dd8d929b..466befc011 100644
--- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/model/TokenActionsModel.kt
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/add/impl/model/TokenActionsModel.kt
@@ -48,7 +48,6 @@ internal class TokenActionsModel @Inject constructor(
private val tokenActionsHandler: TokenActionsHandler =
tokenActionsIntentsFactory.create(
currentAppCurrency = Provider { currentAppCurrency.value },
- updateTokenReceiveBSConfig = { },
onHandleQuickAction = { handledAction -> handledQuickAction(handledAction) },
)
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MarketsPortfolioModel.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MarketsPortfolioModel.kt
index 529f20adbc..0157de78d9 100644
--- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MarketsPortfolioModel.kt
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/MarketsPortfolioModel.kt
@@ -40,7 +40,6 @@ import com.tangem.features.markets.portfolio.impl.loader.PortfolioDataLoader
import com.tangem.features.markets.portfolio.impl.ui.state.MyPortfolioUM
import com.tangem.features.markets.portfolio.impl.ui.state.MyPortfolioUM.Tokens.AddButtonState
import com.tangem.features.markets.portfolio.impl.ui.state.TokenActionsBSContentUM
-import com.tangem.features.tokenreceive.TokenReceiveFeatureToggle
import com.tangem.features.wallet.utils.UserWalletImageFetcher
import com.tangem.lib.crypto.BlockchainUtils
import com.tangem.operations.attestation.ArtworkSize
@@ -69,12 +68,11 @@ internal class MarketsPortfolioModel @Inject constructor(
private val saveMarketTokensUseCase: SaveMarketTokensUseCase,
private val addToPortfolioManager: AddToPortfolioManager,
private val analyticsEventHandler: AnalyticsEventHandler,
- private val tokenReceiveFeatureToggle: TokenReceiveFeatureToggle,
private val userWalletImageFetcher: UserWalletImageFetcher,
private val receiveAddressesFactory: ReceiveAddressesFactory,
- private val accountsFeatureToggles: AccountsFeatureToggles,
+ accountsFeatureToggles: AccountsFeatureToggles,
newAddToPortfolioManagerFactory: NewAddToPortfolioManager.Factory,
- private val newMarketsPortfolioDelegateFactory: NewMarketsPortfolioDelegate.Factory,
+ newMarketsPortfolioDelegateFactory: NewMarketsPortfolioDelegate.Factory,
) : Model() {
private val _state: MutableStateFlow = MutableStateFlow(value = MyPortfolioUM.Loading)
@@ -111,13 +109,6 @@ internal class MarketsPortfolioModel @Inject constructor(
private val tokenActionsHandler = tokenActionsIntentsFactory.create(
currentAppCurrency = Provider { currentAppCurrency.value },
- updateTokenReceiveBSConfig = { updateBlock ->
- if (tokenReceiveFeatureToggle.isNewTokenReceiveEnabled.not()) {
- updateTokensState {
- it.copy(tokenReceiveBSConfig = updateBlock(it.tokenReceiveBSConfig))
- }
- }
- },
onHandleQuickAction = { handledAction ->
val currencyNetwork = handledAction.cryptoCurrencyData.status.currency.network
analyticsEventHandler.send(
@@ -126,9 +117,7 @@ internal class MarketsPortfolioModel @Inject constructor(
blockchainName = currencyNetwork.name,
),
)
- if (tokenReceiveFeatureToggle.isNewTokenReceiveEnabled) {
- configureReceiveAddresses(handledAction)
- }
+ configureReceiveAddresses(handledAction)
},
)
@@ -423,8 +412,7 @@ internal class MarketsPortfolioModel @Inject constructor(
}
private fun configureReceiveAddresses(quickAction: TokenActionsHandler.HandledQuickAction) {
- val isNewReceive = quickAction.action == TokenActionsBSContentUM.Action.Receive &&
- tokenReceiveFeatureToggle.isNewTokenReceiveEnabled
+ val isNewReceive = quickAction.action == TokenActionsBSContentUM.Action.Receive
if (isNewReceive) {
modelScope.launch {
val tokenConfig = receiveAddressesFactory.create(
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/NewMarketsPortfolioDelegate.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/NewMarketsPortfolioDelegate.kt
index 15f9bd9da6..286e1dc18e 100644
--- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/NewMarketsPortfolioDelegate.kt
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/NewMarketsPortfolioDelegate.kt
@@ -3,6 +3,7 @@ package com.tangem.features.markets.portfolio.impl.model
import arrow.core.getOrElse
import com.tangem.blockchainsdk.compatibility.getTokenIdIfL2Network
import com.tangem.common.ui.account.AccountTitleUM
+import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.common.ui.account.toUM
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.extensions.TextReference
@@ -286,7 +287,7 @@ internal class NewMarketsPortfolioDelegate @AssistedInject constructor(
prefixText = TextReference.EMPTY,
name = this.accountName.toUM().value,
icon = when (this) {
- is Account.CryptoPortfolio -> this.icon.toUM()
+ is Account.CryptoPortfolio -> CryptoPortfolioIconConverter.convert(this.icon)
is Account.Payment -> TODO("[REDACTED_JIRA]")
},
),
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokenActionsHandler.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokenActionsHandler.kt
index 853080d794..66ff85116c 100644
--- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokenActionsHandler.kt
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokenActionsHandler.kt
@@ -1,20 +1,16 @@
package com.tangem.features.markets.portfolio.impl.model
import com.tangem.common.routing.AppRoute
-import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheetConfig
import com.tangem.common.ui.bottomsheet.receive.mapToAddressModels
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender
-import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.ui.clipboard.ClipboardManager
-import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.DialogMessage
import com.tangem.core.ui.message.SnackbarMessage
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.demo.IsDemoCardUseCase
-import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.onramp.model.OnrampSource
import com.tangem.domain.redux.ReduxStateHolder
@@ -36,11 +32,9 @@ internal class TokenActionsHandler @AssistedInject constructor(
private val uiMessageSender: UiMessageSender,
private val reduxStateHolder: ReduxStateHolder,
@Assisted private val currentAppCurrency: Provider,
- @Assisted private val updateTokenReceiveBSConfig: ((TangemBottomSheetConfig) -> TangemBottomSheetConfig) -> Unit,
@Assisted private val onHandleQuickAction: (HandledQuickAction) -> Unit,
private val isDemoCardUseCase: IsDemoCardUseCase,
private val messageSender: UiMessageSender,
- private val shareManager: ShareManager,
) {
private val disabledActionsInDemoMode = buildSet {
@@ -60,7 +54,7 @@ internal class TokenActionsHandler @AssistedInject constructor(
when (action) {
TokenActionsBSContentUM.Action.Buy -> onBuyClick(cryptoCurrencyData)
TokenActionsBSContentUM.Action.Exchange -> onExchangeClick(cryptoCurrencyData)
- TokenActionsBSContentUM.Action.Receive -> onReceiveClick(cryptoCurrencyData)
+ TokenActionsBSContentUM.Action.Receive -> Unit
TokenActionsBSContentUM.Action.CopyAddress -> onCopyAddress(cryptoCurrencyData)
TokenActionsBSContentUM.Action.Sell -> onSellClick(cryptoCurrencyData)
TokenActionsBSContentUM.Action.Send -> onSendClick(cryptoCurrencyData)
@@ -87,34 +81,6 @@ internal class TokenActionsHandler @AssistedInject constructor(
messageSender.send(message)
}
- private fun onReceiveClick(cryptoCurrencyData: PortfolioData.CryptoCurrencyData) {
- val cryptoCurrencyStatus = cryptoCurrencyData.status
- val currency = cryptoCurrencyStatus.currency
- val networkAddress = cryptoCurrencyStatus.value.networkAddress ?: return
-
- updateTokenReceiveBSConfig {
- TangemBottomSheetConfig(
- isShown = true,
- onDismissRequest = {
- updateTokenReceiveBSConfig {
- it.copy(isShown = false)
- }
- },
- content = TokenReceiveBottomSheetConfig(
- asset = TokenReceiveBottomSheetConfig.Asset.Currency(
- name = currency.name,
- symbol = currency.symbol,
- ),
- network = currency.network,
- networkAddress = networkAddress,
- showMemoDisclaimer = currency.network.transactionExtrasType != Network.TransactionExtrasType.NONE,
- onCopyClick = { clipboardManager.setText(networkAddress.defaultAddress.value, isSensitive = true) },
- onShareClick = { shareManager.shareText(networkAddress.defaultAddress.value) },
- ),
- )
- }
- }
-
private fun onCopyAddress(cryptoCurrencyData: PortfolioData.CryptoCurrencyData) {
val cryptoCurrencyStatus = cryptoCurrencyData.status
val networkAddress = cryptoCurrencyStatus.value.networkAddress ?: return
@@ -196,7 +162,6 @@ internal class TokenActionsHandler @AssistedInject constructor(
interface Factory {
fun create(
currentAppCurrency: Provider,
- updateTokenReceiveBSConfig: ((TangemBottomSheetConfig) -> TangemBottomSheetConfig) -> Unit,
onHandleQuickAction: (HandledQuickAction) -> Unit,
): TokenActionsHandler
}
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokensPortfolioUMConverter.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokensPortfolioUMConverter.kt
index 47fe6e3cf3..16f7f6554a 100644
--- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokensPortfolioUMConverter.kt
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/model/TokensPortfolioUMConverter.kt
@@ -50,9 +50,6 @@ internal class TokensPortfolioUMConverter(
buttonState = addButtonState,
addToPortfolioBSConfig = bsConfig,
onAddClick = onAddClick,
- tokenReceiveBSConfig = (currentState() as? MyPortfolioUM.Tokens)
- ?.tokenReceiveBSConfig
- ?: TangemBottomSheetConfig.Empty,
)
}
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/MyPortfolio.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/MyPortfolio.kt
index 582bcc4690..1ea37db49c 100644
--- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/MyPortfolio.kt
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/MyPortfolio.kt
@@ -16,7 +16,6 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEachIndexed
import com.tangem.common.ui.account.AccountTitle
-import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheet
import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.SmallButtonShimmer
@@ -141,8 +140,6 @@ private fun TokenList(state: MyPortfolioUM.Tokens, modifier: Modifier = Modifier
}
}
}
-
- TokenReceiveBottomSheet(config = state.tokenReceiveBSConfig)
}
@Composable
diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/preview/PreviewMyPortfolioUMProvider.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/preview/PreviewMyPortfolioUMProvider.kt
index 4e1b5c5bd9..e6deaad138 100644
--- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/preview/PreviewMyPortfolioUMProvider.kt
+++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/portfolio/impl/ui/preview/PreviewMyPortfolioUMProvider.kt
@@ -93,21 +93,18 @@ internal class PreviewMyPortfolioUMProvider : PreviewParameterProvider,
val buttonState: AddButtonState,
- val tokenReceiveBSConfig: TangemBottomSheetConfig,
val onAddClick: () -> Unit,
) : MyPortfolioUM() {
diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/entity/transformer/UpdateDataStateTransformer.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/entity/transformer/UpdateDataStateTransformer.kt
index e0256c93ba..6a2f95803d 100644
--- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/entity/transformer/UpdateDataStateTransformer.kt
+++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/entity/transformer/UpdateDataStateTransformer.kt
@@ -1,6 +1,7 @@
package com.tangem.features.nft.collections.entity.transformer
import com.tangem.common.ui.account.AccountTitleUM
+import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.common.ui.account.toUM
import com.tangem.core.ui.components.fields.entity.SearchBarUM
import com.tangem.core.ui.components.notifications.NotificationConfig
@@ -100,7 +101,7 @@ internal class UpdateDataStateTransformer(
prefixText = TextReference.EMPTY,
name = this.accountName.toUM().value,
icon = when (this) {
- is Account.CryptoPortfolio -> this.icon.toUM()
+ is Account.CryptoPortfolio -> CryptoPortfolioIconConverter.convert(this.icon)
is Account.Payment -> TODO("[REDACTED_JIRA]")
},
),
diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/block/DefaultNFTDetailsBlockComponent.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/block/DefaultNFTDetailsBlockComponent.kt
index a2b7f2f839..ff963aefcb 100644
--- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/block/DefaultNFTDetailsBlockComponent.kt
+++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/details/block/DefaultNFTDetailsBlockComponent.kt
@@ -3,6 +3,7 @@ package com.tangem.features.nft.details.block
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.tangem.common.ui.account.AccountTitleUM
+import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.common.ui.account.toUM
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.extensions.getActiveIconRes
@@ -32,7 +33,7 @@ class DefaultNFTDetailsBlockComponent @AssistedInject constructor(
AccountTitleUM.Account(
prefixText = resourceReference(R.string.common_from),
name = account.accountName.toUM().value,
- icon = account.icon.toUM(),
+ icon = CryptoPortfolioIconConverter.convert(account.icon),
)
} else {
AccountTitleUM.Text(params.walletTitle)
diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/entity/transformer/ShowReceiveBottomSheetTransformer.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/entity/transformer/ShowReceiveBottomSheetTransformer.kt
deleted file mode 100644
index 65e142aaf6..0000000000
--- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/entity/transformer/ShowReceiveBottomSheetTransformer.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.tangem.features.nft.receive.entity.transformer
-
-import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheetConfig
-import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
-import com.tangem.core.ui.components.notifications.NotificationConfig
-import com.tangem.domain.models.network.Network
-import com.tangem.domain.models.network.NetworkAddress
-import com.tangem.features.nft.receive.entity.NFTReceiveUM
-import com.tangem.utils.transformer.Transformer
-import com.tangem.core.ui.R
-import com.tangem.core.ui.extensions.resourceReference
-import com.tangem.lib.crypto.BlockchainUtils
-import kotlinx.collections.immutable.toPersistentList
-
-internal class ShowReceiveBottomSheetTransformer(
- private val network: Network,
- private val networkAddress: NetworkAddress,
- private val onDismissBottomSheet: () -> Unit,
- private val onCopyClick: (String) -> Unit,
- private val onShareClick: (String) -> Unit,
-) : Transformer {
-
- override fun transform(prevState: NFTReceiveUM): NFTReceiveUM = prevState.copy(
- bottomSheetConfig = TangemBottomSheetConfig(
- isShown = true,
- onDismissRequest = onDismissBottomSheet,
- content = TokenReceiveBottomSheetConfig(
- asset = TokenReceiveBottomSheetConfig.Asset.NFT,
- network = network,
- networkAddress = networkAddress,
- showMemoDisclaimer = network.transactionExtrasType != Network.TransactionExtrasType.NONE,
- customNotifications = buildList {
- if (BlockchainUtils.isSolana(network.rawId)) {
- add(
- NotificationConfig(
- title = resourceReference(R.string.nft_receive_unsupported_types),
- subtitle = resourceReference(R.string.nft_receive_unsupported_types_description),
- iconResId = R.drawable.ic_alert_circle_24,
- iconTint = NotificationConfig.IconTint.Attention,
- ),
- )
- }
- }.toPersistentList(),
- onCopyClick = onCopyClick,
- onShareClick = onShareClick,
- ),
- ),
- )
-}
\ No newline at end of file
diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/model/NFTReceiveModel.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/model/NFTReceiveModel.kt
index 889afd28d6..6a32daa0a5 100644
--- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/model/NFTReceiveModel.kt
+++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/model/NFTReceiveModel.kt
@@ -8,8 +8,6 @@ import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.ui.UiMessageSender
-import com.tangem.core.navigation.share.ShareManager
-import com.tangem.core.ui.clipboard.ClipboardManager
import com.tangem.core.ui.components.fields.InputManager
import com.tangem.core.ui.components.fields.entity.SearchBarUM
import com.tangem.core.ui.extensions.TextReference
@@ -37,11 +35,9 @@ import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.nft.impl.R
import com.tangem.features.nft.receive.NFTReceiveComponent
import com.tangem.features.nft.receive.entity.NFTReceiveUM
-import com.tangem.features.nft.receive.entity.transformer.ShowReceiveBottomSheetTransformer
import com.tangem.features.nft.receive.entity.transformer.ToggleSearchBarTransformer
import com.tangem.features.nft.receive.entity.transformer.UpdateDataStateTransformer
import com.tangem.features.nft.receive.entity.transformer.UpdateSearchQueryTransformer
-import com.tangem.features.tokenreceive.TokenReceiveFeatureToggle
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.*
@@ -56,11 +52,8 @@ internal class NFTReceiveModel @Inject constructor(
private val getNFTNetworksUseCase: GetNFTNetworksUseCase,
private val filterNFTAvailableNetworksUseCase: FilterNFTAvailableNetworksUseCase,
private val getNFTNetworkStatusUseCase: GetNFTNetworkStatusUseCase,
- private val clipboardManager: ClipboardManager,
- private val shareManager: ShareManager,
private val analyticsEventHandler: AnalyticsEventHandler,
private val messageSender: UiMessageSender,
- private val tokenReceiveFeatureToggle: TokenReceiveFeatureToggle,
private val getNFTCurrencyUseCase: GetNFTCurrencyUseCase,
private val receiveAddressesFactory: ReceiveAddressesFactory,
private val getUserWalletUseCase: GetUserWalletUseCase,
@@ -167,14 +160,6 @@ internal class NFTReceiveModel @Inject constructor(
}
}
- private fun onReceiveBottomSheetDismiss() {
- _state.update {
- it.copy(
- bottomSheetConfig = it.bottomSheetConfig?.copy(isShown = false),
- )
- }
- }
-
private fun onNetworkClick(network: Network, enabled: Boolean) {
if (!enabled) {
val message = DialogMessage(
@@ -194,24 +179,12 @@ internal class NFTReceiveModel @Inject constructor(
when (val value = networkStatus.value) {
is NetworkStatus.Verified -> {
- if (tokenReceiveFeatureToggle.isNewTokenReceiveEnabled) {
- bottomSheetNavigation.activate(
- configuration = configureReceiveAddresses(
- addresses = value.address,
- network = network,
- ),
- )
- } else {
- _state.update {
- ShowReceiveBottomSheetTransformer(
- network = network,
- networkAddress = value.address,
- onDismissBottomSheet = ::onReceiveBottomSheetDismiss,
- onCopyClick = { text -> onCopyClick(text, network) },
- onShareClick = { text -> onShareClick(text, network) },
- ).transform(it)
- }
- }
+ bottomSheetNavigation.activate(
+ configuration = configureReceiveAddresses(
+ addresses = value.address,
+ network = network,
+ ),
+ )
}
is NetworkStatus.MissedDerivation,
is NetworkStatus.NoAccount,
@@ -222,16 +195,6 @@ internal class NFTReceiveModel @Inject constructor(
}
}
- private fun onCopyClick(text: String, network: Network) {
- analyticsEventHandler.send(NFTAnalyticsEvent.Receive.CopyAddress(network.name))
- clipboardManager.setText(text = text, isSensitive = true)
- }
-
- private fun onShareClick(text: String, network: Network) {
- analyticsEventHandler.send(NFTAnalyticsEvent.Receive.ShareAddress(network.name))
- shareManager.shareText(text = text)
- }
-
private suspend fun configureReceiveAddresses(addresses: NetworkAddress, network: Network): TokenReceiveConfig {
val cryptoCurrency = getNFTCurrencyUseCase.invoke(network)
return receiveAddressesFactory.createForNft(
diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTReceive.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTReceive.kt
index dd9cfc1aa7..10a0c8c9f0 100644
--- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTReceive.kt
+++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/receive/ui/NFTReceive.kt
@@ -2,14 +2,13 @@ package com.tangem.features.nft.receive.ui
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheet
-import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheetConfig
import com.tangem.core.ui.components.appbar.TangemTopAppBar
import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM
-import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.fields.SearchBar
import com.tangem.core.ui.components.fields.TangemSearchBarDefaults
import com.tangem.core.ui.extensions.resolveReference
@@ -48,17 +47,4 @@ internal fun NFTReceive(state: NFTReceiveUM, modifier: Modifier = Modifier) {
is NFTReceiveUM.Networks.Empty -> NFTReceiveNetworksEmpty()
}
}
-
- ShowBottomSheet(state.bottomSheetConfig)
-}
-
-@Composable
-private fun ShowBottomSheet(bottomSheetConfig: TangemBottomSheetConfig?) {
- if (bottomSheetConfig != null) {
- when (bottomSheetConfig.content) {
- is TokenReceiveBottomSheetConfig -> {
- TokenReceiveBottomSheet(config = bottomSheetConfig)
- }
- }
- }
}
\ No newline at end of file
diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/chooseoption/model/Wallet1ChooseOptionModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/chooseoption/model/Wallet1ChooseOptionModel.kt
index 8d508c9a4b..32c406b3b1 100644
--- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/chooseoption/model/Wallet1ChooseOptionModel.kt
+++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/chooseoption/model/Wallet1ChooseOptionModel.kt
@@ -2,6 +2,7 @@ package com.tangem.features.onboarding.v2.multiwallet.impl.child.chooseoption.mo
import androidx.compose.runtime.Stable
import com.tangem.core.analytics.api.AnalyticsEventHandler
+import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
@@ -50,17 +51,20 @@ internal class Wallet1ChooseOptionModel @Inject constructor(
val scanResponse = params.multiWalletState.value.currentScanResponse
val userWallet = createUserWallet(scanResponse)
- saveWalletUseCase(userWallet, canOverride = true)
- .onRight {
- cardRepository.finishCardActivation(scanResponse.card.cardId)
+ saveWalletUseCase(
+ userWallet = userWallet,
+ canOverride = true,
+ analyticsSource = AnalyticsParam.ScreensSources.Onboarding,
+ ).onRight {
+ cardRepository.finishCardActivation(scanResponse.card.cardId)
- // save user wallet for manage tokens screen
- params.multiWalletState.update {
- it.copy(resultUserWallet = userWallet)
- }
-
- returnToParentFlow.emit(Unit)
+ // save user wallet for manage tokens screen
+ params.multiWalletState.update {
+ it.copy(resultUserWallet = userWallet)
}
+
+ returnToParentFlow.emit(Unit)
+ }
}
}
diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/createwallet/model/MultiWalletCreateWalletModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/createwallet/model/MultiWalletCreateWalletModel.kt
index 1cb37f4eb6..42d2db0372 100644
--- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/createwallet/model/MultiWalletCreateWalletModel.kt
+++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/createwallet/model/MultiWalletCreateWalletModel.kt
@@ -135,17 +135,20 @@ internal class MultiWalletCreateWalletModel @Inject constructor(
val scanResponse = params.multiWalletState.value.currentScanResponse
val userWallet = createUserWallet(scanResponse)
- saveWalletUseCase(userWallet, canOverride = true)
- .onRight {
- cardRepository.finishCardActivation(scanResponse.card.cardId)
+ saveWalletUseCase(
+ userWallet = userWallet,
+ canOverride = true,
+ analyticsSource = AnalyticsParam.ScreensSources.Onboarding,
+ ).onRight {
+ cardRepository.finishCardActivation(scanResponse.card.cardId)
- // save user wallet for manage tokens screen
- params.multiWalletState.update {
- it.copy(resultUserWallet = userWallet)
- }
-
- onDone.emit(Step.Done)
+ // save user wallet for manage tokens screen
+ params.multiWalletState.update {
+ it.copy(resultUserWallet = userWallet)
}
+
+ onDone.emit(Step.Done)
+ }
}
}
diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/finalize/model/MultiWalletFinalizeModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/finalize/model/MultiWalletFinalizeModel.kt
index 159ccf4bd9..0fb3c2f011 100644
--- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/finalize/model/MultiWalletFinalizeModel.kt
+++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/finalize/model/MultiWalletFinalizeModel.kt
@@ -5,6 +5,7 @@ import arrow.core.getOrElse
import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.ByteArrayKey
+import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
@@ -267,6 +268,7 @@ internal class MultiWalletFinalizeModel @Inject constructor(
scanResponse = scanResponse.updateScanResponseAfterBackup(),
),
canOverride = true,
+ analyticsSource = AnalyticsParam.ScreensSources.Onboarding,
)
userWalletCreated
}
diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/upgradewallet/model/MultiWalletUpgradeWalletModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/upgradewallet/model/MultiWalletUpgradeWalletModel.kt
index f6b6306cd6..d7e2c6e3e9 100644
--- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/upgradewallet/model/MultiWalletUpgradeWalletModel.kt
+++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/multiwallet/impl/child/upgradewallet/model/MultiWalletUpgradeWalletModel.kt
@@ -4,6 +4,7 @@ import androidx.compose.runtime.Stable
import arrow.core.getOrElse
import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemSdkError
+import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
@@ -134,17 +135,20 @@ internal class MultiWalletUpgradeWalletModel @Inject constructor(
val scanResponse = params.multiWalletState.value.currentScanResponse
val userWallet = createUserWallet(scanResponse)
- saveWalletUseCase(userWallet, canOverride = true)
- .onRight {
- cardRepository.finishCardActivation(scanResponse.card.cardId)
+ saveWalletUseCase(
+ userWallet,
+ canOverride = true,
+ analyticsSource = AnalyticsParam.ScreensSources.Onboarding,
+ ).onRight {
+ cardRepository.finishCardActivation(scanResponse.card.cardId)
- // save user wallet for manage tokens screen
- params.multiWalletState.update {
- it.copy(resultUserWallet = userWallet)
- }
-
- onDone.emit(Step.Done)
+ // save user wallet for manage tokens screen
+ params.multiWalletState.update {
+ it.copy(resultUserWallet = userWallet)
}
+
+ onDone.emit(Step.Done)
+ }
}
}
diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/note/impl/child/create/model/OnboardingNoteCreateWalletModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/note/impl/child/create/model/OnboardingNoteCreateWalletModel.kt
index 51db083121..b286fead4f 100644
--- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/note/impl/child/create/model/OnboardingNoteCreateWalletModel.kt
+++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/note/impl/child/create/model/OnboardingNoteCreateWalletModel.kt
@@ -82,7 +82,11 @@ internal class OnboardingNoteCreateWalletModel @Inject constructor(
private fun createWalletAndNavigateBackWithDone(scanResponse: ScanResponse) {
modelScope.launch {
val userWallet = createUserWallet(scanResponse)
- saveWalletUseCase(userWallet, canOverride = true).onRight {
+ saveWalletUseCase(
+ userWallet,
+ canOverride = true,
+ analyticsSource = AnalyticsParam.ScreensSources.Onboarding,
+ ).onRight {
params.onWalletCreated(userWallet)
}
_uiState.update {
diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/model/OnboardingTwinModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/model/OnboardingTwinModel.kt
index e70843bd7b..79c8741734 100644
--- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/model/OnboardingTwinModel.kt
+++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/twin/impl/model/OnboardingTwinModel.kt
@@ -300,6 +300,7 @@ internal class OnboardingTwinModel @Inject constructor(
saveWalletUseCase(
userWallet = userWallet,
canOverride = true,
+ analyticsSource = AnalyticsParam.ScreensSources.Onboarding,
).onLeft {
Timber.e("Unable to save user wallet: $it")
setLoading(false)
@@ -329,6 +330,7 @@ internal class OnboardingTwinModel @Inject constructor(
saveWalletUseCase(
userWallet = userWallet,
canOverride = true,
+ analyticsSource = AnalyticsParam.ScreensSources.Onboarding,
).onLeft {
Timber.e("Unable to save user wallet: $it")
setLoading(false)
diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/inprogress/model/OnboardingVisaInProgressModel.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/inprogress/model/OnboardingVisaInProgressModel.kt
index 235c201961..abc373283f 100644
--- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/inprogress/model/OnboardingVisaInProgressModel.kt
+++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/visa/impl/child/inprogress/model/OnboardingVisaInProgressModel.kt
@@ -4,6 +4,7 @@ import androidx.compose.runtime.Stable
import arrow.core.getOrElse
import com.tangem.common.extensions.toHexString
import com.tangem.core.analytics.api.AnalyticsEventHandler
+import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
@@ -173,7 +174,10 @@ internal class OnboardingVisaInProgressModel @Inject constructor(
}
val userWallet = createUserWallet(params.scanResponse, newTokens)
- saveWalletUseCase(userWallet)
+ saveWalletUseCase(
+ userWallet = userWallet,
+ analyticsSource = AnalyticsParam.ScreensSources.Onboarding,
+ )
visaAuthTokenStorage.remove(params.scanResponse.card.cardId)
otpStorage.removeOTP(params.scanResponse.card.cardId)
diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/portfolio/entity/OnrampAddTokenUiBuilder.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/portfolio/entity/OnrampAddTokenUiBuilder.kt
index edd05f63b2..a7b415212a 100644
--- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/portfolio/entity/OnrampAddTokenUiBuilder.kt
+++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/hottokens/portfolio/entity/OnrampAddTokenUiBuilder.kt
@@ -1,6 +1,7 @@
package com.tangem.features.onramp.hottokens.portfolio.entity
-import com.tangem.common.ui.account.CryptoPortfolioIconUM
+import com.tangem.common.ui.account.AccountIconUM
+import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.common.ui.account.PortfolioSelectUM
import com.tangem.common.ui.account.toUM
import com.tangem.common.ui.addtoken.AddTokenUM
@@ -36,7 +37,7 @@ internal class OnrampAddTokenUiBuilder @Inject constructor(
}
private suspend fun createPortfolio(tokenToAdd: AddHotCryptoData): PortfolioSelectUM {
- val accountIcon: CryptoPortfolioIconUM?
+ val accountIcon: AccountIconUM.CryptoPortfolio?
val portfolioName: TextReference
val isAccountMode = isAccountsModeEnabledUseCase.invokeSync()
when (isAccountMode) {
@@ -48,7 +49,7 @@ internal class OnrampAddTokenUiBuilder @Inject constructor(
val accountStatus = tokenToAdd.account
portfolioName = accountStatus.account.accountName.toUM().value
accountIcon = when (accountStatus) {
- is AccountStatus.CryptoPortfolio -> accountStatus.account.icon.toUM()
+ is AccountStatus.CryptoPortfolio -> CryptoPortfolioIconConverter.convert(accountStatus.account.icon)
}
}
}
diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/entity/ExchangeCardUM.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/entity/ExchangeCardUM.kt
index 0b159c1423..1d5877f437 100644
--- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/entity/ExchangeCardUM.kt
+++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/entity/ExchangeCardUM.kt
@@ -1,7 +1,7 @@
package com.tangem.features.onramp.swap.entity
import androidx.compose.runtime.Immutable
-import com.tangem.common.ui.account.CryptoPortfolioIconUM
+import com.tangem.common.ui.account.AccountIconUM
import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.core.ui.extensions.TextReference
@@ -57,7 +57,7 @@ internal sealed interface ExchangeCardUM {
data class Account(
val prefixText: TextReference,
val name: TextReference,
- val icon: CryptoPortfolioIconUM,
+ val icon: AccountIconUM.CryptoPortfolio,
) : TitleUM
}
}
\ No newline at end of file
diff --git a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/entity/utils/ExchangeCardUMExt.kt b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/entity/utils/ExchangeCardUMExt.kt
index 757945de5a..892b627a0c 100644
--- a/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/entity/utils/ExchangeCardUMExt.kt
+++ b/features/onramp/impl/src/main/kotlin/com/tangem/features/onramp/swap/entity/utils/ExchangeCardUMExt.kt
@@ -1,5 +1,6 @@
package com.tangem.features.onramp.swap.entity.utils
+import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.common.ui.account.toUM
import com.tangem.core.ui.components.token.state.TokenItemState
import com.tangem.core.ui.extensions.resourceReference
@@ -45,7 +46,7 @@ internal fun ExchangeCardUM.toFilled(
resourceReference(R.string.common_to)
},
name = account.accountName.toUM().value,
- icon = account.icon.toUM(),
+ icon = CryptoPortfolioIconConverter.convert(account.icon),
)
} else {
titleUM
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 02ddfaaf01..c7f8ef28fe 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
@@ -10,23 +10,23 @@ import com.tangem.domain.models.account.DerivationIndex
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase
+import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.feature.referral.domain.errors.ReferralError
import com.tangem.feature.referral.domain.models.ReferralData
import com.tangem.feature.referral.domain.models.TokenData
-import com.tangem.lib.crypto.UserWalletManager
import timber.log.Timber
@Suppress("LongParameterList")
internal class ReferralInteractorImpl(
private val repository: ReferralRepository,
- private val userWalletManager: UserWalletManager,
private val derivePublicKeysUseCase: DerivePublicKeysUseCase,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase,
private val manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
private val singleAccountSupplier: SingleAccountSupplier,
+ private val walletManagersFacade: WalletManagersFacade,
) : ReferralInteractor {
private val tokensForReferral = mutableListOf()
@@ -85,13 +85,14 @@ internal class ReferralInteractorImpl(
}
.onLeft(Timber::e)
- val publicAddress = userWalletManager.getWalletAddress(
- networkId = tokenData.networkId,
- derivationPath = cryptoCurrency.network.derivationPath.value,
+ val publicAddress = walletManagersFacade.getDefaultAddress(
+ userWalletId = userWalletId,
+ network = cryptoCurrency.network,
)
+ ?: error("Address not found: ${cryptoCurrency.network.id}")
return repository.startReferral(
- walletId = userWalletManager.getWalletId(),
+ walletId = userWalletId.stringValue,
networkId = tokenData.networkId,
tokenId = tokenData.id,
address = publicAddress,
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 09a1b77423..c82165bda3 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
@@ -5,12 +5,12 @@ import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase
import com.tangem.domain.account.supplier.SingleAccountSupplier
import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase
+import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.usecase.DerivePublicKeysUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.feature.referral.domain.ReferralInteractor
import com.tangem.feature.referral.domain.ReferralInteractorImpl
import com.tangem.feature.referral.domain.ReferralRepository
-import com.tangem.lib.crypto.UserWalletManager
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@@ -23,21 +23,21 @@ class ReferralDomainModule {
@ModelScoped
fun provideReferralInteractor(
referralRepository: ReferralRepository,
- userWalletManager: UserWalletManager,
derivePublicKeysUseCase: DerivePublicKeysUseCase,
getUserWalletUseCase: GetUserWalletUseCase,
addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase,
manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
singleAccountSupplier: SingleAccountSupplier,
+ walletManagersFacade: WalletManagersFacade,
): ReferralInteractor {
return ReferralInteractorImpl(
repository = referralRepository,
- userWalletManager = userWalletManager,
derivePublicKeysUseCase = derivePublicKeysUseCase,
getUserWalletUseCase = getUserWalletUseCase,
addCryptoCurrenciesUseCase = addCryptoCurrenciesUseCase,
manageCryptoCurrenciesUseCase = manageCryptoCurrenciesUseCase,
singleAccountSupplier = singleAccountSupplier,
+ walletManagersFacade = walletManagersFacade,
)
}
}
\ No newline at end of file
diff --git a/features/referral/impl/src/main/java/com/tangem/feature/referral/model/AccountAwardConverter.kt b/features/referral/impl/src/main/java/com/tangem/feature/referral/model/AccountAwardConverter.kt
index e5f6db7819..e5ad207a29 100644
--- a/features/referral/impl/src/main/java/com/tangem/feature/referral/model/AccountAwardConverter.kt
+++ b/features/referral/impl/src/main/java/com/tangem/feature/referral/model/AccountAwardConverter.kt
@@ -1,5 +1,6 @@
package com.tangem.feature.referral.model
+import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.common.ui.account.PortfolioSelectUM
import com.tangem.common.ui.account.toUM
import com.tangem.common.ui.tokens.TokenItemStateConverter.Companion.getFormattedCryptoAmount
@@ -62,7 +63,7 @@ internal class AccountAwardConverter(
isBalanceHidden = isBalanceHidden,
tokenState = tokenState,
accountSelectUM = PortfolioSelectUM(
- icon = cryptoPortfolio.account.icon.toUM(),
+ icon = CryptoPortfolioIconConverter.convert(cryptoPortfolio.account.icon),
name = cryptoPortfolio.account.accountName.toUM().value,
isAccountMode = true,
onClick = onAccountClick,
diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt
index 31b351c7d3..8f3da0e8ed 100644
--- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt
+++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt
@@ -19,8 +19,11 @@ import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.navigation.url.UrlOpener
+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.domain.models.wallet.isHotWallet
import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles
import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
@@ -592,21 +595,16 @@ internal class SendConfirmModel @Inject constructor(
private fun primaryButtonUM(): NavigationButton {
val confirmUM = uiState.value.confirmUM
- val isReadyToSend = confirmUM is ConfirmUM.Content && !confirmUM.isSending
+ val isContent = confirmUM is ConfirmUM.Content
+ val isReadyToSend = isContent && !confirmUM.isSending
+ val isHoldToConfirm = userWallet.isHotWallet && isContent
return NavigationButton(
- textReference = when (confirmUM) {
- is ConfirmUM.Success -> resourceReference(R.string.common_close)
- is ConfirmUM.Content -> if (confirmUM.isSending) {
- resourceReference(R.string.send_sending)
- } else {
- resourceReference(R.string.common_send)
- }
- else -> resourceReference(R.string.common_send)
- },
+ textReference = getPrimaryButtonText(confirmUM, isHoldToConfirm),
iconRes = walletInterationIcon(userWallet),
- isIconVisible = isReadyToSend,
+ isIconVisible = isReadyToSend && !isHoldToConfirm,
isEnabled = confirmUM.isPrimaryButtonEnabled,
isHapticClick = isReadyToSend,
+ isHoldToConfirm = isHoldToConfirm,
onClick = {
when (confirmUM) {
is ConfirmUM.Success -> appRouter.pop()
@@ -621,6 +619,18 @@ internal class SendConfirmModel @Inject constructor(
)
}
+ private fun getPrimaryButtonText(confirmUM: ConfirmUM, isHoldToConfirm: Boolean): TextReference {
+ return when {
+ isHoldToConfirm -> resourceReference(
+ id = com.tangem.core.ui.R.string.common_hold_to,
+ formatArgs = wrappedList(resourceReference(R.string.common_send)),
+ )
+ confirmUM is ConfirmUM.Success -> resourceReference(R.string.common_close)
+ confirmUM is ConfirmUM.Content && confirmUM.isSending -> resourceReference(R.string.send_sending)
+ else -> resourceReference(R.string.common_send)
+ }
+ }
+
override fun onFeeResult(feeSelectorUM: FeeSelectorUMRedesigned) {
sendIdleTimer = SystemClock.elapsedRealtime()
_uiState.update { it.copy(feeSelectorUM = feeSelectorUM) }
diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/converters/SendRecipientWalletListConverter.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/converters/SendRecipientWalletListConverter.kt
index 94223cd153..c70c805b16 100644
--- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/converters/SendRecipientWalletListConverter.kt
+++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/converters/SendRecipientWalletListConverter.kt
@@ -1,6 +1,7 @@
package com.tangem.features.send.v2.subcomponents.destination.model.converters
import com.tangem.common.ui.account.AccountTitleUM
+import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.common.ui.account.toUM
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.models.currency.CryptoCurrency
@@ -58,7 +59,7 @@ internal class SendRecipientWalletListConverter(
accountTitleUM = if (account != null && isAccountsMode) {
AccountTitleUM.Account(
name = account.accountName.toUM().value,
- icon = account.icon.toUM(),
+ icon = CryptoPortfolioIconConverter.convert(account.icon),
prefixText = stringReference(StringsSigns.DOT),
)
} else {
diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/ui/ListItemWithIcon.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/ui/ListItemWithIcon.kt
index 484ac8b89a..51573ec74f 100644
--- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/ui/ListItemWithIcon.kt
+++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/ui/ListItemWithIcon.kt
@@ -27,7 +27,7 @@ import androidx.compose.ui.unit.dp
import com.tangem.common.ui.account.AccountNameUM
import com.tangem.common.ui.account.AccountTitle
import com.tangem.common.ui.account.AccountTitleUM
-import com.tangem.common.ui.account.toUM
+import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.core.ui.components.CircleShimmer
import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.atoms.text.EllipsisText
@@ -271,7 +271,7 @@ private class ListItemWithIconPreviewProvider : CollectionPreviewParameterProvid
subtitle = "Wallet",
accountTitleUM = AccountTitleUM.Account(
name = AccountNameUM.DefaultMain.value,
- icon = CryptoPortfolioIcon.ofDefaultCustomAccount().toUM(),
+ icon = CryptoPortfolioIconConverter.convert(CryptoPortfolioIcon.ofDefaultCustomAccount()),
prefixText = stringReference(StringsSigns.DOT),
),
),
diff --git a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SendWithSwapConfirmModel.kt b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SendWithSwapConfirmModel.kt
index 6f6b6b2c2e..66d822dd46 100644
--- a/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SendWithSwapConfirmModel.kt
+++ b/features/swap-v2/impl/src/main/java/com/tangem/features/swap/v2/impl/sendviaswap/confirm/model/SendWithSwapConfirmModel.kt
@@ -18,8 +18,11 @@ import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
+import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
+import com.tangem.core.ui.extensions.wrappedList
import com.tangem.domain.express.models.ExpressOperationType
+import com.tangem.domain.models.wallet.isHotWallet
import com.tangem.domain.express.models.ExpressProviderType
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
@@ -485,7 +488,9 @@ internal class SendWithSwapConfirmModel @Inject constructor(
it.second is SendWithSwapRoute.Confirm
}.onEach { (state, _) ->
val confirmUM = state.confirmUM
- val isReadyToSend = confirmUM is ConfirmUM.Content && !confirmUM.isTransactionInProcess
+ val isContent = confirmUM is ConfirmUM.Content
+ val isReadyToSend = isContent && !confirmUM.isTransactionInProcess
+ val isHoldToConfirm = params.userWallet.isHotWallet && isContent
params.callback.onResult(
route = SendWithSwapRoute.Confirm,
sendWithSwapUM = state.copy(
@@ -496,18 +501,11 @@ internal class SendWithSwapConfirmModel @Inject constructor(
backIconRes = R.drawable.ic_back_24,
backIconClick = router::pop,
primaryButton = NavigationButton(
- textReference = when (confirmUM) {
- is ConfirmUM.Success -> resourceReference(R.string.common_close)
- is ConfirmUM.Content -> if (confirmUM.isTransactionInProcess) {
- resourceReference(R.string.send_sending)
- } else {
- resourceReference(R.string.common_send)
- }
- else -> resourceReference(R.string.common_send)
- },
+ textReference = getPrimaryButtonText(confirmUM, isHoldToConfirm),
iconRes = walletInterationIcon(params.userWallet),
- isIconVisible = isReadyToSend,
+ isIconVisible = isReadyToSend && !isHoldToConfirm,
isHapticClick = isReadyToSend,
+ isHoldToConfirm = isHoldToConfirm,
isEnabled = confirmUM.isPrimaryButtonEnabled,
onClick = {
when (confirmUM) {
@@ -525,4 +523,17 @@ internal class SendWithSwapConfirmModel @Inject constructor(
)
}.launchIn(modelScope)
}
+
+ private fun getPrimaryButtonText(confirmUM: ConfirmUM, isHoldToConfirm: Boolean): TextReference {
+ return when {
+ isHoldToConfirm -> resourceReference(
+ id = com.tangem.core.ui.R.string.common_hold_to,
+ formatArgs = wrappedList(resourceReference(R.string.common_send)),
+ )
+ confirmUM is ConfirmUM.Success -> resourceReference(R.string.common_close)
+ confirmUM is ConfirmUM.Content && confirmUM.isTransactionInProcess ->
+ resourceReference(R.string.send_sending)
+ else -> resourceReference(R.string.common_send)
+ }
+ }
}
\ 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 217144c02a..a8b55e5af2 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
@@ -50,6 +50,7 @@ import com.tangem.domain.transaction.usecase.gasless.EstimateFeeForGaslessTxUseC
import com.tangem.domain.transaction.usecase.gasless.EstimateFeeForTokenUseCase
import com.tangem.domain.transaction.usecase.gasless.GetFeeForTokenUseCase
import com.tangem.domain.utils.convertToSdkAmount
+import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.feature.swap.domain.api.SwapRepository
import com.tangem.feature.swap.domain.models.ExpressDataError
@@ -58,8 +59,6 @@ import com.tangem.feature.swap.domain.models.domain.*
import com.tangem.feature.swap.domain.models.toStringWithRightOffset
import com.tangem.feature.swap.domain.models.ui.*
import com.tangem.lib.crypto.BlockchainUtils.SOLANA_TRANSACTION_SIZE_THRESHOLD_BYTES
-import com.tangem.lib.crypto.UserWalletManager
-import com.tangem.lib.crypto.models.ProxyAmount
import com.tangem.utils.coroutines.runSuspendCatching
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -72,7 +71,6 @@ import java.math.RoundingMode
@Suppress("LargeClass", "LongParameterList")
internal class SwapInteractorImpl @AssistedInject constructor(
- private val userWalletManager: UserWalletManager,
private val repository: SwapRepository,
private val allowPermissionsHandler: AllowPermissionsHandler,
private val getMultiCryptoCurrencyStatusUseCase: GetMultiCryptoCurrencyStatusUseCase,
@@ -107,6 +105,7 @@ internal class SwapInteractorImpl @AssistedInject constructor(
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
private val accountsFeatureToggles: AccountsFeatureToggles,
+ private val walletManagersFacade: WalletManagersFacade,
@Assisted private val userWalletId: UserWalletId,
) : SwapInteractor {
@@ -706,11 +705,13 @@ internal class SwapInteractorImpl @AssistedInject constructor(
)
when (feePaidCurrency) {
FeePaidCurrency.Coin -> {
- val nativeBalance = userWalletManager.getNativeTokenBalance(
+ val nativeBalance = walletManagersFacade.getNativeTokenBalance(
+ userWalletId = userWalletId,
networkId = fromTokenStatus.currency.network.backendId,
derivationPath = fromTokenStatus.currency.network.derivationPath.value,
)
- nativeBalance?.let { it.value - fee }
+
+ nativeBalance - fee
}
else -> null // it doesnt matter for this fun
}
@@ -1292,8 +1293,8 @@ internal class SwapInteractorImpl @AssistedInject constructor(
private suspend fun storeLastCryptoCurrencyId(cryptoCurrency: CryptoCurrency) {
swapTransactionRepository.storeLastSwappedCryptoCurrencyId(
- UserWalletId(userWalletManager.getWalletId()),
- cryptoCurrency.id,
+ userWalletId = userWalletId,
+ cryptoCurrencyId = cryptoCurrency.id,
)
}
@@ -1705,12 +1706,12 @@ internal class SwapInteractorImpl @AssistedInject constructor(
feeValue: BigDecimal,
fromToken: CryptoCurrency,
): IncludeFeeInAmount {
- val tokenForFeeBalance =
- userWalletManager.getNativeTokenBalance(
- networkId,
- fromToken.network.derivationPath.value,
- ) ?: ProxyAmount.empty()
- val reducedBalance = tokenForFeeBalance.value - reduceBalanceBy
+ val tokenForFeeBalance = walletManagersFacade.getNativeTokenBalance(
+ userWalletId = userWalletId,
+ networkId = networkId,
+ derivationPath = fromToken.network.derivationPath.value,
+ )
+ val reducedBalance = tokenForFeeBalance - reduceBalanceBy
val amountWithFee = amount.value + feeValue
return when {
fromToken is CryptoCurrency.Token -> {
@@ -1941,13 +1942,14 @@ internal class SwapInteractorImpl @AssistedInject constructor(
fromToken: CryptoCurrency,
selectedToken: CryptoCurrencyStatus? = null,
): Either = either {
- val nativeBalance = userWalletManager.getNativeTokenBalance(
+ val nativeBalance = walletManagersFacade.getNativeTokenBalance(
+ userWalletId = userWalletId,
networkId = network.backendId,
derivationPath = fromToken.network.derivationPath.value,
- ) ?: ProxyAmount.empty()
+ )
// if native balance is zero - we can't calculate fee
- if (nativeBalance.value.signum() == 0) {
+ if (nativeBalance.signum() == 0) {
raise(ExpressDataError.UnknownError)
}
@@ -1956,7 +1958,7 @@ internal class SwapInteractorImpl @AssistedInject constructor(
val amountToSend = createNativeAmountForDex(txAmountValue, fromToken.network)
// transaction.txValue is always native coin
- if (nativeBalance.value < amountToSend.value) {
+ if (nativeBalance < amountToSend.value) {
error("It's impossible to calculate fee for nativeBalance.value < amountToSend.value")
}
@@ -2105,7 +2107,6 @@ internal class SwapInteractorImpl @AssistedInject constructor(
permissionState = PermissionDataState.PermissionLoading,
)
}
- val derivationPath = fromToken.network.derivationPath.value
// setting up amount for approve with given amount for swap [SwapApproveType.Limited]
val callData = SmartContractCallDataProviderFactory.getApprovalCallData(
spenderAddress = requireNotNull(spenderAddress) { "spenderAddress cant be null" },
@@ -2164,7 +2165,7 @@ internal class SwapInteractorImpl @AssistedInject constructor(
permissionState = PermissionDataState.PermissionReadyForRequest(
currency = fromToken.symbol,
amount = INFINITY_SYMBOL,
- walletAddress = getWalletAddress(networkId, derivationPath),
+ walletAddress = getWalletAddress(fromToken.network),
spenderAddress = getTokenAddress(fromToken),
requestApproveData = RequestApproveStateData(
fee = feeState,
@@ -2403,8 +2404,9 @@ internal class SwapInteractorImpl @AssistedInject constructor(
)
}
- private suspend fun getWalletAddress(networkId: String, derivationPath: String?): String {
- return userWalletManager.getWalletAddress(networkId, derivationPath)
+ private suspend fun getWalletAddress(network: Network): String {
+ return walletManagersFacade.getDefaultAddress(userWalletId, network)
+ ?: error("Address not found for network: ${network.id}")
}
private fun getTokenAddress(currency: CryptoCurrency): String {
@@ -2436,31 +2438,29 @@ internal class SwapInteractorImpl @AssistedInject constructor(
val percentsToFeeIncrease = BigDecimal.ONE
return when (val feePaidCurrency = getFeePaidCurrency(fromTokenStatus.currency)) {
FeePaidCurrency.Coin -> {
- val nativeTokenBalance = userWalletManager.getNativeTokenBalance(
- networkId,
- fromTokenStatus.currency.network.derivationPath.value,
+ val nativeTokenBalance = walletManagersFacade.getNativeTokenBalance(
+ userWalletId = userWalletId,
+ networkId = networkId,
+ derivationPath = fromTokenStatus.currency.network.derivationPath.value,
)
- nativeTokenBalance?.let { balance ->
- val balanceToCheck = when (fromTokenStatus.currency) {
- is CryptoCurrency.Token -> {
- balance.value
- }
- is CryptoCurrency.Coin -> {
- // need to check balance minus amount only if amount to swap in native token
- balance.value.minus(spendAmount.value)
- }
+
+ val balanceToCheck = when (fromTokenStatus.currency) {
+ is CryptoCurrency.Token -> nativeTokenBalance
+ is CryptoCurrency.Coin -> {
+ // need to check balance minus amount only if amount to swap in native token
+ nativeTokenBalance.minus(spendAmount.value)
}
- if (balanceToCheck > fee.multiply(percentsToFeeIncrease)) {
- SwapFeeState.Enough
- } else {
- val nativeToken = getNativeToken(fromTokenStatus.currency.network.backendId)
- SwapFeeState.NotEnough(
- feeCurrency = nativeToken,
- currencyName = nativeToken.network.name,
- currencySymbol = nativeToken.symbol,
- )
- }
- } ?: SwapFeeState.NotEnough()
+ }
+ if (balanceToCheck > fee.multiply(percentsToFeeIncrease)) {
+ SwapFeeState.Enough
+ } else {
+ val nativeToken = getNativeToken(fromTokenStatus.currency.network.backendId)
+ SwapFeeState.NotEnough(
+ feeCurrency = nativeToken,
+ currencyName = nativeToken.network.name,
+ currencySymbol = nativeToken.symbol,
+ )
+ }
}
FeePaidCurrency.SameCurrency -> {
val balance = fromTokenStatus.value.amount ?: return SwapFeeState.NotEnough()
diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt
index a007a6697f..8f5bd07b10 100644
--- a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt
+++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt
@@ -104,6 +104,7 @@ import com.tangem.utils.TangemBlogUrlBuilder.RESOURCE_TO_LEARN_ABOUT_APPROVING_I
import com.tangem.utils.coroutines.*
import com.tangem.utils.isNullOrZero
import kotlinx.coroutines.NonCancellable
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -957,6 +958,10 @@ internal class SwapModel @Inject constructor(
if (fee == null && tangemPayInput?.isWithdrawal != true) {
makeDefaultAlert(resourceReference(R.string.swapping_fee_estimation_error_text))
+ modelScope.launch {
+ delay(SWAP_IN_PROGRESS_DELAY)
+ startLoadingQuotesFromLastState()
+ }
return
}
modelScope.launch(dispatchers.main) {
@@ -2250,6 +2255,7 @@ internal class SwapModel @Inject constructor(
const val DEBOUNCE_AMOUNT_DELAY = 1000L
const val DEBOUNCE_SEARCH_DELAY = 500L
const val UPDATE_BALANCE_DELAY_MILLIS = 11000L
+ const val SWAP_IN_PROGRESS_DELAY = 200L
const val CHANGELLY_PROVIDER_ID = "changelly"
}
}
\ No newline at end of file
diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt
index f37d7dd2fe..47517e2801 100644
--- a/features/swap/impl/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt
+++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/models/SwapStateHolder.kt
@@ -79,6 +79,7 @@ data class SwapButton(
@DrawableRes val walletInteractionIcon: Int?,
val isEnabled: Boolean,
val isInProgress: Boolean = false,
+ val isHoldToConfirm: Boolean = false,
val onClick: () -> Unit,
)
diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/preview/SwapSuccessStatePreview.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/preview/SwapSuccessStatePreview.kt
index 77c343f43b..df255cc346 100644
--- a/features/swap/impl/src/main/java/com/tangem/feature/swap/preview/SwapSuccessStatePreview.kt
+++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/preview/SwapSuccessStatePreview.kt
@@ -2,7 +2,7 @@ package com.tangem.feature.swap.preview
import com.tangem.common.ui.account.AccountNameUM
import com.tangem.common.ui.account.AccountTitleUM
-import com.tangem.common.ui.account.toUM
+import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.core.ui.components.currency.icon.CurrencyIconState
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.stringReference
@@ -22,12 +22,12 @@ internal data object SwapSuccessStatePreview {
fromTitle = AccountTitleUM.Account(
prefixText = stringReference("From"),
name = AccountNameUM.DefaultMain.value,
- icon = CryptoPortfolioIcon.ofDefaultCustomAccount().toUM(),
+ icon = CryptoPortfolioIconConverter.convert(CryptoPortfolioIcon.ofDefaultCustomAccount()),
),
toTitle = AccountTitleUM.Account(
prefixText = stringReference("To"),
name = AccountNameUM.DefaultMain.value,
- icon = CryptoPortfolioIcon.ofDefaultCustomAccount().toUM(),
+ icon = CryptoPortfolioIconConverter.convert(CryptoPortfolioIcon.ofDefaultCustomAccount()),
),
fromTokenAmount = TextReference.Str("1 000 DAI"),
toTokenAmount = TextReference.Str("1 000 MATIC"),
diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt
index 33bf20ea2b..f43c1f4caa 100644
--- a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt
+++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt
@@ -3,6 +3,7 @@ package com.tangem.feature.swap.ui
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.TextFieldValue
import com.tangem.common.ui.account.AccountTitleUM
+import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.common.ui.account.toUM
import com.tangem.common.ui.alerts.models.AlertDemoModeUM
import com.tangem.common.ui.bottomsheet.permission.state.*
@@ -24,6 +25,7 @@ import com.tangem.domain.models.account.Account
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
+import com.tangem.domain.models.wallet.isHotWallet
import com.tangem.domain.promo.models.StoryContent
import com.tangem.domain.transaction.usecase.gasless.IsGaslessFeeSupportedForNetwork
import com.tangem.feature.swap.converters.SwapTransactionErrorStateConverter
@@ -125,6 +127,7 @@ internal class StateBuilder(
swapButton = SwapButton(
walletInteractionIcon = walletInterationIcon(userWalletProvider()),
isEnabled = false,
+ isHoldToConfirm = userWalletProvider().isHotWallet,
onClick = {},
),
onRefresh = {},
@@ -184,6 +187,7 @@ internal class StateBuilder(
swapButton = SwapButton(
walletInteractionIcon = walletInterationIcon(userWalletProvider()),
isEnabled = false,
+ isHoldToConfirm = userWalletProvider().isHotWallet,
onClick = { },
),
changeCardsButtonState = ChangeCardsButtonState.DISABLED,
@@ -249,6 +253,7 @@ internal class StateBuilder(
swapButton = SwapButton(
walletInteractionIcon = walletInterationIcon(userWalletProvider()),
isEnabled = false,
+ isHoldToConfirm = userWalletProvider().isHotWallet,
onClick = {},
),
providerState = ProviderState.Loading(),
@@ -371,6 +376,7 @@ internal class StateBuilder(
swapButton = SwapButton(
walletInteractionIcon = walletInterationIcon(userWalletProvider()),
isEnabled = getSwapButtonEnabled(notifications),
+ isHoldToConfirm = userWalletProvider().isHotWallet,
onClick = actions.onSwapClick,
),
changeCardsButtonState = getChangeCardsButtonState(isReverseSwapPossible),
@@ -499,6 +505,7 @@ internal class StateBuilder(
swapButton = SwapButton(
walletInteractionIcon = walletInterationIcon(userWalletProvider()),
isEnabled = false,
+ isHoldToConfirm = userWalletProvider().isHotWallet,
onClick = actions.onSwapClick,
),
changeCardsButtonState = getChangeCardsButtonState(isReverseSwapPossible),
@@ -596,6 +603,7 @@ internal class StateBuilder(
swapButton = SwapButton(
walletInteractionIcon = walletInterationIcon(userWalletProvider()),
isEnabled = false,
+ isHoldToConfirm = userWalletProvider().isHotWallet,
onClick = { },
),
changeCardsButtonState = getChangeCardsButtonState(isReverseSwapPossible),
@@ -1421,7 +1429,7 @@ internal class StateBuilder(
AccountTitleUM.Account(
prefixText = resourceReference(R.string.common_from),
name = fromAccount.accountName.toUM().value,
- icon = fromAccount.icon.toUM(),
+ icon = CryptoPortfolioIconConverter.convert(fromAccount.icon),
)
} else {
AccountTitleUM.Text(resourceReference(R.string.swapping_from_title))
@@ -1433,7 +1441,7 @@ internal class StateBuilder(
AccountTitleUM.Account(
prefixText = resourceReference(R.string.common_to),
name = toAccount.accountName.toUM().value,
- icon = toAccount.icon.toUM(),
+ icon = CryptoPortfolioIconConverter.convert(toAccount.icon),
)
} else {
AccountTitleUM.Text(resourceReference(R.string.swapping_to_title))
diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt
index a1cb85f872..e55c8398bf 100644
--- a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt
+++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/SwapScreenContent.kt
@@ -370,25 +370,42 @@ private fun SwapNotifications(notifications: List) {
@Composable
private fun MainButton(state: SwapStateHolder) {
- if (state.isInsufficientFunds) {
- PrimaryButton(
- modifier = Modifier.fillMaxWidth(),
- text = stringResourceSafe(id = R.string.swapping_insufficient_funds),
- enabled = false,
- onClick = state.swapButton.onClick,
- )
- } else {
- PrimaryButtonIconEnd(
- modifier = Modifier.fillMaxWidth(),
- text = if (state.swapButton.isInProgress) {
- stringResourceSafe(id = R.string.swapping_swap_action_in_progress)
- } else {
- stringResourceSafe(id = R.string.swapping_swap_action)
- },
- iconResId = state.swapButton.walletInteractionIcon,
- enabled = state.swapButton.isEnabled,
- onClick = state.swapButton.onClick,
- )
+ when {
+ state.isInsufficientFunds -> {
+ PrimaryButton(
+ modifier = Modifier.fillMaxWidth(),
+ text = stringResourceSafe(id = R.string.swapping_insufficient_funds),
+ enabled = false,
+ onClick = state.swapButton.onClick,
+ )
+ }
+
+ state.swapButton.isHoldToConfirm -> {
+ HoldToConfirmButton(
+ modifier = Modifier.fillMaxWidth(),
+ text = stringResourceSafe(
+ R.string.common_hold_to,
+ stringResourceSafe(id = R.string.swapping_swap_action),
+ ),
+ enabled = state.swapButton.isEnabled,
+ onConfirm = state.swapButton.onClick,
+ isLoading = state.swapButton.isInProgress,
+ )
+ }
+
+ else -> {
+ PrimaryButtonIconEnd(
+ modifier = Modifier.fillMaxWidth(),
+ text = if (state.swapButton.isInProgress) {
+ stringResourceSafe(id = R.string.swapping_swap_action_in_progress)
+ } else {
+ stringResourceSafe(id = R.string.swapping_swap_action)
+ },
+ iconResId = state.swapButton.walletInteractionIcon,
+ enabled = state.swapButton.isEnabled,
+ onClick = state.swapButton.onClick,
+ )
+ }
}
}
diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt
index 98ad517f01..373d309f0f 100644
--- a/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt
+++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/ui/TransactionCard.kt
@@ -38,7 +38,7 @@ import coil.request.ImageRequest
import com.tangem.common.ui.account.AccountNameUM
import com.tangem.common.ui.account.AccountTitle
import com.tangem.common.ui.account.AccountTitleUM
-import com.tangem.common.ui.account.toUM
+import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.core.ui.R
import com.tangem.core.ui.components.*
import com.tangem.core.ui.extensions.resourceReference
@@ -604,7 +604,7 @@ private fun TransactionCardPreviewWithPriceImpact() {
accountTitleUM = AccountTitleUM.Account(
prefixText = resourceReference(R.string.common_from),
name = AccountNameUM.DefaultMain.value,
- icon = CryptoPortfolioIcon.ofDefaultCustomAccount().toUM(),
+ icon = CryptoPortfolioIconConverter.convert(CryptoPortfolioIcon.ofDefaultCustomAccount()),
),
),
amountEquivalent = "1 000 000",
@@ -626,7 +626,7 @@ private fun TransactionCardPreviewWithoutPriceImpact() {
accountTitleUM = AccountTitleUM.Account(
prefixText = resourceReference(R.string.common_from),
name = AccountNameUM.DefaultMain.value,
- icon = CryptoPortfolioIcon.ofDefaultCustomAccount().toUM(),
+ icon = CryptoPortfolioIconConverter.convert(CryptoPortfolioIcon.ofDefaultCustomAccount()),
),
),
amountEquivalent = "1 000 000",
diff --git a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/actions/TesterActionsViewModel.kt b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/actions/TesterActionsViewModel.kt
index 1c351420c7..599f82475f 100644
--- a/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/actions/TesterActionsViewModel.kt
+++ b/features/tester/impl/src/main/java/com/tangem/feature/tester/presentation/actions/TesterActionsViewModel.kt
@@ -6,14 +6,15 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import arrow.core.getOrElse
+import com.tangem.data.common.account.WalletAccountsSaver
import com.tangem.domain.apptheme.ChangeAppThemeModeUseCase
import com.tangem.domain.apptheme.GetAppThemeModeUseCase
import com.tangem.domain.apptheme.model.AppThemeMode
+import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.feedback.repository.FeedbackRepository
import com.tangem.feature.tester.presentation.actions.TesterActionsContentState.HideAllCurrenciesUM
import com.tangem.feature.tester.presentation.actions.TesterActionsContentState.ToggleAppThemeUM
import com.tangem.feature.tester.presentation.navigation.InnerTesterRouter
-import com.tangem.lib.crypto.UserWalletManager
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn
@@ -24,10 +25,11 @@ import javax.inject.Inject
@HiltViewModel
internal class TesterActionsViewModel @Inject constructor(
- private val userWalletManager: UserWalletManager,
private val changeAppThemeModeUseCase: ChangeAppThemeModeUseCase,
private val getAppThemeModeUseCase: GetAppThemeModeUseCase,
private val feedbackRepository: FeedbackRepository,
+ private val userWalletsListRepository: UserWalletsListRepository,
+ private val walletAccountsSaver: WalletAccountsSaver,
) : ViewModel() {
var uiState: TesterActionsContentState by mutableStateOf(initialState)
@@ -56,7 +58,20 @@ internal class TesterActionsViewModel @Inject constructor(
uiState = uiState.copy(
hideAllCurrenciesUM = HideAllCurrenciesUM.Progress,
)
- userWalletManager.hideAllTokens()
+
+ val userWalletId = userWalletsListRepository.selectedUserWalletSync()?.walletId
+
+ if (userWalletId != null) {
+ walletAccountsSaver.update(userWalletId = userWalletId) { response ->
+ response ?: return@update response
+
+ response.copy(
+ accounts = response.accounts.map { accountDTO ->
+ accountDTO.copy(tokens = emptyList())
+ },
+ )
+ }
+ }
uiState = uiState.copy(
hideAllCurrenciesUM = HideAllCurrenciesUM.Clickable(this@TesterActionsViewModel::hideAllCurrencies),
diff --git a/features/token-recieve/api/src/main/kotlin/com/tangem/features/tokenreceive/TokenReceiveFeatureToggle.kt b/features/token-recieve/api/src/main/kotlin/com/tangem/features/tokenreceive/TokenReceiveFeatureToggle.kt
deleted file mode 100644
index 62175072dd..0000000000
--- a/features/token-recieve/api/src/main/kotlin/com/tangem/features/tokenreceive/TokenReceiveFeatureToggle.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.tangem.features.tokenreceive
-
-interface TokenReceiveFeatureToggle {
-
- val isNewTokenReceiveEnabled: Boolean
-}
\ No newline at end of file
diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/DefaultTokenReceiveFeatureToggle.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/DefaultTokenReceiveFeatureToggle.kt
deleted file mode 100644
index 209317d97b..0000000000
--- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/DefaultTokenReceiveFeatureToggle.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.tangem.features.tokenreceive
-
-import com.tangem.core.configtoggle.feature.FeatureTogglesManager
-
-internal class DefaultTokenReceiveFeatureToggle(
- private val featureTogglesManager: FeatureTogglesManager,
-) : TokenReceiveFeatureToggle {
-
- override val isNewTokenReceiveEnabled: Boolean
- get() = featureTogglesManager.isFeatureEnabled("NEW_TOKEN_RECEIVE_ENABLED")
-}
\ No newline at end of file
diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/di/TokenReceiveModule.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/di/TokenReceiveModule.kt
index 44cfdf6d8b..1b1f5035e2 100644
--- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/di/TokenReceiveModule.kt
+++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/di/TokenReceiveModule.kt
@@ -1,11 +1,8 @@
package com.tangem.features.tokenreceive.di
-import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.core.decompose.di.ModelComponent
import com.tangem.core.decompose.model.Model
-import com.tangem.features.tokenreceive.DefaultTokenReceiveFeatureToggle
import com.tangem.features.tokenreceive.TokenReceiveComponent
-import com.tangem.features.tokenreceive.TokenReceiveFeatureToggle
import com.tangem.features.tokenreceive.component.DefaultTokenReceiveComponent
import com.tangem.features.tokenreceive.model.TokenReceiveAssetsModel
import com.tangem.features.tokenreceive.model.TokenReceiveModel
@@ -13,26 +10,12 @@ import com.tangem.features.tokenreceive.model.TokenReceiveQrCodeModel
import com.tangem.features.tokenreceive.model.TokenReceiveWarningModel
import dagger.Binds
import dagger.Module
-import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
import javax.inject.Singleton
-@Module
-@InstallIn(SingletonComponent::class)
-internal object FeatureToggleModule {
-
- @Provides
- @Singleton
- fun provideTokenReceiveFeatureToggle(featureTogglesManager: FeatureTogglesManager): TokenReceiveFeatureToggle {
- return DefaultTokenReceiveFeatureToggle(
- featureTogglesManager = featureTogglesManager,
- )
- }
-}
-
@Module
@InstallIn(SingletonComponent::class)
internal interface ComponentModule {
diff --git a/features/tokendetails/impl/build.gradle.kts b/features/tokendetails/impl/build.gradle.kts
index 05ec40ad40..10cb869ad1 100644
--- a/features/tokendetails/impl/build.gradle.kts
+++ b/features/tokendetails/impl/build.gradle.kts
@@ -48,6 +48,7 @@ dependencies {
/** Core modules */
implementation(projects.common.routing)
implementation(projects.core.navigation)
+ implementation(projects.core.res)
implementation(projects.core.ui)
implementation(projects.core.utils)
implementation(projects.core.analytics)
diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/CloreMigrationModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/CloreMigrationModel.kt
new file mode 100644
index 0000000000..51e1125b6b
--- /dev/null
+++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/CloreMigrationModel.kt
@@ -0,0 +1,113 @@
+package com.tangem.feature.tokendetails.presentation.tokendetails.model
+
+import com.tangem.core.decompose.ui.UiMessageSender
+import com.tangem.core.ui.clipboard.ClipboardManager
+import com.tangem.core.ui.extensions.resourceReference
+import com.tangem.core.ui.extensions.stringReference
+import com.tangem.core.ui.message.SnackbarMessage
+import com.tangem.domain.models.currency.CryptoCurrency
+import com.tangem.domain.models.wallet.UserWallet
+import com.tangem.domain.transaction.error.SignCloreMessageError
+import com.tangem.domain.transaction.usecase.SignCloreMessageUseCase
+import com.tangem.feature.tokendetails.presentation.router.InnerTokenDetailsRouter
+import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
+import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.TokenDetailsStateFactory
+import com.tangem.features.tokendetails.impl.R
+import com.tangem.utils.coroutines.CoroutineDispatcherProvider
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.launch
+
+// TODO: Remove after Clore migration ends ([REDACTED_TASK_KEY])
+@Suppress("LongParameterList")
+internal class CloreMigrationModel(
+ private val stateFactory: TokenDetailsStateFactory,
+ private val signCloreMessageUseCase: SignCloreMessageUseCase,
+ private val clipboardManager: ClipboardManager,
+ private val uiMessageSender: UiMessageSender,
+ private val router: InnerTokenDetailsRouter,
+ private val userWallet: UserWallet,
+ private val cryptoCurrency: CryptoCurrency,
+ private val coroutineScope: CoroutineScope,
+ private val dispatchers: CoroutineDispatcherProvider,
+ private val onStateUpdate: (TokenDetailsState) -> Unit,
+) {
+
+ private val state = MutableStateFlow(CloreMigrationState())
+
+ fun onCloreMigrationClick() {
+ state.value = CloreMigrationState()
+ updateBottomSheet()
+ }
+
+ fun onCloreSignMessage(message: String) {
+ if (message.isBlank()) return
+
+ state.value = state.value.copy(message = message)
+ updateBottomSheet(isSigningInProgress = true)
+
+ coroutineScope.launch(dispatchers.main) {
+ signCloreMessageUseCase(
+ userWallet = userWallet,
+ currency = cryptoCurrency,
+ message = message,
+ ).fold(
+ ifLeft = { error ->
+ val errorMessage = when (error) {
+ is SignCloreMessageError.SigningFailed -> stringReference(error.message)
+ SignCloreMessageError.MessageSigningNotSupported ->
+ resourceReference(R.string.warning_clore_migration_error_signing_not_supported)
+ SignCloreMessageError.WalletManagerNotFound ->
+ resourceReference(R.string.warning_clore_migration_error_wallet_manager_not_found)
+ }
+ state.value = state.value.copy(signature = "")
+ updateBottomSheet()
+ uiMessageSender.send(SnackbarMessage(errorMessage))
+ },
+ ifRight = { signature ->
+ state.value = state.value.copy(signature = signature)
+ onStateUpdate(stateFactory.getStateWithUpdatedCloreMigrationSignature(signature))
+ },
+ )
+ }
+ }
+
+ fun onOpenCloreClaimPortal() {
+ router.openUrl(CLAIM_PORTAL_URL)
+ }
+
+ private fun updateBottomSheet(isSigningInProgress: Boolean = false) {
+ val currentState = state.value
+ onStateUpdate(
+ stateFactory.getStateWithCloreMigrationBottomSheet(
+ message = currentState.message,
+ signature = currentState.signature,
+ isSigningInProgress = isSigningInProgress,
+ onMessageChange = { newMessage ->
+ state.value = state.value.copy(message = newMessage)
+ updateBottomSheet()
+ },
+ onSignClick = { onCloreSignMessage(state.value.message) },
+ onCopyClick = {
+ val signature = state.value.signature
+ if (signature.isNotBlank()) {
+ clipboardManager.setText(text = signature, isSensitive = false)
+ uiMessageSender.send(
+ SnackbarMessage(resourceReference(R.string.wallet_notification_address_copied)),
+ )
+ }
+ },
+ onOpenPortalClick = { onOpenCloreClaimPortal() },
+ ),
+ )
+ }
+
+ private companion object {
+ const val CLAIM_PORTAL_URL = "https://claim-portal.clore.ai/"
+ }
+}
+
+private data class CloreMigrationState(
+ val message: String = "",
+ val signature: String = "",
+)
\ No newline at end of file
diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsClickIntents.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsClickIntents.kt
index bf41cabfe1..bc6d1d4b7e 100644
--- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsClickIntents.kt
+++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsClickIntents.kt
@@ -64,7 +64,26 @@ interface TokenDetailsClickIntents {
fun onBalanceSelect(config: TokenBalanceSegmentedButtonConfig)
+ fun onGoToRefundedTokenClick(cryptoCurrency: CryptoCurrency)
+
+ fun onOpenUrlClick(url: String)
+
+ fun onConfirmDisposeExpressStatus()
+
+ fun onDisposeExpressStatus()
+
fun onYieldInfoClick()
+
+ // region Clore migration
+ // TODO: Remove after 2025-04-01 when Clore migration ends ([REDACTED_TASK_KEY])
+
+ fun onCloreMigrationClick()
+
+ fun onCloreSignMessage(message: String)
+
+ fun onOpenCloreClaimPortal()
+
+ // endregion Clore migration
}
interface ExpressTransactionsClickIntents {
@@ -146,4 +165,23 @@ internal class EmptyTokenDetailsClickIntents : TokenDetailsClickIntents {
/* no op */
return null
}
+
+ override fun onGoToRefundedTokenClick(cryptoCurrency: CryptoCurrency) { /* no op */ }
+
+ override fun onOpenUrlClick(url: String) { /* no op */ }
+
+ override fun onConfirmDisposeExpressStatus() { /* no op */ }
+
+ override fun onDisposeExpressStatus() { /* no op */ }
+
+ // region Clore migration
+ // TODO: Remove after 2025-04-01 when Clore migration ends ([REDACTED_TASK_KEY])
+
+ override fun onCloreMigrationClick() { /* no op */ }
+
+ override fun onCloreSignMessage(message: String) { /* no op */ }
+
+ override fun onOpenCloreClaimPortal() { /* no op */ }
+
+ // endregion Clore migration
}
\ No newline at end of file
diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt
index 6a929eb75f..a137a93bf5 100644
--- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt
+++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt
@@ -23,7 +23,6 @@ import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.ui.UiMessageSender
-import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.ui.clipboard.ClipboardManager
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
@@ -59,7 +58,10 @@ import com.tangem.domain.tokens.*
import com.tangem.domain.tokens.legacy.TradeCryptoAction
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
import com.tangem.domain.tokens.model.TokenActionsState
-import com.tangem.domain.tokens.model.analytics.*
+import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent
+import com.tangem.domain.tokens.model.analytics.TokenReceiveCopyActionSource
+import com.tangem.domain.tokens.model.analytics.TokenReceiveNewAnalyticsEvent
+import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent
import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent.Companion.toReasonAnalyticsText
import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent.DetailsScreenOpened.TokenBalance
import com.tangem.domain.tokens.model.details.NavigationAction
@@ -87,7 +89,6 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.T
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.express.ExpressStatusFactory
import com.tangem.features.tokendetails.TokenDetailsComponent
import com.tangem.features.tokendetails.impl.R
-import com.tangem.features.tokenreceive.TokenReceiveFeatureToggle
import com.tangem.features.txhistory.entity.TxHistoryContentUpdateEmitter
import com.tangem.features.yield.supply.api.YieldSupplyDepositedWarningComponent
import com.tangem.features.yield.supply.api.YieldSupplyFeatureToggles
@@ -133,7 +134,6 @@ internal class TokenDetailsModel @Inject constructor(
private val analyticsEventsHandler: AnalyticsEventHandler,
private val vibratorHapticManager: VibratorHapticManager,
private val clipboardManager: ClipboardManager,
- private val shareManager: ShareManager,
@GlobalUiMessageSender private val uiMessageSender: UiMessageSender,
private val txHistoryContentUpdateEmitter: TxHistoryContentUpdateEmitter,
paramsContainer: ParamsContainer,
@@ -143,7 +143,6 @@ internal class TokenDetailsModel @Inject constructor(
private val router: InnerTokenDetailsRouter,
private val tokenDetailsDeepLinkActionListener: TokenDetailsDeepLinkActionListener,
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
- private val tokenReceiveFeatureToggle: TokenReceiveFeatureToggle,
private val receiveAddressesFactory: ReceiveAddressesFactory,
private val yieldSupplyFeatureToggles: YieldSupplyFeatureToggles,
private val saveViewedYieldSupplyWarningUseCase: SaveViewedYieldSupplyWarningUseCase,
@@ -153,6 +152,7 @@ internal class TokenDetailsModel @Inject constructor(
private val getAccountCryptoCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase,
private val manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
private val yieldSupplyGetRewardsBalanceUseCase: YieldSupplyGetRewardsBalanceUseCase,
+ private val signCloreMessageUseCase: SignCloreMessageUseCase,
) : Model(),
TokenDetailsClickIntents,
ExpressTransactionsClickIntents,
@@ -162,7 +162,8 @@ internal class TokenDetailsModel @Inject constructor(
private val userWalletId: UserWalletId = params.userWalletId
private val cryptoCurrency: CryptoCurrency = params.currency
- private val userWallet: UserWallet = getUserWalletUseCase(userWalletId).getOrNull() ?: error("UserWallet not found")
+ private val userWallet: UserWallet = getUserWalletUseCase(userWalletId).getOrNull()
+ ?: error("UserWallet not found")
private val marketPriceJobHolder = JobHolder()
private val refreshStateJobHolder = JobHolder()
@@ -195,6 +196,27 @@ internal class TokenDetailsModel @Inject constructor(
yieldSupplyFeatureToggles = yieldSupplyFeatureToggles,
)
+ private val internalUiState = MutableStateFlow(stateFactory.getInitialState(cryptoCurrency))
+ val uiState: StateFlow = internalUiState
+
+ // region Clore migration
+ // TODO: Remove after Clore migration ends ([REDACTED_TASK_KEY])
+ private val cloreMigrationModel by lazy(mode = LazyThreadSafetyMode.NONE) {
+ CloreMigrationModel(
+ stateFactory = stateFactory,
+ signCloreMessageUseCase = signCloreMessageUseCase,
+ clipboardManager = clipboardManager,
+ uiMessageSender = uiMessageSender,
+ router = router,
+ userWallet = userWallet,
+ cryptoCurrency = cryptoCurrency,
+ coroutineScope = modelScope,
+ dispatchers = dispatchers,
+ onStateUpdate = { internalUiState.value = it },
+ )
+ }
+ // endregion
+
private val expressStatusFactory by lazy(mode = LazyThreadSafetyMode.NONE) {
expressStatusFactory.create(
clickIntents = this,
@@ -217,9 +239,6 @@ internal class TokenDetailsModel @Inject constructor(
TokenDetailsCurrencyStatusAnalyticsSender(analyticsEventsHandler)
}
- private val internalUiState = MutableStateFlow(stateFactory.getInitialState(cryptoCurrency))
- val uiState: StateFlow = internalUiState
-
init {
updateTopBarMenu()
initButtons()
@@ -668,9 +687,8 @@ internal class TokenDetailsModel @Inject constructor(
vibratorHapticManager.performOneTime(TangemHapticEffect.OneTime.Click)
clipboardManager.setText(text = extendedKey, isSensitive = true)
- uiMessageSender.send(
- message = SnackbarMessage(message = resourceReference(R.string.wallet_notification_address_copied)),
- )
+ val message = resourceReference(R.string.wallet_notification_address_copied)
+ uiMessageSender.send(message = SnackbarMessage(message = message))
}
}
}
@@ -787,7 +805,8 @@ internal class TokenDetailsModel @Inject constructor(
modelScope.launch(dispatchers.main) {
when (val addresses = currencyStatus.value.networkAddress) {
is NetworkAddress.Selectable -> {
- internalUiState.value = stateFactory.getStateWithChooseAddressBottomSheet(cryptoCurrency, addresses)
+ internalUiState.value =
+ stateFactory.getStateWithChooseAddressBottomSheet(cryptoCurrency, addresses)
}
is NetworkAddress.Single -> {
router.openUrl(
@@ -1250,37 +1269,33 @@ internal class TokenDetailsModel @Inject constructor(
}
private fun navigateToReceive() {
- val networkAddress = cryptoCurrencyStatus?.value?.networkAddress ?: return
- if (tokenReceiveFeatureToggle.isNewTokenReceiveEnabled) {
- modelScope.launch {
- configureReceiveAddresses(cryptoCurrencyStatus = cryptoCurrencyStatus)
- ?.let { bottomSheetNavigation.activate(it) }
- }
- } else {
- analyticsEventsHandler.send(TokenReceiveAnalyticsEvent.ReceiveScreenOpened(cryptoCurrency.symbol))
- internalUiState.value = stateFactory.getStateWithReceiveBottomSheet(
- currency = cryptoCurrency,
- networkAddress = networkAddress,
- onCopyClick = {
- analyticsEventsHandler.send(TokenReceiveAnalyticsEvent.ButtonCopyAddress(cryptoCurrency.symbol))
- clipboardManager.setText(text = it, isSensitive = true)
- },
- onShareClick = {
- analyticsEventsHandler.send(
- TokenReceiveAnalyticsEvent.ButtonShareAddress(cryptoCurrency.symbol),
- )
- shareManager.shareText(text = it)
- },
- )
+ modelScope.launch {
+ configureReceiveAddresses(cryptoCurrencyStatus = cryptoCurrencyStatus)
+ ?.let { bottomSheetNavigation.activate(it) }
}
}
private fun handleNavigationParam() {
- if (params.navigationAction is NavigationAction.Staking) {
- openStaking()
+ when (params.navigationAction) {
+ is NavigationAction.Staking -> openStaking()
+ is NavigationAction.CloreMigration -> onCloreMigrationClick()
+ is NavigationAction.YieldSupply,
+ null,
+ -> Unit
}
}
+ // region Clore migration
+ // TODO: Remove after Clore migration ends ([REDACTED_TASK_KEY])
+
+ override fun onCloreMigrationClick() = cloreMigrationModel.onCloreMigrationClick()
+
+ override fun onCloreSignMessage(message: String) = cloreMigrationModel.onCloreSignMessage(message)
+
+ override fun onOpenCloreClaimPortal() = cloreMigrationModel.onOpenCloreClaimPortal()
+
+ // endregion Clore migration
+
private companion object {
const val EXPRESS_STATUS_UPDATE_DELAY = 10_000L
}
diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/TokenDetailsNotification.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/TokenDetailsNotification.kt
index 5733db0e18..feb6279306 100644
--- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/TokenDetailsNotification.kt
+++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/components/TokenDetailsNotification.kt
@@ -258,9 +258,15 @@ internal sealed class TokenDetailsNotification(val config: NotificationConfig) {
subtitle = resourceReference(id = R.string.warning_matic_migration_message),
)
- data object MigrationClore : Warning(
+ data class MigrationClore(
+ private val onMigrationClick: () -> Unit,
+ ) : Warning(
title = resourceReference(id = R.string.warning_clore_migration_title),
- subtitle = resourceReference(id = R.string.warning_clore_migration_message),
+ subtitle = resourceReference(id = R.string.warning_clore_migration_description),
+ buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
+ text = resourceReference(id = R.string.warning_clore_migration_button),
+ onClick = onMigrationClick,
+ ),
)
data object UsedOutdatedData : TokenDetailsNotification(
diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsNotificationConverter.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsNotificationConverter.kt
index eb54aca878..38588b2f69 100644
--- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsNotificationConverter.kt
+++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsNotificationConverter.kt
@@ -155,7 +155,9 @@ internal class TokenDetailsNotificationConverter(
},
)
is CryptoCurrencyWarning.MigrationMaticToPol -> MigrationMaticToPol
- is CryptoCurrencyWarning.MigrationClore -> MigrationClore
+ is CryptoCurrencyWarning.MigrationClore -> MigrationClore(
+ onMigrationClick = clickIntents::onCloreMigrationClick,
+ )
is CryptoCurrencyWarning.UsedOutdatedDataWarning -> UsedOutdatedData
}
}
diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt
index 87552d058e..323b323e72 100644
--- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt
+++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/factory/TokenDetailsStateFactory.kt
@@ -2,9 +2,9 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory
import arrow.core.Either
import com.tangem.common.ui.bottomsheet.chooseaddress.ChooseAddressBottomSheetConfig
-import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheetConfig
import com.tangem.common.ui.tokendetails.TokenDetailsDialogConfig
import com.tangem.common.ui.tokens.getUnavailabilityReasonText
+import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.clore.CloreMigrationBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.dropdownmenu.TangemDropdownMenuItem
import com.tangem.core.ui.extensions.TextReference
@@ -15,7 +15,6 @@ import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
-import com.tangem.domain.models.network.Network
import com.tangem.domain.models.network.NetworkAddress
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
@@ -210,31 +209,6 @@ internal class TokenDetailsStateFactory(
return refreshStateConverter.convert(false)
}
- fun getStateWithReceiveBottomSheet(
- currency: CryptoCurrency,
- networkAddress: NetworkAddress,
- onCopyClick: (String) -> Unit,
- onShareClick: (String) -> Unit,
- ): TokenDetailsState {
- return currentStateProvider().copy(
- bottomSheetConfig = TangemBottomSheetConfig(
- isShown = true,
- onDismissRequest = expressTransactionsClickIntents::onDismissBottomSheet,
- content = TokenReceiveBottomSheetConfig(
- asset = TokenReceiveBottomSheetConfig.Asset.Currency(
- name = currency.name,
- symbol = currency.symbol,
- ),
- network = currency.network,
- networkAddress = networkAddress,
- showMemoDisclaimer = currency.network.transactionExtrasType != Network.TransactionExtrasType.NONE,
- onCopyClick = onCopyClick,
- onShareClick = onShareClick,
- ),
- ),
- )
- }
-
fun getStateWithChooseAddressBottomSheet(
currency: CryptoCurrency,
networkAddress: NetworkAddress,
@@ -244,11 +218,7 @@ internal class TokenDetailsStateFactory(
isShown = true,
onDismissRequest = expressTransactionsClickIntents::onDismissBottomSheet,
content = ChooseAddressBottomSheetConfig(
- asset = TokenReceiveBottomSheetConfig.Asset.Currency(
- name = currency.name,
- symbol = currency.symbol,
- ),
- network = currency.network,
+ currency = currency,
networkAddress = networkAddress,
onClick = tokenDetailsClickIntents::onAddressTypeSelected,
),
@@ -380,4 +350,62 @@ internal class TokenDetailsStateFactory(
}.toImmutableList(),
)
}
+
+ // region Clore migration
+ // TODO: Remove after Clore migration ends ([REDACTED_TASK_KEY])
+
+ fun getStateWithCloreMigrationBottomSheet(
+ message: String,
+ signature: String,
+ isSigningInProgress: Boolean,
+ onMessageChange: (String) -> Unit,
+ onSignClick: () -> Unit,
+ onCopyClick: () -> Unit,
+ onOpenPortalClick: () -> Unit,
+ ): TokenDetailsState {
+ return currentStateProvider().copy(
+ bottomSheetConfig = TangemBottomSheetConfig(
+ isShown = true,
+ onDismissRequest = expressTransactionsClickIntents::onDismissBottomSheet,
+ content = CloreMigrationBottomSheetConfig(
+ message = message,
+ signature = signature,
+ isSigningInProgress = isSigningInProgress,
+ onMessageChange = onMessageChange,
+ onSignClick = onSignClick,
+ onCopyClick = onCopyClick,
+ onOpenPortalClick = onOpenPortalClick,
+ ),
+ ),
+ )
+ }
+
+ fun getStateWithUpdatedCloreMigrationSignature(signature: String): TokenDetailsState {
+ val state = currentStateProvider()
+ val bottomSheetConfig = state.bottomSheetConfig ?: return state
+ val content = bottomSheetConfig.content as? CloreMigrationBottomSheetConfig ?: return state
+
+ return state.copy(
+ bottomSheetConfig = bottomSheetConfig.copy(
+ content = content.copy(
+ signature = signature,
+ isSigningInProgress = false,
+ ),
+ ),
+ )
+ }
+
+ fun getStateWithCloreMigrationSigning(): TokenDetailsState {
+ val state = currentStateProvider()
+ val bottomSheetConfig = state.bottomSheetConfig ?: return state
+ val content = bottomSheetConfig.content as? CloreMigrationBottomSheetConfig ?: return state
+
+ return state.copy(
+ bottomSheetConfig = bottomSheetConfig.copy(
+ content = content.copy(isSigningInProgress = true),
+ ),
+ )
+ }
+
+ // endregion Clore migration
}
\ 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 2e2ea39238..abf30bd8f0 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
@@ -16,8 +16,6 @@ import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameter
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.common.ui.bottomsheet.chooseaddress.ChooseAddressBottomSheet
import com.tangem.common.ui.bottomsheet.chooseaddress.ChooseAddressBottomSheetConfig
-import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheet
-import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheetConfig
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
import com.tangem.common.ui.expressStatus.expressTransactionsItems
import com.tangem.core.ui.components.containers.pullToRefresh.TangemPullToRefreshContainer
@@ -35,6 +33,8 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.T
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenDetailsDialogs
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenDetailsTopAppBar
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.TokenInfoBlock
+import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.clore.CloreMigrationBottomSheet
+import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.clore.CloreMigrationBottomSheetConfig
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.express.ExpressStatusBottomSheet
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.staking.TokenStakingBlock
import com.tangem.features.markets.token.block.TokenMarketBlockComponent
@@ -169,15 +169,15 @@ internal fun TokenDetailsScreen(
state.bottomSheetConfig?.let { config ->
when (config.content) {
- is TokenReceiveBottomSheetConfig -> {
- TokenReceiveBottomSheet(config = config)
- }
is ChooseAddressBottomSheetConfig -> {
ChooseAddressBottomSheet(config = config)
}
is ExpressStatusBottomSheetConfig -> {
ExpressStatusBottomSheet(config = config)
}
+ is CloreMigrationBottomSheetConfig -> {
+ CloreMigrationBottomSheet(config = config)
+ }
}
}
}
diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/clore/CloreMigrationBottomSheet.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/clore/CloreMigrationBottomSheet.kt
new file mode 100644
index 0000000000..9875449e80
--- /dev/null
+++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/clore/CloreMigrationBottomSheet.kt
@@ -0,0 +1,220 @@
+package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.clore
+
+import android.content.res.Configuration
+import androidx.compose.foundation.border
+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 androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.tangem.core.ui.R as CoreR
+import com.tangem.core.ui.components.PrimaryButton
+import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
+import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
+import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
+import com.tangem.core.ui.components.buttons.SecondarySmallButton
+import com.tangem.core.ui.components.buttons.SmallButtonConfig
+import com.tangem.core.ui.components.fields.SimpleTextField
+import com.tangem.core.ui.extensions.TextReference
+import com.tangem.core.ui.extensions.stringResourceSafe
+import com.tangem.core.ui.res.TangemTheme
+import com.tangem.core.ui.res.TangemThemePreview
+import com.tangem.features.tokendetails.impl.R
+
+@Composable
+internal fun CloreMigrationBottomSheet(config: TangemBottomSheetConfig) {
+ TangemModalBottomSheet(
+ config = config,
+ title = {
+ TangemModalBottomSheetTitle(
+ endIconRes = CoreR.drawable.ic_close_24,
+ onEndClick = config.onDismissRequest,
+ )
+ },
+ ) { content: CloreMigrationBottomSheetConfig ->
+ CloreMigrationBottomSheetContent(content = content)
+ }
+}
+
+@Composable
+private fun CloreMigrationBottomSheetContent(content: CloreMigrationBottomSheetConfig) {
+ Column(
+ modifier = Modifier
+ .padding(bottom = 16.dp)
+ .padding(horizontal = 16.dp),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(space = 16.dp),
+ ) {
+ Text(
+ text = stringResourceSafe(R.string.warning_clore_migration_sheet_title),
+ color = TangemTheme.colors.text.primary1,
+ textAlign = TextAlign.Center,
+ style = TangemTheme.typography.h3,
+ )
+
+ Text(
+ text = stringResourceSafe(R.string.warning_clore_migration_sheet_description),
+ color = TangemTheme.colors.text.secondary,
+ textAlign = TextAlign.Center,
+ style = TangemTheme.typography.body2,
+ modifier = Modifier.padding(horizontal = 8.dp),
+ )
+
+ Spacer(modifier = Modifier.height(8.dp))
+
+ MessageField(
+ placeholder = stringResourceSafe(R.string.warning_clore_migration_message_label),
+ value = content.message,
+ onValueChange = content.onMessageChange,
+ buttonText = stringResourceSafe(R.string.warning_clore_migration_sign_button),
+ onButtonClick = content.onSignClick,
+ isButtonEnabled = content.message.isNotBlank() && !content.isSigningInProgress,
+ isLoading = content.isSigningInProgress,
+ )
+
+ SignatureField(
+ placeholder = stringResourceSafe(R.string.warning_clore_migration_signature_label),
+ value = content.signature,
+ onCopyClick = content.onCopyClick,
+ )
+
+ Spacer(modifier = Modifier.height(8.dp))
+
+ PrimaryButton(
+ text = stringResourceSafe(R.string.warning_clore_migration_open_portal_button),
+ onClick = content.onOpenPortalClick,
+ modifier = Modifier.fillMaxWidth(),
+ )
+ }
+}
+
+@Suppress("LongParameterList")
+@Composable
+private fun MessageField(
+ placeholder: String,
+ value: String,
+ onValueChange: (String) -> Unit,
+ buttonText: String,
+ onButtonClick: () -> Unit,
+ isButtonEnabled: Boolean,
+ isLoading: Boolean,
+) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .border(
+ width = 1.dp,
+ color = TangemTheme.colors.stroke.primary,
+ shape = TangemTheme.shapes.roundedCornersSmall,
+ )
+ .heightIn(min = 56.dp)
+ .padding(12.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ SimpleTextField(
+ value = value,
+ onValueChange = onValueChange,
+ placeholder = TextReference.Str(placeholder),
+ modifier = Modifier.weight(1f),
+ singleLine = false,
+ )
+
+ Spacer(modifier = Modifier.width(8.dp))
+
+ SecondarySmallButton(
+ config = SmallButtonConfig(
+ text = TextReference.Str(buttonText),
+ onClick = onButtonClick,
+ isEnabled = isButtonEnabled,
+ isLoading = isLoading,
+ ),
+ )
+ }
+}
+
+@Suppress("FunctionSignature")
+@Composable
+private fun SignatureField(placeholder: String, value: String, onCopyClick: () -> Unit) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .border(
+ width = 1.dp,
+ color = TangemTheme.colors.stroke.primary,
+ shape = TangemTheme.shapes.roundedCornersSmall,
+ )
+ .heightIn(min = 56.dp)
+ .padding(12.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ SimpleTextField(
+ value = value,
+ onValueChange = {},
+ placeholder = TextReference.Str(placeholder),
+ modifier = Modifier.weight(1f),
+ readOnly = true,
+ singleLine = false,
+ )
+
+ Spacer(modifier = Modifier.width(8.dp))
+
+ SecondarySmallButton(
+ config = SmallButtonConfig(
+ text = TextReference.Res(R.string.warning_clore_migration_copy_button),
+ onClick = onCopyClick,
+ isEnabled = value.isNotBlank(),
+ ),
+ )
+ }
+}
+
+// region Preview
+@Composable
+@Preview(showBackground = true, widthDp = 360)
+@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
+private fun Preview_CloreMigrationBottomSheet() {
+ TangemThemePreview {
+ val config = TangemBottomSheetConfig(
+ isShown = true,
+ content = CloreMigrationBottomSheetConfig(
+ message = "Claim request for CLORE tokens to Ethereum address " +
+ "0x742d35Cc6634C0532925a3b844Bc9e7595f60126 from CEsMERNgBVPo9Dkc99pRDMPD3mxwPMxHZi",
+ signature = "H4sIAAAAAAAEAGNgGAWjYBSMglEwCkYBHQEA",
+ isSigningInProgress = false,
+ onMessageChange = {},
+ onSignClick = {},
+ onCopyClick = {},
+ onOpenPortalClick = {},
+ ),
+ onDismissRequest = {},
+ )
+
+ CloreMigrationBottomSheet(config)
+ }
+}
+
+@Composable
+@Preview(showBackground = true, widthDp = 360)
+private fun Preview_CloreMigrationBottomSheet_Empty() {
+ TangemThemePreview {
+ val config = TangemBottomSheetConfig(
+ isShown = true,
+ content = CloreMigrationBottomSheetConfig(
+ message = "",
+ signature = "",
+ isSigningInProgress = false,
+ onMessageChange = {},
+ onSignClick = {},
+ onCopyClick = {},
+ onOpenPortalClick = {},
+ ),
+ onDismissRequest = {},
+ )
+
+ CloreMigrationBottomSheet(config)
+ }
+}
+// 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/clore/CloreMigrationBottomSheetConfig.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/clore/CloreMigrationBottomSheetConfig.kt
new file mode 100644
index 0000000000..9adf70e4af
--- /dev/null
+++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/clore/CloreMigrationBottomSheetConfig.kt
@@ -0,0 +1,15 @@
+package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.clore
+
+import androidx.compose.runtime.Immutable
+import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
+
+@Immutable
+data class CloreMigrationBottomSheetConfig(
+ val message: String = "",
+ val signature: String = "",
+ val isSigningInProgress: Boolean = false,
+ val onMessageChange: (String) -> Unit,
+ val onSignClick: () -> Unit,
+ val onCopyClick: () -> Unit,
+ val onOpenPortalClick: () -> Unit,
+) : TangemBottomSheetConfigContent
\ No newline at end of file
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntents.kt
index b9373e40e8..58d1a6866a 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntents.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntents.kt
@@ -199,6 +199,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
cryptoCurrencyStatus = currencyStatus,
apy = apy,
)
+ is NavigationAction.CloreMigration -> Unit
}
}
@@ -273,6 +274,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
},
)
}
+ is NavigationAction.CloreMigration -> return
}
analyticsEventHandler.send(event)
}
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletCurrencyActionsClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletCurrencyActionsClickIntents.kt
index 9cc74286c9..78956da6ab 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletCurrencyActionsClickIntents.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletCurrencyActionsClickIntents.kt
@@ -5,7 +5,6 @@ import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.common.ui.bottomsheet.chooseaddress.ChooseAddressBottomSheetConfig
import com.tangem.common.ui.bottomsheet.receive.AddressModel
-import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheetConfig
import com.tangem.common.ui.bottomsheet.receive.mapToAddressModels
import com.tangem.common.ui.tokens.getUnavailabilityReasonText
import com.tangem.core.analytics.api.AnalyticsEventHandler
@@ -13,9 +12,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
import com.tangem.core.decompose.di.ModelScoped
-import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.ui.clipboard.ClipboardManager
-import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.WrappedList
@@ -35,7 +32,6 @@ import com.tangem.domain.markets.TokenMarketParams
import com.tangem.domain.models.TokenReceiveConfig
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
-import com.tangem.domain.models.network.Network
import com.tangem.domain.models.network.NetworkAddress
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
@@ -47,7 +43,6 @@ import com.tangem.domain.staking.model.StakingOption
import com.tangem.domain.tokens.*
import com.tangem.domain.tokens.legacy.TradeCryptoAction
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
-import com.tangem.domain.tokens.model.analytics.TokenReceiveAnalyticsEvent
import com.tangem.domain.tokens.model.analytics.TokenReceiveCopyActionSource
import com.tangem.domain.tokens.model.analytics.TokenReceiveNewAnalyticsEvent
import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent
@@ -67,7 +62,6 @@ 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.CloseBottomSheetTransformer
import com.tangem.feature.wallet.presentation.wallet.state.utils.WalletEventSender
-import com.tangem.features.tokenreceive.TokenReceiveFeatureToggle
import com.tangem.features.yield.supply.api.YieldSupplyFeatureToggles
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.toImmutableList
@@ -144,10 +138,8 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
private val reduxStateHolder: ReduxStateHolder,
private val vibratorHapticManager: VibratorHapticManager,
private val clipboardManager: ClipboardManager,
- private val shareManager: ShareManager,
private val appRouter: AppRouter,
private val rampStateManager: RampStateManager,
- private val tokenReceiveFeatureToggle: TokenReceiveFeatureToggle,
private val saveViewedTokenReceiveWarningUseCase: SaveViewedTokenReceiveWarningUseCase,
private val receiveAddressesFactory: ReceiveAddressesFactory,
private val yieldSupplyFeatureToggles: YieldSupplyFeatureToggles,
@@ -249,29 +241,6 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
return resourceReference(R.string.wallet_notification_address_copied)
}
- private fun createReceiveBottomSheetContent(
- currency: CryptoCurrency,
- addresses: NetworkAddress,
- ): TangemBottomSheetConfigContent {
- return TokenReceiveBottomSheetConfig(
- asset = TokenReceiveBottomSheetConfig.Asset.Currency(
- name = currency.name,
- symbol = currency.symbol,
- ),
- network = currency.network,
- networkAddress = addresses,
- showMemoDisclaimer = currency.network.transactionExtrasType != Network.TransactionExtrasType.NONE,
- onCopyClick = {
- analyticsEventHandler.send(TokenReceiveAnalyticsEvent.ButtonCopyAddress(currency.symbol))
- clipboardManager.setText(text = it, isSensitive = true)
- },
- onShareClick = {
- analyticsEventHandler.send(TokenReceiveAnalyticsEvent.ButtonShareAddress(currency.symbol))
- shareManager.shareText(text = it)
- },
- )
- }
-
override fun onCopyAddressClick(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus) {
analyticsEventHandler.send(
event = TokenReceiveNewAnalyticsEvent.ButtonCopyAddress(
@@ -596,11 +565,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
) {
stateHolder.showBottomSheet(
ChooseAddressBottomSheetConfig(
- asset = TokenReceiveBottomSheetConfig.Asset.Currency(
- name = currency.name,
- symbol = currency.symbol,
- ),
- network = currency.network,
+ currency = currency,
networkAddress = addresses,
onClick = {
onAddressTypeSelected(
@@ -768,25 +733,11 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
}
private fun navigateToReceive(cryptoCurrencyStatus: CryptoCurrencyStatus) {
- val userWalletId = stateHolder.getSelectedWalletId()
- if (tokenReceiveFeatureToggle.isNewTokenReceiveEnabled) {
- stateHolder.hideBottomSheet()
- modelScope.launch {
- configureReceiveAddresses(cryptoCurrencyStatus = cryptoCurrencyStatus)?.let {
- router.openTokenReceiveBottomSheet(it)
- }
+ stateHolder.hideBottomSheet()
+ modelScope.launch {
+ configureReceiveAddresses(cryptoCurrencyStatus = cryptoCurrencyStatus)?.let {
+ router.openTokenReceiveBottomSheet(it)
}
- } else {
- analyticsEventHandler.send(
- event = TokenReceiveAnalyticsEvent.ReceiveScreenOpened(cryptoCurrencyStatus.currency.symbol),
- )
- stateHolder.showBottomSheet(
- createReceiveBottomSheetContent(
- currency = cryptoCurrencyStatus.currency,
- addresses = cryptoCurrencyStatus.value.networkAddress ?: return,
- ),
- userWalletId,
- )
}
}
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt
index ed19a09083..55dd5e9a5c 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletWarningsClickIntents.kt
@@ -18,6 +18,7 @@ import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.currency.CryptoCurrency
+import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.requireColdWallet
@@ -35,6 +36,7 @@ import com.tangem.domain.staking.multi.MultiStakingBalanceFetcher
import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent
import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent.Program
import com.tangem.domain.tokens.model.analytics.PromoAnalyticsEvent.PromotionBannerClicked
+import com.tangem.domain.tokens.model.details.NavigationAction
import com.tangem.domain.wallets.legacy.UserWalletsListManager.Lockable.UnlockType
import com.tangem.domain.wallets.models.UnlockWalletsError
import com.tangem.domain.wallets.usecase.*
@@ -65,6 +67,8 @@ internal interface WalletWarningsClickIntents {
fun onAddBackupCardClick()
+ fun onCloreMigrationClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
+
fun onCloseAlreadySignedHashesWarningClick()
fun onGenerateMissedAddressesClick(missedAddressCurrencies: List)
@@ -104,6 +108,8 @@ internal interface WalletWarningsClickIntents {
fun onDenyPermissions()
fun onFinishWalletActivationClick(isBackupExists: Boolean)
+
+ fun onYieldPromoTermsAndConditionsClick()
}
@Suppress("LargeClass", "LongParameterList", "TooManyFunctions")
@@ -146,6 +152,16 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
prepareAndStartOnboardingProcess()
}
+ override fun onCloreMigrationClick(cryptoCurrencyStatus: CryptoCurrencyStatus) {
+ val userWallet = getSelectedUserWallet() ?: return
+
+ router.openTokenDetails(
+ userWalletId = userWallet.walletId,
+ currencyStatus = cryptoCurrencyStatus,
+ navigationAction = NavigationAction.CloreMigration,
+ )
+ }
+
private fun prepareAndStartOnboardingProcess() {
modelScope.launch(dispatchers.main) {
getSelectedUserWallet()?.requireColdWallet()?.let {
@@ -323,6 +339,11 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
program = Program.OnePlusOne,
action = PromotionBannerClicked.BannerAction.Closed(),
)
+ PromoId.YieldPromo -> PromotionBannerClicked(
+ source = AnalyticsParam.ScreensSources.Main,
+ program = Program.YieldPromo,
+ action = PromotionBannerClicked.BannerAction.Closed(),
+ )
},
)
@@ -380,6 +401,7 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
)
urlOpener.openUrl(ONE_PLUS_ONE_PROMO_LINK)
}
+ PromoId.YieldPromo -> Unit // banner is not clickable, only terms and conditions button
}
}
@@ -597,6 +619,17 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
}
}
+ override fun onYieldPromoTermsAndConditionsClick() {
+ analyticsEventHandler.send(
+ PromotionBannerClicked(
+ source = AnalyticsParam.ScreensSources.Main,
+ program = Program.YieldPromo,
+ action = PromotionBannerClicked.BannerAction.Clicked(),
+ ),
+ )
+ urlOpener.openUrl(YIELD_PROMO_TERMS_LINK)
+ }
+
private companion object {
const val VISA_PROMO_LINK = "https://tangem.com/en/cardwaitlist/?utm_source=tangem-app-banner" +
"&utm_medium=banner" +
@@ -611,5 +644,6 @@ internal class WalletWarningsClickIntentsImplementor @Inject constructor(
"&utm_source=tangem-app-banner" +
"&utm_medium=banner" +
"&utm_campaign=BOGO50"
+ const val YIELD_PROMO_TERMS_LINK = "https://tangem.com/docs/yield-mode-toc.html"
}
}
\ No newline at end of file
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/deeplink/DefaultPromoDeeplinkHandler.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/deeplink/DefaultPromoDeeplinkHandler.kt
index af455b27ac..0cb63b07c9 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/deeplink/DefaultPromoDeeplinkHandler.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/deeplink/DefaultPromoDeeplinkHandler.kt
@@ -59,14 +59,15 @@ internal class DefaultPromoDeeplinkHandler @AssistedInject constructor(
init {
analyticsEventsHandler.send(PromoActivationAnalytics.PromoDeepLinkActivationStart())
+
+ saveAndBindRefcode()
+
val promoCode = queryParams[PROMO_CODE_KEY].orEmpty()
if (promoCode.isEmpty()) {
showAlert(InvalidPromoCode)
} else {
findSelectedWallet(promoCode)
}
-
- saveAndBindRefcode()
}
private fun findSelectedWallet(promoCode: String) {
@@ -183,14 +184,19 @@ internal class DefaultPromoDeeplinkHandler @AssistedInject constructor(
message = message,
dismissOnFirstAction = true,
firstActionBuilder = {
- okAction { }
+ okAction {
+ uiMessageSender.send(GlobalLoadingMessage(false))
+ }
},
),
)
}
+ @Suppress("NullableToStringCall")
private fun saveAndBindRefcode() {
scope.launch(dispatchers.default) {
+ Timber.i("saveAndBindRefcode: refcode = $refcode, campaign = $campaign")
+
if (!refcode.isNullOrBlank()) {
val conversionData = AppsFlyerConversionData(refcode = refcode, campaign = campaign)
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt
index 1eb6944175..e766bf4333 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/analytics/WalletScreenAnalyticsEvent.kt
@@ -72,7 +72,7 @@ sealed class WalletScreenAnalyticsEvent {
}
put("Wallet Type", seedPhrase)
},
- )
+ ), AppsFlyerIncludedEvent
data class NoticeFinishActivation(
private val activationState: ActivationState,
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 94ede58c14..3852ada00e 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
@@ -66,6 +66,10 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
source = AnalyticsParam.ScreensSources.Main,
program = Program.OnePlusOne,
)
+ is WalletNotification.YieldPromo -> NoticePromotionBanner(
+ source = AnalyticsParam.ScreensSources.Main,
+ program = Program.YieldPromo,
+ )
is WalletNotification.ReferralPromo -> MainScreen.ReferralPromo()
is WalletNotification.VisaPresalePromo -> VisaWaitlistPromo()
is WalletNotification.UnlockWallets -> null // See [SelectedWalletAnalyticsSender]
@@ -76,6 +80,7 @@ internal class WalletWarningsAnalyticsSender @Inject constructor(
is WalletNotification.UsedOutdatedData,
is WalletNotification.UnlockVisaAccess,
is WalletNotification.Warning.YeildSupplyApprove, // TODO apply correct event
+ is WalletNotification.CloreMigration,
-> null
is WalletNotification.FinishWalletActivation -> {
val activationState = if (warning.isBackupExists) {
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 b757338349..fe3861e0c2 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
@@ -33,6 +33,7 @@ import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.account.AccountDependencies
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
import com.tangem.hot.sdk.model.HotWalletId
+import com.tangem.lib.crypto.BlockchainUtils
import com.tangem.utils.extensions.addIf
import com.tangem.utils.extensions.isPositive
import com.tangem.utils.extensions.orZero
@@ -95,6 +96,8 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
notificationsRepository.getShouldShowNotification(NotificationId.EnablePushesReminderNotification.key)
.distinctUntilChanged(),
getAccessCodeSkippedUseCase(userWallet.walletId).distinctUntilChanged(),
+ shouldShowPromoWalletUseCase(userWalletId = userWallet.walletId, promoId = PromoId.YieldPromo)
+ .distinctUntilChanged(),
) { array -> array }
.combine(tokenListFlow()) { array, any: Any -> arrayOf(any).plus(elements = array) }
.map { array ->
@@ -107,6 +110,7 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
val shouldShowOnePlusOnePromo = array[4] as Boolean
val shouldShowEnablePushesReminderNotification = array[5] as Boolean
val shouldAccessCodeSkipped = array[6] as Boolean
+ val shouldShowYieldPromo = array[7] as Boolean
buildList {
addUsedOutdatedDataNotification(totalFiatBalance)
@@ -122,6 +126,8 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
addOnePlusOnePromoNotification(clickIntents, shouldShowOnePlusOnePromo)
+ addYieldPromoNotification(clickIntents, shouldShowYieldPromo)
+
addInformationalNotifications(userWallet, cardTypesResolver, flattenCurrencies, clickIntents)
addWarningNotifications(cardTypesResolver, flattenCurrencies, isNeedToBackup, clickIntents)
@@ -286,6 +292,19 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
)
}
+ private fun MutableList.addYieldPromoNotification(
+ clickIntents: WalletClickIntents,
+ shouldShowPromo: Boolean,
+ ) {
+ addIf(
+ element = WalletNotification.YieldPromo(
+ onCloseClick = { clickIntents.onClosePromoClick(promoId = PromoId.YieldPromo) },
+ onTermsAndConditionsClick = { clickIntents.onYieldPromoTermsAndConditionsClick() },
+ ),
+ condition = shouldShowPromo,
+ )
+ }
+
// private fun MutableList.addYieldSupplyNotifications(
// flattenCurrencies: Lce>,
// ) {
@@ -317,6 +336,29 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
element = WalletNotification.Warning.SomeNetworksUnreachable,
condition = flattenCurrencies.hasUnreachableNetworks(),
)
+
+ addCloreMigrationNotification(flattenCurrencies, clickIntents)
+ }
+
+ private fun MutableList.addCloreMigrationNotification(
+ flattenCurrencies: Lce>,
+ clickIntents: WalletClickIntents,
+ ) {
+ val cloreCurrency = flattenCurrencies.findCloreCurrency() ?: return
+
+ add(
+ WalletNotification.CloreMigration(
+ onStartMigrationClick = { clickIntents.onCloreMigrationClick(cloreCurrency) },
+ ),
+ )
+ }
+
+ private fun Lce>.findCloreCurrency(): CryptoCurrencyStatus? {
+ val currencies = getOrNull(isPartialContentAccepted = true) ?: return null
+
+ return currencies.find { currencyStatus ->
+ BlockchainUtils.isClore(currencyStatus.currency.network.rawId)
+ }
}
private fun MutableList.addPushReminderNotification(
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt
index e2dc6648fc..9abf719a45 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletNotification.kt
@@ -396,6 +396,23 @@ sealed class WalletNotification(val config: NotificationConfig) {
),
)
+ data class YieldPromo(
+ val onCloseClick: () -> Unit,
+ val onTermsAndConditionsClick: () -> Unit,
+ ) : WalletNotification(
+ config = NotificationConfig(
+ title = resourceReference(R.string.notification_yield_promo_title),
+ subtitle = resourceReference(R.string.notification_yield_promo_text),
+ iconResId = R.drawable.ic_yield_promo_36,
+ onCloseClick = onCloseClick,
+ buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
+ text = resourceReference(R.string.notification_yield_promo_button),
+ onClick = onTermsAndConditionsClick,
+ ),
+ iconSize = 36.dp,
+ ),
+ )
+
data class PushNotifications(
val onCloseClick: () -> Unit,
val onEnabledClick: () -> Unit,
@@ -414,4 +431,18 @@ sealed class WalletNotification(val config: NotificationConfig) {
iconSize = 54.dp,
),
)
+
+ data class CloreMigration(
+ val onStartMigrationClick: () -> Unit,
+ ) : WalletNotification(
+ config = NotificationConfig(
+ title = resourceReference(com.tangem.core.res.R.string.warning_clore_migration_title),
+ subtitle = resourceReference(com.tangem.core.res.R.string.warning_clore_migration_description),
+ iconResId = R.drawable.img_attention_20,
+ buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
+ text = resourceReference(com.tangem.core.res.R.string.warning_clore_migration_button),
+ onClick = onStartMigrationClick,
+ ),
+ ),
+ )
}
\ 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 5790de110c..eacebf5eb3 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
@@ -42,8 +42,6 @@ import androidx.compose.ui.unit.dp
import androidx.paging.compose.collectAsLazyPagingItems
import com.tangem.common.ui.bottomsheet.chooseaddress.ChooseAddressBottomSheet
import com.tangem.common.ui.bottomsheet.chooseaddress.ChooseAddressBottomSheetConfig
-import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheet
-import com.tangem.common.ui.bottomsheet.receive.TokenReceiveBottomSheetConfig
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheet
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
import com.tangem.common.ui.expressStatus.expressTransactionsItems
@@ -709,7 +707,6 @@ private fun ShowBottomSheet(bottomSheetConfig: TangemBottomSheetConfig?) {
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 ExpressStatusBottomSheetConfig -> ExpressStatusBottomSheet(config = bottomSheetConfig)
diff --git a/features/wallet/impl/src/main/res/drawable/ic_yield_promo_36.xml b/features/wallet/impl/src/main/res/drawable/ic_yield_promo_36.xml
new file mode 100644
index 0000000000..4a4921466d
--- /dev/null
+++ b/features/wallet/impl/src/main/res/drawable/ic_yield_promo_36.xml
@@ -0,0 +1,15 @@
+
+
+
+
diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcPairModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcPairModel.kt
index bdc6f34464..285229cc87 100644
--- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcPairModel.kt
+++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcPairModel.kt
@@ -5,7 +5,8 @@ import com.arkivanov.decompose.router.stack.StackNavigation
import com.arkivanov.decompose.router.stack.pop
import com.arkivanov.decompose.router.stack.pushNew
import com.domain.blockaid.models.dapp.CheckDAppResult
-import com.tangem.common.ui.account.CryptoPortfolioIconUM
+import com.tangem.common.ui.account.AccountIconUM
+import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.common.ui.account.PortfolioSelectUM
import com.tangem.common.ui.account.toUM
import com.tangem.core.analytics.api.AnalyticsEventHandler
@@ -276,10 +277,10 @@ internal class WcPairModel @Inject constructor(
is Account.Payment -> TODO("[REDACTED_JIRA]")
}
val isAccountMode = selectorController.isAccountMode.first()
- val icon: CryptoPortfolioIconUM?
+ val icon: AccountIconUM.CryptoPortfolio?
val name: TextReference
if (isAccountMode) {
- icon = account.icon.toUM()
+ icon = CryptoPortfolioIconConverter.convert(account.icon)
name = account.accountName.toUM().value
} else {
icon = null
diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcSessionsAccountModeTransformer.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcSessionsAccountModeTransformer.kt
index 598ca31911..fa672da841 100644
--- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcSessionsAccountModeTransformer.kt
+++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcSessionsAccountModeTransformer.kt
@@ -1,6 +1,7 @@
package com.tangem.features.walletconnect.connections.model.transformers
import com.tangem.common.ui.account.AccountTitleUM
+import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.common.ui.account.toUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.account.models.AccountList
@@ -70,7 +71,7 @@ internal class WcSessionsAccountModeTransformer(
val accountTitle = AccountTitleUM.Account(
prefixText = TextReference.EMPTY,
name = account.accountName.toUM().value,
- icon = accountIcon.toUM(),
+ icon = CryptoPortfolioIconConverter.convert(accountIcon),
)
val accountConnections = WcConnectionsItem.PortfolioConnections(
diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcPortfolioNameDelegate.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcPortfolioNameDelegate.kt
index ce57447912..5df48f4e4d 100644
--- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcPortfolioNameDelegate.kt
+++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcPortfolioNameDelegate.kt
@@ -1,6 +1,7 @@
package com.tangem.features.walletconnect.transaction.converter
import com.tangem.common.ui.account.AccountTitleUM
+import com.tangem.common.ui.account.CryptoPortfolioIconConverter
import com.tangem.common.ui.account.toUM
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.stringReference
@@ -32,7 +33,7 @@ internal class WcPortfolioNameDelegate @AssistedInject constructor(
AccountTitleUM.Account(
prefixText = TextReference.EMPTY,
name = account.accountName.toUM().value,
- icon = account.icon.toUM(),
+ icon = CryptoPortfolioIconConverter.convert(account.icon),
)
} else {
AccountTitleUM.Text(stringReference(value.wallet.name))
diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSendTransactionUMConverter.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSendTransactionUMConverter.kt
index dbba45773d..e4969a134c 100644
--- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSendTransactionUMConverter.kt
+++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSendTransactionUMConverter.kt
@@ -16,6 +16,7 @@ import com.tangem.features.walletconnect.transaction.entity.common.WcTransaction
import com.tangem.features.walletconnect.transaction.entity.send.WcSendTransactionItemUM
import com.tangem.features.walletconnect.transaction.entity.send.WcSendTransactionUM
import com.tangem.features.walletconnect.utils.WcNotificationsFactory
+import com.tangem.domain.models.wallet.isHotWallet
import com.tangem.utils.converter.Converter
import kotlinx.collections.immutable.toImmutableList
import javax.inject.Inject
@@ -62,6 +63,7 @@ internal class WcSendTransactionUMConverter @Inject constructor(
}
},
feeErrorNotification = feeErrorNotification,
+ isHoldToConfirmEnabled = value.context.session.wallet.isHotWallet,
),
feeSelectorUM = when (value.feeState) {
WcTransactionFeeState.None -> FeeSelectorUM.Loading
diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSignTransactionUMConverter.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSignTransactionUMConverter.kt
index 91f3cbf530..4fd75570a0 100644
--- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSignTransactionUMConverter.kt
+++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSignTransactionUMConverter.kt
@@ -10,6 +10,7 @@ import com.tangem.features.walletconnect.transaction.entity.common.WcTransaction
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionRequestInfoUM
import com.tangem.features.walletconnect.transaction.entity.sign.WcSignTransactionItemUM
import com.tangem.features.walletconnect.transaction.entity.sign.WcSignTransactionUM
+import com.tangem.domain.models.wallet.isHotWallet
import com.tangem.utils.converter.Converter
import kotlinx.collections.immutable.toImmutableList
import javax.inject.Inject
@@ -35,6 +36,7 @@ internal class WcSignTransactionUMConverter @Inject constructor(
isLoading = value.signState.domainStep == WcSignStep.Signing,
address = WcAddressConverter.convert(value.context.derivationState),
walletInteractionIcon = walletInterationIcon(value.context.session.wallet),
+ isHoldToConfirmEnabled = value.context.session.wallet.isHotWallet,
),
transactionRequestInfo = WcTransactionRequestInfoUM(
requestBlockUMConverter.convert(
diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSignTypedDataUMConverter.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSignTypedDataUMConverter.kt
index 5bc7aa70e3..6f185ecfd9 100644
--- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSignTypedDataUMConverter.kt
+++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/converter/WcSignTypedDataUMConverter.kt
@@ -10,6 +10,7 @@ import com.tangem.features.walletconnect.transaction.entity.common.WcTransaction
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionRequestInfoUM
import com.tangem.features.walletconnect.transaction.entity.sign.WcSignTransactionItemUM
import com.tangem.features.walletconnect.transaction.entity.sign.WcSignTransactionUM
+import com.tangem.domain.models.wallet.isHotWallet
import com.tangem.utils.converter.Converter
import kotlinx.collections.immutable.toImmutableList
import javax.inject.Inject
@@ -35,6 +36,7 @@ internal class WcSignTypedDataUMConverter @Inject constructor(
address = WcAddressConverter.convert(value.context.derivationState),
isLoading = value.signState.domainStep == WcSignStep.Signing,
walletInteractionIcon = walletInterationIcon(value.context.session.wallet),
+ isHoldToConfirmEnabled = value.context.session.wallet.isHotWallet,
),
transactionRequestInfo = WcTransactionRequestInfoUM(
blocks = buildList {
diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/send/WcSendTransactionUM.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/send/WcSendTransactionUM.kt
index f98dcecdc5..61a43bb901 100644
--- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/send/WcSendTransactionUM.kt
+++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/send/WcSendTransactionUM.kt
@@ -34,4 +34,5 @@ internal data class WcSendTransactionItemUM(
val feeErrorNotification: NotificationUM.Info?,
val isLoading: Boolean = false,
val walletInteractionIcon: Int? = null,
+ val isHoldToConfirmEnabled: Boolean = false,
) : TangemBottomSheetConfigContent
\ No newline at end of file
diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/sign/WcSignTransactionUM.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/sign/WcSignTransactionUM.kt
index 3d63016c10..0316e5b72b 100644
--- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/sign/WcSignTransactionUM.kt
+++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/entity/sign/WcSignTransactionUM.kt
@@ -21,4 +21,5 @@ internal data class WcSignTransactionItemUM(
val address: String?,
val walletInteractionIcon: Int?,
val isLoading: Boolean = false,
+ val isHoldToConfirmEnabled: Boolean = false,
) : TangemBottomSheetConfigContent
\ No newline at end of file
diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcTransactionRequestButtons.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcTransactionRequestButtons.kt
index f60920591d..199cc48016 100644
--- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcTransactionRequestButtons.kt
+++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/common/WcTransactionRequestButtons.kt
@@ -8,9 +8,11 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.domain.blockaid.models.transaction.ValidationResult
+import com.tangem.core.ui.components.HoldToConfirmButton
import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.components.PrimaryButtonIconEnd
import com.tangem.core.ui.components.SecondaryButton
+import com.tangem.core.ui.R as CoreR
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringResourceSafe
@@ -28,37 +30,50 @@ internal fun WcTransactionRequestButtons(
onClickActiveButton: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
+ isHoldToConfirmEnabled: Boolean = false,
) {
WcCommonButtons(
onDismiss = onDismiss,
modifier = modifier,
primaryButton = {
+ val buttonModifier = Modifier
+ .fillMaxWidth()
+ .weight(1f)
+
// Before change, make sure you are align with WcSendTransactionModel::onSign
- when (validationResult) {
- ValidationResult.UNSAFE,
- ValidationResult.WARNING,
- -> PrimaryButton(
- modifier = Modifier
- .fillMaxWidth()
- .weight(1f),
- text = stringResourceSafe(R.string.common_continue),
- onClick = onClickActiveButton,
- showProgress = isLoading,
- enabled = enabled,
- )
- ValidationResult.SAFE,
- ValidationResult.FAILED_TO_VALIDATE,
- null,
- -> PrimaryButtonIconEnd(
- modifier = Modifier
- .fillMaxWidth()
- .weight(1f),
- text = activeButtonText.resolveReference(),
- onClick = onClickActiveButton,
- iconResId = walletInteractionIcon,
- showProgress = isLoading,
- enabled = enabled,
- )
+ when {
+ validationResult == ValidationResult.UNSAFE ||
+ validationResult == ValidationResult.WARNING -> {
+ PrimaryButton(
+ modifier = buttonModifier,
+ text = stringResourceSafe(R.string.common_continue),
+ onClick = onClickActiveButton,
+ showProgress = isLoading,
+ enabled = enabled,
+ )
+ }
+ isHoldToConfirmEnabled -> {
+ HoldToConfirmButton(
+ modifier = buttonModifier,
+ text = stringResourceSafe(
+ CoreR.string.common_hold_to,
+ activeButtonText.resolveReference(),
+ ),
+ onConfirm = onClickActiveButton,
+ isLoading = isLoading,
+ enabled = enabled,
+ )
+ }
+ else -> {
+ PrimaryButtonIconEnd(
+ modifier = buttonModifier,
+ text = activeButtonText.resolveReference(),
+ onClick = onClickActiveButton,
+ iconResId = walletInteractionIcon,
+ showProgress = isLoading,
+ enabled = enabled,
+ )
+ }
}
},
)
diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/send/WcSendTransactionModalBottomSheet.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/send/WcSendTransactionModalBottomSheet.kt
index 313408a064..76d2c41365 100644
--- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/send/WcSendTransactionModalBottomSheet.kt
+++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/send/WcSendTransactionModalBottomSheet.kt
@@ -144,6 +144,7 @@ internal fun WcSendTransactionModalBottomSheet(
enabled = state.sendEnabled,
walletInteractionIcon = state.walletInteractionIcon,
validationResult = state.transactionValidationResult,
+ isHoldToConfirmEnabled = state.isHoldToConfirmEnabled,
)
},
)
diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/sign/WcSignTransactionModalBottomSheetContent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/sign/WcSignTransactionModalBottomSheetContent.kt
index d4e6d4a916..5a4d2abb63 100644
--- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/sign/WcSignTransactionModalBottomSheetContent.kt
+++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/ui/sign/WcSignTransactionModalBottomSheetContent.kt
@@ -101,6 +101,7 @@ internal fun WcSignTransactionModalBottomSheetContent(
isLoading = state.isLoading,
walletInteractionIcon = state.walletInteractionIcon,
validationResult = null,
+ isHoldToConfirmEnabled = state.isHoldToConfirmEnabled,
)
},
)
diff --git a/features/welcome/impl/src/main/kotlin/com/tangem/features/welcome/impl/model/WelcomeModel.kt b/features/welcome/impl/src/main/kotlin/com/tangem/features/welcome/impl/model/WelcomeModel.kt
index 4179ffefe8..83ab025490 100644
--- a/features/welcome/impl/src/main/kotlin/com/tangem/features/welcome/impl/model/WelcomeModel.kt
+++ b/features/welcome/impl/src/main/kotlin/com/tangem/features/welcome/impl/model/WelcomeModel.kt
@@ -197,18 +197,20 @@ internal class WelcomeModel @Inject constructor(
onSuccess = { scanResponse ->
val userWallet =
coldUserWalletBuilderFactory.create(scanResponse = scanResponse).build() ?: return@scan
- saveWalletUseCase.invoke(userWallet)
- .onLeft { error ->
- if (error is SaveWalletError.WalletAlreadySaved) {
- userWalletsListRepository.unlock(
- userWallet.walletId,
- unlockMethod = UserWalletsListRepository.UnlockMethod.Scan(scanResponse),
- ).onRight {
- userWalletsListRepository.select(userWallet.walletId)
- router.replaceAll(AppRoute.Wallet)
- }
+ saveWalletUseCase.invoke(
+ userWallet = userWallet,
+ analyticsSource = AnalyticsParam.ScreensSources.SignIn,
+ ).onLeft { error ->
+ if (error is SaveWalletError.WalletAlreadySaved) {
+ userWalletsListRepository.unlock(
+ userWallet.walletId,
+ unlockMethod = UserWalletsListRepository.UnlockMethod.Scan(scanResponse),
+ ).onRight {
+ userWalletsListRepository.select(userWallet.walletId)
+ router.replaceAll(AppRoute.Wallet)
}
}
+ }
.onRight {
router.replaceAll(AppRoute.Wallet)
}
diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/common/entity/YieldSupplyFeeUM.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/common/entity/YieldSupplyFeeUM.kt
index 20e09cc6cb..5b2edf1a67 100644
--- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/common/entity/YieldSupplyFeeUM.kt
+++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/common/entity/YieldSupplyFeeUM.kt
@@ -31,4 +31,5 @@ internal data class YieldSupplyActionUM(
val yieldSupplyFeeUM: YieldSupplyFeeUM,
val isPrimaryButtonEnabled: Boolean,
val isTransactionSending: Boolean,
+ val isHoldToConfirmEnabled: Boolean,
)
\ No newline at end of file
diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/common/ui/YieldSupplyActionContent.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/common/ui/YieldSupplyActionContent.kt
index 71645d1247..f45cde0551 100644
--- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/common/ui/YieldSupplyActionContent.kt
+++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/common/ui/YieldSupplyActionContent.kt
@@ -156,6 +156,7 @@ private class YieldSupplyActionContentPreviewProvider : PreviewParameterProvider
),
isPrimaryButtonEnabled = false,
isTransactionSending = false,
+ isHoldToConfirmEnabled = false,
),
)
}
diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/YieldSupplyApproveComponent.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/YieldSupplyApproveComponent.kt
index 9e641d0513..f56edfc633 100644
--- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/YieldSupplyApproveComponent.kt
+++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/YieldSupplyApproveComponent.kt
@@ -18,7 +18,9 @@ import com.tangem.common.ui.userwallet.ext.walletInterationIcon
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.child
import com.tangem.core.decompose.model.getOrCreateModel
+import com.tangem.core.ui.components.HoldToConfirmButton
import com.tangem.core.ui.components.PrimaryButtonIconEnd
+import com.tangem.core.ui.R as CoreR
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
@@ -74,15 +76,30 @@ internal class YieldSupplyApproveComponent(
)
},
footer = {
- PrimaryButtonIconEnd(
- text = stringResourceSafe(R.string.common_confirm),
- onClick = model::onClick,
- iconResId = walletInterationIcon(params.userWallet),
- enabled = state.isPrimaryButtonEnabled,
- modifier = Modifier
- .fillMaxWidth()
- .padding(16.dp),
- )
+ val modifier = Modifier
+ .fillMaxWidth()
+ .padding(16.dp)
+
+ if (state.isHoldToConfirmEnabled) {
+ HoldToConfirmButton(
+ text = stringResourceSafe(
+ CoreR.string.common_hold_to,
+ stringResourceSafe(R.string.common_confirm),
+ ),
+ onConfirm = model::onClick,
+ enabled = state.isPrimaryButtonEnabled,
+ isLoading = state.isTransactionSending,
+ modifier = modifier,
+ )
+ } else {
+ PrimaryButtonIconEnd(
+ text = stringResourceSafe(R.string.common_confirm),
+ onClick = model::onClick,
+ iconResId = walletInterationIcon(params.userWallet),
+ enabled = state.isPrimaryButtonEnabled,
+ modifier = modifier,
+ )
+ }
},
content = {
YieldSupplyActionContent(
diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/model/YieldSupplyApproveModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/model/YieldSupplyApproveModel.kt
index cc24881811..4ac6a9f1ce 100644
--- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/model/YieldSupplyApproveModel.kt
+++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/model/YieldSupplyApproveModel.kt
@@ -18,6 +18,7 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
+import com.tangem.domain.models.wallet.isHotWallet
import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.transaction.usecase.CreateApprovalTransactionUseCase
import com.tangem.domain.transaction.usecase.GetFeeUseCase
@@ -97,6 +98,7 @@ internal class YieldSupplyApproveModel @Inject constructor(
yieldSupplyFeeUM = YieldSupplyFeeUM.Loading,
isPrimaryButtonEnabled = false,
isTransactionSending = false,
+ isHoldToConfirmEnabled = params.userWallet.isHotWallet,
),
)
diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/YieldSupplyStartEarningComponent.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/YieldSupplyStartEarningComponent.kt
index 1783ee53c8..c7957a5d41 100644
--- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/YieldSupplyStartEarningComponent.kt
+++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/YieldSupplyStartEarningComponent.kt
@@ -17,6 +17,7 @@ import com.tangem.common.ui.userwallet.ext.walletInterationIcon
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.child
import com.tangem.core.decompose.model.getOrCreateModel
+import com.tangem.core.ui.components.HoldToConfirmButton
import com.tangem.core.ui.components.PrimaryButtonIconEnd
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
import com.tangem.core.ui.components.currency.icon.CurrencyIcon
@@ -25,6 +26,7 @@ import com.tangem.core.ui.extensions.stringResourceSafe
import com.tangem.core.ui.res.TangemTheme
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.wallet.UserWalletId
+import com.tangem.core.ui.R as CoreR
import com.tangem.features.yield.supply.impl.R
import com.tangem.features.yield.supply.impl.common.entity.YieldSupplyActionUM
import com.tangem.features.yield.supply.impl.common.entity.YieldSupplyFeeUM
@@ -106,22 +108,36 @@ internal class YieldSupplyStartEarningComponent(
override fun Footer() {
val state by model.uiState.collectAsStateWithLifecycle()
- val icon = if (state.isPrimaryButtonEnabled) {
- walletInterationIcon(model.userWallet)
- } else {
- null
- }
+ val modifier = Modifier
+ .fillMaxWidth()
+ .padding(16.dp)
- PrimaryButtonIconEnd(
- text = stringResourceSafe(R.string.yield_module_start_earning),
- onClick = model::onClick,
- enabled = state.isPrimaryButtonEnabled,
- iconResId = icon,
- showProgress = state.isTransactionSending,
- modifier = Modifier
- .fillMaxWidth()
- .padding(16.dp),
- )
+ if (state.isHoldToConfirmEnabled) {
+ HoldToConfirmButton(
+ text = stringResourceSafe(
+ CoreR.string.common_hold_to,
+ stringResourceSafe(R.string.yield_module_start_earning),
+ ),
+ onConfirm = model::onClick,
+ enabled = state.isPrimaryButtonEnabled,
+ isLoading = state.isTransactionSending,
+ modifier = modifier,
+ )
+ } else {
+ val icon = if (state.isPrimaryButtonEnabled) {
+ walletInterationIcon(model.userWallet)
+ } else {
+ null
+ }
+ PrimaryButtonIconEnd(
+ text = stringResourceSafe(R.string.yield_module_start_earning),
+ onClick = model::onClick,
+ enabled = state.isPrimaryButtonEnabled,
+ iconResId = icon,
+ showProgress = state.isTransactionSending,
+ modifier = modifier,
+ )
+ }
}
data class Params(
diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/YieldSupplyStartEarningEntryModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/YieldSupplyStartEarningEntryModel.kt
index 88108f3a9d..62a991685c 100644
--- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/YieldSupplyStartEarningEntryModel.kt
+++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/YieldSupplyStartEarningEntryModel.kt
@@ -49,6 +49,7 @@ internal class YieldSupplyStartEarningEntryModel @Inject constructor(
yieldSupplyFeeUM = YieldSupplyFeeUM.Loading,
isPrimaryButtonEnabled = false,
isTransactionSending = false,
+ isHoldToConfirmEnabled = false,
),
)
diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/YieldSupplyStartEarningModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/YieldSupplyStartEarningModel.kt
index c5fda7511e..ce6782467d 100644
--- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/YieldSupplyStartEarningModel.kt
+++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/startearning/model/YieldSupplyStartEarningModel.kt
@@ -15,6 +15,7 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.wallet.UserWallet
+import com.tangem.domain.models.wallet.isHotWallet
import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.transaction.error.GetFeeError
@@ -107,6 +108,7 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
yieldSupplyFeeUM = YieldSupplyFeeUM.Loading,
isPrimaryButtonEnabled = false,
isTransactionSending = false,
+ isHoldToConfirmEnabled = false,
),
)
@@ -307,6 +309,9 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
getUserWalletUseCase(userWalletId).fold(
ifRight = { wallet ->
userWallet = wallet
+ uiState.update {
+ it.copy(isHoldToConfirmEnabled = wallet.isHotWallet)
+ }
getCurrenciesStatusUpdates()
},
ifLeft = {
diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/YieldSupplyStopEarningComponent.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/YieldSupplyStopEarningComponent.kt
index d67dc087bb..eae26ea8e1 100644
--- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/YieldSupplyStopEarningComponent.kt
+++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/YieldSupplyStopEarningComponent.kt
@@ -19,7 +19,9 @@ import com.tangem.common.ui.userwallet.ext.walletInterationIcon
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.child
import com.tangem.core.decompose.model.getOrCreateModel
+import com.tangem.core.ui.components.HoldToConfirmButton
import com.tangem.core.ui.components.PrimaryButtonIconEnd
+import com.tangem.core.ui.R as CoreR
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
@@ -78,16 +80,31 @@ internal class YieldSupplyStopEarningComponent(
)
},
footer = {
- PrimaryButtonIconEnd(
- text = stringResourceSafe(R.string.common_confirm),
- onClick = model::onClick,
- iconResId = walletInterationIcon(params.userWallet),
- enabled = state.isPrimaryButtonEnabled,
- showProgress = state.isTransactionSending,
- modifier = Modifier
- .fillMaxWidth()
- .padding(16.dp),
- )
+ val modifier = Modifier
+ .fillMaxWidth()
+ .padding(16.dp)
+
+ if (state.isHoldToConfirmEnabled) {
+ HoldToConfirmButton(
+ text = stringResourceSafe(
+ CoreR.string.common_hold_to,
+ stringResourceSafe(R.string.common_confirm),
+ ),
+ onConfirm = model::onClick,
+ enabled = state.isPrimaryButtonEnabled,
+ isLoading = state.isTransactionSending,
+ modifier = modifier,
+ )
+ } else {
+ PrimaryButtonIconEnd(
+ text = stringResourceSafe(R.string.common_confirm),
+ onClick = model::onClick,
+ iconResId = walletInterationIcon(params.userWallet),
+ enabled = state.isPrimaryButtonEnabled,
+ showProgress = state.isTransactionSending,
+ modifier = modifier,
+ )
+ }
},
content = {
YieldSupplyActionContent(
diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/model/YieldSupplyStopEarningModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/model/YieldSupplyStopEarningModel.kt
index fc7f22db11..875b0b0fb4 100644
--- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/model/YieldSupplyStopEarningModel.kt
+++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/model/YieldSupplyStopEarningModel.kt
@@ -15,6 +15,7 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
+import com.tangem.domain.models.wallet.isHotWallet
import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.transaction.usecase.GetFeeUseCase
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
@@ -99,6 +100,7 @@ internal class YieldSupplyStopEarningModel @Inject constructor(
yieldSupplyFeeUM = YieldSupplyFeeUM.Loading,
isPrimaryButtonEnabled = false,
isTransactionSending = false,
+ isHoldToConfirmEnabled = params.userWallet.isHotWallet,
),
)
diff --git a/gradle/tangem_dependencies.toml b/gradle/tangem_dependencies.toml
index 9f554a7f91..abf365f0c3 100644
--- a/gradle/tangem_dependencies.toml
+++ b/gradle/tangem_dependencies.toml
@@ -5,13 +5,13 @@
# https://github.com/tangem/tangem-sdk-android/
# https://github.com/tangem/vico
-tangemBlockchainSdk = "releases-5.33-1406"
+tangemBlockchainSdk = "develop-1408"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
-tangemCardSdk = "releases-5.33-576"
+tangemCardSdk = "develop-577"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
tangemVico = "2.0.0-alpha.25-tangem12"
#tangemVico = "0.0.1" # Keep it! - used for local builds ^
-tangemHotSdk = "develop-539"
+tangemHotSdk = "develop-541"
#tangemHotSdk = "0.0.1" # Keep it! - used for local builds ^
diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/UserWalletManager.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/UserWalletManager.kt
deleted file mode 100644
index 9bdde82e08..0000000000
--- a/libs/crypto/src/main/java/com/tangem/lib/crypto/UserWalletManager.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.tangem.lib.crypto
-
-import com.tangem.lib.crypto.models.ProxyAmount
-
-/**
- * Provider for user tokens data
- */
-interface UserWalletManager {
-
- /**
- * Returns user walletId or empty string
- */
- fun getWalletId(): String
-
- suspend fun hideAllTokens()
-
- /**
- * Returns wallet public address for token
- *
- * @param networkId for currency
- * @param derivationPath if null uses default
- */
- @Throws(IllegalStateException::class)
- suspend fun getWalletAddress(networkId: String, derivationPath: String?): String
-
- @Throws(IllegalStateException::class)
- suspend fun getNativeTokenBalance(networkId: String, derivationPath: String?): ProxyAmount?
-
- @Throws(IllegalStateException::class)
- suspend fun getLastTransactionHash(networkId: String, derivationPath: String?): String?
-}
\ No newline at end of file
diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/AnalyticsData.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/AnalyticsData.kt
deleted file mode 100644
index 71a0845c8d..0000000000
--- a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/AnalyticsData.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.tangem.lib.crypto.models
-
-/**
- * Analytics data for send events in analytics engine
- *
- * @property feeType type of fee (min,max,normal)
- * @property tokenSymbol symbol
- * @property permissionType optional parameter used for type tx approve
- */
-data class AnalyticsData(
- val feeType: String,
- val tokenSymbol: String,
- val permissionType: String? = null,
-)
\ No newline at end of file
diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/ApproveTxData.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/ApproveTxData.kt
deleted file mode 100644
index f2bfab34e3..0000000000
--- a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/ApproveTxData.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.tangem.lib.crypto.models
-
-import java.math.BigDecimal
-
-/**
- * Tx data for create and make approve transaction
- *
- * @property networkId id of network
- * @property feeAmount amount of fee
- * @property gasLimit gasLimit for given tx
- * @property destinationAddress address to send tx
- * @property dataToSign data to sing with signer
- */
-data class ApproveTxData(
- val networkId: String,
- val feeAmount: BigDecimal,
- val gasLimit: Int,
- val destinationAddress: String,
- val dataToSign: String,
-)
\ No newline at end of file
diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/Currency.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/Currency.kt
deleted file mode 100644
index 1bdb582455..0000000000
--- a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/Currency.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.tangem.lib.crypto.models
-
-/**
- * Currency data class that can be native blockchain Token
- * or custom Token (used in this lib to replace and divide logic Currency from app module)
- */
-sealed class Currency(
- open val id: String,
- open val name: String,
- open val symbol: String,
- open val networkId: String,
-) {
-
- data class NativeToken(
- override val id: String,
- override val name: String,
- override val symbol: String,
- override val networkId: String,
- ) : Currency(id, name, symbol, networkId)
-
- class NonNativeToken(
- override val id: String,
- override val name: String,
- override val symbol: String,
- override val networkId: String,
- val contractAddress: String,
- val decimalCount: Int,
- ) : Currency(id, name, symbol, networkId)
-}
\ No newline at end of file
diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/ProxyAmount.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/ProxyAmount.kt
deleted file mode 100644
index 4a877ccca3..0000000000
--- a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/ProxyAmount.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.tangem.lib.crypto.models
-
-import java.math.BigDecimal
-
-/**
- * Proxy amount is always has a Coin type
- *
- * @property currencySymbol
- * @property value amount in [BigDecimal]
- * @property decimals count for token
- */
-data class ProxyAmount(
- val currencySymbol: String,
- var value: BigDecimal,
- val decimals: Int,
-) {
-
- companion object {
- fun empty(): ProxyAmount {
- return ProxyAmount("", BigDecimal.ZERO, 0)
- }
- }
-}
\ No newline at end of file
diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/ProxyFiatCurrency.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/ProxyFiatCurrency.kt
deleted file mode 100644
index 5a46bdef84..0000000000
--- a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/ProxyFiatCurrency.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.tangem.lib.crypto.models
-
-data class ProxyFiatCurrency(
- val code: String,
- val name: String,
- val symbol: String,
-)
\ No newline at end of file
diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/SwapTxData.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/SwapTxData.kt
deleted file mode 100644
index 4732f49fcb..0000000000
--- a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/SwapTxData.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.tangem.lib.crypto.models
-
-import java.math.BigDecimal
-
-/**
- * Tx data for create and make transaction
- *
- * @property networkId id of network
- * @property feeAmount amount of fee
- * @property gasLimit gasLimit for given tx
- * @property destinationAddress address to send tx
- * @property dataToSign data to sing with signer
- * @property amountToSend amount of tx
- * @property currencyToSend currency for tx
- */
-// TODO split to cex,dex
-data class SwapTxData(
- val networkId: String,
- val feeAmount: BigDecimal,
- val gasLimit: Int,
- val destinationAddress: String,
- val dataToSign: String,
- val amountToSend: BigDecimal,
- val currencyToSend: Currency,
-)
\ No newline at end of file
diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/transactions/SendTxResult.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/transactions/SendTxResult.kt
deleted file mode 100644
index b3bf9c7e7b..0000000000
--- a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/transactions/SendTxResult.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.tangem.lib.crypto.models.transactions
-
-sealed interface SendTxResult {
-
- object Success : SendTxResult
- object UserCancelledError : SendTxResult
- data class TangemSdkError(val code: Int, val cause: Throwable?) : SendTxResult
- data class BlockchainSdkError(val code: Int, val cause: Throwable?) : SendTxResult
- data class NetworkError(val ex: Exception? = null) : SendTxResult
- data class UnknownError(val ex: Exception? = null) : SendTxResult
-}
\ No newline at end of file