Updated on 2026-08-14
This commit is contained in:
commit
5b003011fc
74 changed files with 576 additions and 452 deletions
|
|
@ -80,6 +80,7 @@ dependencies {
|
|||
implementation(projects.data.analytics)
|
||||
implementation(projects.data.transaction)
|
||||
implementation(projects.data.visa)
|
||||
implementation(projects.data.promo)
|
||||
|
||||
/** Features */
|
||||
implementation(projects.features.onboarding)
|
||||
|
|
|
|||
|
|
@ -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 ->
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ internal object TokensDomainModule {
|
|||
marketCryptoCurrencyRepository: MarketCryptoCurrencyRepository,
|
||||
swapRepository: SwapRepository,
|
||||
showSwapPromoTokenUseCase: ShouldShowSwapPromoTokenUseCase,
|
||||
promoRepository: PromoRepository,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): GetCurrencyWarningsUseCase {
|
||||
return GetCurrencyWarningsUseCase(
|
||||
|
|
@ -110,6 +111,7 @@ internal object TokensDomainModule {
|
|||
marketCryptoCurrencyRepository = marketCryptoCurrencyRepository,
|
||||
swapRepository = swapRepository,
|
||||
showSwapPromoTokenUseCase = showSwapPromoTokenUseCase,
|
||||
promoRepository = promoRepository,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,22 +5,37 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_add_card"
|
||||
style="@style/TapSecondaryIconButton"
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="92dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="@id/btn_continue"
|
||||
app:layout_constraintStart_toStartOf="@id/btn_continue"
|
||||
tools:icon="@drawable/ic_tangem_24"
|
||||
tools:text="Add backup card" />
|
||||
app:layout_constraintStart_toStartOf="@id/btn_continue">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_add_card"
|
||||
style="@style/TapSecondaryIconButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:icon="@drawable/ic_tangem_24"
|
||||
tools:text="Add backup card" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/btn_progress"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center"
|
||||
android:elevation="18dp"
|
||||
android:visibility="gone"
|
||||
android:indeterminate="true"
|
||||
android:indeterminateTint="@color/text_primary_1" />
|
||||
</FrameLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_continue"
|
||||
android:layout_width="match_parent"
|
||||
style="@style/TapPrimaryButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="32dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
|
|
|
|||
|
|
@ -59,7 +59,6 @@
|
|||
|
||||
<color name="background_splash">#000000</color>
|
||||
<color name="transparent">#00000000</color>
|
||||
d
|
||||
|
||||
<color name="gradient_start_color">#FFFFFF</color>
|
||||
<color name="gradient_center_color">#F0FFFF</color>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ object ScanResponseMockFactory {
|
|||
batchId = "NEVER-MIND",
|
||||
cardPublicKey = ByteArray(0),
|
||||
firmwareVersion = when (cardConfig) {
|
||||
GenericCardConfig -> genericFirmwareVersion
|
||||
is GenericCardConfig -> genericFirmwareVersion
|
||||
MultiWalletCardConfig -> walletFirmwareVersion
|
||||
Wallet2CardConfig -> wallet2FirmwareVersion
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
package com.tangem.datasource.api.promotion
|
||||
|
||||
import com.tangem.datasource.api.promotion.models.*
|
||||
import retrofit2.http.*
|
||||
|
||||
/**
|
||||
*
|
||||
* Promotion API
|
||||
* @see <a href = "https://www.notion.so/tangem/Promotion-Program-API-0907159c3fdb4975aac761be632f44da">Documentation<a/>
|
||||
[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
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
|
|
@ -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()
|
||||
|
|
@ -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?,
|
||||
)
|
||||
|
|
@ -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()
|
||||
|
|
@ -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?,
|
||||
)
|
||||
|
|
@ -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()
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
|
|
@ -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()
|
||||
|
|
@ -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<QuotesResponse>
|
||||
|
||||
@GET("promotion")
|
||||
suspend fun getPromotionInfo(@Query("programName") name: String): ApiResponse<PromotionInfoResponse>
|
||||
}
|
||||
|
|
@ -5,4 +5,4 @@ import javax.inject.Qualifier
|
|||
@Qualifier
|
||||
@MustBeDocumented
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class PromotionOneInch
|
||||
annotation class DevTangemApi
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
/** Preferences keys set that should be migrated from "PreferencesDataSource" to a new DataStore<Preferences> */
|
||||
|
|
|
|||
|
|
@ -273,8 +273,8 @@
|
|||
<string name="main_promotion_credited">1INCH токены будут зачислены на адрес вашего кошелька в сети %s в течение 48 часов</string>
|
||||
<string name="main_scan_card_warning_view_subtitle">Чтобы получить доступ ко всем сетям, вам необходимо отсканировать карту</string>
|
||||
<string name="main_scan_card_warning_view_title">Отсканируйте карту</string>
|
||||
<string name="main_swap_promotion_message">Обменивайте свои цифровые активы между различными сетями</string>
|
||||
<string name="main_swap_promotion_title">Кроссчейн-своп теперь доступен</string>
|
||||
<string name="main_swap_changelly_promotion_message">Обменивайте свои токены с %1$s комиссии провайдера через Changelly с %2$s по %3$s февраля.</string>
|
||||
<string name="main_swap_changelly_promotion_title">Обмен с Changelly, %s комиссии</string>
|
||||
<string name="main_tokens">Токены</string>
|
||||
<string name="manage_tokens_add">Добавить</string>
|
||||
<string name="manage_tokens_edit">Изменить</string>
|
||||
|
|
@ -575,9 +575,9 @@
|
|||
<string name="token_details_unable_hide_alert_message">Токен %1$s является основной валютой в сети %2$s и не может быть скрыт до тех пор, пока у вас в списке есть другие токены этой сети.</string>
|
||||
<string name="token_details_unable_hide_alert_title">Невозможно скрыть %s</string>
|
||||
<string name="token_item_no_rate">Нет цены</string>
|
||||
<string name="token_swap_changelly_promotion_message">Обменивайте этот токен на другие с %1$s комиссии за обслуживание с %2$s по %3$s февраля.</string>
|
||||
<string name="token_swap_changelly_promotion_title">Обмен с Changelly, %s комиссии</string>
|
||||
<string name="token_swap_promotion_button">Обменять</string>
|
||||
<string name="token_swap_promotion_message">Обменяйте этот токен на другие активы в вашем портфеле</string>
|
||||
<string name="token_swap_promotion_title">Представляем кроссчейн-своп</string>
|
||||
<string name="transaction_history_contract_address">контракт: %s</string>
|
||||
<string name="transaction_history_empty_transactions">У вас еще нет транзакций</string>
|
||||
<string name="transaction_history_error_failed_to_load">Не удалось загрузить историю транзакций.\nНажмите на кнопку перезагрузки, чтобы обновить информацию.</string>
|
||||
|
|
|
|||
|
|
@ -273,8 +273,8 @@
|
|||
<string name="main_promotion_credited">1INCH tokens will be credited to your %s wallet address within 48 hours</string>
|
||||
<string name="main_scan_card_warning_view_subtitle">To access all the networks you need to scan the card</string>
|
||||
<string name="main_scan_card_warning_view_title">Scan your card</string>
|
||||
<string name="main_swap_promotion_message">Swap multiple currencies across several blockchains</string>
|
||||
<string name="main_swap_promotion_title">Cross-chain swaps are now available</string>
|
||||
<string name="main_swap_changelly_promotion_message">Enjoy %1$s service fees on swaps via Changelly from February %2$s-%3$s</string>
|
||||
<string name="main_swap_changelly_promotion_title">Swap with Changelly, %s fees</string>
|
||||
<string name="main_tokens">Tokens</string>
|
||||
<string name="manage_tokens_add">Add</string>
|
||||
<string name="manage_tokens_edit">Edit</string>
|
||||
|
|
@ -587,8 +587,8 @@
|
|||
<string name="token_details_unable_hide_alert_message">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.</string>
|
||||
<string name="token_details_unable_hide_alert_title">Unable to hide %s</string>
|
||||
<string name="token_item_no_rate">No rate</string>
|
||||
<string name="token_swap_promotion_message">Exchange this token for other assets in your portfolio</string>
|
||||
<string name="token_swap_promotion_title">Introducing cross-chain swaps</string>
|
||||
<string name="token_swap_changelly_promotion_message">Exchange this token for another at %1$s service fees from February %2$s-%3$s.</string>
|
||||
<string name="token_swap_changelly_promotion_title">Swap with Changelly, %s fees</string>
|
||||
<string name="token_swap_promotion_button">Swap now</string>
|
||||
<string name="transaction_history_contract_address">contract: %s</string>
|
||||
<string name="transaction_history_empty_transactions">You don\'t have any transactions yet</string>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -34,9 +36,13 @@ fun TangemButton(
|
|||
textStyle: TextStyle = TangemTheme.typography.button,
|
||||
shape: Shape = size.toShape(),
|
||||
) {
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -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<NotificationConfig>
|
||||
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 = {},
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
1
data/promo/.gitignore
vendored
Normal file
1
data/promo/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
25
data/promo/build.gradle.kts
Normal file
25
data/promo/build.gradle.kts
Normal file
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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<PromotionInfoResponse, PromoBanner?> {
|
||||
|
||||
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),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.data.promo.di
|
||||
|
||||
import com.tangem.data.promo.DefaultPromoRepository
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.di.DevTangemApi
|
||||
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(
|
||||
@DevTangemApi tangemTechApi: TangemTechApi,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): PromoRepository {
|
||||
return DefaultPromoRepository(tangemTechApi, dispatchers)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Boolean> {
|
||||
return appPreferencesStore.get(IS_WALLET_SWAP_PROMO_SHOW_KEY, true)
|
||||
.map { it && checkPromoPeriod() }
|
||||
override fun isReadyToShowWalletPromo(): Flow<Boolean> {
|
||||
return appPreferencesStore.get(IS_WALLET_SWAP_PROMO_CHANGELLY_SHOW_KEY, true)
|
||||
}
|
||||
|
||||
override fun isReadyToShowToken(): Flow<Boolean> {
|
||||
return appPreferencesStore.get(IS_TOKEN_SWAP_PROMO_SHOW_KEY, true)
|
||||
.map { it && checkPromoPeriod() }
|
||||
override fun isReadyToShowTokenPromo(): Flow<Boolean> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ sealed interface CardConfig {
|
|||
) {
|
||||
return MultiWalletCardConfig
|
||||
}
|
||||
return GenericCardConfig
|
||||
return GenericCardConfig(cardDTO.settings.maxWalletsCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<EllipticCurve>
|
||||
get() = listOf(
|
||||
EllipticCurve.Secp256k1,
|
||||
EllipticCurve.Ed25519,
|
||||
)
|
||||
class GenericCardConfig(maxWalletCount: Int) : CardConfig {
|
||||
|
||||
override val mandatoryCurves: List<EllipticCurve> = buildList {
|
||||
add(EllipticCurve.Secp256k1)
|
||||
if (maxWalletCount > 1) {
|
||||
add(EllipticCurve.Ed25519)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Old logic to determine primary curve for blockchain
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import kotlinx.coroutines.flow.Flow
|
|||
|
||||
class ShouldShowSwapPromoTokenUseCase(private val swapPromoRepository: SwapPromoRepository) {
|
||||
|
||||
operator fun invoke(): Flow<Boolean> = swapPromoRepository.isReadyToShowToken()
|
||||
operator fun invoke(): Flow<Boolean> = swapPromoRepository.isReadyToShowTokenPromo()
|
||||
|
||||
suspend fun neverToShow() = swapPromoRepository.setNeverToShowToken()
|
||||
suspend fun neverToShow() = swapPromoRepository.setNeverToShowTokenPromo()
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import kotlinx.coroutines.flow.Flow
|
|||
|
||||
class ShouldShowSwapPromoWalletUseCase(private val swapPromoRepository: SwapPromoRepository) {
|
||||
|
||||
operator fun invoke(): Flow<Boolean> = swapPromoRepository.isReadyToShowWallet()
|
||||
operator fun invoke(): Flow<Boolean> = swapPromoRepository.isReadyToShowWalletPromo()
|
||||
|
||||
suspend fun neverToShow() = swapPromoRepository.setNeverToShowWallet()
|
||||
suspend fun neverToShow() = swapPromoRepository.setNeverToShowWalletPromo()
|
||||
}
|
||||
|
|
@ -3,11 +3,11 @@ package com.tangem.domain.settings.repositories
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface SwapPromoRepository {
|
||||
fun isReadyToShowWallet(): Flow<Boolean>
|
||||
fun isReadyToShowWalletPromo(): Flow<Boolean>
|
||||
|
||||
fun isReadyToShowToken(): Flow<Boolean>
|
||||
fun isReadyToShowTokenPromo(): Flow<Boolean>
|
||||
|
||||
suspend fun setNeverToShowWallet()
|
||||
suspend fun setNeverToShowWalletPromo()
|
||||
|
||||
suspend fun setNeverToShowToken()
|
||||
suspend fun setNeverToShowTokenPromo()
|
||||
}
|
||||
|
|
@ -15,4 +15,5 @@ dependencies {
|
|||
implementation(deps.tangem.blockchain) {
|
||||
exclude(module = "joda-time")
|
||||
}
|
||||
implementation(deps.jodatime)
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,10 +7,7 @@ import com.tangem.domain.tokens.model.FeePaidCurrency
|
|||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
|
||||
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.tokens.repository.MarketCryptoCurrencyRepository
|
||||
import com.tangem.domain.tokens.repository.NetworksRepository
|
||||
import com.tangem.domain.tokens.repository.QuotesRepository
|
||||
import com.tangem.domain.tokens.repository.*
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.swap.domain.api.SwapRepository
|
||||
|
|
@ -29,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,
|
||||
) {
|
||||
|
|
@ -86,11 +84,14 @@ class GetCurrencyWarningsUseCase(
|
|||
): Flow<CryptoCurrencyWarning?> {
|
||||
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 ->
|
||||
|
|
@ -124,7 +125,10 @@ class GetCurrencyWarningsUseCase(
|
|||
}
|
||||
}
|
||||
if (showPromo) {
|
||||
CryptoCurrencyWarning.SwapPromo
|
||||
CryptoCurrencyWarning.SwapPromo(
|
||||
startDateTime = promoBanner.bannerState.timeline.start,
|
||||
endDateTime = promoBanner.bannerState.timeline.end,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.domain.tokens.repository
|
||||
|
||||
import com.tangem.domain.promo.PromoBanner
|
||||
|
||||
interface PromoRepository {
|
||||
|
||||
suspend fun getChangellyPromoBanner(): PromoBanner?
|
||||
}
|
||||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -46,7 +46,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,
|
||||
)
|
||||
|
|
@ -57,7 +57,7 @@ internal object WalletPreviewData {
|
|||
id = UserWalletId("321"),
|
||||
title = "Wallet 1",
|
||||
imageResId = R.drawable.ill_wallet2_cards3_120_106,
|
||||
onRenameClick = { _, _ -> },
|
||||
onRenameClick = { _ -> },
|
||||
onDeleteClick = {},
|
||||
)
|
||||
}
|
||||
|
|
@ -67,7 +67,7 @@ internal object WalletPreviewData {
|
|||
id = UserWalletId("24"),
|
||||
title = "Wallet 1",
|
||||
imageResId = R.drawable.ill_wallet2_cards3_120_106,
|
||||
onRenameClick = { _, _ -> },
|
||||
onRenameClick = { _ -> },
|
||||
onDeleteClick = {},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.components.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<WalletNotification>.addSwapPromoNotification(
|
||||
shouldShowPromo: Boolean,
|
||||
promoBanner: PromoBanner?,
|
||||
clickIntents: WalletClickIntentsV2,
|
||||
) {
|
||||
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<WalletNotification>.addCriticalNotifications(cardTypesResolver: CardTypesResolver) {
|
||||
addIf(
|
||||
element = WalletNotification.Critical.DevCard,
|
||||
|
|
|
|||
|
|
@ -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. " +
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ internal class WalletSkeletonStateConverter(
|
|||
title = name,
|
||||
additionalInfo = WalletAdditionalInfoFactory.resolve(wallet = this),
|
||||
imageResId = createImageResId(),
|
||||
onRenameClick = clickIntents::onRenameClick,
|
||||
onRenameClick = clickIntents::onRenameBeforeConfirmationClick,
|
||||
onDeleteClick = clickIntents::onDeleteBeforeConfirmationClick,
|
||||
)
|
||||
}
|
||||
|
|
@ -130,7 +130,7 @@ internal class WalletSkeletonStateConverter(
|
|||
title = name,
|
||||
additionalInfo = if (isMultiCurrency) WalletAdditionalInfoFactory.resolve(wallet = this) else null,
|
||||
imageResId = createImageResId(),
|
||||
onRenameClick = clickIntents::onRenameClick,
|
||||
onRenameClick = clickIntents::onRenameBeforeConfirmationClick,
|
||||
onDeleteClick = clickIntents::onDeleteBeforeConfirmationClick,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,7 +90,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,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,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,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.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(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -4,8 +4,11 @@ import androidx.annotation.DrawableRes
|
|||
import androidx.annotation.StringRes
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.indication
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.interaction.PressInteraction
|
||||
import androidx.compose.foundation.layout.*
|
||||
|
|
@ -31,13 +34,17 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import androidx.compose.ui.unit.*
|
||||
import androidx.constraintlayout.compose.*
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import androidx.constraintlayout.compose.ConstraintLayoutScope
|
||||
import androidx.constraintlayout.compose.Dimension
|
||||
import com.tangem.common.Strings
|
||||
import com.tangem.core.ui.components.FontSizeRange
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
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
|
||||
|
|
@ -61,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 ->
|
||||
|
|
@ -141,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),
|
||||
|
|
@ -196,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() })
|
||||
}
|
||||
|
|
@ -206,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")
|
||||
|
|
@ -365,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
|
||||
|
|
|
|||
|
|
@ -41,7 +41,9 @@ internal interface WalletClickIntents {
|
|||
|
||||
fun onTokenItemLongClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
|
||||
fun onRenameClick(userWalletId: UserWalletId, name: String)
|
||||
fun onRenameBeforeConfirmationClick(userWalletId: UserWalletId)
|
||||
|
||||
fun onRenameAfterConfirmationClick(userWalletId: UserWalletId, name: String)
|
||||
|
||||
fun onDeleteBeforeConfirmationClick(userWalletId: UserWalletId)
|
||||
|
||||
|
|
@ -72,6 +74,4 @@ internal interface WalletClickIntents {
|
|||
fun onExploreClick()
|
||||
|
||||
fun onTransactionClick(txHash: String)
|
||||
|
||||
fun onCloseSwapPromoNotificationClick()
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@ import com.tangem.domain.common.CardTypesResolver
|
|||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.settings.IsReadyToShowRateAppUseCase
|
||||
import com.tangem.domain.settings.ShouldShowSwapPromoWalletUseCase
|
||||
import com.tangem.domain.tokens.GetMissedAddressesCryptoCurrenciesUseCase
|
||||
import com.tangem.domain.tokens.error.GetCurrenciesError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
|
|
@ -41,7 +40,6 @@ internal class WalletNotificationsListFactory(
|
|||
private val isNeedToBackupUseCase: IsNeedToBackupUseCase,
|
||||
private val getMissedAddressCryptoCurrenciesUseCase: GetMissedAddressesCryptoCurrenciesUseCase,
|
||||
private val hasSingleWalletSignedHashesUseCase: HasSingleWalletSignedHashesUseCase,
|
||||
private val shouldShowSwapPromoWalletUseCase: ShouldShowSwapPromoWalletUseCase,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
) {
|
||||
|
||||
|
|
@ -57,12 +55,9 @@ internal class WalletNotificationsListFactory(
|
|||
flow2 = isReadyToShowRateAppUseCase().conflate(),
|
||||
flow3 = isNeedToBackupUseCase(selectedWallet.walletId).conflate(),
|
||||
flow4 = getMissedAddressCryptoCurrenciesUseCase(selectedWallet.walletId).conflate(),
|
||||
flow5 = shouldShowSwapPromoWalletUseCase().conflate(),
|
||||
) { hasSignedHashes, isReadyToShowRating, isNeedToBackup, maybeMissedAddressCurrencies, isShowSwapPromo ->
|
||||
) { hasSignedHashes, isReadyToShowRating, isNeedToBackup, maybeMissedAddressCurrencies ->
|
||||
readyForRateAppNotification = true
|
||||
buildList {
|
||||
addSwapPromoNotification(isShowSwapPromo, cardTypesResolver)
|
||||
|
||||
addCriticalNotifications(cardTypesResolver)
|
||||
|
||||
addInformationalNotifications(cardTypesResolver, maybeMissedAddressCurrencies)
|
||||
|
|
@ -86,18 +81,6 @@ internal class WalletNotificationsListFactory(
|
|||
}
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addSwapPromoNotification(
|
||||
showSwapPromo: Boolean,
|
||||
cardTypesResolver: CardTypesResolver,
|
||||
) {
|
||||
addIf(
|
||||
element = WalletNotification.SwapPromo(
|
||||
clickIntents::onCloseSwapPromoNotificationClick,
|
||||
),
|
||||
condition = showSwapPromo && cardTypesResolver.isMultiwalletAllowed(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun MutableList<WalletNotification>.addCriticalNotifications(cardTypesResolver: CardTypesResolver) {
|
||||
addIf(
|
||||
element = WalletNotification.Critical.DevCard,
|
||||
|
|
|
|||
|
|
@ -129,7 +129,6 @@ internal class WalletViewModel @Inject constructor(
|
|||
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
|
||||
private val isDemoCardUseCase: IsDemoCardUseCase,
|
||||
private val scanCardToUnlockWalletUseCase: ScanCardToUnlockWalletClickHandler,
|
||||
private val shouldShowSwapPromoWalletUseCase: ShouldShowSwapPromoWalletUseCase,
|
||||
isReadyToShowRateAppUseCase: IsReadyToShowRateAppUseCase,
|
||||
isNeedToBackupUseCase: IsNeedToBackupUseCase,
|
||||
getMissedAddressesCryptoCurrenciesUseCase: GetMissedAddressesCryptoCurrenciesUseCase,
|
||||
|
|
@ -148,7 +147,6 @@ internal class WalletViewModel @Inject constructor(
|
|||
isNeedToBackupUseCase = isNeedToBackupUseCase,
|
||||
getMissedAddressCryptoCurrenciesUseCase = getMissedAddressesCryptoCurrenciesUseCase,
|
||||
hasSingleWalletSignedHashesUseCase = hasSingleWalletSignedHashesUseCase,
|
||||
shouldShowSwapPromoWalletUseCase = shouldShowSwapPromoWalletUseCase,
|
||||
clickIntents = this,
|
||||
)
|
||||
|
||||
|
|
@ -795,12 +793,6 @@ internal class WalletViewModel @Inject constructor(
|
|||
refreshSingleCurrencyContent(selectedWalletIndex)
|
||||
}
|
||||
|
||||
override fun onCloseSwapPromoNotificationClick() {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
shouldShowSwapPromoWalletUseCase.neverToShow()
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: refreshSingleCurrencyContent mustn't update the TxHistory and Buttons. It only must fetch primary
|
||||
// currency. Now it not works because GetPrimaryCurrency's subscriber uses .distinctUntilChanged()
|
||||
private fun refreshSingleCurrencyContent(walletIndex: Int) {
|
||||
|
|
@ -966,9 +958,23 @@ internal class WalletViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onRenameClick(userWalletId: UserWalletId, name: String) {
|
||||
override fun onRenameBeforeConfirmationClick(userWalletId: UserWalletId) {
|
||||
val state = uiState as? WalletState.ContentState ?: return
|
||||
uiState = stateFactory.getStateAndTriggerEvent(
|
||||
state = uiState,
|
||||
event = WalletEvent.ShowAlert(
|
||||
state = WalletAlertState.RenameWalletAlert(
|
||||
text = state.walletsListConfig.wallets[state.walletsListConfig.selectedWalletIndex].title,
|
||||
onConfirmClick = { onRenameAfterConfirmationClick(userWalletId, it) },
|
||||
),
|
||||
),
|
||||
setUiState = { uiState = it },
|
||||
)
|
||||
}
|
||||
|
||||
override fun onRenameAfterConfirmationClick(userWalletId: UserWalletId, name: String) {
|
||||
viewModelScope.launch(dispatchers.io) {
|
||||
updateWalletUseCase(userWalletId = userWalletId, update = { it.copy(name) })
|
||||
updateWalletUseCase(userWalletId = userWalletId, update = { it.copy(name = name) })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,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
|
||||
|
|
@ -62,6 +63,8 @@ internal interface WalletWarningsClickIntents {
|
|||
fun onDislikeAppClick()
|
||||
|
||||
fun onCloseRateAppWarningClick()
|
||||
|
||||
fun onCloseSwapPromoClick()
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
|
|
@ -82,6 +85,7 @@ internal class WalletWarningsClickIntentsImplementer @Inject constructor(
|
|||
private val reduxStateHolder: ReduxStateHolder,
|
||||
private val testerFeatureToggles: TesterFeatureToggles,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val shouldShowSwapPromoWalletUseCase: ShouldShowSwapPromoWalletUseCase,
|
||||
) : BaseWalletClickIntents(), WalletWarningsClickIntents {
|
||||
|
||||
override fun onAddBackupCardClick() {
|
||||
|
|
@ -223,6 +227,8 @@ internal class WalletWarningsClickIntentsImplementer @Inject constructor(
|
|||
class DerivationData(val derivations: Pair<ByteArrayKey, List<DerivationPath>>)
|
||||
|
||||
override fun onOpenUnlockWalletsBottomSheetClick() {
|
||||
analyticsEventHandler.send(MainScreen.WalletUnlockTapped)
|
||||
|
||||
val config = requireNotNull(stateHolder.getSelectedWallet().bottomSheetConfig) {
|
||||
"Impossible to open unlock wallet bottom sheet if it's null"
|
||||
}
|
||||
|
|
@ -302,4 +308,10 @@ internal class WalletWarningsClickIntentsImplementer @Inject constructor(
|
|||
remindToRateAppLaterUseCase()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCloseSwapPromoClick() {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
shouldShowSwapPromoWalletUseCase.neverToShow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -152,4 +152,5 @@ include(":data:wallets")
|
|||
include(":data:analytics")
|
||||
include(":data:transaction")
|
||||
include(":data:visa")
|
||||
include(":data:promo")
|
||||
// endregion Data modules
|
||||
Loading…
Add table
Add a link
Reference in a new issue