diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 4ff3173b40..3cf379b69f 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -83,6 +83,7 @@ dependencies {
implementation(projects.data.analytics)
implementation(projects.data.transaction)
implementation(projects.data.visa)
+ implementation(projects.data.promo)
/** Features */
implementation(projects.features.onboarding)
diff --git a/app/src/main/java/com/tangem/tap/common/feedback/AdditionalFeedbackInfo.kt b/app/src/main/java/com/tangem/tap/common/feedback/AdditionalFeedbackInfo.kt
index b588d0d641..975fb1fb2f 100644
--- a/app/src/main/java/com/tangem/tap/common/feedback/AdditionalFeedbackInfo.kt
+++ b/app/src/main/java/com/tangem/tap/common/feedback/AdditionalFeedbackInfo.kt
@@ -72,9 +72,11 @@ class AdditionalFeedbackInfo {
cardIssuer = data.card.issuer.name
signedHashesCount = formatSignedHashes(data.card.wallets)
userWalletId = UserWalletIdBuilder.scanResponse(data).build()?.stringValue ?: ""
- extendedPublicKey = data.card.wallets.firstOrNull { it.extendedPublicKey != null }
- ?.extendedPublicKey
- ?.serialize(networkType = NetworkType.Mainnet).orEmpty()
+ extendedPublicKey = runCatching {
+ data.card.wallets.firstOrNull { it.extendedPublicKey != null }
+ ?.extendedPublicKey
+ ?.serialize(networkType = NetworkType.Mainnet).orEmpty()
+ }.getOrDefault("couldn't retrieve extended public key")
}
@Deprecated("Don't use it directly")
diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt
index 841c3ff5a4..666a16ab82 100644
--- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt
@@ -4,17 +4,14 @@ import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.guard
import com.tangem.core.analytics.Analytics
+import com.tangem.core.analytics.models.Basic
import com.tangem.core.navigation.StateDialog
import com.tangem.datasource.config.models.Config
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.common.LogConfig
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
-import com.tangem.core.analytics.models.Basic
-import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
-import com.tangem.tap.common.extensions.dispatchDialogShow
-import com.tangem.tap.common.extensions.dispatchOnMain
-import com.tangem.tap.common.extensions.dispatchWithMain
+import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.features.send.redux.SendAction
import com.tangem.tap.mainScope
@@ -167,6 +164,7 @@ private fun handleAction(action: Action, appState: () -> AppState?) {
action.manager.selectedUserWallet
.distinctUntilChanged()
.onEach { userWallet ->
+ Analytics.setContext(userWallet.scanResponse)
Analytics.send(Basic.WalletOpened())
store.state.globalState.feedbackManager?.infoHolder?.let { infoHolder ->
diff --git a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt
index acdcfa699e..c67960a860 100644
--- a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt
+++ b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt
@@ -101,6 +101,7 @@ internal object TokensDomainModule {
swapRepository: SwapRepository,
currencyChecksRepository: CurrencyChecksRepository,
showSwapPromoTokenUseCase: ShouldShowSwapPromoTokenUseCase,
+ promoRepository: PromoRepository,
dispatchers: CoroutineDispatcherProvider,
): GetCurrencyWarningsUseCase {
return GetCurrencyWarningsUseCase(
@@ -112,6 +113,7 @@ internal object TokensDomainModule {
currencyChecksRepository = currencyChecksRepository,
swapRepository = swapRepository,
showSwapPromoTokenUseCase = showSwapPromoTokenUseCase,
+ promoRepository = promoRepository,
dispatchers = dispatchers,
)
}
diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt
index f039fbb8b1..508f29e664 100644
--- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt
@@ -122,7 +122,6 @@ class DetailsMiddleware {
doBeforeErase()
tangemSdkManager.resetToFactorySettings(card.cardId, true)
- .flatMap { userWalletsListManager.delete(listOfNotNull(userWalletId)) }
.flatMap { tangemSdkManager.deleteSavedUserCodes(setOf(card.cardId)) }
.doOnSuccess {
Analytics.send(Settings.CardSettings.FactoryResetFinished())
@@ -130,16 +129,21 @@ class DetailsMiddleware {
val selectedUserWallet = userWalletsListManager.selectedUserWalletSync
if (selectedUserWallet != null) {
if (userWalletsListManager.isLockedSync) {
+ userWalletsListManager.delete(listOfNotNull(userWalletId))
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Welcome))
} else {
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Wallet))
+ delay(timeMillis = 500)
+ userWalletsListManager.delete(listOfNotNull(userWalletId))
store.onUserWalletSelected(selectedUserWallet)
}
} else {
+ userWalletsListManager.delete(listOfNotNull(userWalletId))
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Home))
}
}
.doOnFailure { error ->
+ userWalletsListManager.delete(listOfNotNull(userWalletId))
if (error is TangemSdkError && error !is TangemSdkError.UserCancelled) {
Analytics.send(Settings.CardSettings.FactoryResetFinished(error))
}
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt
index 3ee9db75ea..243319a1c0 100644
--- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt
@@ -1,12 +1,15 @@
package com.tangem.tap.features.onboarding.products.otherCards.redux
import com.tangem.common.CompletionResult
+import com.tangem.common.core.TangemSdkError
import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.common.analytics.events.Onboarding
+import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.postUi
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
+import com.tangem.tap.features.onboarding.OnboardingDialog
import com.tangem.tap.features.onboarding.OnboardingHelper
import com.tangem.tap.scope
import com.tangem.tap.store
@@ -72,7 +75,10 @@ private fun handleOtherCardsAction(action: Action) {
}
is OnboardingOtherCardsAction.CreateWallet -> {
scope.launch {
- val result = tangemSdkManager.createProductWallet(onboardingManager.scanResponse)
+ val result = tangemSdkManager.createProductWallet(
+ scanResponse = onboardingManager.scanResponse,
+ shouldReset = globalState.onboardingState.shouldResetOnCreate,
+ )
withMainContext {
when (result) {
is CompletionResult.Success -> {
@@ -87,7 +93,11 @@ private fun handleOtherCardsAction(action: Action) {
delay(DELAY_SDK_DIALOG_CLOSE)
store.dispatch(OnboardingOtherCardsAction.SetStepOfScreen(OnboardingOtherCardsStep.Done))
}
- is CompletionResult.Failure -> Unit
+ is CompletionResult.Failure -> {
+ if (result.error is TangemSdkError.WalletAlreadyCreated) {
+ handleActivationError()
+ }
+ }
}
}
}
@@ -101,4 +111,14 @@ private fun handleOtherCardsAction(action: Action) {
}
else -> Unit
}
+}
+
+private fun handleActivationError() {
+ store.dispatchDialogShow(
+ OnboardingDialog.WalletActivationError(
+ onConfirm = {
+ store.dispatch(GlobalAction.Onboarding.ShouldResetCardOnCreate(true))
+ },
+ ),
+ )
}
\ No newline at end of file
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt
index ce2c8784a8..6b536fc754 100644
--- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletAction.kt
@@ -65,6 +65,7 @@ sealed class BackupAction : Action {
object StartAddingBackupCards : BackupAction()
object AddBackupCard : BackupAction() {
object Success : BackupAction()
+ data class ChangeButtonLoading(val isLoading: Boolean) : BackupAction()
}
object FinishAddingBackupCards : BackupAction()
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt
index 418ca44742..2a4ca101c4 100644
--- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt
@@ -1,6 +1,7 @@
package com.tangem.tap.features.onboarding.products.wallet.redux
import android.net.Uri
+import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.guard
@@ -70,8 +71,8 @@ private fun handleWalletAction(action: Action) {
when (action) {
OnboardingWalletAction.Init -> {
- ifNotNull(onboardingManager, card) { manager, card ->
- if (!manager.isActivationStarted(card.cardId)) {
+ ifNotNull(onboardingManager, card) { manager, notNullCard ->
+ if (!manager.isActivationStarted(notNullCard.cardId)) {
Analytics.send(Onboarding.Started())
}
}
@@ -394,17 +395,19 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
}
}
is BackupAction.AddBackupCard -> {
+ store.dispatchOnMain(BackupAction.AddBackupCard.ChangeButtonLoading(true))
backupService.addBackupCard { result ->
backupService.skipCompatibilityChecks = false
store.state.daggerGraphState.get(DaggerGraphState::cardSdkConfigRepository).sdk
.config.filter.cardIdFilter = null
-
+ store.dispatchOnMain(BackupAction.AddBackupCard.ChangeButtonLoading(false))
when (result) {
is CompletionResult.Success -> {
store.dispatchOnMain(BackupAction.AddBackupCard.Success)
}
-
is CompletionResult.Failure -> {
+ val crashlytics = FirebaseCrashlytics.getInstance()
+
when (val error = result.error) {
is TangemSdkError.BackupFailedNotEmptyWallets -> {
if (card?.canSkipBackup == false) {
@@ -413,6 +416,8 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
BackupDialog.ResetBackupCard(error.cardId),
),
)
+ } else {
+ crashlytics.recordException(error)
}
}
is TangemSdkError.IssuerSignatureLoadingFailed -> {
@@ -420,7 +425,7 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
GlobalAction.ShowDialog(BackupDialog.AttestationFailed),
)
}
- else -> Unit
+ else -> crashlytics.recordException(error)
}
}
}
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt
index 1e6dc646b8..61bee3be0f 100644
--- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletReducer.kt
@@ -81,6 +81,7 @@ private object BackupReducer {
BackupAction.StartAddingPrimaryCard -> state.copy(backupStep = BackupStep.ScanOriginCard)
BackupAction.StartAddingBackupCards -> state.copy(backupStep = BackupStep.AddBackupCards)
BackupAction.AddBackupCard.Success -> state.copy(backupCardsNumber = state.backupCardsNumber + 1)
+ is BackupAction.AddBackupCard.ChangeButtonLoading -> state.copy(showBtnLoading = action.isLoading)
BackupAction.ShowAccessCodeInfoScreen -> state.copy(backupStep = BackupStep.SetAccessCode)
BackupAction.ShowEnterAccessCodeScreen -> state.copy(
backupStep = BackupStep.EnterAccessCode,
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt
index 508e40db10..0920fb4a9b 100644
--- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt
@@ -69,6 +69,7 @@ data class BackupState(
val maxBackupCards: Int = 2,
val canSkipBackup: Boolean = true,
val isInterruptedBackup: Boolean = false,
+ val showBtnLoading: Boolean = false,
)
enum class AccessCodeError {
diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt
index fa12a0f3d5..9a08513cdb 100644
--- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt
+++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt
@@ -1,5 +1,6 @@
package com.tangem.tap.features.onboarding.products.wallet.ui
+import android.content.res.ColorStateList
import android.net.Uri
import android.os.Bundle
import android.util.TypedValue
@@ -296,10 +297,26 @@ class OnboardingWalletFragment :
layoutButtonsAddCards.root.show()
layoutButtonsCommon.root.hide()
- layoutButtonsAddCards.btnAddCard.text = getText(R.string.onboarding_button_add_backup_card)
layoutButtonsAddCards.btnAddCard.setIconResource(R.drawable.ic_tangem_24)
+ if (state.showBtnLoading) {
+ layoutButtonsAddCards.btnAddCard.iconTint = ColorStateList.valueOf(
+ resources.getColor(R.color.button_secondary),
+ )
+ layoutButtonsAddCards.btnAddCard.text = ""
+ layoutButtonsAddCards.btnAddCard.isEnabled = false
+ layoutButtonsAddCards.btnProgress.show()
+ } else {
+ layoutButtonsAddCards.btnAddCard.iconTint = ColorStateList.valueOf(
+ resources.getColor(R.color.icon_primary_1),
+ )
+ layoutButtonsAddCards.btnProgress.hide()
+ layoutButtonsAddCards.btnAddCard.text = getText(R.string.onboarding_button_add_backup_card)
+ layoutButtonsAddCards.btnAddCard.isEnabled = true
+ }
if (state.backupCardsNumber < state.maxBackupCards) {
- layoutButtonsAddCards.btnAddCard.setOnClickListener { store.dispatch(BackupAction.AddBackupCard) }
+ layoutButtonsAddCards.btnAddCard.setOnClickListener {
+ store.dispatch(BackupAction.AddBackupCard)
+ }
} else {
layoutButtonsAddCards.btnAddCard.isEnabled = false
}
diff --git a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt
index 7ab41ec265..9408cbcc9e 100644
--- a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt
+++ b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt
@@ -129,7 +129,14 @@ internal class SaveWalletMiddleware {
}
store.dispatchOnMain(SaveWalletAction.Save.Success)
- store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Wallet))
+
+ store.dispatchOnMain(
+ if (store.state.navigationState.backStack.contains(AppScreen.Wallet)) {
+ NavigationAction.PopBackTo(AppScreen.Wallet)
+ } else {
+ NavigationAction.NavigateTo(AppScreen.Wallet)
+ },
+ )
}
}
}
diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt
index a19ca0cd84..d0f3c8269e 100644
--- a/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt
+++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt
@@ -36,7 +36,7 @@ internal class MercuryoService(private val environment: MercuryoEnvironment) : E
val availableCurrency = availableMercuryoCurrencies.firstOrNull {
it.currencySymbol == currency.currencySymbol &&
it.network == mercuryoNetwork &&
- it.contractAddress == contractAddress
+ it.contractAddress.equals(contractAddress, ignoreCase = true)
}
return availableCurrency != null
}
diff --git a/app/src/main/res/layout/layout_onboarding_buttons_add_cards.xml b/app/src/main/res/layout/layout_onboarding_buttons_add_cards.xml
index 505d485b6f..a6fd01cc5b 100644
--- a/app/src/main/res/layout/layout_onboarding_buttons_add_cards.xml
+++ b/app/src/main/res/layout/layout_onboarding_buttons_add_cards.xml
@@ -5,22 +5,37 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
-
+ app:layout_constraintStart_toStartOf="@id/btn_continue">
+
+
+
+
+
#000000
#00000000
- d
#FFFFFF
#F0FFFF
diff --git a/app/src/test/kotlin/com/tangem/tap/domain/card/DefaultDerivationsRepositoryTest.kt b/app/src/test/kotlin/com/tangem/tap/domain/card/DefaultDerivationsRepositoryTest.kt
index e818b118ab..730ddb8d96 100644
--- a/app/src/test/kotlin/com/tangem/tap/domain/card/DefaultDerivationsRepositoryTest.kt
+++ b/app/src/test/kotlin/com/tangem/tap/domain/card/DefaultDerivationsRepositoryTest.kt
@@ -36,7 +36,7 @@ internal class DefaultDerivationsRepositoryTest {
artworkUrl = "",
cardsInWallet = setOf(),
isMultiCurrency = false,
- scanResponse = ScanResponseMockFactory.create(cardConfig = GenericCardConfig, derivedKeys = emptyMap()),
+ scanResponse = ScanResponseMockFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap()),
)
@Test
diff --git a/app/src/test/kotlin/com/tangem/tap/domain/card/MissedDerivationsFinderTest.kt b/app/src/test/kotlin/com/tangem/tap/domain/card/MissedDerivationsFinderTest.kt
index 2506e539a5..7091e26f4f 100644
--- a/app/src/test/kotlin/com/tangem/tap/domain/card/MissedDerivationsFinderTest.kt
+++ b/app/src/test/kotlin/com/tangem/tap/domain/card/MissedDerivationsFinderTest.kt
@@ -20,7 +20,7 @@ internal class MissedDerivationsFinderTest {
@Test
fun `empty derivations for empty currencies`() {
- val scanResponse = ScanResponseMockFactory.create(cardConfig = GenericCardConfig, derivedKeys = emptyMap())
+ val scanResponse = ScanResponseMockFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap())
val finder = MissedDerivationsFinder(scanResponse)
val actual = finder.find(emptyList())
@@ -31,7 +31,7 @@ internal class MissedDerivationsFinderTest {
@Test
fun `empty derivations for non supported blockchains`() {
// Bls is not supported
- val scanResponse = ScanResponseMockFactory.create(cardConfig = GenericCardConfig, derivedKeys = emptyMap())
+ val scanResponse = ScanResponseMockFactory.create(cardConfig = GenericCardConfig(2), derivedKeys = emptyMap())
val finder = MissedDerivationsFinder(scanResponse)
val currencies = CryptoCurrenciesMocks(scanResponse).chia
@@ -44,7 +44,7 @@ internal class MissedDerivationsFinderTest {
fun `derivations ONLY for supported blockchains`() {
// Bls is not supported
val scanResponse = ScanResponseMockFactory.create(
- cardConfig = GenericCardConfig,
+ cardConfig = GenericCardConfig(2),
derivedKeys = emptyMap(),
).let {
it.copy(
diff --git a/app/src/test/kotlin/com/tangem/tap/domain/card/ScanResponseMockFactory.kt b/app/src/test/kotlin/com/tangem/tap/domain/card/ScanResponseMockFactory.kt
index 359cde99df..da79e800f0 100644
--- a/app/src/test/kotlin/com/tangem/tap/domain/card/ScanResponseMockFactory.kt
+++ b/app/src/test/kotlin/com/tangem/tap/domain/card/ScanResponseMockFactory.kt
@@ -2,10 +2,7 @@ package com.tangem.tap.domain.card
import com.tangem.common.card.CardWallet
import com.tangem.common.card.FirmwareVersion
-import com.tangem.domain.common.configs.CardConfig
-import com.tangem.domain.common.configs.GenericCardConfig
-import com.tangem.domain.common.configs.MultiWalletCardConfig
-import com.tangem.domain.common.configs.Wallet2CardConfig
+import com.tangem.domain.common.configs.*
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.KeyWalletPublicKey
import com.tangem.domain.models.scan.ProductType
@@ -47,9 +44,10 @@ object ScanResponseMockFactory {
batchId = "NEVER-MIND",
cardPublicKey = ByteArray(0),
firmwareVersion = when (cardConfig) {
- GenericCardConfig -> genericFirmwareVersion
+ is GenericCardConfig -> genericFirmwareVersion
MultiWalletCardConfig -> walletFirmwareVersion
Wallet2CardConfig -> wallet2FirmwareVersion
+ EdSingleCurrencyCardConfig -> genericFirmwareVersion
},
manufacturer = CardDTO.Manufacturer(name = "NEVER-MIND", manufactureDate = Date(), signature = null),
issuer = CardDTO.Issuer(name = "NEVER-MIND", publicKey = ByteArray(0)),
diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/PromotionApi.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/PromotionApi.kt
deleted file mode 100644
index d71061c21a..0000000000
--- a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/PromotionApi.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.tangem.datasource.api.promotion
-
-import com.tangem.datasource.api.promotion.models.*
-import retrofit2.http.*
-
-/**
- *
- * Promotion API
- * @see Documentation
-[REDACTED_AUTHOR]
- */
-interface PromotionApi {
-
- @Headers("Cache-Control: max-age=3600")
- @GET("promotion")
- suspend fun getPromotionInfo(@Query("programName") name: String): PromotionInfoResponse
-
- @POST("promotion/code/validate")
- suspend fun validateCode(@Body request: CodeValidateRequestBody): CodeValidateResponse
-
- @POST("promotion/code/award")
- suspend fun requestAwardByCode(@Body request: CodeAwardRequestBody): CodeAwardResponse
-
- @POST("promotion/validate")
- suspend fun validate(@Body request: ValidateRequestBody): ValidateResponse
-
- @POST("promotion/award")
- suspend fun requestAward(@Body request: AwardRequestBody): AwardResponse
-}
\ No newline at end of file
diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AbstractPromotionResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AbstractPromotionResponse.kt
deleted file mode 100644
index edfc8e990d..0000000000
--- a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AbstractPromotionResponse.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.tangem.datasource.api.promotion.models
-
-import com.squareup.moshi.Json
-
-/**
-[REDACTED_AUTHOR]
- */
-abstract class AbstractPromotionResponse {
-
- abstract val error: Error?
-
- fun isError(): Boolean = error != null
-
- data class Error(
- @Json(name = "code") val code: Int,
- @Json(name = "message") val message: String,
- )
-}
\ No newline at end of file
diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AwardRequestBody.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AwardRequestBody.kt
deleted file mode 100644
index c8ac7faed1..0000000000
--- a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AwardRequestBody.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.tangem.datasource.api.promotion.models
-
-import com.squareup.moshi.Json
-
-/**
-[REDACTED_AUTHOR]
- */
-data class AwardRequestBody(
- @Json(name = "walletId") val walletId: String,
- @Json(name = "address") val address: String,
- @Json(name = "programName") val programName: String,
-)
\ No newline at end of file
diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AwardResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AwardResponse.kt
deleted file mode 100644
index c43c59fa45..0000000000
--- a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/AwardResponse.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.tangem.datasource.api.promotion.models
-
-import com.squareup.moshi.Json
-
-/**
-[REDACTED_AUTHOR]
- */
-data class AwardResponse(
- @Json(name = "status") val status: Boolean?,
- @Json(name = "error") override val error: Error? = null,
-) : AbstractPromotionResponse()
\ No newline at end of file
diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeAwardRequestBody.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeAwardRequestBody.kt
deleted file mode 100644
index 51e8626d4b..0000000000
--- a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeAwardRequestBody.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.tangem.datasource.api.promotion.models
-
-import com.squareup.moshi.Json
-
-/**
-[REDACTED_AUTHOR]
- */
-data class CodeAwardRequestBody(
- @Json(name = "walletId") val walletId: String,
- @Json(name = "address") val address: String,
- @Json(name = "code") val code: String?,
-)
\ No newline at end of file
diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeAwardResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeAwardResponse.kt
deleted file mode 100644
index 33d9313c42..0000000000
--- a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeAwardResponse.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.tangem.datasource.api.promotion.models
-
-import com.squareup.moshi.Json
-
-/**
-[REDACTED_AUTHOR]
- */
-data class CodeAwardResponse(
- @Json(name = "status") val status: Boolean?,
- @Json(name = "error") override val error: Error? = null,
-) : AbstractPromotionResponse()
\ No newline at end of file
diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeValidateRequestBody.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeValidateRequestBody.kt
deleted file mode 100644
index 769ecdad26..0000000000
--- a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeValidateRequestBody.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.tangem.datasource.api.promotion.models
-
-import com.squareup.moshi.Json
-
-/**
-[REDACTED_AUTHOR]
- */
-data class CodeValidateRequestBody(
- @Json(name = "walletId") val walletId: String,
- @Json(name = "code") val code: String?,
-)
\ No newline at end of file
diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeValidateResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeValidateResponse.kt
deleted file mode 100644
index e6e87e0ff3..0000000000
--- a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/CodeValidateResponse.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.tangem.datasource.api.promotion.models
-
-import com.squareup.moshi.Json
-
-/**
-[REDACTED_AUTHOR]
- */
-data class CodeValidateResponse(
- @Json(name = "valid") val valid: Boolean?,
- @Json(name = "error") override val error: Error? = null,
-) : AbstractPromotionResponse()
\ No newline at end of file
diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/PromotionInfoResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/PromotionInfoResponse.kt
index ca5406aba5..fa9140eea1 100644
--- a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/PromotionInfoResponse.kt
+++ b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/PromotionInfoResponse.kt
@@ -1,40 +1,23 @@
package com.tangem.datasource.api.promotion.models
import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
-/**
-[REDACTED_AUTHOR]
- */
+@JsonClass(generateAdapter = true)
data class PromotionInfoResponse(
- @Json(name = "newCard") val newCard: Data?,
- @Json(name = "oldCard") val oldCard: Data?,
- @Json(name = "awardPaymentToken") val awardPaymentToken: TokenData?,
- @Json(name = "error") override val error: Error? = null,
-) : AbstractPromotionResponse() {
+ @Json(name = "name") val name: String,
+ @Json(name = "all") val bannerState: BannerState?,
+) {
- data class Data(
- @Json(name = "status") val status: Status,
- @Json(name = "award") val award: Double,
+ @JsonClass(generateAdapter = true)
+ data class BannerState(
+ @Json(name = "timeline") val timeline: Timeline,
+ @Json(name = "status") val status: String,
)
- enum class Status(val value: String) {
- @Json(name = "pending")
- PENDING("pending"),
-
- @Json(name = "active")
- ACTIVE("active"),
-
- @Json(name = "finished")
- FINISHED("finished"),
- }
-
- data class TokenData(
- @Json(name = "id") val id: String,
- @Json(name = "name") val name: String,
- @Json(name = "symbol") val symbol: String,
- @Json(name = "active") val active: Boolean,
- @Json(name = "networkId") val networkId: String,
- @Json(name = "contractAddress") val contractAddress: String,
- @Json(name = "decimalCount") val decimalCount: Int,
+ @JsonClass(generateAdapter = true)
+ data class Timeline(
+ @Json(name = "start") val start: String,
+ @Json(name = "end") val end: String,
)
}
\ No newline at end of file
diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/ValidateRequestBody.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/ValidateRequestBody.kt
deleted file mode 100644
index 090f0e0d29..0000000000
--- a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/ValidateRequestBody.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.tangem.datasource.api.promotion.models
-
-import com.squareup.moshi.Json
-
-/**
-[REDACTED_AUTHOR]
- */
-data class ValidateRequestBody(
- @Json(name = "walletId") val walletId: String,
- @Json(name = "programName") val programName: String,
-)
\ No newline at end of file
diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/ValidateResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/ValidateResponse.kt
deleted file mode 100644
index 97cd74645d..0000000000
--- a/core/datasource/src/main/java/com/tangem/datasource/api/promotion/models/ValidateResponse.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.tangem.datasource.api.promotion.models
-
-import com.squareup.moshi.Json
-
-/**
-[REDACTED_AUTHOR]
- */
-data class ValidateResponse(
- @Json(name = "valid") val valid: Boolean?,
- @Json(name = "error") override val error: Error? = null,
-) : AbstractPromotionResponse()
\ 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 9992d45c5f..43ff3af6e5 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
@@ -1,6 +1,7 @@
package com.tangem.datasource.api.tangemTech
import com.tangem.datasource.api.common.response.ApiResponse
+import com.tangem.datasource.api.promotion.models.PromotionInfoResponse
import com.tangem.datasource.api.tangemTech.models.*
import retrofit2.http.*
@@ -71,4 +72,7 @@ interface TangemTechApi {
@Query("coinIds") coinIds: String,
@Query("fields") fields: String = "price,priceChange24h,lastUpdatedAt",
): ApiResponse
+
+ @GET("promotion")
+ suspend fun getPromotionInfo(@Query("programName") name: String): ApiResponse
}
\ No newline at end of file
diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/PromotionOneInch.kt b/core/datasource/src/main/java/com/tangem/datasource/di/DevTangemApi.kt
similarity index 80%
rename from core/datasource/src/main/java/com/tangem/datasource/di/PromotionOneInch.kt
rename to core/datasource/src/main/java/com/tangem/datasource/di/DevTangemApi.kt
index 9e2d6b3981..4bfacf89db 100644
--- a/core/datasource/src/main/java/com/tangem/datasource/di/PromotionOneInch.kt
+++ b/core/datasource/src/main/java/com/tangem/datasource/di/DevTangemApi.kt
@@ -5,4 +5,4 @@ import javax.inject.Qualifier
@Qualifier
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)
-annotation class PromotionOneInch
\ No newline at end of file
+annotation class DevTangemApi
\ No newline at end of file
diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt
index 754936c7a5..eb61db86b2 100644
--- a/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt
+++ b/core/datasource/src/main/java/com/tangem/datasource/di/NetworkModule.kt
@@ -5,12 +5,10 @@ import com.squareup.moshi.Moshi
import com.tangem.datasource.BuildConfig
import com.tangem.datasource.api.common.response.ApiResponseCallAdapterFactory
import com.tangem.datasource.api.express.TangemExpressApi
-import com.tangem.datasource.api.promotion.PromotionApi
import com.tangem.datasource.api.tangemTech.TangemTechApi
import com.tangem.datasource.utils.RequestHeader.*
import com.tangem.datasource.utils.addHeaders
import com.tangem.datasource.utils.addLoggers
-import com.tangem.lib.auth.AuthProvider
import com.tangem.lib.auth.ExpressAuthProvider
import dagger.Module
import dagger.Provides
@@ -20,7 +18,6 @@ import dagger.hilt.components.SingletonComponent
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
-import java.util.concurrent.TimeUnit
import javax.inject.Singleton
@Module
@@ -75,31 +72,25 @@ class NetworkModule {
}
@Provides
+ @DevTangemApi
@Singleton
- @PromotionOneInch
- fun providePromotionOneInchApi(
- authProvider: AuthProvider,
- @NetworkMoshi moshi: Moshi,
- @ApplicationContext context: Context,
- ): PromotionApi {
- val okClient = OkHttpClient.Builder()
- .addHeaders(AuthenticationHeader(authProvider))
- .addLoggers(context)
- .callTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)
- .connectTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)
- .readTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)
- .writeTimeout(API_ONE_INCH_TIMEOUT_MS, TimeUnit.MILLISECONDS)
- .build()
- return createBasePromotionRetrofit(okClient, moshi)
- }
-
- private fun createBasePromotionRetrofit(okHttpClient: OkHttpClient, moshi: Moshi): PromotionApi {
+ fun provideTangemTechDevApi(@NetworkMoshi moshi: Moshi, @ApplicationContext context: Context): TangemTechApi {
return Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
- .baseUrl(PROD_TANGEM_TECH_BASE_URL)
- .client(okHttpClient)
+ .addCallAdapterFactory(ApiResponseCallAdapterFactory.create())
+ .baseUrl(DEV_TANGEM_TECH_BASE_URL)
+ .client(
+ OkHttpClient.Builder()
+ .addHeaders(
+ CacheControlHeader,
+ // TODO("refactor header init") get auth data after biometric auth to avoid race condition
+ // AuthenticationHeader(authProvider),
+ )
+ .addLoggers(context)
+ .build(),
+ )
.build()
- .create(PromotionApi::class.java)
+ .create(TangemTechApi::class.java)
}
private companion object {
diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt
index 8295d9e9a9..c5c54133ac 100644
--- a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt
+++ b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/PreferencesKeys.kt
@@ -45,9 +45,13 @@ object PreferencesKeys {
val LAST_SWAPPED_CRYPTOCURRENCY_ID_KEY by lazy { stringPreferencesKey(name = "lastSwappedCryptoCurrency") }
- val IS_WALLET_SWAP_PROMO_SHOW_KEY by lazy { booleanPreferencesKey(name = "isWalletSwapPromoShown") }
+ val IS_WALLET_SWAP_PROMO_CHANGELLY_SHOW_KEY by lazy {
+ booleanPreferencesKey(name = "isWalletSwapPromoChangellyShown")
+ }
- val IS_TOKEN_SWAP_PROMO_SHOW_KEY by lazy { booleanPreferencesKey(name = "isTokenSwapPromoShown") }
+ val IS_TOKEN_SWAP_PROMO_CHANGELLY_SHOW_KEY by lazy {
+ booleanPreferencesKey(name = "isTokenSwapPromoChangellyShown")
+ }
val FEATURE_TOGGLES_KEY by lazy { stringPreferencesKey(name = "featureToggles") }
}
diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml
index 8c7c16f2b5..007f6d05eb 100644
--- a/core/res/src/main/res/values-ru/strings.xml
+++ b/core/res/src/main/res/values-ru/strings.xml
@@ -273,8 +273,8 @@
1INCH токены будут зачислены на адрес вашего кошелька в сети %s в течение 48 часов
Чтобы получить доступ ко всем сетям, вам необходимо отсканировать карту
Отсканируйте карту
- Обменивайте свои цифровые активы между различными сетями
- Кроссчейн-своп теперь доступен
+ Обменивайте свои токены с %1$s комиссии провайдера через Changelly с %2$s по %3$s февраля.
+ Обмен с Changelly, %s комиссии
Токены
Добавить
Изменить
@@ -576,9 +576,9 @@
Токен %1$s является основной валютой в сети %2$s и не может быть скрыт до тех пор, пока у вас в списке есть другие токены этой сети.
Невозможно скрыть %s
Нет цены
+ Обменивайте этот токен на другие с %1$s комиссии за обслуживание с %2$s по %3$s февраля.
+ Обмен с Changelly, %s комиссии
Обменять
- Обменяйте этот токен на другие активы в вашем портфеле
- Представляем кроссчейн-своп
контракт: %s
У вас еще нет транзакций
Не удалось загрузить историю транзакций.\nНажмите на кнопку перезагрузки, чтобы обновить информацию.
diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index 636726e3ca..0040f9b3b0 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -273,8 +273,8 @@
1INCH tokens will be credited to your %s wallet address within 48 hours
To access all the networks you need to scan the card
Scan your card
- Swap multiple currencies across several blockchains
- Cross-chain swaps are now available
+ Enjoy %1$s service fees on swaps via Changelly from February %2$s-%3$s
+ Swap with Changelly, %s fees
Tokens
Add
Edit
@@ -587,8 +587,8 @@
The %1$s token is the main currency on the %2$s network and cannot be hidden as long as you have other tokens on this network in the list.
Unable to hide %s
No rate
- Exchange this token for other assets in your portfolio
- Introducing cross-chain swaps
+ Exchange this token for another at %1$s service fees from February %2$s-%3$s.
+ Swap with Changelly, %s fees
Swap now
contract: %s
You don\'t have any transactions yet
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButton.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButton.kt
index c1d501e021..11fcaeb099 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButton.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButton.kt
@@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
@@ -17,6 +18,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.sp
import com.tangem.core.ui.components.ResizableText
import com.tangem.core.ui.res.TangemTheme
+import com.tangem.core.ui.utils.MultipleClickPreventer
@Suppress("LongParameterList")
@Composable
@@ -35,9 +37,13 @@ fun TangemButton(
shape: Shape = size.toShape(),
iconPadding: Dp = size.toIconPadding(),
) {
+ val multipleClickPreventer = remember { MultipleClickPreventer.get() }
+
Button(
modifier = modifier.heightIn(min = size.toHeightDp()),
- onClick = { if (!showProgress) onClick() },
+ onClick = {
+ multipleClickPreventer.processEvent { if (!showProgress) onClick() }
+ },
enabled = enabled,
elevation = elevation,
shape = shape,
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/NotificationWithBackground.kt b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/NotificationWithBackground.kt
index 691cded7ba..e4e8a06833 100644
--- a/core/ui/src/main/java/com/tangem/core/ui/components/notifications/NotificationWithBackground.kt
+++ b/core/ui/src/main/java/com/tangem/core/ui/components/notifications/NotificationWithBackground.kt
@@ -28,6 +28,7 @@ import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
+import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.res.LocalIsInDarkTheme
import com.tangem.core.ui.res.TangemColorPalette.Dark6
import com.tangem.core.ui.res.TangemColorPalette.Light4
@@ -55,7 +56,7 @@ fun NotificationWithBackground(config: NotificationConfig, modifier: Modifier =
val spacing14 = TangemTheme.dimens.spacing14
Image(
- painter = painterResource(config.backgroundResId ?: R.drawable.img_swap_promo_banner_background),
+ painter = painterResource(config.backgroundResId ?: R.drawable.img_swap_promo_blue_banner_background),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.constrainAs(backgroundRef) {
@@ -171,13 +172,19 @@ private class NotificationWithBackgroundPreviewProvider : PreviewParameterProvid
override val values: Sequence
get() = sequenceOf(
NotificationConfig(
- title = resourceReference(id = R.string.main_swap_promotion_title),
- subtitle = resourceReference(id = R.string.main_swap_promotion_message),
+ title = resourceReference(id = R.string.main_swap_changelly_promotion_title),
+ subtitle = resourceReference(
+ id = R.string.main_swap_changelly_promotion_message,
+ formatArgs = wrappedList("1", "2"),
+ ),
iconResId = R.drawable.img_swap_promo,
- backgroundResId = R.drawable.img_swap_promo_banner_background,
+ backgroundResId = R.drawable.img_swap_promo_blue_banner_background,
),
NotificationConfig(
- title = resourceReference(id = R.string.token_swap_promotion_title),
+ title = resourceReference(
+ id = R.string.token_swap_changelly_promotion_title,
+ formatArgs = wrappedList("1", "2"),
+ ),
subtitle = stringReference(
"Swap multiple currencies between any chains you wish. Swap multiple " +
"currencies between any chains you wish. Swap multiple " +
@@ -185,7 +192,7 @@ private class NotificationWithBackgroundPreviewProvider : PreviewParameterProvid
"till Dec 31.",
),
iconResId = R.drawable.img_swap_promo,
- backgroundResId = R.drawable.img_swap_promo_banner_background,
+ backgroundResId = R.drawable.img_swap_promo_blue_banner_background,
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
text = resourceReference(id = R.string.token_swap_promotion_button),
onClick = {},
diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/wallets/RenameWalletDialogContent.kt b/core/ui/src/main/java/com/tangem/core/ui/components/wallets/RenameWalletDialogContent.kt
deleted file mode 100644
index eabd0fbeb8..0000000000
--- a/core/ui/src/main/java/com/tangem/core/ui/components/wallets/RenameWalletDialogContent.kt
+++ /dev/null
@@ -1,59 +0,0 @@
-package com.tangem.core.ui.components.wallets
-
-import androidx.compose.runtime.*
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.input.TextFieldValue
-import androidx.compose.ui.tooling.preview.Preview
-import com.tangem.core.ui.R
-import com.tangem.core.ui.components.AdditionalTextInputDialogParams
-import com.tangem.core.ui.components.DialogButton
-import com.tangem.core.ui.components.TextInputDialog
-import com.tangem.core.ui.res.TangemTheme
-
-/**
- * Rename a wallet dialog
- *
- * @param name wallet name
- * @param onConfirm lambda be invoked when Confirm button is clicked
- * @param onDismiss lambda be invoked when dialog is dismissed
- */
-@Composable
-fun RenameWalletDialogContent(name: String, onConfirm: (newName: String) -> Unit, onDismiss: () -> Unit) {
- var value by remember { mutableStateOf(TextFieldValue(text = name)) }
-
- TextInputDialog(
- fieldValue = value,
- confirmButton = DialogButton(
- title = stringResource(id = R.string.common_ok),
- enabled = value.text.isNotEmpty() && value.text != name,
- onClick = { onConfirm(value.text) },
- ),
- onDismissDialog = onDismiss,
- onValueChange = { value = it },
- title = stringResource(R.string.user_wallet_list_rename_popup_title),
- dismissButton = DialogButton(title = stringResource(id = R.string.common_cancel), onClick = onDismiss),
- textFieldParams = AdditionalTextInputDialogParams(
- label = stringResource(R.string.user_wallet_list_rename_popup_placeholder),
- ),
- )
-}
-
-// region Preview
-
-@Preview(showBackground = true, widthDp = 360)
-@Composable
-private fun RenameWalletDialogContentPreview_Light() {
- TangemTheme(isDark = false) {
- RenameWalletDialogContent(name = "", onConfirm = {}, onDismiss = {})
- }
-}
-
-@Preview(showBackground = true, widthDp = 360)
-@Composable
-private fun RenameWalletDialogContentPreview_Dark() {
- TangemTheme(isDark = true) {
- RenameWalletDialogContent(name = "", onConfirm = {}, onDismiss = {})
- }
-}
-
-// endregion Preview
\ No newline at end of file
diff --git a/core/ui/src/main/java/com/tangem/core/ui/utils/MultipleClickPreventer.kt b/core/ui/src/main/java/com/tangem/core/ui/utils/MultipleClickPreventer.kt
new file mode 100644
index 0000000000..15ede204ae
--- /dev/null
+++ b/core/ui/src/main/java/com/tangem/core/ui/utils/MultipleClickPreventer.kt
@@ -0,0 +1,27 @@
+package com.tangem.core.ui.utils
+
+import android.os.SystemClock
+
+interface MultipleClickPreventer {
+
+ fun processEvent(event: () -> Unit)
+
+ companion object {
+
+ fun get(): MultipleClickPreventer = DefaultMultipleClickPreventer()
+ }
+}
+
+private class DefaultMultipleClickPreventer : MultipleClickPreventer {
+ private val now: Long get() = SystemClock.elapsedRealtime()
+ private var lastEventTimeMs: Long = 0
+
+ override fun processEvent(event: () -> Unit) {
+ if (now - lastEventTimeMs >= TIME_BETWEEN_CLICK_IN_MS) event()
+ lastEventTimeMs = now
+ }
+
+ companion object {
+ private const val TIME_BETWEEN_CLICK_IN_MS = 500L
+ }
+}
\ No newline at end of file
diff --git a/core/ui/src/main/res/drawable/img_swap_promo_banner_background.webp b/core/ui/src/main/res/drawable/img_swap_promo_blue_banner_background.webp
similarity index 100%
rename from core/ui/src/main/res/drawable/img_swap_promo_banner_background.webp
rename to core/ui/src/main/res/drawable/img_swap_promo_blue_banner_background.webp
diff --git a/core/ui/src/main/res/drawable/img_swap_promo_green_banner_background.webp b/core/ui/src/main/res/drawable/img_swap_promo_green_banner_background.webp
new file mode 100644
index 0000000000..ffaced2ad2
Binary files /dev/null and b/core/ui/src/main/res/drawable/img_swap_promo_green_banner_background.webp differ
diff --git a/data/promo/.gitignore b/data/promo/.gitignore
new file mode 100644
index 0000000000..42afabfd2a
--- /dev/null
+++ b/data/promo/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/data/promo/build.gradle.kts b/data/promo/build.gradle.kts
new file mode 100644
index 0000000000..b11dc26ed9
--- /dev/null
+++ b/data/promo/build.gradle.kts
@@ -0,0 +1,25 @@
+plugins {
+ alias(deps.plugins.android.library)
+ alias(deps.plugins.kotlin.android)
+ alias(deps.plugins.kotlin.kapt)
+ alias(deps.plugins.hilt.android)
+ id("configuration")
+}
+
+android {
+ namespace = "com.tangem.data.promo"
+}
+
+dependencies {
+ implementation(deps.androidx.datastore)
+
+ implementation(deps.jodatime)
+ implementation(deps.hilt.android)
+ kapt(deps.hilt.kapt)
+
+ implementation(projects.domain.tokens)
+ implementation(projects.domain.tokens.models)
+
+ implementation(projects.core.datasource)
+ implementation(projects.core.utils)
+}
\ No newline at end of file
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
new file mode 100644
index 0000000000..3121a961d7
--- /dev/null
+++ b/data/promo/src/main/java/com/tangem/data/promo/DefaultPromoRepository.kt
@@ -0,0 +1,29 @@
+package com.tangem.data.promo
+
+import com.tangem.data.promo.converters.PromoResponseConverter
+import com.tangem.datasource.api.common.response.getOrThrow
+import com.tangem.datasource.api.tangemTech.TangemTechApi
+import com.tangem.domain.promo.PromoBanner
+import com.tangem.domain.tokens.repository.PromoRepository
+import com.tangem.utils.coroutines.CoroutineDispatcherProvider
+import com.tangem.utils.coroutines.runCatching
+
+internal class DefaultPromoRepository(
+ private val tangemApi: TangemTechApi,
+ private val dispatchers: CoroutineDispatcherProvider,
+) : PromoRepository {
+
+ private val promoResponseConverter = PromoResponseConverter()
+ override suspend fun getChangellyPromoBanner(): PromoBanner? {
+ return runCatching(dispatchers.io) {
+ promoResponseConverter.convert(
+ tangemApi.getPromotionInfo(CHANGELLY_NAME)
+ .getOrThrow(),
+ )
+ }.getOrNull()
+ }
+
+ private companion object {
+ private const val CHANGELLY_NAME = "changelly"
+ }
+}
\ No newline at end of file
diff --git a/data/promo/src/main/java/com/tangem/data/promo/converters/PromoResponseConverter.kt b/data/promo/src/main/java/com/tangem/data/promo/converters/PromoResponseConverter.kt
new file mode 100644
index 0000000000..c7ad8f331a
--- /dev/null
+++ b/data/promo/src/main/java/com/tangem/data/promo/converters/PromoResponseConverter.kt
@@ -0,0 +1,23 @@
+package com.tangem.data.promo.converters
+
+import com.tangem.datasource.api.promotion.models.PromotionInfoResponse
+import com.tangem.domain.promo.PromoBanner
+import com.tangem.utils.converter.Converter
+import org.joda.time.DateTime
+
+class PromoResponseConverter : Converter {
+
+ override fun convert(value: PromotionInfoResponse): PromoBanner? {
+ val bannerState = value.bannerState ?: return null
+ return PromoBanner(
+ name = value.name,
+ bannerState = PromoBanner.BannerState(
+ status = bannerState.status,
+ timeline = PromoBanner.Timeline(
+ start = DateTime.parse(bannerState.timeline.start),
+ end = DateTime.parse(bannerState.timeline.end),
+ ),
+ ),
+ )
+ }
+}
\ No newline at end of file
diff --git a/data/promo/src/main/java/com/tangem/data/promo/di/PromoDataModule.kt b/data/promo/src/main/java/com/tangem/data/promo/di/PromoDataModule.kt
new file mode 100644
index 0000000000..5ed2c6855f
--- /dev/null
+++ b/data/promo/src/main/java/com/tangem/data/promo/di/PromoDataModule.kt
@@ -0,0 +1,25 @@
+package com.tangem.data.promo.di
+
+import com.tangem.data.promo.DefaultPromoRepository
+import com.tangem.datasource.api.tangemTech.TangemTechApi
+import com.tangem.domain.tokens.repository.PromoRepository
+import com.tangem.utils.coroutines.CoroutineDispatcherProvider
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+internal object PromoDataModule {
+
+ @Provides
+ @Singleton
+ fun providePromoRepository(
+ tangemTechApi: TangemTechApi,
+ dispatchers: CoroutineDispatcherProvider,
+ ): PromoRepository {
+ return DefaultPromoRepository(tangemTechApi, dispatchers)
+ }
+}
\ No newline at end of file
diff --git a/data/settings/src/main/java/com/tangem/data/settings/DefaultSwapPromoRepository.kt b/data/settings/src/main/java/com/tangem/data/settings/DefaultSwapPromoRepository.kt
index 8ba9fe211e..6d9ccf740a 100644
--- a/data/settings/src/main/java/com/tangem/data/settings/DefaultSwapPromoRepository.kt
+++ b/data/settings/src/main/java/com/tangem/data/settings/DefaultSwapPromoRepository.kt
@@ -1,14 +1,12 @@
package com.tangem.data.settings
import com.tangem.datasource.local.preferences.AppPreferencesStore
-import com.tangem.datasource.local.preferences.PreferencesKeys.IS_TOKEN_SWAP_PROMO_SHOW_KEY
-import com.tangem.datasource.local.preferences.PreferencesKeys.IS_WALLET_SWAP_PROMO_SHOW_KEY
+import com.tangem.datasource.local.preferences.PreferencesKeys.IS_TOKEN_SWAP_PROMO_CHANGELLY_SHOW_KEY
+import com.tangem.datasource.local.preferences.PreferencesKeys.IS_WALLET_SWAP_PROMO_CHANGELLY_SHOW_KEY
import com.tangem.datasource.local.preferences.utils.get
import com.tangem.datasource.local.preferences.utils.store
import com.tangem.domain.settings.repositories.SwapPromoRepository
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.map
-import java.util.Calendar
/**
* Repository for showing swap promo notification.
@@ -16,47 +14,25 @@ import java.util.Calendar
class DefaultSwapPromoRepository(
private val appPreferencesStore: AppPreferencesStore,
) : SwapPromoRepository {
- override fun isReadyToShowWallet(): Flow {
- return appPreferencesStore.get(IS_WALLET_SWAP_PROMO_SHOW_KEY, true)
- .map { it && checkPromoPeriod() }
+ override fun isReadyToShowWalletPromo(): Flow {
+ return appPreferencesStore.get(IS_WALLET_SWAP_PROMO_CHANGELLY_SHOW_KEY, true)
}
- override fun isReadyToShowToken(): Flow {
- return appPreferencesStore.get(IS_TOKEN_SWAP_PROMO_SHOW_KEY, true)
- .map { it && checkPromoPeriod() }
+ override fun isReadyToShowTokenPromo(): Flow {
+ return appPreferencesStore.get(IS_TOKEN_SWAP_PROMO_CHANGELLY_SHOW_KEY, true)
}
- override suspend fun setNeverToShowWallet() {
+ override suspend fun setNeverToShowWalletPromo() {
appPreferencesStore.store(
- key = IS_WALLET_SWAP_PROMO_SHOW_KEY,
+ key = IS_WALLET_SWAP_PROMO_CHANGELLY_SHOW_KEY,
value = false,
)
}
- override suspend fun setNeverToShowToken() {
+ override suspend fun setNeverToShowTokenPromo() {
appPreferencesStore.store(
- key = IS_TOKEN_SWAP_PROMO_SHOW_KEY,
+ key = IS_TOKEN_SWAP_PROMO_CHANGELLY_SHOW_KEY,
value = false,
)
}
-
- private suspend fun checkPromoPeriod(): Boolean {
- val calendar = Calendar.getInstance()
- val currentTime = calendar.timeInMillis
- calendar.set(END_YEAR_KEY, END_MONTH_KEY, END_DAY_KEY, 0, 0, 0)
- val endTime = calendar.timeInMillis
-
- val shouldShow = endTime - currentTime > 0
- if (!shouldShow) {
- setNeverToShowToken()
- setNeverToShowWallet()
- }
- return shouldShow
- }
-
- companion object {
- private const val END_DAY_KEY = 1
- private const val END_MONTH_KEY = 1 // February
- private const val END_YEAR_KEY = 2024
- }
}
\ No newline at end of file
diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/configs/CardConfig.kt b/domain/legacy/src/main/java/com/tangem/domain/common/configs/CardConfig.kt
index 51ff7de9b3..8099ff7d09 100644
--- a/domain/legacy/src/main/java/com/tangem/domain/common/configs/CardConfig.kt
+++ b/domain/legacy/src/main/java/com/tangem/domain/common/configs/CardConfig.kt
@@ -22,7 +22,12 @@ sealed interface CardConfig {
) {
return MultiWalletCardConfig
}
- return GenericCardConfig
+ if (cardDTO.supportedCurves.size == 1 &&
+ cardDTO.supportedCurves.contains(EllipticCurve.Ed25519)
+ ) {
+ return EdSingleCurrencyCardConfig
+ }
+ return GenericCardConfig(cardDTO.settings.maxWalletsCount)
}
}
}
\ No newline at end of file
diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/configs/EdSingleCurrencyCardConfig.kt b/domain/legacy/src/main/java/com/tangem/domain/common/configs/EdSingleCurrencyCardConfig.kt
new file mode 100644
index 0000000000..6792eeff3e
--- /dev/null
+++ b/domain/legacy/src/main/java/com/tangem/domain/common/configs/EdSingleCurrencyCardConfig.kt
@@ -0,0 +1,22 @@
+package com.tangem.domain.common.configs
+
+import com.tangem.blockchain.common.Blockchain
+import com.tangem.common.card.EllipticCurve
+import timber.log.Timber
+
+object EdSingleCurrencyCardConfig : CardConfig {
+
+ override val mandatoryCurves: List = listOf(EllipticCurve.Ed25519)
+
+ override fun primaryCurve(blockchain: Blockchain): EllipticCurve? {
+ return when {
+ blockchain.getSupportedCurves().contains(EllipticCurve.Ed25519) -> {
+ EllipticCurve.Ed25519
+ }
+ else -> {
+ Timber.e("Unsupported blockchain, curve not found")
+ null
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/configs/GenericCardConfig.kt b/domain/legacy/src/main/java/com/tangem/domain/common/configs/GenericCardConfig.kt
index 4955dbdb11..e75ff6da09 100644
--- a/domain/legacy/src/main/java/com/tangem/domain/common/configs/GenericCardConfig.kt
+++ b/domain/legacy/src/main/java/com/tangem/domain/common/configs/GenericCardConfig.kt
@@ -4,12 +4,14 @@ import com.tangem.blockchain.common.Blockchain
import com.tangem.common.card.EllipticCurve
import timber.log.Timber
-object GenericCardConfig : CardConfig {
- override val mandatoryCurves: List
- get() = listOf(
- EllipticCurve.Secp256k1,
- EllipticCurve.Ed25519,
- )
+class GenericCardConfig(maxWalletCount: Int) : CardConfig {
+
+ override val mandatoryCurves: List = buildList {
+ add(EllipticCurve.Secp256k1)
+ if (maxWalletCount > 1) {
+ add(EllipticCurve.Ed25519)
+ }
+ }
/**
* Old logic to determine primary curve for blockchain
diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/ShouldShowSwapPromoTokenUseCase.kt b/domain/settings/src/main/java/com/tangem/domain/settings/ShouldShowSwapPromoTokenUseCase.kt
index 0c75a4f52a..966e1bcf83 100644
--- a/domain/settings/src/main/java/com/tangem/domain/settings/ShouldShowSwapPromoTokenUseCase.kt
+++ b/domain/settings/src/main/java/com/tangem/domain/settings/ShouldShowSwapPromoTokenUseCase.kt
@@ -5,7 +5,7 @@ import kotlinx.coroutines.flow.Flow
class ShouldShowSwapPromoTokenUseCase(private val swapPromoRepository: SwapPromoRepository) {
- operator fun invoke(): Flow = swapPromoRepository.isReadyToShowToken()
+ operator fun invoke(): Flow = swapPromoRepository.isReadyToShowTokenPromo()
- suspend fun neverToShow() = swapPromoRepository.setNeverToShowToken()
+ suspend fun neverToShow() = swapPromoRepository.setNeverToShowTokenPromo()
}
\ No newline at end of file
diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/ShouldShowSwapPromoWalletUseCase.kt b/domain/settings/src/main/java/com/tangem/domain/settings/ShouldShowSwapPromoWalletUseCase.kt
index 573a5ea94b..e2b30e1ba5 100644
--- a/domain/settings/src/main/java/com/tangem/domain/settings/ShouldShowSwapPromoWalletUseCase.kt
+++ b/domain/settings/src/main/java/com/tangem/domain/settings/ShouldShowSwapPromoWalletUseCase.kt
@@ -5,7 +5,7 @@ import kotlinx.coroutines.flow.Flow
class ShouldShowSwapPromoWalletUseCase(private val swapPromoRepository: SwapPromoRepository) {
- operator fun invoke(): Flow = swapPromoRepository.isReadyToShowWallet()
+ operator fun invoke(): Flow = swapPromoRepository.isReadyToShowWalletPromo()
- suspend fun neverToShow() = swapPromoRepository.setNeverToShowWallet()
+ suspend fun neverToShow() = swapPromoRepository.setNeverToShowWalletPromo()
}
\ No newline at end of file
diff --git a/domain/settings/src/main/java/com/tangem/domain/settings/repositories/SwapPromoRepository.kt b/domain/settings/src/main/java/com/tangem/domain/settings/repositories/SwapPromoRepository.kt
index 95d31f65d8..ca97755e66 100644
--- a/domain/settings/src/main/java/com/tangem/domain/settings/repositories/SwapPromoRepository.kt
+++ b/domain/settings/src/main/java/com/tangem/domain/settings/repositories/SwapPromoRepository.kt
@@ -3,11 +3,11 @@ package com.tangem.domain.settings.repositories
import kotlinx.coroutines.flow.Flow
interface SwapPromoRepository {
- fun isReadyToShowWallet(): Flow
+ fun isReadyToShowWalletPromo(): Flow
- fun isReadyToShowToken(): Flow
+ fun isReadyToShowTokenPromo(): Flow
- suspend fun setNeverToShowWallet()
+ suspend fun setNeverToShowWalletPromo()
- suspend fun setNeverToShowToken()
+ suspend fun setNeverToShowTokenPromo()
}
\ No newline at end of file
diff --git a/domain/tokens/models/build.gradle.kts b/domain/tokens/models/build.gradle.kts
index a354fa4700..f3bf4ce59c 100644
--- a/domain/tokens/models/build.gradle.kts
+++ b/domain/tokens/models/build.gradle.kts
@@ -15,4 +15,5 @@ dependencies {
implementation(deps.tangem.blockchain) {
exclude(module = "joda-time")
}
+ implementation(deps.jodatime)
}
\ No newline at end of file
diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/promo/PromoBanner.kt b/domain/tokens/models/src/main/java/com/tangem/domain/promo/PromoBanner.kt
new file mode 100644
index 0000000000..564a49e3d4
--- /dev/null
+++ b/domain/tokens/models/src/main/java/com/tangem/domain/promo/PromoBanner.kt
@@ -0,0 +1,25 @@
+package com.tangem.domain.promo
+
+import org.joda.time.DateTime
+
+data class PromoBanner(
+ val name: String,
+ val bannerState: BannerState,
+) {
+
+ val isActive = bannerState.status == ACTIVE_STATUS && bannerState.timeline.end.isAfterNow
+
+ data class BannerState(
+ val timeline: Timeline,
+ val status: String,
+ )
+
+ data class Timeline(
+ val start: DateTime,
+ val end: DateTime,
+ )
+
+ private companion object {
+ const val ACTIVE_STATUS = "active"
+ }
+}
\ No newline at end of file
diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyWarning.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyWarning.kt
index e393e4fbd3..b90d787043 100644
--- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyWarning.kt
+++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/model/warnings/CryptoCurrencyWarning.kt
@@ -1,6 +1,7 @@
package com.tangem.domain.tokens.model.warnings
import com.tangem.domain.tokens.model.CryptoCurrency
+import org.joda.time.DateTime
import java.math.BigDecimal
sealed class CryptoCurrencyWarning {
@@ -42,5 +43,8 @@ sealed class CryptoCurrencyWarning {
data class HasPendingTransactions(val blockchainSymbol: String) : CryptoCurrencyWarning()
- object SwapPromo : CryptoCurrencyWarning()
+ data class SwapPromo(
+ val startDateTime: DateTime,
+ val endDateTime: DateTime,
+ ) : CryptoCurrencyWarning()
}
\ No newline at end of file
diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/analytics/TokenExchangeAnalyticsEvent.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/analytics/TokenExchangeAnalyticsEvent.kt
index a2161adc46..260c9ce93f 100644
--- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/analytics/TokenExchangeAnalyticsEvent.kt
+++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/analytics/TokenExchangeAnalyticsEvent.kt
@@ -8,12 +8,12 @@ class TokenExchangeAnalyticsEvent(
) : AnalyticsEvent("Token", event, params, null) {
class CexTxStatusOpened(token: String) : TokenScreenAnalyticsEvent(
- event = "ChangeNow Status Opened",
+ event = "Swap Status Opened",
params = mapOf("Token" to token),
)
class CexTxStatusChanged(token: String, status: String) : TokenScreenAnalyticsEvent(
- event = "ChangeNow Status",
+ event = "Swap Status",
params = mapOf("Token" to token, "Status" to status),
)
diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt
index 304c27e7ce..6e18152f25 100644
--- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt
+++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyWarningsUseCase.kt
@@ -26,6 +26,7 @@ class GetCurrencyWarningsUseCase(
private val networksRepository: NetworksRepository,
private val swapRepository: SwapRepository,
private val marketCryptoCurrencyRepository: MarketCryptoCurrencyRepository,
+ private val promoRepository: PromoRepository,
private val showSwapPromoTokenUseCase: ShouldShowSwapPromoTokenUseCase,
private val dispatchers: CoroutineDispatcherProvider,
private val currencyChecksRepository: CurrencyChecksRepository,
@@ -84,11 +85,14 @@ class GetCurrencyWarningsUseCase(
): Flow {
val currency = currencyStatus.currency
val cryptoStatuses = operations.getCurrenciesStatusesSync()
+ val promoBanner = promoRepository.getChangellyPromoBanner()
return combine(
showSwapPromoTokenUseCase().conflate(),
flowOf(marketCryptoCurrencyRepository.isExchangeable(userWalletId, currency)).conflate(),
) { shouldShowSwapPromo, isExchangeable ->
- if (shouldShowSwapPromo && isExchangeable && currencyStatus.value !is CryptoCurrencyStatus.Unreachable) {
+ promoBanner ?: return@combine null
+ val showPromo = promoBanner.isActive && shouldShowSwapPromo
+ if (showPromo && isExchangeable && currencyStatus.value !is CryptoCurrencyStatus.Unreachable) {
cryptoStatuses.fold(
ifLeft = { null },
ifRight = { cryptoCurrencyStatuses ->
@@ -122,7 +126,10 @@ class GetCurrencyWarningsUseCase(
}
}
if (showPromo) {
- CryptoCurrencyWarning.SwapPromo
+ CryptoCurrencyWarning.SwapPromo(
+ startDateTime = promoBanner.bannerState.timeline.start,
+ endDateTime = promoBanner.bannerState.timeline.end,
+ )
} else {
null
}
diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/PromoRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/PromoRepository.kt
new file mode 100644
index 0000000000..b15e05db54
--- /dev/null
+++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/PromoRepository.kt
@@ -0,0 +1,8 @@
+package com.tangem.domain.tokens.repository
+
+import com.tangem.domain.promo.PromoBanner
+
+interface PromoRepository {
+
+ suspend fun getChangellyPromoBanner(): PromoBanner?
+}
\ No newline at end of file
diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsTopAppBarConfig.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsTopAppBarConfig.kt
index 88ecf53046..f72f09f20f 100644
--- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsTopAppBarConfig.kt
+++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/state/TokenDetailsTopAppBarConfig.kt
@@ -2,5 +2,5 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state
internal data class TokenDetailsTopAppBarConfig(
val onBackClick: () -> Unit,
- val tokenDetailsAppBarMenuConfig: TokenDetailsAppBarMenuConfig,
+ val tokenDetailsAppBarMenuConfig: TokenDetailsAppBarMenuConfig?,
)
\ No newline at end of file
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 8a4732ea16..185abeff5d 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
@@ -9,6 +9,7 @@ import com.tangem.core.ui.extensions.wrappedList
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
import com.tangem.features.tokendetails.impl.R
+import org.joda.time.DateTime
@Immutable
internal sealed class TokenDetailsNotification(val config: NotificationConfig) {
@@ -41,14 +42,22 @@ internal sealed class TokenDetailsNotification(val config: NotificationConfig) {
)
data class SwapPromo(
+ val startDateTime: DateTime,
+ val endDateTime: DateTime,
val onSwapClick: () -> Unit,
val onCloseClick: () -> Unit,
) : TokenDetailsNotification(
config = NotificationConfig(
- title = resourceReference(id = R.string.token_swap_promotion_title),
- subtitle = resourceReference(id = R.string.token_swap_promotion_message),
+ title = resourceReference(
+ id = R.string.token_swap_changelly_promotion_title,
+ formatArgs = wrappedList("0%"),
+ ),
+ subtitle = resourceReference(
+ id = R.string.token_swap_changelly_promotion_message,
+ formatArgs = wrappedList("0%", startDateTime.dayOfMonth, endDateTime.dayOfMonth),
+ ),
iconResId = R.drawable.img_swap_promo,
- backgroundResId = R.drawable.img_swap_promo_banner_background,
+ backgroundResId = R.drawable.img_swap_promo_green_banner_background,
onCloseClick = onCloseClick,
buttonsState = NotificationConfig.ButtonsState.SecondaryButtonConfig(
text = resourceReference(id = com.tangem.core.ui.R.string.token_swap_promotion_button),
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 c2a5ea5591..bf37784e25 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
@@ -68,6 +68,8 @@ internal class TokenDetailsNotificationConverter(
coinSymbol = warning.blockchainSymbol,
)
is CryptoCurrencyWarning.SwapPromo -> SwapPromo(
+ startDateTime = warning.startDateTime,
+ endDateTime = warning.endDateTime,
onSwapClick = clickIntents::onSwapPromoClick,
onCloseClick = clickIntents::onSwapPromoDismiss,
)
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 b4b567a444..c0e14ea621 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
@@ -11,7 +11,9 @@ import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.core.ui.event.consumedEvent
import com.tangem.core.ui.event.triggeredEvent
import com.tangem.core.ui.extensions.TextReference
+import com.tangem.core.ui.res.TangemTheme
import com.tangem.domain.appcurrency.model.AppCurrency
+import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
@@ -22,6 +24,7 @@ import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.domain.txhistory.models.TxHistoryListError
import com.tangem.domain.txhistory.models.TxHistoryStateError
import com.tangem.feature.tokendetails.presentation.tokendetails.state.SwapTransactionsState
+import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsAppBarMenuConfig
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsDialogConfig
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadedTxHistoryConverter
@@ -29,7 +32,9 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.t
import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.txhistory.TokenDetailsLoadingTxHistoryConverter.TokenDetailsLoadingTxHistoryModel
import com.tangem.feature.tokendetails.presentation.tokendetails.ui.components.exchange.ExchangeStatusBottomSheetConfig
import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.TokenDetailsClickIntents
+import com.tangem.features.tokendetails.impl.R
import com.tangem.utils.Provider
+import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -276,4 +281,31 @@ internal class TokenDetailsStateFactory(
),
)
}
+
+ fun getStateWithUpdatedMenu(cardTypesResolver: CardTypesResolver): TokenDetailsState {
+ return with(currentStateProvider()) {
+ copy(
+ topAppBarConfig = topAppBarConfig.copy(
+ tokenDetailsAppBarMenuConfig = topAppBarConfig.tokenDetailsAppBarMenuConfig
+ ?.updateMenu(cardTypesResolver),
+ ),
+ )
+ }
+ }
+
+ private fun TokenDetailsAppBarMenuConfig.updateMenu(
+ cardTypesResolver: CardTypesResolver,
+ ): TokenDetailsAppBarMenuConfig? {
+ if (cardTypesResolver.isSingleWalletWithToken()) return null
+
+ return copy(
+ items = persistentListOf(
+ TokenDetailsAppBarMenuConfig.MenuItem(
+ title = TextReference.Res(id = R.string.token_details_hide_token),
+ textColorProvider = { TangemTheme.colors.text.warning },
+ onClick = clickIntents::onHideClick,
+ ),
+ ),
+ )
+ }
}
\ No newline at end of file
diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsTopAppBar.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsTopAppBar.kt
index b5e0839679..1ed6603ba3 100644
--- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsTopAppBar.kt
+++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/components/TokenDetailsTopAppBar.kt
@@ -1,5 +1,6 @@
package com.tangem.feature.tokendetails.presentation.tokendetails.ui.components
+import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.padding
@@ -38,12 +39,17 @@ internal fun TokenDetailsTopAppBar(config: TokenDetailsTopAppBarConfig) {
},
title = {},
actions = {
- IconButton(onClick = { showDropdownMenu = true }) {
- Icon(
- painter = painterResource(id = R.drawable.ic_more_vertical_24),
- tint = TangemTheme.colors.icon.primary1,
- contentDescription = "More",
- )
+ AnimatedVisibility(
+ visible = config.tokenDetailsAppBarMenuConfig != null &&
+ config.tokenDetailsAppBarMenuConfig.items.isNotEmpty(),
+ ) {
+ IconButton(onClick = { showDropdownMenu = true }) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_more_vertical_24),
+ tint = TangemTheme.colors.icon.primary1,
+ contentDescription = "More",
+ )
+ }
}
TangemDropdownMenu(
@@ -52,7 +58,7 @@ internal fun TokenDetailsTopAppBar(config: TokenDetailsTopAppBarConfig) {
onDismissRequest = { showDropdownMenu = false },
offset = DpOffset(x = TangemTheme.dimens.spacing20, y = TangemTheme.dimens.spacing10.times(-1)),
content = {
- config.tokenDetailsAppBarMenuConfig.items.fastForEach {
+ config.tokenDetailsAppBarMenuConfig?.items?.fastForEach {
AppBarDropdownItem(
item = it,
dismissParent = { showDropdownMenu = false },
diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt
index 1fba010ecc..382d9ca76e 100644
--- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt
+++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt
@@ -185,6 +185,7 @@ internal class TokenDetailsViewModel @Inject constructor(
analyticsEventsHandler.send(
event = TokenScreenAnalyticsEvent.DetailsScreenOpened(token = cryptoCurrency.symbol),
)
+ updateTopBarMenu()
updateContent()
handleBalanceHiding(owner)
}
@@ -339,6 +340,14 @@ internal class TokenDetailsViewModel @Inject constructor(
}
}
+ private fun updateTopBarMenu() {
+ viewModelScope.launch(dispatchers.main) {
+ val wallet = getUserWalletUseCase(userWalletId).getOrElse { return@launch }
+
+ uiState = stateFactory.getStateWithUpdatedMenu(wallet.scanResponse.cardTypesResolver)
+ }
+ }
+
private fun createSelectedAppCurrencyFlow(): StateFlow {
return getSelectedAppCurrencyUseCase()
.map { maybeAppCurrency ->
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt
index aed97693c7..92abf8f92f 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt
@@ -32,7 +32,7 @@ internal object WalletPreviewData {
content = TextReference.Str("3 cards • Seed phrase3 cards • Seed phrasephrasephrasephrase"),
),
imageResId = R.drawable.ill_wallet2_cards3_120_106,
- onRenameClick = { _, _ -> },
+ onRenameClick = { _ -> },
onDeleteClick = {},
cardCount = 1,
)
@@ -43,7 +43,7 @@ internal object WalletPreviewData {
id = UserWalletId("321"),
title = "Wallet 1",
imageResId = R.drawable.ill_wallet2_cards3_120_106,
- onRenameClick = { _, _ -> },
+ onRenameClick = { _ -> },
onDeleteClick = {},
)
}
@@ -53,7 +53,7 @@ internal object WalletPreviewData {
id = UserWalletId("24"),
title = "Wallet 1",
imageResId = R.drawable.ill_wallet2_cards3_120_106,
- onRenameClick = { _, _ -> },
+ onRenameClick = { _ -> },
onDeleteClick = {},
)
}
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 79fef13194..2717ca2dfa 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
@@ -61,7 +61,7 @@ sealed class WalletScreenAnalyticsEvent {
object NoticeBackupYourWalletTapped : MainScreen(event = "Notice - Backup Your Wallet Tapped")
object NoticeScanYourCardTapped : MainScreen(event = "Notice - Scan Your Card Tapped")
object NoticeWalletLocked : MainScreen(event = "Notice - Wallet Locked")
- object WalletUnlockTapped : MainScreen(event = "Button - Wallet Unlock Tapped")
+ object WalletUnlockTapped : MainScreen(event = "Notice - Wallet Unlock Tapped")
object NetworksUnreachable : MainScreen(event = "Notice - Networks Unreachable")
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 6b9d320700..7de96e0c75 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
@@ -4,13 +4,16 @@ import arrow.core.Either
import com.tangem.domain.common.CardTypesResolver
import com.tangem.domain.common.util.cardTypesResolver
import com.tangem.domain.demo.IsDemoCardUseCase
+import com.tangem.domain.promo.PromoBanner
import com.tangem.domain.settings.IsReadyToShowRateAppUseCase
+import com.tangem.domain.settings.ShouldShowSwapPromoWalletUseCase
import com.tangem.domain.tokens.GetTokenListUseCase
import com.tangem.domain.tokens.error.TokenListError
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.NetworkGroup
import com.tangem.domain.tokens.model.TokenList
+import com.tangem.domain.tokens.repository.PromoRepository
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import com.tangem.domain.wallets.usecase.IsNeedToBackupUseCase
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletNotification
@@ -19,19 +22,19 @@ import dagger.hilt.android.scopes.ViewModelScoped
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.conflate
-import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.*
import timber.log.Timber
import javax.inject.Inject
+@Suppress("LongParameterList")
@ViewModelScoped
internal class GetMultiWalletWarningsFactory @Inject constructor(
private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
private val getTokenListUseCase: GetTokenListUseCase,
private val isDemoCardUseCase: IsDemoCardUseCase,
private val isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase,
+ private val shouldShowSwapPromoWalletUseCase: ShouldShowSwapPromoWalletUseCase,
+ private val promoRepository: PromoRepository,
private val isNeedToBackupUseCase: IsNeedToBackupUseCase,
) {
@@ -48,13 +51,19 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
val cardTypesResolver = userWallet.scanResponse.cardTypesResolver
+ val promoFlow = flow { emit(promoRepository.getChangellyPromoBanner()) }
return combine(
flow = getTokenListUseCase(userWallet.walletId).conflate(),
flow2 = isReadyToShowRateAppUseCase().conflate(),
flow3 = isNeedToBackupUseCase(userWallet.walletId).conflate(),
- ) { maybeTokenList, isReadyToShowRating, isNeedToBackup ->
+ flow4 = shouldShowSwapPromoWalletUseCase().conflate(),
+ flow5 = promoFlow,
+ ) { maybeTokenList, isReadyToShowRating, isNeedToBackup, shouldShowPromo, promoBanner ->
+
readyForRateAppNotification = true
buildList {
+ addSwapPromoNotification(shouldShowPromo, promoBanner, clickIntents)
+
addCriticalNotifications(cardTypesResolver)
addInformationalNotifications(cardTypesResolver, maybeTokenList, clickIntents)
@@ -66,6 +75,23 @@ internal class GetMultiWalletWarningsFactory @Inject constructor(
}
}
+ private fun MutableList.addSwapPromoNotification(
+ shouldShowPromo: Boolean,
+ promoBanner: PromoBanner?,
+ clickIntents: WalletClickIntents,
+ ) {
+ promoBanner ?: return
+ val promoNotification = WalletNotification.SwapPromo(
+ startDateTime = promoBanner.bannerState.timeline.start,
+ endDateTime = promoBanner.bannerState.timeline.end,
+ onCloseClick = clickIntents::onCloseSwapPromoClick,
+ )
+ addIf(
+ element = promoNotification,
+ condition = shouldShowPromo && promoBanner.isActive,
+ )
+ }
+
private fun MutableList.addCriticalNotifications(cardTypesResolver: CardTypesResolver) {
addIf(
element = WalletNotification.Critical.DevCard,
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletAlertState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletAlertState.kt
index 74b0c80769..2b1df72669 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletAlertState.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletAlertState.kt
@@ -7,40 +7,60 @@ import com.tangem.core.ui.extensions.stringReference
import com.tangem.feature.wallet.impl.R
@Immutable
-internal sealed class WalletAlertState {
+internal sealed interface WalletAlertState {
- abstract val title: TextReference?
- abstract val message: TextReference
- open val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok)
- open val isWarningConfirmButton: Boolean = false
- abstract val onConfirmClick: (() -> Unit)?
+ @Immutable
+ sealed class Basic : WalletAlertState {
+ abstract val title: TextReference?
+ abstract val message: TextReference
+ open val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok)
+ open val isWarningConfirmButton: Boolean = false
+ abstract val onConfirmClick: (() -> Unit)?
+ }
+
+ @Immutable
+ sealed class TextInput : WalletAlertState {
+ abstract val title: TextReference
+ abstract val label: TextReference
+ open val text: String = ""
+ open val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok)
+ abstract val onConfirmClick: (String) -> Unit
+ }
data class DefaultAlert(
override val title: TextReference,
override val message: TextReference,
override val onConfirmClick: (() -> Unit)?,
- ) : WalletAlertState()
+ ) : Basic()
- data class RemoveWalletAlert(override val onConfirmClick: (() -> Unit)?) : WalletAlertState() {
+ data class RenameWalletAlert(
+ override val text: String,
+ override val onConfirmClick: (String) -> Unit,
+ ) : TextInput() {
+ override val title: TextReference = resourceReference(id = R.string.user_wallet_list_rename_popup_title)
+ override val label: TextReference = resourceReference(id = R.string.user_wallet_list_rename_popup_placeholder)
+ }
+
+ data class RemoveWalletAlert(override val onConfirmClick: (() -> Unit)?) : Basic() {
override val title: TextReference? = null
override val message: TextReference = resourceReference(id = R.string.user_wallet_list_delete_prompt)
override val confirmButtonText: TextReference = resourceReference(id = R.string.common_delete)
override val isWarningConfirmButton: Boolean = true
}
- object WrongCardIsScanned : WalletAlertState() {
+ object WrongCardIsScanned : Basic() {
override val title: TextReference = resourceReference(R.string.common_warning)
override val message: TextReference = resourceReference(R.string.error_wrong_wallet_tapped)
override val onConfirmClick: (() -> Unit)? = null
}
- object RescanWallets : WalletAlertState() {
+ object RescanWallets : Basic() {
override val title: TextReference = resourceReference(R.string.common_attention)
override val message: TextReference = resourceReference(R.string.key_invalidated_warning_description)
override val onConfirmClick: (() -> Unit)? = null
}
- object VisaBalancesInfo : WalletAlertState() {
+ object VisaBalancesInfo : Basic() {
override val title: TextReference? = null
override val message: TextReference = stringReference(
value = "Available balance is actual funds available, considering pending transactions, " +
@@ -49,7 +69,7 @@ internal sealed class WalletAlertState {
override val onConfirmClick: (() -> Unit)? = null
}
- object VisaLimitsInfo : WalletAlertState() {
+ object VisaLimitsInfo : Basic() {
override val title: TextReference? = null
override val message: TextReference = stringReference(
value = "Limits are needed to control costs, improve security, manage risk. " +
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletCardState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletCardState.kt
index a533eeabfb..d4feb6a295 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletCardState.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/model/WalletCardState.kt
@@ -24,7 +24,7 @@ internal sealed interface WalletCardState {
val imageResId: Int?
/** Lambda be invoked when Rename button is clicked */
- val onRenameClick: (UserWalletId, String) -> Unit
+ val onRenameClick: (UserWalletId) -> Unit
/** Lambda be invoked when Delete button is clicked */
val onDeleteClick: (UserWalletId) -> Unit
@@ -46,7 +46,7 @@ internal sealed interface WalletCardState {
override val title: String,
override val additionalInfo: WalletAdditionalInfo,
override val imageResId: Int?,
- override val onRenameClick: (UserWalletId, String) -> Unit,
+ override val onRenameClick: (UserWalletId) -> Unit,
override val onDeleteClick: (UserWalletId) -> Unit,
val cardCount: Int?,
val balance: String,
@@ -67,7 +67,7 @@ internal sealed interface WalletCardState {
override val title: String,
override val additionalInfo: WalletAdditionalInfo,
override val imageResId: Int?,
- override val onRenameClick: (UserWalletId, String) -> Unit,
+ override val onRenameClick: (UserWalletId) -> Unit,
override val onDeleteClick: (UserWalletId) -> Unit,
) : WalletCardState
@@ -85,7 +85,7 @@ internal sealed interface WalletCardState {
override val title: String,
override val additionalInfo: WalletAdditionalInfo? = defaultAdditionalInfo,
override val imageResId: Int?,
- override val onRenameClick: (UserWalletId, String) -> Unit,
+ override val onRenameClick: (UserWalletId) -> Unit,
override val onDeleteClick: (UserWalletId) -> Unit,
) : WalletCardState {
@@ -109,7 +109,7 @@ internal sealed interface WalletCardState {
override val title: String,
override val additionalInfo: WalletAdditionalInfo? = null,
override val imageResId: Int?,
- override val onRenameClick: (UserWalletId, String) -> Unit,
+ override val onRenameClick: (UserWalletId) -> Unit,
override val onDeleteClick: (UserWalletId) -> Unit,
) : WalletCardState
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 1b9721b2cf..e7c5e885c4 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
@@ -7,6 +7,7 @@ import com.tangem.core.ui.extensions.pluralReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.feature.wallet.impl.R
+import org.joda.time.DateTime
/**
* Wallet notification component state
@@ -168,13 +169,21 @@ sealed class WalletNotification(val config: NotificationConfig) {
)
data class SwapPromo(
+ val startDateTime: DateTime,
+ val endDateTime: DateTime,
val onCloseClick: () -> Unit,
) : WalletNotification(
config = NotificationConfig(
- title = resourceReference(id = R.string.main_swap_promotion_title),
- subtitle = resourceReference(id = R.string.main_swap_promotion_message),
+ title = resourceReference(
+ id = R.string.main_swap_changelly_promotion_title,
+ formatArgs = wrappedList("0%"),
+ ),
+ subtitle = resourceReference(
+ id = R.string.main_swap_changelly_promotion_message,
+ formatArgs = wrappedList("0%", startDateTime.dayOfMonth, endDateTime.dayOfMonth),
+ ),
iconResId = R.drawable.img_swap_promo,
- backgroundResId = R.drawable.img_swap_promo_banner_background,
+ backgroundResId = R.drawable.img_swap_promo_green_banner_background,
onCloseClick = onCloseClick,
),
)
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/InitializeWalletsTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/InitializeWalletsTransformer.kt
index 630c7dfb2c..7b94f4d770 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/InitializeWalletsTransformer.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/InitializeWalletsTransformer.kt
@@ -86,7 +86,7 @@ internal class InitializeWalletsTransformer(
title = name,
additionalInfo = WalletAdditionalInfoFactory.resolve(wallet = this),
imageResId = WalletImageResolver.resolve(userWallet = this),
- onRenameClick = clickIntents::onRenameClick,
+ onRenameClick = clickIntents::onRenameBeforeConfirmationClick,
onDeleteClick = clickIntents::onDeleteBeforeConfirmationClick,
)
}
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletLoadingStateFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletLoadingStateFactory.kt
index 9315797f9a..866f9981a0 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletLoadingStateFactory.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/utils/WalletLoadingStateFactory.kt
@@ -81,7 +81,7 @@ internal class WalletLoadingStateFactory(private val clickIntents: WalletClickIn
title = name,
additionalInfo = if (isMultiCurrency) WalletAdditionalInfoFactory.resolve(wallet = this) else null,
imageResId = WalletImageResolver.resolve(userWallet = this),
- onRenameClick = clickIntents::onRenameClick,
+ onRenameClick = clickIntents::onRenameBeforeConfirmationClick,
onDeleteClick = clickIntents::onDeleteBeforeConfirmationClick,
)
}
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletAlert.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletAlert.kt
index ab8a96b36b..c5303e085e 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletAlert.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletAlert.kt
@@ -1,20 +1,26 @@
package com.tangem.feature.wallet.presentation.wallet.ui
-import androidx.compose.runtime.Composable
+import androidx.compose.runtime.*
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.input.TextFieldValue
+import com.tangem.core.ui.components.AdditionalTextInputDialogParams
import com.tangem.core.ui.components.BasicDialog
import com.tangem.core.ui.components.DialogButton
+import com.tangem.core.ui.components.TextInputDialog
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.feature.wallet.impl.R
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletAlertState
@Composable
internal fun WalletAlert(state: WalletAlertState, onDismiss: () -> Unit) {
- DefaultAlert(state = state, onDismiss = onDismiss)
+ when (state) {
+ is WalletAlertState.Basic -> BasicAlert(state, onDismiss)
+ is WalletAlertState.TextInput -> TextInputAlert(state, onDismiss)
+ }
}
@Composable
-private fun DefaultAlert(state: WalletAlertState, onDismiss: () -> Unit) {
+private fun BasicAlert(state: WalletAlertState.Basic, onDismiss: () -> Unit) {
val confirmButton: DialogButton
val dismissButton: DialogButton?
@@ -48,4 +54,28 @@ private fun DefaultAlert(state: WalletAlertState, onDismiss: () -> Unit) {
title = state.title?.resolveReference(),
dismissButton = dismissButton,
)
+}
+
+@Composable
+private fun TextInputAlert(state: WalletAlertState.TextInput, onDismiss: () -> Unit) {
+ var value by remember { mutableStateOf(TextFieldValue(text = state.text)) }
+
+ TextInputDialog(
+ fieldValue = value,
+ confirmButton = DialogButton(
+ title = state.confirmButtonText.resolveReference(),
+ enabled = value.text.isNotEmpty() && value.text != state.text,
+ onClick = {
+ state.onConfirmClick(value.text)
+ onDismiss()
+ },
+ ),
+ onDismissDialog = onDismiss,
+ onValueChange = { value = it },
+ title = state.title.resolveReference(),
+ dismissButton = DialogButton(title = stringResource(id = R.string.common_cancel), onClick = onDismiss),
+ textFieldParams = AdditionalTextInputDialogParams(
+ label = state.label.resolveReference(),
+ ),
+ )
}
\ No newline at end of file
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt
index 35a4a9bb1a..eed63a9195 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt
@@ -45,7 +45,6 @@ import com.tangem.common.Strings
import com.tangem.core.ui.components.FontSizeRange
import com.tangem.core.ui.components.RectangleShimmer
import com.tangem.core.ui.components.ResizableText
-import com.tangem.core.ui.components.wallets.RenameWalletDialogContent
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemDimens
@@ -69,9 +68,8 @@ private const val HALF_OF_ITEM_WIDTH = 0.5
internal fun WalletCard(state: WalletCardState, isBalanceHidden: Boolean, modifier: Modifier = Modifier) {
@Suppress("DestructuringDeclarationWithTooManyEntries")
CardContainer(
- name = state.title,
onDeleteClick = { state.onDeleteClick(state.id) },
- onRenameClick = { state.onRenameClick(state.id, it) },
+ onRenameClick = { state.onRenameClick(state.id) },
isLockedState = state is WalletCardState.LockedContent,
modifier = modifier,
) { itemSize ->
@@ -149,9 +147,8 @@ internal fun WalletCard(state: WalletCardState, isBalanceHidden: Boolean, modifi
@Composable
private fun CardContainer(
- name: String,
onDeleteClick: () -> Unit,
- onRenameClick: (String) -> Unit,
+ onRenameClick: () -> Unit,
isLockedState: Boolean,
modifier: Modifier = Modifier,
content: @Composable (ConstraintLayoutScope.(IntSize) -> Unit),
@@ -204,8 +201,6 @@ private fun CardContainer(
}
}
- var isRenameWalletDialogVisible by rememberSaveable { mutableStateOf(value = false) }
-
val itemHeight by remember(itemSize.height) {
mutableStateOf(value = with(density) { itemSize.height.toDp() })
}
@@ -214,20 +209,9 @@ private fun CardContainer(
pressOffset = pressOffset,
itemHeight = itemHeight,
onDismissRequest = { isMenuVisible = false },
- onShowRenameWalletDialogClick = { isRenameWalletDialogVisible = true },
+ onShowRenameWalletDialogClick = onRenameClick,
onDeleteClick = onDeleteClick,
)
-
- if (isRenameWalletDialogVisible) {
- RenameWalletDialogContent(
- name = name,
- onConfirm = {
- onRenameClick(it)
- isRenameWalletDialogVisible = false
- },
- onDismiss = { isRenameWalletDialogVisible = false },
- )
- }
}
@Suppress("LongParameterList")
@@ -373,7 +357,7 @@ private fun AdditionalInfoText(text: TextReference) {
}
private fun Modifier.nonContentAdditionalInfoSize(dimens: TangemDimens): Modifier {
- return size(width = dimens.size84, height = dimens.size16)
+ return this.size(width = dimens.size84, height = dimens.size16)
}
@Composable
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCardClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCardClickIntents.kt
index f3ae886366..fcbbdab810 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCardClickIntents.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletCardClickIntents.kt
@@ -20,7 +20,9 @@ import javax.inject.Inject
internal interface WalletCardClickIntents {
- fun onRenameClick(userWalletId: UserWalletId, name: String)
+ fun onRenameBeforeConfirmationClick(userWalletId: UserWalletId)
+
+ fun onRenameAfterConfirmationClick(userWalletId: UserWalletId, name: String)
fun onDeleteBeforeConfirmationClick(userWalletId: UserWalletId)
@@ -39,15 +41,28 @@ internal class WalletCardClickIntentsImplementor @Inject constructor(
private val dispatchers: CoroutineDispatcherProvider,
) : BaseWalletClickIntents(), WalletCardClickIntents {
- override fun onRenameClick(userWalletId: UserWalletId, name: String) {
+ override fun onRenameBeforeConfirmationClick(userWalletId: UserWalletId) {
analyticsEventHandler.send(MainScreen.EditWalletTapped)
+ walletEventSender.send(
+ event = WalletEvent.ShowAlert(
+ state = WalletAlertState.RenameWalletAlert(
+ text = stateHolder.getSelectedWallet().walletCardState.title,
+ onConfirmClick = { onRenameAfterConfirmationClick(userWalletId, it) },
+ ),
+ ),
+ )
+ }
+
+ override fun onRenameAfterConfirmationClick(userWalletId: UserWalletId, name: String) {
viewModelScope.launch(dispatchers.main) {
- updateWalletUseCase(userWalletId = userWalletId, update = { it.copy(name) })
+ updateWalletUseCase(userWalletId = userWalletId, update = { it.copy(name = name) })
}
}
override fun onDeleteBeforeConfirmationClick(userWalletId: UserWalletId) {
+ analyticsEventHandler.send(MainScreen.DeleteWalletTapped)
+
walletEventSender.send(
event = WalletEvent.ShowAlert(
state = WalletAlertState.RemoveWalletAlert(
@@ -58,8 +73,6 @@ internal class WalletCardClickIntentsImplementor @Inject constructor(
}
override fun onDeleteAfterConfirmationClick(userWalletId: UserWalletId) {
- analyticsEventHandler.send(MainScreen.DeleteWalletTapped)
-
viewModelScope.launch(dispatchers.main) {
walletScreenContentLoader.cancel(userWalletId)
deleteWalletUseCase(userWalletId)
diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt
index e9e03ac291..5f6133df1b 100644
--- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt
+++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletWarningsClickIntents.kt
@@ -9,6 +9,7 @@ import com.tangem.domain.redux.LegacyAction
import com.tangem.domain.redux.ReduxStateHolder
import com.tangem.domain.settings.NeverToSuggestRateAppUseCase
import com.tangem.domain.settings.RemindToRateAppLaterUseCase
+import com.tangem.domain.settings.ShouldShowSwapPromoWalletUseCase
import com.tangem.domain.tokens.FetchTokenListUseCase
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.wallets.models.UnlockWalletsError
@@ -50,6 +51,8 @@ internal interface WalletWarningsClickIntents {
fun onDislikeAppClick()
fun onCloseRateAppWarningClick()
+
+ fun onCloseSwapPromoClick()
}
@Suppress("LongParameterList")
@@ -68,6 +71,7 @@ internal class WalletWarningsClickIntentsImplementer @Inject constructor(
private val analyticsEventHandler: AnalyticsEventHandler,
private val reduxStateHolder: ReduxStateHolder,
private val dispatchers: CoroutineDispatcherProvider,
+ private val shouldShowSwapPromoWalletUseCase: ShouldShowSwapPromoWalletUseCase,
) : BaseWalletClickIntents(), WalletWarningsClickIntents {
override fun onAddBackupCardClick() {
@@ -113,6 +117,8 @@ internal class WalletWarningsClickIntentsImplementer @Inject constructor(
}
override fun onOpenUnlockWalletsBottomSheetClick() {
+ analyticsEventHandler.send(MainScreen.WalletUnlockTapped)
+
val config = requireNotNull(stateHolder.getSelectedWallet().bottomSheetConfig) {
"Impossible to open unlock wallet bottom sheet if it's null"
}
@@ -192,4 +198,10 @@ internal class WalletWarningsClickIntentsImplementer @Inject constructor(
remindToRateAppLaterUseCase()
}
}
+
+ override fun onCloseSwapPromoClick() {
+ viewModelScope.launch(dispatchers.main) {
+ shouldShowSwapPromoWalletUseCase.neverToShow()
+ }
+ }
}
\ No newline at end of file
diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml
index e3b7fbf13e..e291150114 100644
--- a/gradle/dependencies.toml
+++ b/gradle/dependencies.toml
@@ -85,7 +85,7 @@ web3j = "4.10.1"
# endregion Other libraries
# region Tangem
-tangemBlockchainSdk = "develop-484"
+tangemBlockchainSdk = "develop-485"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
tangemCardSdk = "develop-324"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 3df3dad60b..19fcb80b60 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -153,4 +153,5 @@ include(":data:wallets")
include(":data:analytics")
include(":data:transaction")
include(":data:visa")
+include(":data:promo")
// endregion Data modules
\ No newline at end of file