Updated on 2026-08-14
This commit is contained in:
parent
23264bc7f0
commit
8c37dbd5b0
31 changed files with 422 additions and 121 deletions
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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?,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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?,
|
||||
|
|
|
|||
|
|
@ -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<UniversalError, DataForReceive> {
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ internal class DefaultOnboardingRepository @Inject constructor(
|
|||
balance = balance.availableBalance,
|
||||
currencyCode = balance.currency,
|
||||
customerWalletAddress = paymentAccount.customerWalletAddress,
|
||||
depositAddress = response.depositAddress,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
|
|
|
|||
|
|
@ -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<VisaAuthTokens>? = null
|
||||
|
||||
private val visaErrorAdapter = VisaErrorResponseJsonAdapter(moshi)
|
||||
private val errorConverter = TangemPayErrorConverter(moshi)
|
||||
|
||||
suspend fun <T : Any> runWithErrorLogs(tag: String, requestBlock: suspend () -> T): Either<UniversalError, T> {
|
||||
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<List<UserWallet>> = if (hotWalletFeatureToggles.isHotWalletEnabled) {
|
||||
userWalletsListRepository.userWallets.map { requireNotNull(it) }
|
||||
} else {
|
||||
userWalletsListManager.userWallets
|
||||
}
|
||||
|
||||
private suspend fun <T : Any> performRequest(
|
||||
requestBlock: suspend (header: String) -> ApiResponse<T>,
|
||||
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(
|
||||
|
|
|
|||
|
|
@ -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<Throwable, UniversalError> {
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>?): UserWallet.Cold {
|
||||
return userWallets?.find { it is UserWallet.Cold } as? UserWallet.Cold
|
||||
?: error("Cannot find cold user wallet")
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ data class TokenReceiveConfig(
|
|||
val receiveAddress: List<ReceiveAddressModel>,
|
||||
val tokenReceiveNotification: List<TokenReceiveNotification> = 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()
|
||||
}
|
||||
|
|
@ -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?,
|
||||
)
|
||||
|
|
@ -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<UniversalError, DataForReceive>
|
||||
}
|
||||
|
||||
data class DataForReceive(
|
||||
val walletId: UserWalletId,
|
||||
val currency: CryptoCurrency,
|
||||
val receiveAddress: List<ReceiveAddressModel>,
|
||||
)
|
||||
|
|
@ -23,5 +23,6 @@ data class CustomerInfo(
|
|||
val balance: BigDecimal,
|
||||
val currencyCode: String,
|
||||
val customerWalletAddress: String,
|
||||
val depositAddress: String?,
|
||||
)
|
||||
}
|
||||
|
|
@ -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<Params, TangemPayDetailsComponent>
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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<TangemPayDetailsUM>
|
||||
field = MutableStateFlow(getInitialState())
|
||||
field = MutableStateFlow(stateFactory.getInitialState())
|
||||
|
||||
private val refreshStateJobHolder = JobHolder()
|
||||
private val fetchBalanceJobHolder = JobHolder()
|
||||
private val revealCardDetailsJobHolder = JobHolder()
|
||||
|
||||
val bottomSheetNavigation: SlotNavigation<TokenReceiveConfig> = 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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<TangemPayDetailsUM> {
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
@ -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<TangemPayDetailsUM> {
|
||||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
@ -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<ReceiveAddressModel>,
|
||||
private val tokenReceiveNotification: List<TokenReceiveNotification>,
|
||||
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,
|
||||
)
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue