diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt index c858ffedc0..3713ecba7b 100644 --- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt +++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt @@ -616,10 +616,7 @@ internal class ChildFactory @Inject constructor( is AppRoute.TangemPayDetails -> { createComponentChild( context = context, - params = TangemPayDetailsComponent.Params( - customerWalletAddress = route.customerWalletAddress, - cardNumberEnd = route.cardNumberEnd, - ), + params = TangemPayDetailsComponent.Params(config = route.config), componentFactory = tangemPayDetailsComponentFactory, ) } diff --git a/common/routing/build.gradle.kts b/common/routing/build.gradle.kts index 17e65d1d6a..9fe3f2a5e4 100644 --- a/common/routing/build.gradle.kts +++ b/common/routing/build.gradle.kts @@ -25,6 +25,7 @@ dependencies { implementation(projects.domain.appCurrency.models) implementation(projects.domain.nft.models) implementation(projects.domain.feedback.models) + implementation(projects.domain.visa.models) /* Libs - Other */ api(deps.kotlin.serialization) diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt index cdba5c18f3..74f9b32244 100644 --- a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt @@ -18,6 +18,7 @@ import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.nft.models.NFTAsset import com.tangem.domain.onramp.model.OnrampSource +import com.tangem.domain.pay.TangemPayDetailsConfig import kotlinx.serialization.Serializable @SuppressLint("UnsafeOptInUsageError") @@ -388,8 +389,7 @@ sealed class AppRoute(val path: String) : Route { @Serializable data class TangemPayDetails( - val customerWalletAddress: String, - val cardNumberEnd: String, + val config: TangemPayDetailsConfig, ) : AppRoute(path = "/tangem_pay_details") @Serializable diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/pay/models/response/CustomerMeResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/pay/models/response/CustomerMeResponse.kt index 24b58f4b95..65a28d997e 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/pay/models/response/CustomerMeResponse.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/pay/models/response/CustomerMeResponse.kt @@ -17,6 +17,7 @@ data class CustomerMeResponse( @Json(name = "product_instance") val productInstance: ProductInstance?, @Json(name = "payment_account") val paymentAccount: PaymentAccount?, @Json(name = "kyc") val kyc: Kyc?, + @Json(name = "depositAddress") val depositAddress: String?, @Json(name = "card") val card: Card?, @Json(name = "balance") val balance: Balance?, ) diff --git a/data/common/src/main/kotlin/com/tangem/data/common/currency/CryptoCurrencyFactory.kt b/data/common/src/main/kotlin/com/tangem/data/common/currency/CryptoCurrencyFactory.kt index 36e2001891..311c07652a 100644 --- a/data/common/src/main/kotlin/com/tangem/data/common/currency/CryptoCurrencyFactory.kt +++ b/data/common/src/main/kotlin/com/tangem/data/common/currency/CryptoCurrencyFactory.kt @@ -1,5 +1,6 @@ package com.tangem.data.common.currency +import com.tangem.blockchain.blockchains.ethereum.Chain import com.tangem.blockchain.common.Blockchain import com.tangem.blockchainsdk.utils.ExcludedBlockchains import com.tangem.blockchainsdk.utils.fromNetworkId @@ -73,6 +74,17 @@ class CryptoCurrencyFactory( ) } + fun createCoin(chainId: Int, extraDerivationPath: String?, userWallet: UserWallet): CryptoCurrency.Coin? { + val blockchain: Blockchain? = Chain.entries.find { it.id == chainId }?.blockchain + + return if (blockchain != null) { + createCoin(blockchain, extraDerivationPath, userWallet) + } else { + Timber.e("Unable to get blockchain from chainId == $chainId") + null + } + } + fun createCoin( blockchain: Blockchain, extraDerivationPath: String?, diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/DefaultDataForReceiveFactory.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/DefaultDataForReceiveFactory.kt new file mode 100644 index 0000000000..76a1ab3929 --- /dev/null +++ b/data/visa/src/main/kotlin/com/tangem/data/pay/DefaultDataForReceiveFactory.kt @@ -0,0 +1,66 @@ +package com.tangem.data.pay + +import arrow.core.Either +import com.squareup.moshi.Moshi +import com.tangem.blockchainsdk.utils.ExcludedBlockchains +import com.tangem.core.error.UniversalError +import com.tangem.data.common.currency.CryptoCurrencyFactory +import com.tangem.data.pay.util.TangemPayErrorConverter +import com.tangem.data.pay.util.TangemPayWalletsManager +import com.tangem.datasource.di.NetworkMoshi +import com.tangem.domain.models.ReceiveAddressModel +import com.tangem.domain.models.ReceiveAddressModel.NameService +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.pay.DataForReceive +import com.tangem.domain.pay.DataForReceiveFactory +import kotlinx.coroutines.CancellationException +import timber.log.Timber +import javax.inject.Inject + +private const val TAG = "TangemPay: TokenReceiveConfigFactory" + +internal class DefaultDataForReceiveFactory @Inject constructor( + @NetworkMoshi moshi: Moshi, + private val tangemPayWalletsManager: TangemPayWalletsManager, + excludedBlockchains: ExcludedBlockchains, +) : DataForReceiveFactory { + + private val cryptoCurrencyFactory by lazy(mode = LazyThreadSafetyMode.NONE) { + CryptoCurrencyFactory(excludedBlockchains) + } + private val errorConverter by lazy(mode = LazyThreadSafetyMode.NONE) { TangemPayErrorConverter(moshi) } + + override fun getDataForReceive(depositAddress: String, chainId: Int): Either { + return try { + val wallet = tangemPayWalletsManager.getDefaultWalletForTangemPayBlocking() + + /** + * Create [CryptoCurrency.Coin] only for F&F. + * Later will use [CryptoCurrency.Token] when contractAddresses will be provided by BFF. + */ + val currency = cryptoCurrencyFactory.createCoin( + chainId = chainId, + extraDerivationPath = null, + userWallet = wallet, + ) ?: error("Cannot create crypto currency from chainId $chainId") + + val result = DataForReceive( + currency = currency, + walletId = wallet.walletId, + receiveAddress = listOf(ReceiveAddressModel(nameService = NameService.Default, value = depositAddress)), + ) + + Either.Right(result) + } catch (exception: Exception) { + when (exception) { + is CancellationException -> { + throw exception + } + else -> { + Timber.tag(TAG).e(exception) + Either.Left(errorConverter.convert(exception)) + } + } + } + } +} \ No newline at end of file diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/di/TangemPayDataModule.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/di/TangemPayDataModule.kt index 8f407caa11..c6222c62c7 100644 --- a/data/visa/src/main/kotlin/com/tangem/data/pay/di/TangemPayDataModule.kt +++ b/data/visa/src/main/kotlin/com/tangem/data/pay/di/TangemPayDataModule.kt @@ -1,9 +1,11 @@ package com.tangem.data.pay.di +import com.tangem.data.pay.DefaultDataForReceiveFactory import com.tangem.data.pay.repository.DefaultCardDetailsRepository import com.tangem.data.pay.repository.DefaultKycRepository import com.tangem.data.pay.repository.DefaultTangemPayTxHistoryRepository import com.tangem.data.pay.repository.DefaultOnboardingRepository +import com.tangem.domain.pay.DataForReceiveFactory import com.tangem.domain.pay.repository.CardDetailsRepository import com.tangem.domain.pay.repository.KycRepository import com.tangem.domain.pay.repository.OnboardingRepository @@ -37,6 +39,10 @@ internal interface TangemPayDataModule { @Singleton fun bindCardDetailsRepository(repository: DefaultCardDetailsRepository): CardDetailsRepository + @Binds + @Singleton + fun bindDataForReceiveFactory(factory: DefaultDataForReceiveFactory): DataForReceiveFactory + companion object { @Provides @Singleton diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultOnboardingRepository.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultOnboardingRepository.kt index d0d1af52ab..dbe40d9c20 100644 --- a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultOnboardingRepository.kt +++ b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/DefaultOnboardingRepository.kt @@ -102,6 +102,7 @@ internal class DefaultOnboardingRepository @Inject constructor( balance = balance.availableBalance, currencyCode = balance.currency, customerWalletAddress = paymentAccount.customerWalletAddress, + depositAddress = response.depositAddress, ) } else { null diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/TangemPayRequestPerformer.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/TangemPayRequestPerformer.kt index 0865a2fd21..c52ec65d24 100644 --- a/data/visa/src/main/kotlin/com/tangem/data/pay/repository/TangemPayRequestPerformer.kt +++ b/data/visa/src/main/kotlin/com/tangem/data/pay/repository/TangemPayRequestPerformer.kt @@ -5,27 +5,19 @@ import com.squareup.moshi.Moshi import com.tangem.blockchain.common.Blockchain import com.tangem.core.error.UniversalError import com.tangem.data.common.network.NetworkFactory +import com.tangem.data.pay.util.TangemPayErrorConverter +import com.tangem.data.pay.util.TangemPayWalletsManager import com.tangem.datasource.api.common.response.ApiResponse import com.tangem.datasource.api.common.response.ApiResponseError import com.tangem.datasource.api.common.response.getOrThrow -import com.tangem.datasource.api.pay.models.response.VisaErrorResponseJsonAdapter import com.tangem.datasource.di.NetworkMoshi import com.tangem.datasource.local.visa.TangemPayStorage -import com.tangem.domain.common.wallets.UserWalletsListRepository -import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.pay.datasource.TangemPayAuthDataSource -import com.tangem.domain.visa.error.VisaApiError import com.tangem.domain.visa.model.VisaAuthTokens import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.derivations.derivationStyleProvider -import com.tangem.domain.wallets.legacy.UserWalletsListManager -import com.tangem.features.hotwallet.HotWalletFeatureToggles import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.* -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.map import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import timber.log.Timber @@ -37,9 +29,7 @@ internal class TangemPayRequestPerformer @Inject constructor( private val dispatchers: CoroutineDispatcherProvider, private val tangemPayStorage: TangemPayStorage, private val authDataSource: TangemPayAuthDataSource, - private val userWalletsListManager: UserWalletsListManager, - private val userWalletsListRepository: UserWalletsListRepository, - private val hotWalletFeatureToggles: HotWalletFeatureToggles, + private val tangemPayWalletsManager: TangemPayWalletsManager, private val walletManagersFacade: WalletManagersFacade, private val networkFactory: NetworkFactory, ) { @@ -49,7 +39,7 @@ internal class TangemPayRequestPerformer @Inject constructor( private val refreshTokensMutex = Mutex() private var refreshTokensJob: Deferred? = null - private val visaErrorAdapter = VisaErrorResponseJsonAdapter(moshi) + private val errorConverter = TangemPayErrorConverter(moshi) suspend fun runWithErrorLogs(tag: String, requestBlock: suspend () -> T): Either { return try { @@ -62,7 +52,7 @@ internal class TangemPayRequestPerformer @Inject constructor( } else -> { Timber.tag(tag).e(exception) - Either.Left(mapError(exception)) + Either.Left(errorConverter.convert(exception)) } } } @@ -86,12 +76,6 @@ internal class TangemPayRequestPerformer @Inject constructor( ) } - private fun getWallets(): Flow> = if (hotWalletFeatureToggles.isHotWalletEnabled) { - userWalletsListRepository.userWallets.map { requireNotNull(it) } - } else { - userWalletsListManager.userWallets - } - private suspend fun performRequest( requestBlock: suspend (header: String) -> ApiResponse, getTokens: (suspend () -> VisaAuthTokens), @@ -143,11 +127,7 @@ internal class TangemPayRequestPerformer @Inject constructor( } private suspend fun fetchAuthInputData(): AuthInputData { - val userWallets = getWallets() - .filter { it.isNotEmpty() } - .first() - val wallet = userWallets.find { it is UserWallet.Cold } as? UserWallet.Cold - ?: error("Cannot find cold user wallet") + val wallet = tangemPayWalletsManager.getDefaultWalletForTangemPay() val network = networkFactory.create( blockchain = Blockchain.Polygon, @@ -179,21 +159,6 @@ internal class TangemPayRequestPerformer @Inject constructor( tangemPayStorage.storeAuthTokens(customerWalletAddress, tokens) return tokens } - - private fun mapError(throwable: Throwable): UniversalError { - return if (throwable is ApiResponseError.HttpException) { - val errorBody = throwable.errorBody ?: return VisaApiError.UnknownWithoutCode - return runCatching { - visaErrorAdapter.fromJson(errorBody)?.error?.code ?: throwable.code.numericCode - }.map { - VisaApiError.fromBackendError(it) - }.getOrElse { - VisaApiError.UnknownWithoutCode - } - } else { - VisaApiError.UnknownWithoutCode - } - } } internal data class AuthInputData( diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/util/TangemPayErrorConverter.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/util/TangemPayErrorConverter.kt new file mode 100644 index 0000000000..d5f0ebf004 --- /dev/null +++ b/data/visa/src/main/kotlin/com/tangem/data/pay/util/TangemPayErrorConverter.kt @@ -0,0 +1,28 @@ +package com.tangem.data.pay.util + +import com.squareup.moshi.Moshi +import com.tangem.core.error.UniversalError +import com.tangem.datasource.api.common.response.ApiResponseError +import com.tangem.datasource.api.pay.models.response.VisaErrorResponseJsonAdapter +import com.tangem.domain.visa.error.VisaApiError +import com.tangem.utils.converter.Converter + +class TangemPayErrorConverter(moshi: Moshi) : Converter { + + private val visaErrorAdapter = VisaErrorResponseJsonAdapter(moshi) + + override fun convert(value: Throwable): UniversalError { + return if (value is ApiResponseError.HttpException) { + val errorBody = value.errorBody ?: return VisaApiError.UnknownWithoutCode + return runCatching { + visaErrorAdapter.fromJson(errorBody)?.error?.code ?: value.code.numericCode + }.map { + VisaApiError.fromBackendError(it) + }.getOrElse { + VisaApiError.UnknownWithoutCode + } + } else { + VisaApiError.UnknownWithoutCode + } + } +} \ No newline at end of file diff --git a/data/visa/src/main/kotlin/com/tangem/data/pay/util/TangemPayWalletsManager.kt b/data/visa/src/main/kotlin/com/tangem/data/pay/util/TangemPayWalletsManager.kt new file mode 100644 index 0000000000..32c775d61a --- /dev/null +++ b/data/visa/src/main/kotlin/com/tangem/data/pay/util/TangemPayWalletsManager.kt @@ -0,0 +1,34 @@ +package com.tangem.data.pay.util + +import com.tangem.domain.common.wallets.UserWalletsListRepository +import com.tangem.domain.models.wallet.UserWallet +import com.tangem.domain.wallets.legacy.UserWalletsListManager +import com.tangem.features.hotwallet.HotWalletFeatureToggles +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.first +import javax.inject.Inject + +internal class TangemPayWalletsManager @Inject constructor( + private val manager: UserWalletsListManager, + private val repository: UserWalletsListRepository, + private val hotWalletFeatureToggles: HotWalletFeatureToggles, +) { + + suspend fun getDefaultWalletForTangemPay(): UserWallet.Cold { + val userWalletsFlow = if (useNewRepository()) repository.userWallets else manager.userWallets + val userWallets = userWalletsFlow.filter { !it.isNullOrEmpty() }.first() + return findColdWallet(userWallets) + } + + fun getDefaultWalletForTangemPayBlocking(): UserWallet.Cold { + val userWallets = if (useNewRepository()) repository.userWallets.value else manager.userWalletsSync + return findColdWallet(userWallets) + } + + private fun useNewRepository(): Boolean = hotWalletFeatureToggles.isHotWalletEnabled + + private fun findColdWallet(userWallets: List?): UserWallet.Cold { + return userWallets?.find { it is UserWallet.Cold } as? UserWallet.Cold + ?: error("Cannot find cold user wallet") + } +} \ No newline at end of file diff --git a/domain/models/src/main/kotlin/com/tangem/domain/models/TokenReceiveConfig.kt b/domain/models/src/main/kotlin/com/tangem/domain/models/TokenReceiveConfig.kt index 032940c3ee..467675af70 100644 --- a/domain/models/src/main/kotlin/com/tangem/domain/models/TokenReceiveConfig.kt +++ b/domain/models/src/main/kotlin/com/tangem/domain/models/TokenReceiveConfig.kt @@ -13,6 +13,7 @@ data class TokenReceiveConfig( val receiveAddress: List, val tokenReceiveNotification: List = emptyList(), val asset: Asset = Asset.Currency, + val type: TokenReceiveType = TokenReceiveType.Default, ) @Serializable @@ -34,4 +35,25 @@ data class TokenReceiveNotification( enum class Asset { Currency, NFT +} + +@Serializable +sealed class TokenReceiveType { + + /** + * Default setting. + * TokenReceiveComponent will use [CryptoCurrency] to get token icon and name + */ + data object Default : TokenReceiveType() + + /** + * Custom setting. + * TokenReceiveComponent will use custom icon and name + */ + data class Custom( + val tokenIconUrl: String, + val tokenName: String, + val fallbackTint: Int, + val fallbackBackground: Int, + ) : TokenReceiveType() } \ No newline at end of file diff --git a/domain/visa/models/src/main/kotlin/com/tangem/domain/pay/TangemPayDetailsConfig.kt b/domain/visa/models/src/main/kotlin/com/tangem/domain/pay/TangemPayDetailsConfig.kt new file mode 100644 index 0000000000..b42e77c698 --- /dev/null +++ b/domain/visa/models/src/main/kotlin/com/tangem/domain/pay/TangemPayDetailsConfig.kt @@ -0,0 +1,11 @@ +package com.tangem.domain.pay + +import kotlinx.serialization.Serializable + +@Serializable +data class TangemPayDetailsConfig( + val customerWalletAddress: String, + val cardNumberEnd: String, + val chainId: Int, + val depositAddress: String?, +) \ No newline at end of file diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/pay/DataForReceiveFactory.kt b/domain/visa/src/main/kotlin/com/tangem/domain/pay/DataForReceiveFactory.kt new file mode 100644 index 0000000000..1b78f76580 --- /dev/null +++ b/domain/visa/src/main/kotlin/com/tangem/domain/pay/DataForReceiveFactory.kt @@ -0,0 +1,18 @@ +package com.tangem.domain.pay + +import arrow.core.Either +import com.tangem.core.error.UniversalError +import com.tangem.domain.models.ReceiveAddressModel +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.models.wallet.UserWalletId + +interface DataForReceiveFactory { + + fun getDataForReceive(depositAddress: String, chainId: Int): Either +} + +data class DataForReceive( + val walletId: UserWalletId, + val currency: CryptoCurrency, + val receiveAddress: List, +) \ No newline at end of file diff --git a/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/CustomerInfo.kt b/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/CustomerInfo.kt index cb32317055..9937d2e1ff 100644 --- a/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/CustomerInfo.kt +++ b/domain/visa/src/main/kotlin/com/tangem/domain/pay/model/CustomerInfo.kt @@ -23,5 +23,6 @@ data class CustomerInfo( val balance: BigDecimal, val currencyCode: String, val customerWalletAddress: String, + val depositAddress: String?, ) } \ No newline at end of file diff --git a/features/tangempay/details/api/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayDetailsComponent.kt b/features/tangempay/details/api/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayDetailsComponent.kt index ab118672a6..428f063730 100644 --- a/features/tangempay/details/api/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayDetailsComponent.kt +++ b/features/tangempay/details/api/src/main/kotlin/com/tangem/features/tangempay/components/TangemPayDetailsComponent.kt @@ -2,8 +2,9 @@ package com.tangem.features.tangempay.components import com.tangem.core.decompose.factory.ComponentFactory import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.domain.pay.TangemPayDetailsConfig interface TangemPayDetailsComponent : ComposableContentComponent { - data class Params(val customerWalletAddress: String, val cardNumberEnd: String) + data class Params(val config: TangemPayDetailsConfig) interface Factory : ComponentFactory } \ No newline at end of file diff --git a/features/tangempay/details/impl/build.gradle.kts b/features/tangempay/details/impl/build.gradle.kts index 689a03be9f..cc2ac540ec 100644 --- a/features/tangempay/details/impl/build.gradle.kts +++ b/features/tangempay/details/impl/build.gradle.kts @@ -21,6 +21,7 @@ dependencies { /** Features api */ implementation(projects.features.tangempay.details.api) implementation(projects.features.txhistory.api) + implementation(projects.features.tokenRecieve.api) /** Domain */ implementation(projects.domain.balanceHiding) @@ -36,6 +37,7 @@ dependencies { implementation(deps.compose.material3) implementation(deps.compose.ui) implementation(deps.compose.ui.tooling) + implementation(deps.decompose.ext.compose) /** DI */ implementation(deps.hilt.android) diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/DefaultTangemPayDetailsComponent.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/DefaultTangemPayDetailsComponent.kt index 0aa60f2cf3..35bb54bd7c 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/DefaultTangemPayDetailsComponent.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/components/DefaultTangemPayDetailsComponent.kt @@ -4,13 +4,21 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.router.slot.childSlot +import com.arkivanov.decompose.router.slot.dismiss +import com.arkivanov.decompose.extensions.compose.subscribeAsState import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.child +import com.tangem.core.decompose.context.childByContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.components.NavigationBar3ButtonsScrim +import com.tangem.core.ui.decompose.ComposableBottomSheetComponent +import com.tangem.domain.models.TokenReceiveConfig import com.tangem.features.tangempay.components.txHistory.DefaultTangemPayTxHistoryComponent import com.tangem.features.tangempay.model.TangemPayDetailsModel import com.tangem.features.tangempay.ui.TangemPayDetailsScreen +import com.tangem.features.tokenreceive.TokenReceiveComponent import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -18,17 +26,26 @@ import dagger.assisted.AssistedInject internal class DefaultTangemPayDetailsComponent @AssistedInject constructor( @Assisted private val appComponentContext: AppComponentContext, @Assisted private val params: TangemPayDetailsComponent.Params, + private val tokenReceiveComponentFactory: TokenReceiveComponent.Factory, ) : AppComponentContext by appComponentContext, TangemPayDetailsComponent { private val model: TangemPayDetailsModel = getOrCreateModel(params = params) + + private val bottomSheetSlot = childSlot( + source = model.bottomSheetNavigation, + serializer = TokenReceiveConfig.serializer(), + handleBackButton = false, + childFactory = ::bottomSheetChild, + ) private val txHistoryComponent = DefaultTangemPayTxHistoryComponent( appComponentContext = child("txHistoryComponent"), - params = DefaultTangemPayTxHistoryComponent.Params(customerWalletAddress = params.customerWalletAddress), + params = DefaultTangemPayTxHistoryComponent.Params(customerWalletAddress = params.config.customerWalletAddress), ) @Composable override fun Content(modifier: Modifier) { val state by model.uiState.collectAsStateWithLifecycle() + val bottomSheet by bottomSheetSlot.subscribeAsState() NavigationBar3ButtonsScrim() TangemPayDetailsScreen( @@ -36,8 +53,20 @@ internal class DefaultTangemPayDetailsComponent @AssistedInject constructor( txHistoryComponent = txHistoryComponent, modifier = modifier, ) + bottomSheet.child?.instance?.BottomSheet() } + private fun bottomSheetChild( + config: TokenReceiveConfig, + componentContext: ComponentContext, + ): ComposableBottomSheetComponent = tokenReceiveComponentFactory.create( + context = childByContext(componentContext), + params = TokenReceiveComponent.Params( + config = config, + onDismiss = model.bottomSheetNavigation::dismiss, + ), + ) + @AssistedFactory interface Factory : TangemPayDetailsComponent.Factory { override fun create( diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsStateFactory.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsStateFactory.kt new file mode 100644 index 0000000000..0946f1aa37 --- /dev/null +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/entity/TangemPayDetailsStateFactory.kt @@ -0,0 +1,56 @@ +package com.tangem.features.tangempay.entity + +import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig +import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig +import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig.ShowRefreshState +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.features.tangempay.details.impl.R +import com.tangem.features.tangempay.utils.CardDetailsFormatUtil +import com.tangem.utils.StringsSigns +import kotlinx.collections.immutable.persistentListOf + +private const val CARD_NUMBER_SART_DIGITS_COUNT = 12 +private const val DATE_PART_LENGTH = 2 +private const val CVV_LENGTH = 3 + +internal class TangemPayDetailsStateFactory( + private val cardNumberEnd: String, + private val onBack: () -> Unit, + private val onRefresh: (ShowRefreshState) -> Unit, + private val onReceive: () -> Unit, + private val onReveal: () -> Unit, + private val onCopy: (String) -> Unit, +) { + + private val cardStartMasked = maskedBlock(CARD_NUMBER_SART_DIGITS_COUNT) + private val dateMasked = maskedBlock(DATE_PART_LENGTH) + private val cvvMasked = maskedBlock(CVV_LENGTH) + + fun getInitialState() = TangemPayDetailsUM( + topBarConfig = TangemPayDetailsTopBarConfig(onBackClick = onBack, items = null), + pullToRefreshConfig = PullToRefreshConfig(isRefreshing = false, onRefresh = onRefresh), + balanceBlockState = TangemPayDetailsBalanceBlockState.Loading( + actionButtons = persistentListOf( + ActionButtonConfig( + text = resourceReference(id = R.string.common_receive), + iconResId = R.drawable.ic_arrow_down_24, + onClick = onReceive, + ), + ), + ), + cardDetailsUM = TangemPayCardDetailsUM( + number = CardDetailsFormatUtil.formatCardNumber(cardNumber = "$cardStartMasked$cardNumberEnd"), + expiry = CardDetailsFormatUtil.formatDate(month = dateMasked, year = dateMasked), + cvv = cvvMasked, + buttonText = TextReference.Res(R.string.tangempay_card_details_reveal_text), + onClick = onReveal, + onCopy = onCopy, + isHidden = true, + ), + isBalanceHidden = false, + + ) + + private fun maskedBlock(count: Int) = StringsSigns.DOT.repeat(count) +} \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayDetailsModel.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayDetailsModel.kt index 0760c76fda..d1504026e6 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayDetailsModel.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/TangemPayDetailsModel.kt @@ -1,35 +1,45 @@ package com.tangem.features.tangempay.model import androidx.compose.runtime.Stable +import androidx.compose.ui.graphics.toArgb +import com.arkivanov.decompose.router.slot.SlotNavigation +import com.arkivanov.decompose.router.slot.activate import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.navigation.Router import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.ui.clipboard.ClipboardManager -import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig.ShowRefreshState import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.message.SnackbarMessage +import com.tangem.core.ui.res.TangemColorPalette +import com.tangem.domain.models.TokenReceiveConfig +import com.tangem.domain.models.TokenReceiveType +import com.tangem.domain.pay.DataForReceiveFactory import com.tangem.domain.pay.repository.CardDetailsRepository import com.tangem.features.tangempay.components.TangemPayDetailsComponent import com.tangem.features.tangempay.details.impl.R -import com.tangem.features.tangempay.entity.TangemPayCardDetailsUM -import com.tangem.features.tangempay.entity.TangemPayDetailsBalanceBlockState -import com.tangem.features.tangempay.entity.TangemPayDetailsTopBarConfig +import com.tangem.features.tangempay.entity.TangemPayDetailsStateFactory import com.tangem.features.tangempay.entity.TangemPayDetailsUM import com.tangem.features.tangempay.model.transformers.* import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.JobHolder import com.tangem.utils.coroutines.saveIn import com.tangem.utils.transformer.update -import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import javax.inject.Inject +/** + * Custom token name and icon url. Will be used only for F&F. + */ +private const val TOKEN_NAME = "USDC" +private const val TOKEN_ICON_URL = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/large/usd-coin.png" + +@Suppress("LongParameterList") @Stable @ModelScoped internal class TangemPayDetailsModel @Inject constructor( @@ -37,23 +47,61 @@ internal class TangemPayDetailsModel @Inject constructor( override val dispatchers: CoroutineDispatcherProvider, private val router: Router, private val cardDetailsRepository: CardDetailsRepository, + private val dataForReceiveFactory: DataForReceiveFactory, private val clipboardManager: ClipboardManager, private val uiMessageSender: UiMessageSender, ) : Model() { private val params: TangemPayDetailsComponent.Params = paramsContainer.require() + private val stateFactory = TangemPayDetailsStateFactory( + cardNumberEnd = params.config.cardNumberEnd, + onBack = router::pop, + onRefresh = ::onRefreshSwipe, + onReceive = ::onClickReceive, + onReveal = ::revealCardDetails, + onCopy = ::copyData, + ) + val uiState: StateFlow - field = MutableStateFlow(getInitialState()) + field = MutableStateFlow(stateFactory.getInitialState()) private val refreshStateJobHolder = JobHolder() private val fetchBalanceJobHolder = JobHolder() private val revealCardDetailsJobHolder = JobHolder() + val bottomSheetNavigation: SlotNavigation = SlotNavigation() + init { fetchBalance() } + private fun onClickReceive() { + val depositAddress = params.config.depositAddress + if (depositAddress == null) { + showError() + } else { + dataForReceiveFactory.getDataForReceive(depositAddress = depositAddress, chainId = params.config.chainId) + .onRight { + val config = TokenReceiveConfig( + shouldShowWarning = false, + cryptoCurrency = it.currency, + userWalletId = it.walletId, + showMemoDisclaimer = false, + receiveAddress = it.receiveAddress, + type = TokenReceiveType.Custom( + tokenName = TOKEN_NAME, + tokenIconUrl = TOKEN_ICON_URL, + fallbackTint = TangemColorPalette.Black.toArgb(), + fallbackBackground = TangemColorPalette.Meadow.toArgb(), + ), + ) + bottomSheetNavigation.activate(config) + } + .onLeft { showError() } + } + } + private fun fetchBalance(): Job { return modelScope.launch { val result = cardDetailsRepository.getCardBalance() @@ -63,25 +111,13 @@ internal class TangemPayDetailsModel @Inject constructor( private fun onRefreshSwipe(refreshState: ShowRefreshState) { modelScope.launch { + hideCardDetails() uiState.update(TangemPayDetailsRefreshTransformer(isRefreshing = refreshState.value)) fetchBalance().join() uiState.update(TangemPayDetailsRefreshTransformer(isRefreshing = false)) }.saveIn(refreshStateJobHolder) } - private fun getInitialState() = TangemPayDetailsUM( - topBarConfig = TangemPayDetailsTopBarConfig(onBackClick = router::pop, items = null), - pullToRefreshConfig = PullToRefreshConfig(isRefreshing = false, onRefresh = ::onRefreshSwipe), - balanceBlockState = TangemPayDetailsBalanceBlockState.Loading(actionButtons = persistentListOf()), - cardDetailsUM = TangemPayCardDetailsUM(), - isBalanceHidden = false, - ).let { - DetailsHiddenStateTransformer( - onClickReveal = ::revealCardDetails, - cardNumberEnd = params.cardNumberEnd, - ).transform(it) - } - private fun revealCardDetails() { modelScope.launch { uiState.update( @@ -93,17 +129,11 @@ internal class TangemPayDetailsModel @Inject constructor( transformer = DetailsRevealedStateTransformer( details = it, onClickHide = ::hideCardDetails, - onClickCopy = ::copyData, ), ) } .onLeft { - uiState.update( - transformer = DetailsHiddenStateTransformer( - onClickReveal = ::revealCardDetails, - cardNumberEnd = params.cardNumberEnd, - ), - ) + uiState.update(transformer = DetailsHiddenStateTransformer(stateFactory)) showError() } }.saveIn(revealCardDetailsJobHolder) @@ -112,12 +142,7 @@ internal class TangemPayDetailsModel @Inject constructor( private fun hideCardDetails() { modelScope.launch { revealCardDetailsJobHolder.cancel() - uiState.update( - transformer = DetailsHiddenStateTransformer( - onClickReveal = ::revealCardDetails, - cardNumberEnd = params.cardNumberEnd, - ), - ) + uiState.update(transformer = DetailsHiddenStateTransformer(stateFactory)) } } diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsBalanceTransformer.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsBalanceTransformer.kt index 14627ed23d..61b1413c22 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsBalanceTransformer.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsBalanceTransformer.kt @@ -26,7 +26,7 @@ internal class DetailsBalanceTransformer( fiatBalance = getBalanceText(balance.value), // TODO [REDACTED_TASK_KEY]: Add crypto balance when the BFF is ready cryptoBalance = "", - actionButtons = persistentListOf(), + actionButtons = prevState.balanceBlockState.actionButtons, ) } } diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsHiddenStateTransformer.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsHiddenStateTransformer.kt index 35025ce2a3..218ff38231 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsHiddenStateTransformer.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsHiddenStateTransformer.kt @@ -1,38 +1,14 @@ package com.tangem.features.tangempay.model.transformers -import com.tangem.core.ui.extensions.TextReference -import com.tangem.features.tangempay.details.impl.R -import com.tangem.features.tangempay.entity.TangemPayCardDetailsUM +import com.tangem.features.tangempay.entity.TangemPayDetailsStateFactory import com.tangem.features.tangempay.entity.TangemPayDetailsUM -import com.tangem.features.tangempay.utils.CardDetailsFormatUtil -import com.tangem.utils.StringsSigns import com.tangem.utils.transformer.Transformer -private const val CARD_NUMBER_SART_DIGITS_COUNT = 12 -private const val DATE_PART_LENGTH = 2 -private const val CVV_LENGTH = 3 - internal class DetailsHiddenStateTransformer( - private val onClickReveal: () -> Unit, - private val cardNumberEnd: String, + private val stateFactory: TangemPayDetailsStateFactory, ) : Transformer { - private val cardStartMasked = maskedBlock(CARD_NUMBER_SART_DIGITS_COUNT) - private val dateMasked = maskedBlock(DATE_PART_LENGTH) - private val cvvMasked = maskedBlock(CVV_LENGTH) - override fun transform(prevState: TangemPayDetailsUM): TangemPayDetailsUM { - val cardDetailsUM = TangemPayCardDetailsUM( - number = CardDetailsFormatUtil.formatCardNumber(cardNumber = "$cardStartMasked$cardNumberEnd"), - expiry = CardDetailsFormatUtil.formatDate(month = dateMasked, year = dateMasked), - cvv = cvvMasked, - buttonText = TextReference.Res(R.string.tangempay_card_details_reveal_text), - onClick = onClickReveal, - onCopy = {}, - isHidden = true, - ) - return prevState.copy(cardDetailsUM = cardDetailsUM) + return prevState.copy(cardDetailsUM = stateFactory.getInitialState().cardDetailsUM) } - - private fun maskedBlock(count: Int) = StringsSigns.DOT.repeat(count) } \ No newline at end of file diff --git a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsRevealedStateTransformer.kt b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsRevealedStateTransformer.kt index 45ba18f069..477f82b511 100644 --- a/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsRevealedStateTransformer.kt +++ b/features/tangempay/details/impl/src/main/kotlin/com/tangem/features/tangempay/model/transformers/DetailsRevealedStateTransformer.kt @@ -11,7 +11,6 @@ import com.tangem.utils.transformer.Transformer internal class DetailsRevealedStateTransformer( private val details: TangemPayCardDetails, private val onClickHide: (() -> Unit), - private val onClickCopy: ((String) -> Unit), ) : Transformer { override fun transform(prevState: TangemPayDetailsUM): TangemPayDetailsUM { @@ -21,7 +20,7 @@ internal class DetailsRevealedStateTransformer( cvv = details.cvv, onClick = onClickHide, buttonText = TextReference.Res(R.string.tangempay_card_details_hide_text), - onCopy = onClickCopy, + onCopy = prevState.cardDetailsUM.onCopy, isHidden = false, ) return prevState.copy(cardDetailsUM = cardDetailsUM) diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/DefaultTokenReceiveComponent.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/DefaultTokenReceiveComponent.kt index 24d68dcfea..3dd7bc3947 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/DefaultTokenReceiveComponent.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/DefaultTokenReceiveComponent.kt @@ -77,6 +77,7 @@ internal class DefaultTokenReceiveComponent @AssistedInject constructor( is TokenReceiveRoutes.QrCode -> TokenReceiveQrCodeComponent( appComponentContext = appComponentContext, params = TokenReceiveQrCodeComponent.TokenReceiveQrCodeParams( + type = model.params.config.type, cryptoCurrency = model.params.config.cryptoCurrency, address = model.state.value.addresses.find { it.value == config.address } ?: error( "Address has to be there", diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/TokenReceiveQrCodeComponent.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/TokenReceiveQrCodeComponent.kt index 02123bbf3a..6780fd2740 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/TokenReceiveQrCodeComponent.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/component/TokenReceiveQrCodeComponent.kt @@ -7,6 +7,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.domain.models.TokenReceiveType import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.tokens.model.analytics.TokenReceiveCopyActionSource import com.tangem.features.tokenreceive.entity.ReceiveAddress @@ -36,5 +37,6 @@ internal class TokenReceiveQrCodeComponent( val address: ReceiveAddress, val callback: TokenReceiveQrCodeModelCallback, val onDismiss: () -> Unit, + val type: TokenReceiveType = TokenReceiveType.Default, ) } \ No newline at end of file diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/entity/TokenReceiveStateFactory.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/entity/TokenReceiveStateFactory.kt index 02595a5ca4..2d014adf53 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/entity/TokenReceiveStateFactory.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/entity/TokenReceiveStateFactory.kt @@ -1,13 +1,17 @@ package com.tangem.features.tokenreceive.entity +import androidx.compose.ui.graphics.Color import com.tangem.common.ui.notifications.NotificationUM import com.tangem.core.ui.R +import com.tangem.core.ui.components.currency.icon.CurrencyIconState import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter import com.tangem.core.ui.components.notifications.NotificationConfig import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.networkIconResId import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.wrappedList import com.tangem.domain.models.ReceiveAddressModel +import com.tangem.domain.models.TokenReceiveType import com.tangem.domain.models.TokenReceiveNotification import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.ens.EnsAddress @@ -25,6 +29,7 @@ internal class TokenReceiveStateFactory( private val cryptoCurrency: CryptoCurrency, private val addresses: List, private val tokenReceiveNotification: List, + private val tokenReceiveType: TokenReceiveType, ) { private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter) @@ -35,7 +40,10 @@ internal class TokenReceiveStateFactory( addresses = addresses, cryptoCurrency = cryptoCurrency, ), - iconState = iconStateConverter.convert(cryptoCurrency), + iconState = when (tokenReceiveType) { + is TokenReceiveType.Default -> iconStateConverter.convert(cryptoCurrency) + is TokenReceiveType.Custom -> getCustomCurrencyIconState(tokenReceiveType) + }, network = cryptoCurrency.network.name, isEnsResultLoading = false, notificationConfigs = getNotifications( @@ -159,4 +167,13 @@ internal class TokenReceiveStateFactory( } } } + + private fun getCustomCurrencyIconState(type: TokenReceiveType.Custom) = CurrencyIconState.TokenIcon( + url = type.tokenIconUrl, + topBadgeIconResId = cryptoCurrency.networkIconResId, + fallbackTint = Color(type.fallbackTint), + fallbackBackground = Color(type.fallbackBackground), + isGrayscale = false, + shouldShowCustomBadge = false, + ) } \ No newline at end of file diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveModel.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveModel.kt index 4229545563..91e67ea518 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveModel.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveModel.kt @@ -9,6 +9,7 @@ import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.navigation.share.ShareManager import com.tangem.core.ui.clipboard.ClipboardManager import com.tangem.domain.models.Asset +import com.tangem.domain.models.TokenReceiveType import com.tangem.domain.models.network.Network import com.tangem.domain.tokens.SaveViewedTokenReceiveWarningUseCase import com.tangem.domain.tokens.model.analytics.TokenReceiveCopyActionSource @@ -48,6 +49,7 @@ internal class TokenReceiveModel @Inject constructor( addresses = params.config.receiveAddress, tokenReceiveNotification = params.config.tokenReceiveNotification, currentStateProvider = Provider { state.value }, + tokenReceiveType = params.config.type, ) } @@ -96,9 +98,12 @@ internal class TokenReceiveModel @Inject constructor( } internal fun getTokenName(): String { - return when (val asset = params.config.asset) { - Asset.Currency -> params.config.cryptoCurrency.symbol - Asset.NFT -> asset.name + return when (val type = params.config.type) { + is TokenReceiveType.Default -> when (val asset = params.config.asset) { + Asset.Currency -> params.config.cryptoCurrency.symbol + Asset.NFT -> asset.name + } + is TokenReceiveType.Custom -> type.tokenName } } diff --git a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveQrCodeModel.kt b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveQrCodeModel.kt index 6f10eea53a..43ab4296aa 100644 --- a/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveQrCodeModel.kt +++ b/features/token-recieve/impl/src/main/java/com/tangem/features/tokenreceive/model/TokenReceiveQrCodeModel.kt @@ -5,6 +5,8 @@ import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.ui.extensions.TextReference +import com.tangem.domain.models.TokenReceiveType.Default +import com.tangem.domain.models.TokenReceiveType.Custom import com.tangem.domain.tokens.model.analytics.TokenReceiveCopyActionSource import com.tangem.features.tokenreceive.component.TokenReceiveQrCodeComponent import com.tangem.features.tokenreceive.ui.state.QrCodeUM @@ -27,7 +29,12 @@ internal class TokenReceiveQrCodeModel @Inject constructor( QrCodeUM( network = params.cryptoCurrency.network.name, addressValue = params.address.value, - addressName = TextReference.Str("${params.cryptoCurrency.name} (${params.cryptoCurrency.symbol})"), + addressName = when (params.type) { + is Default -> + TextReference.Str("${params.cryptoCurrency.name} (${params.cryptoCurrency.symbol})") + is Custom -> + TextReference.Str(params.type.tokenName) + }, onCopyClick = { params.callback.onCopyClick( address = params.address, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index f2f41882cc..8213172d17 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -12,6 +12,7 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.pay.TangemPayDetailsConfig import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.redux.StateDialog import com.tangem.domain.tokens.model.details.TokenAction @@ -108,8 +109,8 @@ internal class DefaultWalletRouter @Inject constructor( router.push(AppRoute.TangemPayOnboarding(AppRoute.TangemPayOnboarding.Mode.ContinueOnboarding)) } - override fun openTangemPayDetails(customerWalletAddress: String, cardNumberEnd: String) { - router.push(AppRoute.TangemPayDetails(customerWalletAddress, cardNumberEnd)) + override fun openTangemPayDetails(config: TangemPayDetailsConfig) { + router.push(AppRoute.TangemPayDetails(config)) } override fun openYieldSupplyBottomSheet( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt index 4c6c7ba002..d869ca8b84 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt @@ -8,6 +8,7 @@ import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.pay.TangemPayDetailsConfig import com.tangem.domain.tokens.model.details.TokenAction import com.tangem.feature.wallet.navigation.WalletRoute import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig @@ -59,7 +60,7 @@ internal interface InnerWalletRouter { fun openTangemPayOnboarding() - fun openTangemPayDetails(customerWalletAddress: String, cardNumberEnd: String) + fun openTangemPayDetails(config: TangemPayDetailsConfig) /** Open BS abput yield supply active and all money deposited in AAVE */ fun openYieldSupplyBottomSheet( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayInitialStateTransformer.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayInitialStateTransformer.kt index 4af0c5bdc7..a83f41f964 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayInitialStateTransformer.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/TangemPayInitialStateTransformer.kt @@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.format.bigdecimal.fiat import com.tangem.core.ui.format.bigdecimal.format +import com.tangem.domain.pay.TangemPayDetailsConfig import com.tangem.domain.pay.model.CustomerInfo.CardInfo import com.tangem.domain.pay.model.MainScreenCustomerInfo import com.tangem.domain.pay.model.OrderStatus.CANCELED @@ -14,11 +15,17 @@ import com.tangem.feature.wallet.presentation.wallet.state.util.TangemPayStateCr import com.tangem.feature.wallet.presentation.wallet.state.util.TangemPayStateCreator.createKycInProgressState import java.util.Currency +/** + * Hardcode Polygon chain id only for F&F. + * Later chain id will be fetched from BFF. + */ +private const val POLYGON_CHAIN_ID = 137 + internal class TangemPayInitialStateTransformer( private val value: MainScreenCustomerInfo? = null, private val onClickIssue: () -> Unit = {}, private val onClickKyc: () -> Unit = {}, - private val openDetails: (customerWalletAddress: String, cardNumberEnd: String) -> Unit = { _, _ -> }, + private val openDetails: (config: TangemPayDetailsConfig) -> Unit = {}, ) : WalletScreenStateTransformer { override fun transform(prevState: WalletScreenState): WalletScreenState { @@ -40,7 +47,16 @@ internal class TangemPayInitialStateTransformer( private fun getCardInfoState(cardInfo: CardInfo): TangemPayState = TangemPayState.Card( lastFourDigits = TextReference.Str("*${cardInfo.lastFourDigits}"), balanceText = TextReference.Str(getBalanceText(cardInfo)), - onClick = { openDetails(cardInfo.customerWalletAddress, cardInfo.lastFourDigits) }, + onClick = { + openDetails( + TangemPayDetailsConfig( + customerWalletAddress = cardInfo.customerWalletAddress, + cardNumberEnd = cardInfo.lastFourDigits, + chainId = POLYGON_CHAIN_ID, + depositAddress = cardInfo.depositAddress, + ), + ) + }, ) private fun getBalanceText(cardInfo: CardInfo): String {