diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 834e2bdd48..8c5969e599 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -52,6 +52,7 @@ dependencies { implementation(projects.domain.balanceHiding.models) implementation(projects.domain.transaction) implementation(projects.domain.analytics) + implementation(projects.domain.visa) implementation(projects.common) implementation(projects.core.analytics) @@ -78,6 +79,7 @@ dependencies { implementation(projects.data.wallets) implementation(projects.data.analytics) implementation(projects.data.transaction) + implementation(projects.data.visa) /** Features */ implementation(projects.features.onboarding) diff --git a/app/src/main/java/com/tangem/tap/di/domain/VisaDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/VisaDomainModule.kt new file mode 100644 index 0000000000..61170463f0 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/di/domain/VisaDomainModule.kt @@ -0,0 +1,18 @@ +package com.tangem.tap.di.domain + +import com.tangem.domain.visa.GetVisaCurrencyUseCase +import com.tangem.domain.visa.repository.VisaRepository +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ViewModelComponent + +@Module +@InstallIn(ViewModelComponent::class) +internal object VisaDomainModule { + + @Provides + fun provideVisaCurrencyUseCase(visaRepository: VisaRepository): GetVisaCurrencyUseCase { + return GetVisaCurrencyUseCase(visaRepository) + } +} \ No newline at end of file diff --git a/data/visa/.gitignore b/data/visa/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/data/visa/.gitignore @@ -0,0 +1 @@ +/build diff --git a/data/visa/build.gradle.kts b/data/visa/build.gradle.kts new file mode 100644 index 0000000000..e57696df79 --- /dev/null +++ b/data/visa/build.gradle.kts @@ -0,0 +1,21 @@ +plugins { + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) + alias(deps.plugins.kotlin.kapt) + id("configuration") +} + +android { + namespace = "com.tangem.data.visa" +} + +dependencies { + + /** Project - Domain */ + implementation(projects.domain.visa) + implementation(projects.domain.wallets.models) + + /** DI */ + implementation(deps.hilt.core) + kapt(deps.hilt.kapt) +} \ No newline at end of file diff --git a/data/visa/src/main/kotlin/com/tangem/data/visa/DummyVisaRepository.kt b/data/visa/src/main/kotlin/com/tangem/data/visa/DummyVisaRepository.kt new file mode 100644 index 0000000000..40815fac3c --- /dev/null +++ b/data/visa/src/main/kotlin/com/tangem/data/visa/DummyVisaRepository.kt @@ -0,0 +1,12 @@ +package com.tangem.data.visa + +import com.tangem.domain.visa.model.VisaCurrency +import com.tangem.domain.visa.repository.VisaRepository +import com.tangem.domain.wallets.models.UserWalletId + +internal class DummyVisaRepository : VisaRepository { + + override suspend fun getVisaCurrency(userWalletId: UserWalletId, isRefresh: Boolean): VisaCurrency { + TODO(reason = "Implement in [REDACTED_JIRA]") + } +} \ No newline at end of file diff --git a/data/visa/src/main/kotlin/com/tangem/data/visa/di/VisaDataModule.kt b/data/visa/src/main/kotlin/com/tangem/data/visa/di/VisaDataModule.kt new file mode 100644 index 0000000000..ab68249dd9 --- /dev/null +++ b/data/visa/src/main/kotlin/com/tangem/data/visa/di/VisaDataModule.kt @@ -0,0 +1,20 @@ +package com.tangem.data.visa.di + +import com.tangem.data.visa.DummyVisaRepository +import com.tangem.domain.visa.repository.VisaRepository +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 VisaDataModule { + + @Provides + @Singleton + fun provideVisaRepository(): VisaRepository { + return DummyVisaRepository() + } +} \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt b/domain/legacy/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt index db6be16a4d..077ffe5de1 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt @@ -72,7 +72,10 @@ internal class TangemCardTypesResolver( override fun isSingleWalletWithToken(): Boolean = walletData?.token != null && !isMultiwalletAllowed() override fun isMultiwalletAllowed(): Boolean { - return !isTangemTwins() && !card.isStart2Coin && !isTangemNote() && + return !isTangemTwins() && + !card.isStart2Coin && + !isTangemNote() && + !isVisaWallet() && (multiWalletAvailable() || card.wallets.firstOrNull()?.curve == EllipticCurve.Secp256k1) } @@ -81,6 +84,7 @@ internal class TangemCardTypesResolver( override fun getBlockchain(): Blockchain { return when (productType) { ProductType.Start2Coin -> if (card.isTestCard) Blockchain.BitcoinTestnet else Blockchain.Bitcoin + ProductType.Visa -> Blockchain.PolygonTestnet else -> { val blockchainName: String = walletData?.blockchain ?: if (productType == ProductType.Note) { diff --git a/domain/visa/.gitignore b/domain/visa/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/domain/visa/.gitignore @@ -0,0 +1 @@ +/build diff --git a/domain/visa/build.gradle.kts b/domain/visa/build.gradle.kts new file mode 100644 index 0000000000..64262e878a --- /dev/null +++ b/domain/visa/build.gradle.kts @@ -0,0 +1,22 @@ +plugins { + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) + id("configuration") +} + +android { + namespace = "com.tangem.domain.visa" +} + +dependencies { + + /** Project - Domain */ + implementation(projects.core.utils) + implementation(projects.domain.core) + implementation(projects.domain.wallets.models) + implementation(projects.domain.tokens.models) + implementation(projects.domain.appCurrency.models) + + /** Libs - Other */ + implementation(deps.jodatime) +} \ No newline at end of file diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/visa/GetVisaCurrencyUseCase.kt b/domain/visa/src/main/kotlin/com/tangem/domain/visa/GetVisaCurrencyUseCase.kt new file mode 100644 index 0000000000..d5b71eff26 --- /dev/null +++ b/domain/visa/src/main/kotlin/com/tangem/domain/visa/GetVisaCurrencyUseCase.kt @@ -0,0 +1,18 @@ +package com.tangem.domain.visa + +import arrow.core.Either +import com.tangem.domain.visa.model.VisaCurrency +import com.tangem.domain.visa.repository.VisaRepository +import com.tangem.domain.wallets.models.UserWalletId + +class GetVisaCurrencyUseCase( + private val repository: VisaRepository, +) { + + suspend operator fun invoke( + userWalletId: UserWalletId, + isRefresh: Boolean = false, + ): Either { + return Either.catch { repository.getVisaCurrency(userWalletId, isRefresh) } + } +} \ No newline at end of file diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/visa/model/VisaCurrency.kt b/domain/visa/src/main/kotlin/com/tangem/domain/visa/model/VisaCurrency.kt new file mode 100644 index 0000000000..1e6a0c55a1 --- /dev/null +++ b/domain/visa/src/main/kotlin/com/tangem/domain/visa/model/VisaCurrency.kt @@ -0,0 +1,32 @@ +package com.tangem.domain.visa.model + +import com.tangem.domain.appcurrency.model.AppCurrency +import org.joda.time.DateTime +import java.math.BigDecimal + +data class VisaCurrency( + val networkName: String, + val symbol: String, + val decimals: Int, + val fiatRate: BigDecimal?, + val fiatCurrency: AppCurrency, + val balances: Balances, + val limits: Limits, +) { + + data class Balances( + val total: BigDecimal, + val verified: BigDecimal, + val available: BigDecimal, + val blocked: BigDecimal, + val debt: BigDecimal, + val pendingRefund: BigDecimal, + ) + + data class Limits( + val remainingOtp: BigDecimal, + val remainingNoOtp: BigDecimal, + val singleTransaction: BigDecimal, + val expirationDate: DateTime, + ) +} \ No newline at end of file diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/visa/repository/VisaRepository.kt b/domain/visa/src/main/kotlin/com/tangem/domain/visa/repository/VisaRepository.kt new file mode 100644 index 0000000000..96d6efd7fd --- /dev/null +++ b/domain/visa/src/main/kotlin/com/tangem/domain/visa/repository/VisaRepository.kt @@ -0,0 +1,9 @@ +package com.tangem.domain.visa.repository + +import com.tangem.domain.visa.model.VisaCurrency +import com.tangem.domain.wallets.models.UserWalletId + +interface VisaRepository { + + suspend fun getVisaCurrency(userWalletId: UserWalletId, isRefresh: Boolean = false): VisaCurrency +} \ No newline at end of file diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts index decfa1c37e..a24c30a994 100644 --- a/features/wallet/impl/build.gradle.kts +++ b/features/wallet/impl/build.gradle.kts @@ -69,6 +69,7 @@ dependencies { implementation(projects.domain.balanceHiding) implementation(projects.domain.balanceHiding.models) implementation(projects.domain.analytics) + implementation(projects.domain.visa) //TODO: Create api/impl modules for onboarding [REDACTED_JIRA] implementation(projects.features.onboarding) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/VisaWalletContentLoader.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/VisaWalletContentLoader.kt index 8a221a2d24..7d41dcb257 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/VisaWalletContentLoader.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/VisaWalletContentLoader.kt @@ -1,14 +1,11 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors -import com.tangem.core.analytics.api.AnalyticsEventHandler -import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase -import com.tangem.domain.settings.SetWalletWithFundsFoundUseCase import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase +import com.tangem.domain.visa.GetVisaCurrencyUseCase import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.subscribers.PrimaryCurrencySubscriber import com.tangem.feature.wallet.presentation.wallet.subscribers.TxHistorySubscriber import com.tangem.feature.wallet.presentation.wallet.subscribers.VisaWalletBalancesAndLimitsSubscriber import com.tangem.feature.wallet.presentation.wallet.subscribers.WalletSubscriber @@ -21,24 +18,20 @@ internal class VisaWalletContentLoader( private val isRefresh: Boolean, private val stateHolder: WalletStateController, private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase, - private val setWalletWithFundsFoundUseCase: SetWalletWithFundsFoundUseCase, private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase, private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase, - private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, - private val analyticsEventHandler: AnalyticsEventHandler, + private val getVisaCurrencyUseCase: GetVisaCurrencyUseCase, ) : WalletContentLoader(id = userWallet.walletId) { override fun create(): List { return listOf( - PrimaryCurrencySubscriber( + VisaWalletBalancesAndLimitsSubscriber( userWallet = userWallet, stateHolder = stateHolder, - getPrimaryCurrencyStatusUpdatesUseCase = getPrimaryCurrencyStatusUpdatesUseCase, - setWalletWithFundsFoundUseCase = setWalletWithFundsFoundUseCase, - analyticsEventHandler = analyticsEventHandler, - getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase, + isRefresh = isRefresh, + getVisaCurrencyUseCase = getVisaCurrencyUseCase, + clickIntents = clickIntents, ), - VisaWalletBalancesAndLimitsSubscriber(userWallet, stateHolder, clickIntents), TxHistorySubscriber( userWallet = userWallet, isRefresh = isRefresh, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/VisaWalletContentLoaderFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/VisaWalletContentLoaderFactory.kt index 61db482fa2..e81f30afff 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/VisaWalletContentLoaderFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/VisaWalletContentLoaderFactory.kt @@ -1,11 +1,9 @@ package com.tangem.feature.wallet.presentation.wallet.loaders.implementors -import com.tangem.core.analytics.api.AnalyticsEventHandler -import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase -import com.tangem.domain.settings.SetWalletWithFundsFoundUseCase import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase +import com.tangem.domain.visa.GetVisaCurrencyUseCase import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 @@ -17,11 +15,9 @@ import javax.inject.Inject internal class VisaWalletContentLoaderFactory @Inject constructor( private val stateHolder: WalletStateController, private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase, - private val setWalletWithFundsFoundUseCase: SetWalletWithFundsFoundUseCase, private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase, private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase, - private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, - private val analyticsEventHandler: AnalyticsEventHandler, + private val getVisaCurrencyUseCase: GetVisaCurrencyUseCase, ) { fun create(userWallet: UserWallet, clickIntents: WalletClickIntentsV2, isRefresh: Boolean): WalletContentLoader { @@ -31,11 +27,9 @@ internal class VisaWalletContentLoaderFactory @Inject constructor( isRefresh = isRefresh, stateHolder = stateHolder, getPrimaryCurrencyStatusUpdatesUseCase = getPrimaryCurrencyStatusUpdatesUseCase, - setWalletWithFundsFoundUseCase = setWalletWithFundsFoundUseCase, txHistoryItemsCountUseCase = txHistoryItemsCountUseCase, txHistoryItemsUseCase = txHistoryItemsUseCase, - getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase, - analyticsEventHandler = analyticsEventHandler, + getVisaCurrencyUseCase = getVisaCurrencyUseCase, ) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/BalancesAndLimitsBlockState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/BalancesAndLimitsBlockState.kt index 883ce0b468..a64e659900 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/BalancesAndLimitsBlockState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/model/BalancesAndLimitsBlockState.kt @@ -11,7 +11,6 @@ internal sealed class BalancesAndLimitsBlockState { data class Content( val availableBalance: String, - val currencySymbol: String, val limitDays: Int, val isEnabled: Boolean, val onClick: () -> Unit, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetBalancesAndLimitsTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetBalancesAndLimitsTransformer.kt index 4092298ae5..9af5df2c08 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetBalancesAndLimitsTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetBalancesAndLimitsTransformer.kt @@ -1,32 +1,98 @@ package com.tangem.feature.wallet.presentation.wallet.state2.transformers +import arrow.core.Either +import arrow.core.getOrElse +import com.tangem.core.ui.extensions.stringReference +import com.tangem.core.ui.utils.BigDecimalFormatter +import com.tangem.domain.visa.model.VisaCurrency import com.tangem.domain.wallets.models.UserWallet +import com.tangem.feature.wallet.presentation.wallet.domain.getCardsCount +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletAdditionalInfo +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState import com.tangem.feature.wallet.presentation.wallet.state2.model.BalancesAndLimitsBlockState import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 +import org.joda.time.DateTime +import org.joda.time.Days internal class SetBalancesAndLimitsTransformer( - userWallet: UserWallet, + private val userWallet: UserWallet, + private val maybeVisaCurrency: Either, private val clickIntents: WalletClickIntentsV2, ) : WalletStateTransformer(userWallet.walletId) { override fun transform(prevState: WalletState): WalletState { return prevState.transformWhenInState { state -> + val visaCurrency = maybeVisaCurrency.getOrElse { + return state.copy( + walletCardState = getErrorWalletCardState(state.walletCardState), + balancesAndLimitBlockState = BalancesAndLimitsBlockState.Error, + ) + } + state.copy( - balancesAndLimitBlockState = state.balancesAndLimitBlockState.toLoadedState(), + walletCardState = getContentWalletCardState(state.walletCardState, visaCurrency), + balancesAndLimitBlockState = getContentBlockState(visaCurrency), ) } } - // TODO: Implement in [REDACTED_JIRA] - @Suppress("UnusedReceiverParameter") - private fun BalancesAndLimitsBlockState.toLoadedState(): BalancesAndLimitsBlockState { - return BalancesAndLimitsBlockState.Content( - availableBalance = "400.00", - currencySymbol = "USDT", - limitDays = 7, - isEnabled = true, - onClick = clickIntents::onBalancesAndLimitsClick, + private fun getContentBlockState(visaCurrency: VisaCurrency) = BalancesAndLimitsBlockState.Content( + availableBalance = BigDecimalFormatter.formatCryptoAmount( + visaCurrency.limits.remainingOtp, + visaCurrency.symbol, + visaCurrency.decimals, + ), + limitDays = Days.daysBetween(DateTime.now(), visaCurrency.limits.expirationDate).days.inc(), + isEnabled = true, + onClick = clickIntents::onBalancesAndLimitsClick, + ) + + private fun getErrorWalletCardState(prevState: WalletCardState): WalletCardState { + return with(prevState) { + WalletCardState.Error( + id = id, + title = title, + imageResId = imageResId, + onRenameClick = onRenameClick, + onDeleteClick = onDeleteClick, + ) + } + } + + private fun getContentWalletCardState(prevState: WalletCardState, visaCurrency: VisaCurrency): WalletCardState { + return with(prevState) { + WalletCardState.Content( + id = id, + title = title, + additionalInfo = createAdditionalInfo(visaCurrency), + imageResId = imageResId, + onRenameClick = onRenameClick, + onDeleteClick = onDeleteClick, + balance = BigDecimalFormatter.formatCryptoAmount( + visaCurrency.balances.available, + visaCurrency.symbol, + visaCurrency.decimals, + ), + cardCount = userWallet.getCardsCount(), + ) + } + } + + private fun createAdditionalInfo(visaCurrency: VisaCurrency): WalletAdditionalInfo { + val fiatAmount = BigDecimalFormatter.formatFiatAmount( + fiatAmount = visaCurrency.fiatRate?.let { visaCurrency.balances.available.multiply(it) }, + fiatCurrencyCode = visaCurrency.fiatCurrency.code, + fiatCurrencySymbol = visaCurrency.fiatCurrency.symbol, ) + val infoContent = stringReference( + value = buildString { + append(fiatAmount) + append(" • ") + append(visaCurrency.networkName) + }, + ) + + return WalletAdditionalInfo(hideable = true, infoContent) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetPrimaryCurrencyTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetPrimaryCurrencyTransformer.kt index ade63f113c..da21391c07 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetPrimaryCurrencyTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/SetPrimaryCurrencyTransformer.kt @@ -8,7 +8,6 @@ import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCard import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState import com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter.SingleWalletCardStateConverter import com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter.SingleWalletMarketPriceConverter -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter.VisaWalletCardStateConverter import timber.log.Timber internal class SetPrimaryCurrencyTransformer( @@ -25,15 +24,11 @@ internal class SetPrimaryCurrencyTransformer( marketPriceBlockState = prevState.marketPriceBlockState.toLoadedState(), ) } - is WalletState.Visa.Content -> { - prevState.copy( - walletCardState = prevState.walletCardState.toLoadedVisaState(), - depositButtonState = prevState.depositButtonState.copy(isEnabled = true), - ) + is WalletState.Visa -> { + Timber.w("Impossible to load primary currency status for VISA wallet") + prevState } - is WalletState.Visa.Locked, - is WalletState.SingleCurrency.Locked, - -> { + is WalletState.SingleCurrency.Locked -> { Timber.w("Impossible to load primary currency status for locked wallet") prevState } @@ -48,10 +43,6 @@ internal class SetPrimaryCurrencyTransformer( return SingleWalletCardStateConverter(status.value, userWallet, appCurrency).convert(value = this) } - private fun WalletCardState.toLoadedVisaState(): WalletCardState { - return VisaWalletCardStateConverter(status, userWallet, appCurrency).convert(value = this) - } - private fun MarketPriceBlockState.toLoadedState(): MarketPriceBlockState { return SingleWalletMarketPriceConverter(status.value, appCurrency).convert(value = this) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/BalancesAndLimitsBottomSheetConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/BalancesAndLimitsBottomSheetConverter.kt new file mode 100644 index 0000000000..2a6139aa60 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/BalancesAndLimitsBottomSheetConverter.kt @@ -0,0 +1,52 @@ +package com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter + +import com.tangem.core.ui.utils.BigDecimalFormatter +import com.tangem.core.ui.utils.DateTimeFormatters +import com.tangem.domain.visa.model.VisaCurrency +import com.tangem.feature.wallet.presentation.wallet.state.WalletAlertState +import com.tangem.feature.wallet.presentation.wallet.state.WalletEvent +import com.tangem.feature.wallet.presentation.wallet.state2.model.BalancesAndLimitsBottomSheetConfig +import com.tangem.feature.wallet.presentation.wallet.state2.utils.WalletEventSender +import com.tangem.utils.converter.Converter +import java.math.BigDecimal + +internal class BalancesAndLimitsBottomSheetConverter( + private val eventSender: WalletEventSender, +) : Converter { + + override fun convert(value: VisaCurrency): BalancesAndLimitsBottomSheetConfig { + return BalancesAndLimitsBottomSheetConfig( + currency = value.symbol, + balance = BalancesAndLimitsBottomSheetConfig.Balance( + totalBalance = value.balances.total.let(::formatAmount), + availableBalance = value.balances.available.let(::formatAmount), + blockedBalance = value.balances.blocked.let(::formatAmount), + debit = value.balances.debt.let(::formatAmount), + pending = value.balances.pendingRefund.let(::formatAmount), + amlVerified = value.balances.verified.let(::formatAmount), + ), + limit = BalancesAndLimitsBottomSheetConfig.Limit( + availableBy = DateTimeFormatters.formatDate(date = value.limits.expirationDate), + inStore = value.limits.remainingOtp.let(::formatAmount), + other = value.limits.remainingNoOtp.let(::formatAmount), + singleTransaction = value.limits.singleTransaction.let(::formatAmount), + ), + onBalanceInfoClick = this::showBalanceInfo, + onLimitInfoClick = this::showLimitInfo, + ) + } + + private fun formatAmount(amount: BigDecimal): String = BigDecimalFormatter.formatCryptoAmount( + amount, + cryptoCurrency = "", + decimals = 2, + ) + + private fun showBalanceInfo() { + eventSender.send(WalletEvent.ShowAlert(WalletAlertState.VisaBalancesInfo)) + } + + private fun showLimitInfo() { + eventSender.send(WalletEvent.ShowAlert(WalletAlertState.VisaLimitsInfo)) + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/VisaWalletCardStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/VisaWalletCardStateConverter.kt deleted file mode 100644 index 56766efc40..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state2/transformers/converter/VisaWalletCardStateConverter.kt +++ /dev/null @@ -1,89 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter - -import com.tangem.core.ui.extensions.stringReference -import com.tangem.core.ui.utils.BigDecimalFormatter -import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.tokens.model.CryptoCurrencyStatus -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.presentation.wallet.domain.getCardsCount -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletAdditionalInfo -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState -import com.tangem.utils.converter.Converter - -internal class VisaWalletCardStateConverter( - private val status: CryptoCurrencyStatus, - private val selectedWallet: UserWallet, - private val appCurrency: AppCurrency, -) : Converter { - - override fun convert(value: WalletCardState): WalletCardState { - return when (status.value) { - is CryptoCurrencyStatus.Loading -> value.toLoadingState() - is CryptoCurrencyStatus.Custom, - is CryptoCurrencyStatus.MissedDerivation, - is CryptoCurrencyStatus.Unreachable, - -> value.toErrorState() - is CryptoCurrencyStatus.NoQuote, - is CryptoCurrencyStatus.Loaded, - is CryptoCurrencyStatus.NoAccount, - is CryptoCurrencyStatus.NoAmount, - -> value.toContentState(status) - } - } - - private fun WalletCardState.toLoadingState(): WalletCardState { - return WalletCardState.Loading( - id = id, - title = title, - imageResId = imageResId, - onRenameClick = onRenameClick, - onDeleteClick = onDeleteClick, - ) - } - - private fun WalletCardState.toErrorState(): WalletCardState { - return WalletCardState.Error( - id = id, - title = title, - imageResId = imageResId, - onRenameClick = onRenameClick, - onDeleteClick = onDeleteClick, - ) - } - - private fun WalletCardState.toContentState(status: CryptoCurrencyStatus): WalletCardState { - return WalletCardState.Content( - id = id, - title = title, - additionalInfo = createAdditionalInfo(status), - imageResId = imageResId, - onRenameClick = onRenameClick, - onDeleteClick = onDeleteClick, - balance = formatAmount(status), - cardCount = selectedWallet.getCardsCount(), - ) - } - - private fun createAdditionalInfo(status: CryptoCurrencyStatus): WalletAdditionalInfo { - val fiatAmount = BigDecimalFormatter.formatFiatAmount( - status.value.fiatAmount, - fiatCurrencyCode = appCurrency.code, - fiatCurrencySymbol = appCurrency.symbol, - ) - val infoContent = stringReference( - value = buildString { - append(fiatAmount) - append(" • ") - append(status.currency.network.name) - }, - ) - - return WalletAdditionalInfo(hideable = true, infoContent) - } - - private fun formatAmount(status: CryptoCurrencyStatus): String { - val amount = status.value.amount ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN - - return BigDecimalFormatter.formatCryptoAmount(amount, status.currency) - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/TxHistorySubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/TxHistorySubscriber.kt index a1e47bf291..d9d435e6f0 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/TxHistorySubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/TxHistorySubscriber.kt @@ -3,6 +3,8 @@ package com.tangem.feature.wallet.presentation.wallet.subscribers import androidx.paging.PagingData import androidx.paging.cachedIn import arrow.core.Either +import com.tangem.core.ui.components.transactions.state.TxHistoryState +import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.txhistory.models.TxHistoryItem @@ -13,10 +15,8 @@ import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.domain.collectLatest import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetTxHistoryCountErrorTransformer -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetTxHistoryCountTransformer -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetTxHistoryItemsErrorTransformer -import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetTxHistoryItemsTransformer +import com.tangem.feature.wallet.presentation.wallet.state2.model.WalletState +import com.tangem.feature.wallet.presentation.wallet.state2.transformers.* import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow @@ -37,6 +37,24 @@ internal class TxHistorySubscriber( ) : WalletSubscriber() { override fun create(coroutineScope: CoroutineScope): Flow> { + // TODO: [REDACTED_JIRA] + if (userWallet.scanResponse.cardTypesResolver.isVisaWallet()) { + return flow { + stateHolder.update( + object : WalletStateTransformer(userWallet.walletId) { + override fun transform(prevState: WalletState): WalletState { + return when (prevState) { + is WalletState.Visa.Content -> prevState.copy( + txHistoryState = TxHistoryState.Empty(onExploreClick = {}), + ) + else -> prevState + } + } + }, + ) + } + } + return flow { getPrimaryCurrencyStatusUpdatesUseCase.collectLatest(userWalletId = userWallet.walletId) { status -> val maybeTxHistoryItemCount = txHistoryItemsCountUseCase( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/VisaWalletBalancesAndLimitsSubscriber.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/VisaWalletBalancesAndLimitsSubscriber.kt index af94827eb2..a25af15cdc 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/VisaWalletBalancesAndLimitsSubscriber.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/VisaWalletBalancesAndLimitsSubscriber.kt @@ -1,23 +1,32 @@ package com.tangem.feature.wallet.presentation.wallet.subscribers +import com.tangem.domain.visa.GetVisaCurrencyUseCase import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController import com.tangem.feature.wallet.presentation.wallet.state2.transformers.SetBalancesAndLimitsTransformer import com.tangem.feature.wallet.presentation.wallet.viewmodels.intents.WalletClickIntentsV2 import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.asFlow +import kotlinx.coroutines.flow.flow +@Suppress("LongParameterList") internal class VisaWalletBalancesAndLimitsSubscriber( private val userWallet: UserWallet, private val stateHolder: WalletStateController, + private val isRefresh: Boolean, + private val getVisaCurrencyUseCase: GetVisaCurrencyUseCase, private val clickIntents: WalletClickIntentsV2, ) : WalletSubscriber() { - // TODO: Implement in [REDACTED_JIRA] - override fun create(coroutineScope: CoroutineScope): Flow<*> = suspend { - delay(timeMillis = 500) - stateHolder.update(SetBalancesAndLimitsTransformer(userWallet, clickIntents)) - }.asFlow() + override fun create(coroutineScope: CoroutineScope): Flow<*> { + return flow { + stateHolder.update( + SetBalancesAndLimitsTransformer( + userWallet = userWallet, + maybeVisaCurrency = getVisaCurrencyUseCase(userWallet.walletId, isRefresh), + clickIntents = clickIntents, + ), + ) + } + } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/BalancesAndLimitsBlock.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/BalancesAndLimitsBlock.kt index 26ede92649..5855ab22f6 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/BalancesAndLimitsBlock.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/visa/BalancesAndLimitsBlock.kt @@ -113,7 +113,6 @@ private fun Content(state: BalancesAndLimitsBlockState, modifier: Modifier = Mod is BalancesAndLimitsBlockState.Content -> with(blockState) { AvailableLimit( availableBalance = availableBalance, - currencySymbol = currencySymbol, limitDays = limitDays, ) } @@ -136,19 +135,14 @@ private fun Content(state: BalancesAndLimitsBlockState, modifier: Modifier = Mod } @Composable -private fun AvailableLimit( - availableBalance: String, - currencySymbol: String, - limitDays: Int, - modifier: Modifier = Modifier, -) { +private fun AvailableLimit(availableBalance: String, limitDays: Int, modifier: Modifier = Modifier) { Row( modifier = modifier, verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8), ) { Text( - text = "$availableBalance $currencySymbol", + text = availableBalance, style = TangemTheme.typography.body2, color = TangemTheme.colors.text.primary1, ) @@ -186,8 +180,7 @@ private class BalancesAndLimitsBlockParameterProvider : CollectionPreviewParamet BalancesAndLimitsBlockState.Loading, BalancesAndLimitsBlockState.Error, BalancesAndLimitsBlockState.Content( - availableBalance = "400.00", - currencySymbol = "USDT", + availableBalance = "400.00 USDT", limitDays = 7, isEnabled = true, onClick = {}, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/VisaWalletIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/VisaWalletIntents.kt index fc0b879db4..5dad048ccd 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/VisaWalletIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/VisaWalletIntents.kt @@ -4,18 +4,16 @@ import arrow.core.getOrElse import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheetConfig import com.tangem.core.ui.components.bottomsheets.tokenreceive.mapToAddressModels -import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase +import com.tangem.domain.tokens.GetCryptoCurrencyStatusSyncUseCase import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.visa.GetVisaCurrencyUseCase import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.wallet.presentation.wallet.state.WalletAlertState -import com.tangem.feature.wallet.presentation.wallet.state.WalletEvent import com.tangem.feature.wallet.presentation.wallet.state2.WalletStateController -import com.tangem.feature.wallet.presentation.wallet.state2.model.BalancesAndLimitsBottomSheetConfig +import com.tangem.feature.wallet.presentation.wallet.state2.transformers.converter.BalancesAndLimitsBottomSheetConverter import com.tangem.feature.wallet.presentation.wallet.state2.utils.WalletEventSender import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.hilt.android.scopes.ViewModelScoped import kotlinx.collections.immutable.toImmutableList -import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @@ -31,10 +29,15 @@ internal interface VisaWalletIntents { internal class VisaWalletIntentsImplementor @Inject constructor( private val stateController: WalletStateController, private val eventSender: WalletEventSender, - private val getPrimaryCurrencyUseCase: GetPrimaryCurrencyStatusUpdatesUseCase, + private val getCurrencyStatusUseCase: GetCryptoCurrencyStatusSyncUseCase, + private val getVisaCurrencyUseCase: GetVisaCurrencyUseCase, private val dispatchers: CoroutineDispatcherProvider, ) : BaseWalletClickIntents(), VisaWalletIntents { + private val balancesAndLimitsBottomSheetConverter by lazy(mode = LazyThreadSafetyMode.NONE) { + BalancesAndLimitsBottomSheetConverter(eventSender) + } + override fun onDepositClick() { val userWalletId = stateController.getSelectedWalletId() @@ -47,15 +50,6 @@ internal class VisaWalletIntentsImplementor @Inject constructor( } } - private suspend fun getPrimaryCurrencyStatus(userWalletId: UserWalletId): CryptoCurrencyStatus? { - return getPrimaryCurrencyUseCase(userWalletId) - .firstOrNull() - ?.getOrElse { - Timber.e("Failed to get primary currency $it") - null - } - } - private fun createReceiveBottomSheetContent(currencyStatus: CryptoCurrencyStatus): TangemBottomSheetConfigContent? { val currency = currencyStatus.currency val addresses = currencyStatus.value.networkAddress?.availableAddresses @@ -76,35 +70,27 @@ internal class VisaWalletIntentsImplementor @Inject constructor( } override fun onBalancesAndLimitsClick() { - stateController.showBottomSheet(getBalancesAndLimitsConfig()) + viewModelScope.launch(dispatchers.main) { + val userWalletId = stateController.getSelectedWalletId() + val balancesAndLimits = getVisaCurrencyUseCase(userWalletId) + .getOrElse { + Timber.e("Unable to get balances and limits: $it") + return@launch + } + + val bottomSheetContent = balancesAndLimitsBottomSheetConverter.convert( + value = balancesAndLimits, + ) + + stateController.showBottomSheet(bottomSheetContent) + } } - // TODO: Implement - private fun getBalancesAndLimitsConfig() = BalancesAndLimitsBottomSheetConfig( - currency = "USDT", - balance = BalancesAndLimitsBottomSheetConfig.Balance( - totalBalance = "492.45", - availableBalance = "392.45", - blockedBalance = "36.00", - debit = "00.00", - pending = "20.99", - amlVerified = "356.45", - ), - limit = BalancesAndLimitsBottomSheetConfig.Limit( - availableBy = "Nov, 11", - inStore = "563.00", - other = "100.00", - singleTransaction = "100.00", - ), - onBalanceInfoClick = this::showBalanceInfo, - onLimitInfoClick = this::showLimitInfo, - ) - - private fun showBalanceInfo() { - eventSender.send(WalletEvent.ShowAlert(WalletAlertState.VisaBalancesInfo)) - } - - private fun showLimitInfo() { - eventSender.send(WalletEvent.ShowAlert(WalletAlertState.VisaLimitsInfo)) + private suspend fun getPrimaryCurrencyStatus(userWalletId: UserWalletId): CryptoCurrencyStatus? { + return getCurrencyStatusUseCase(userWalletId) + .getOrElse { + Timber.e("Failed to get primary currency $it") + null + } } } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 3ac4a3dcf4..7fc4e85cba 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -135,6 +135,7 @@ include(":domain:balance-hiding") include(":domain:balance-hiding:models") include(":domain:transaction") include(":domain:analytics") +include(":domain:visa") // endregion Domain modules // region Data modules @@ -150,4 +151,5 @@ include(":data:txhistory") include(":data:wallets") include(":data:analytics") include(":data:transaction") +include(":data:visa") // endregion Data modules \ No newline at end of file