Updated on 2026-08-14
This commit is contained in:
commit
aeb60e0cec
49 changed files with 402 additions and 192 deletions
|
|
@ -1,6 +0,0 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
fun String.removePrefixOrNull(prefix: String): String? = when {
|
||||
startsWith(prefix) -> substring(prefix.length)
|
||||
else -> null
|
||||
}
|
||||
|
|
@ -15,7 +15,6 @@ import com.tangem.crypto.hdWallet.DerivationPath
|
|||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.domain.card.common.visa.VisaUtilities
|
||||
import com.tangem.domain.visa.error.VisaActivationError
|
||||
import com.tangem.operations.ScanTask
|
||||
import com.tangem.operations.derivation.DeriveWalletPublicKeyTask
|
||||
import com.tangem.operations.sign.SignHashCommand
|
||||
|
||||
|
|
@ -92,25 +91,7 @@ class TangemPaySignWithdrawalHashTask(
|
|||
?: targetWalletPublicKey.toDecompressedPublicKey(),
|
||||
).asRSVLegacyEVM().toHexString().lowercase()
|
||||
|
||||
scanCard(
|
||||
session = session,
|
||||
callback = callback,
|
||||
signedData = rsvSignature,
|
||||
)
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
callback(CompletionResult.Failure(result.error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun scanCard(signedData: String, session: CardSession, callback: CompletionCallback<String>) {
|
||||
val scanTask = ScanTask()
|
||||
scanTask.run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
callback(CompletionResult.Success(signedData))
|
||||
callback(CompletionResult.Success(rsvSignature))
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
callback(CompletionResult.Failure(result.error))
|
||||
|
|
|
|||
|
|
@ -660,8 +660,13 @@ internal class ChildFactory @Inject constructor(
|
|||
createComponentChild(
|
||||
context = context,
|
||||
params = when (val mode = route.mode) {
|
||||
is AppRoute.TangemPayOnboarding.Mode.ContinueOnboarding -> ContinueOnboarding
|
||||
is AppRoute.TangemPayOnboarding.Mode.Deeplink -> Deeplink(deeplink = mode.deeplink)
|
||||
is AppRoute.TangemPayOnboarding.Mode.ContinueOnboarding -> ContinueOnboarding(
|
||||
userWalletId = mode.userWalletId,
|
||||
)
|
||||
is AppRoute.TangemPayOnboarding.Mode.Deeplink -> Deeplink(
|
||||
deeplink = mode.deeplink,
|
||||
userWalletId = mode.userWalletId,
|
||||
)
|
||||
},
|
||||
componentFactory = tangemPayOnboardingComponentFactory,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -431,10 +431,13 @@ sealed class AppRoute(val path: String) : Route {
|
|||
@Serializable
|
||||
data class Deeplink(
|
||||
val deeplink: String,
|
||||
val userWalletId: UserWalletId?,
|
||||
) : Mode()
|
||||
|
||||
@Serializable
|
||||
object ContinueOnboarding : Mode()
|
||||
data class ContinueOnboarding(
|
||||
val userWalletId: UserWalletId?,
|
||||
) : Mode()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ class TokenItemStateConverter(
|
|||
private val iconStateProvider: (CryptoCurrencyStatus) -> CurrencyIconState = {
|
||||
CryptoCurrencyToIconStateConverter().convert(it)
|
||||
},
|
||||
private val onApyLabelClick: ((CryptoCurrencyStatus, String) -> Unit)? = null,
|
||||
private val onApyLabelClick: ((CryptoCurrencyStatus, ApySource, String) -> Unit)? = null,
|
||||
private val titleStateProvider: (CryptoCurrencyStatus) -> TokenItemState.TitleState = { currencyStatus ->
|
||||
createTitleState(
|
||||
currencyStatus = currencyStatus,
|
||||
|
|
@ -166,7 +166,7 @@ class TokenItemStateConverter(
|
|||
currencyStatus: CryptoCurrencyStatus,
|
||||
yieldModuleApyMap: Map<String, String>,
|
||||
stakingApyMap: Map<String, List<Yield.Validator>>,
|
||||
onApyLabelClick: ((CryptoCurrencyStatus, String) -> Unit)?,
|
||||
onApyLabelClick: ((CryptoCurrencyStatus, ApySource, String) -> Unit)?,
|
||||
): TokenItemState.TitleState {
|
||||
return when (val value = currencyStatus.value) {
|
||||
is CryptoCurrencyStatus.Loading,
|
||||
|
|
@ -192,7 +192,7 @@ class TokenItemStateConverter(
|
|||
earnApy = apyInfo?.text,
|
||||
earnApyIsActive = apyInfo?.isActive == true,
|
||||
onApyLabelClick = if (apyInfo?.apy != null && onApyLabelClick != null) {
|
||||
{ onApyLabelClick.invoke(currencyStatus, apyInfo.apy) }
|
||||
{ onApyLabelClick.invoke(currencyStatus, apyInfo.source, apyInfo.apy) }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
|
|
@ -224,6 +224,7 @@ class TokenItemStateConverter(
|
|||
),
|
||||
isActive = isActive,
|
||||
apy = yieldSupplyApy,
|
||||
source = ApySource.YIELD_SUPPLY,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -249,6 +250,7 @@ class TokenItemStateConverter(
|
|||
),
|
||||
isActive = stakingInfo.isActive,
|
||||
apy = apyString,
|
||||
source = ApySource.STAKING,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -433,5 +435,11 @@ class TokenItemStateConverter(
|
|||
val text: TextReference?,
|
||||
val isActive: Boolean,
|
||||
val apy: String?,
|
||||
val source: ApySource,
|
||||
)
|
||||
|
||||
enum class ApySource {
|
||||
STAKING,
|
||||
YIELD_SUPPLY,
|
||||
}
|
||||
}
|
||||
|
|
@ -9,4 +9,8 @@ fun String.uriValidate(): Boolean {
|
|||
val regex = DEEPLINK_VALIDATION_REGEX.toRegex()
|
||||
|
||||
return !regex.containsMatchIn(this)
|
||||
}
|
||||
|
||||
fun String.addHexPrefix(): String {
|
||||
return if (this.startsWith("0x")) this else "0x$this"
|
||||
}
|
||||
|
|
@ -9,7 +9,6 @@ import com.tangem.core.error.UniversalError
|
|||
import com.tangem.data.common.currency.CryptoCurrencyFactory
|
||||
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.di.NetworkMoshi
|
||||
import com.tangem.domain.models.ReceiveAddressModel
|
||||
import com.tangem.domain.models.ReceiveAddressModel.NameService
|
||||
|
|
@ -32,7 +31,6 @@ private const val TOKEN_DECIMALS = 6
|
|||
|
||||
internal class DefaultTangemPaySwapDataFactory @Inject constructor(
|
||||
@NetworkMoshi moshi: Moshi,
|
||||
private val tangemPayWalletsManager: TangemPayWalletsManager,
|
||||
excludedBlockchains: ExcludedBlockchains,
|
||||
) : TangemPaySwapDataFactory {
|
||||
|
||||
|
|
@ -63,17 +61,17 @@ internal class DefaultTangemPaySwapDataFactory @Inject constructor(
|
|||
}
|
||||
|
||||
override fun create(
|
||||
userWallet: UserWallet,
|
||||
depositAddress: String,
|
||||
chainId: Int,
|
||||
cryptoBalance: BigDecimal,
|
||||
fiatBalance: BigDecimal,
|
||||
): Either<UniversalError, TangemPayTopUpData> {
|
||||
return catch {
|
||||
val wallet = tangemPayWalletsManager.getDefaultWalletForTangemPayBlocking()
|
||||
val currency = getCurrency(wallet, chainId)
|
||||
val currency = getCurrency(userWallet, chainId)
|
||||
TangemPayTopUpData(
|
||||
currency = currency,
|
||||
walletId = wallet.walletId,
|
||||
walletId = userWallet.walletId,
|
||||
cryptoBalance = cryptoBalance,
|
||||
fiatBalance = fiatBalance,
|
||||
depositAddress = depositAddress,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import com.tangem.domain.pay.repository.TangemPaySwapRepository
|
|||
import com.tangem.domain.visa.error.VisaApiError
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.features.hotwallet.HotWalletFeatureToggles
|
||||
import com.tangem.utils.extensions.addHexPrefix
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
import java.util.Currency
|
||||
|
|
@ -66,7 +67,7 @@ internal class DefaultTangemPaySwapRepository @Inject constructor(
|
|||
recipientAddress = receiverAddress,
|
||||
adminSalt = result.salt,
|
||||
senderAddress = result.senderAddress,
|
||||
adminSignature = signatureResult.signature,
|
||||
adminSignature = signatureResult.signature.addHexPrefix(),
|
||||
)
|
||||
tangemPayApi.withdraw(authHeader = authHeader, body = request)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
|||
import javax.inject.Inject
|
||||
|
||||
private const val INITIAL_CURSOR = "initial_cursor_key"
|
||||
private const val TAG = "TangemPay: TangemPayTxHistoryRepository:"
|
||||
|
||||
internal class DefaultTangemPayTxHistoryRepository @Inject constructor(
|
||||
private val requestPerformer: TangemPayRequestPerformer,
|
||||
|
|
@ -106,12 +105,14 @@ internal class DefaultTangemPayTxHistoryRepository @Inject constructor(
|
|||
cursor: String?,
|
||||
pageSize: Int,
|
||||
) {
|
||||
requestPerformer.runWithErrorLogs(TAG) {
|
||||
val result = requestPerformer.request(userWalletId) { authHeader ->
|
||||
visaApi.getTangemPayTxHistory(authHeader = authHeader, limit = pageSize, cursor = cursor)
|
||||
}.result
|
||||
requestPerformer.performRequest(userWalletId = userWalletId) { authHeader ->
|
||||
visaApi.getTangemPayTxHistory(authHeader = authHeader, limit = pageSize, cursor = cursor)
|
||||
}.onLeft {
|
||||
error(it.toString())
|
||||
}.onRight { response ->
|
||||
val result = response.result
|
||||
val items = txHistoryItemConverter.convertList(result.transactions).filterNotNull()
|
||||
txHistoryItemsStore.store(key = customerWalletAddress, cursor = cursor ?: INITIAL_CURSOR, value = items)
|
||||
}.onLeft { error(it.toString()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,11 +21,11 @@ import com.tangem.domain.visa.model.TangemPayAuthTokens
|
|||
import com.tangem.domain.visa.model.getAuthHeader
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ internal class TangemPayRequestPerformer @Inject constructor(
|
|||
private val tangemPayStorage: TangemPayStorage,
|
||||
) {
|
||||
|
||||
private val customerWalletAddress = MutableStateFlow<String?>(null)
|
||||
private val customerWalletAddresses = ConcurrentHashMap<UserWalletId, String>()
|
||||
private val tokensMutex = Mutex()
|
||||
private val errorConverter = TangemPayErrorConverter(moshi)
|
||||
|
||||
|
|
@ -110,7 +110,7 @@ internal class TangemPayRequestPerformer @Inject constructor(
|
|||
}
|
||||
|
||||
suspend fun getCustomerWalletAddress(userWalletId: UserWalletId): String {
|
||||
val existingAddress = customerWalletAddress.value
|
||||
val existingAddress = customerWalletAddresses[userWalletId]
|
||||
if (existingAddress != null) {
|
||||
return existingAddress
|
||||
}
|
||||
|
|
@ -118,7 +118,7 @@ internal class TangemPayRequestPerformer @Inject constructor(
|
|||
userWalletId = userWalletId,
|
||||
) ?: error("Can not find customer address")
|
||||
|
||||
customerWalletAddress.value = storedAddress
|
||||
customerWalletAddresses[userWalletId] = storedAddress
|
||||
return storedAddress
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,12 +15,14 @@ class TangemPayWalletsManager @Inject constructor(
|
|||
private val hotWalletFeatureToggles: HotWalletFeatureToggles,
|
||||
) {
|
||||
|
||||
@Deprecated("Don't use and put userWallet in features that need it")
|
||||
suspend fun getDefaultWalletForTangemPay(): UserWallet.Cold {
|
||||
val userWalletsFlow = if (useNewRepository()) repository.userWallets else manager.userWallets
|
||||
val userWallets = userWalletsFlow.filter { !it.isNullOrEmpty() }.first()
|
||||
return findColdWallet(userWallets)
|
||||
}
|
||||
|
||||
@Deprecated("Don't use and put userWallet in features that need it")
|
||||
fun getDefaultWalletForTangemPayBlocking(): UserWallet.Cold {
|
||||
val userWallets = if (useNewRepository()) repository.userWallets.value else manager.userWalletsSync
|
||||
return findColdWallet(userWallets)
|
||||
|
|
@ -29,7 +31,8 @@ class TangemPayWalletsManager @Inject constructor(
|
|||
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")
|
||||
return userWallets?.find {
|
||||
it is UserWallet.Cold && it.isMultiCurrency
|
||||
} as? UserWallet.Cold ?: error("Cannot find cold user wallet")
|
||||
}
|
||||
}
|
||||
|
|
@ -4,12 +4,14 @@ 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.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import java.math.BigDecimal
|
||||
|
||||
interface TangemPaySwapDataFactory {
|
||||
|
||||
fun create(
|
||||
userWallet: UserWallet,
|
||||
depositAddress: String,
|
||||
chainId: Int,
|
||||
cryptoBalance: BigDecimal,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,11 @@ package com.tangem.domain.pay.model
|
|||
import com.tangem.domain.visa.model.TangemPayCardFrozenState
|
||||
import java.math.BigDecimal
|
||||
|
||||
sealed class MainCustomerInfoContentState {
|
||||
object Loading : MainCustomerInfoContentState()
|
||||
data class Content(val info: MainScreenCustomerInfo) : MainCustomerInfoContentState()
|
||||
}
|
||||
|
||||
data class MainScreenCustomerInfo(
|
||||
val info: CustomerInfo,
|
||||
val orderStatus: OrderStatus,
|
||||
|
|
|
|||
|
|
@ -2,16 +2,13 @@ package com.tangem.domain.pay.usecase
|
|||
|
||||
import arrow.core.Either
|
||||
import arrow.core.left
|
||||
import arrow.core.raise.catch
|
||||
import arrow.core.right
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.model.CustomerInfo
|
||||
import com.tangem.domain.pay.model.MainScreenCustomerInfo
|
||||
import com.tangem.domain.pay.model.OrderStatus
|
||||
import com.tangem.domain.pay.model.TangemPayCustomerInfoError
|
||||
import com.tangem.domain.pay.model.*
|
||||
import com.tangem.domain.pay.repository.CustomerOrderRepository
|
||||
import com.tangem.domain.pay.repository.OnboardingRepository
|
||||
import com.tangem.domain.visa.error.VisaApiError
|
||||
import kotlinx.coroutines.flow.*
|
||||
import timber.log.Timber
|
||||
|
||||
private const val TAG = "TangemPayMainScreenCustomerInfoUseCase"
|
||||
|
|
@ -26,32 +23,50 @@ class TangemPayMainScreenCustomerInfoUseCase(
|
|||
private val tangemPayOnboardingRepository: OnboardingRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
): Either<TangemPayCustomerInfoError, MainScreenCustomerInfo> = catch(
|
||||
block = {
|
||||
Timber.tag(TAG).d("checkCustomerWallet")
|
||||
repository.checkCustomerWallet(userWalletId)
|
||||
.fold(
|
||||
ifLeft = { error ->
|
||||
Timber.tag(TAG).e("Failed to check customer wallet ${error.javaClass.simpleName}")
|
||||
TangemPayCustomerInfoError.UnknownError.left()
|
||||
},
|
||||
ifRight = { hasTangemPay ->
|
||||
Timber.tag(TAG).i("checkCustomerWallet $hasTangemPay")
|
||||
if (hasTangemPay) {
|
||||
proceedWithPaeraCustomerResult(userWalletId)
|
||||
} else {
|
||||
TangemPayCustomerInfoError.UnknownError.left() // ignore if there's no TangemPay
|
||||
val state: StateFlow<Map<UserWalletId, Either<TangemPayCustomerInfoError, MainCustomerInfoContentState>>>
|
||||
field = MutableStateFlow(value = mapOf())
|
||||
|
||||
suspend fun fetch(userWalletId: UserWalletId) {
|
||||
Timber.tag(TAG).i("fetch: $userWalletId")
|
||||
repository.checkCustomerWallet(userWalletId)
|
||||
.fold(
|
||||
ifLeft = { error ->
|
||||
Timber.tag(TAG).e("Failed checkCustomerWallet for $userWalletId: ${error.javaClass.simpleName}")
|
||||
updateState(userWalletId, TangemPayCustomerInfoError.UnknownError.left())
|
||||
},
|
||||
ifRight = { hasTangemPay ->
|
||||
Timber.tag(TAG).i("checkCustomerWallet for $userWalletId: $hasTangemPay")
|
||||
if (hasTangemPay) {
|
||||
val oldResult = state.value[userWalletId]
|
||||
if (oldResult == null) {
|
||||
updateState(userWalletId, MainCustomerInfoContentState.Loading.right())
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
catch = { error ->
|
||||
Timber.tag(TAG).e(error)
|
||||
TangemPayCustomerInfoError.UnknownError.left()
|
||||
},
|
||||
)
|
||||
|
||||
val result = proceedWithPaeraCustomerResult(userWalletId)
|
||||
.map(MainCustomerInfoContentState::Content)
|
||||
updateState(userWalletId, result)
|
||||
} else {
|
||||
// ignore if there's no TangemPay
|
||||
updateState(userWalletId, TangemPayCustomerInfoError.UnknownError.left())
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
): Flow<Either<TangemPayCustomerInfoError, MainCustomerInfoContentState>> {
|
||||
return state.mapNotNull { map -> map[userWalletId] }
|
||||
}
|
||||
|
||||
private fun updateState(
|
||||
userWalletId: UserWalletId,
|
||||
either: Either<TangemPayCustomerInfoError, MainCustomerInfoContentState>,
|
||||
) {
|
||||
state.update { currentMap ->
|
||||
currentMap.toMutableMap().apply { this[userWalletId] = either }
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun proceedWithPaeraCustomerResult(
|
||||
userWalletId: UserWalletId,
|
||||
|
|
@ -70,14 +85,13 @@ class TangemPayMainScreenCustomerInfoUseCase(
|
|||
private suspend fun proceedWithoutOrder(
|
||||
userWalletId: UserWalletId,
|
||||
): Either<TangemPayCustomerInfoError, MainScreenCustomerInfo> {
|
||||
Timber.tag(TAG).d("proceedWithoutOrder")
|
||||
return repository.getCustomerInfo(userWalletId)
|
||||
.mapLeft { error ->
|
||||
Timber.tag(TAG).e("mapErrorForCustomer: $error")
|
||||
error.mapErrorForCustomer()
|
||||
}
|
||||
.map { customerInfo ->
|
||||
Timber.tag(TAG).d("customerInfo")
|
||||
Timber.tag(TAG).i("customerInfo")
|
||||
if (customerInfo.cardInfo == null && customerInfo.isKycApproved) {
|
||||
// If order id wasn't saved -> start order creation and get customer info
|
||||
repository.createOrder(userWalletId)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@ import kotlinx.coroutines.flow.update
|
|||
|
||||
internal typealias ChangedCurrencies = Map<ManagedCryptoCurrency.Token, Set<Network>>
|
||||
|
||||
internal data class CurrencyUpdates(
|
||||
val toAdd: ChangedCurrencies = emptyMap(),
|
||||
val toRemove: ChangedCurrencies = emptyMap(),
|
||||
)
|
||||
|
||||
internal class ChangedCurrenciesManager {
|
||||
|
||||
val currenciesToAdd: MutableStateFlow<ChangedCurrencies> = MutableStateFlow(emptyMap())
|
||||
|
|
|
|||
|
|
@ -329,6 +329,10 @@ internal class ManageTokensListManager @AssistedInject constructor(
|
|||
val updatedUiItem = uiBatch.data[currencyIndex].toggleExpanded(
|
||||
currency = currencyBatch.data[currencyIndex],
|
||||
isEditable = batches.canEditItems,
|
||||
updates = CurrencyUpdates(
|
||||
toAdd = currenciesToAdd.value,
|
||||
toRemove = currenciesToRemove.value,
|
||||
),
|
||||
onSelectCurrencyNetwork = { networkId, isSelected ->
|
||||
selectNetwork(currencyBatch.key, currency, networkId, isSelected)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -5,20 +5,28 @@ import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork
|
|||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.features.managetokens.entity.item.CurrencyItemUM.Basic.NetworksUM
|
||||
import com.tangem.features.managetokens.entity.item.CurrencyNetworkUM
|
||||
import com.tangem.features.managetokens.utils.list.CurrencyUpdates
|
||||
import com.tangem.features.managetokens.utils.ui.getIconRes
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
internal fun ManagedCryptoCurrency.Token.toUiNetworksModel(
|
||||
isExpanded: Boolean,
|
||||
isItemsEditable: Boolean,
|
||||
updates: CurrencyUpdates,
|
||||
onSelectedStateChange: (SourceNetwork, Boolean) -> Unit,
|
||||
onLongTap: (SourceNetwork) -> Unit,
|
||||
): NetworksUM {
|
||||
return if (isExpanded) {
|
||||
NetworksUM.Expanded(
|
||||
networks = availableNetworks.map {
|
||||
it.toCurrencyNetworkModel(
|
||||
isSelected = it.network in addedIn,
|
||||
networks = availableNetworks.map { sourceNetwork ->
|
||||
val isSelectedByDefault = sourceNetwork.network in addedIn
|
||||
val isSelected = when (sourceNetwork.network) {
|
||||
in updates.toAdd[this].orEmpty() -> true
|
||||
in updates.toRemove[this].orEmpty() -> false
|
||||
else -> isSelectedByDefault
|
||||
}
|
||||
sourceNetwork.toCurrencyNetworkModel(
|
||||
isSelected = isSelected,
|
||||
isEditable = isItemsEditable,
|
||||
onSelectedStateChange = onSelectedStateChange,
|
||||
onLongTap = onLongTap,
|
||||
|
|
|
|||
|
|
@ -4,12 +4,14 @@ import com.tangem.domain.managetokens.model.ManagedCryptoCurrency
|
|||
import com.tangem.domain.managetokens.model.ManagedCryptoCurrency.SourceNetwork
|
||||
import com.tangem.features.managetokens.entity.item.CurrencyItemUM
|
||||
import com.tangem.features.managetokens.entity.item.CurrencyItemUM.Basic.NetworksUM
|
||||
import com.tangem.features.managetokens.utils.list.CurrencyUpdates
|
||||
import com.tangem.features.managetokens.utils.mapper.toUiNetworksModel
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
internal fun CurrencyItemUM.toggleExpanded(
|
||||
currency: ManagedCryptoCurrency,
|
||||
isEditable: Boolean,
|
||||
updates: CurrencyUpdates,
|
||||
onSelectCurrencyNetwork: (SourceNetwork, Boolean) -> Unit,
|
||||
onLongTap: (SourceNetwork) -> Unit,
|
||||
): CurrencyItemUM {
|
||||
|
|
@ -30,6 +32,7 @@ internal fun CurrencyItemUM.toggleExpanded(
|
|||
networks = currency.toUiNetworksModel(
|
||||
isExpanded = isExpanded,
|
||||
isItemsEditable = isEditable,
|
||||
updates = updates,
|
||||
onSelectedStateChange = onSelectCurrencyNetwork,
|
||||
onLongTap = onLongTap,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -29,13 +29,13 @@ internal class OnrampSettingsModel @Inject constructor(
|
|||
paramsContainer: ParamsContainer,
|
||||
) : Model() {
|
||||
|
||||
private val params: OnrampSettingsComponent.Params = paramsContainer.require()
|
||||
|
||||
val state: StateFlow<OnrampSettingsUM>
|
||||
field = MutableStateFlow(getInitialState())
|
||||
|
||||
val bottomSheetNavigation: SlotNavigation<OnrampSettingsConfig> = SlotNavigation()
|
||||
|
||||
private val params: OnrampSettingsComponent.Params = paramsContainer.require()
|
||||
|
||||
init {
|
||||
analyticsEventHandler.send(OnrampAnalyticsEvent.SettingsOpened)
|
||||
subscribeOnUpdateState()
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ internal class TangemPayDetailsComponent(
|
|||
appComponentContext = context,
|
||||
params = TangemPayTxHistoryDetailsComponent.Params(
|
||||
transaction = navigation.transaction,
|
||||
isBalanceHidden = navigation.isBalanceHidden,
|
||||
userWalletId = params.userWalletId,
|
||||
onDismiss = model.bottomSheetNavigation::dismiss,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
package com.tangem.features.tangempay.components.txHistory
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
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.ComposableBottomSheetComponent
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
|
||||
import com.tangem.features.tangempay.model.TangemPayTxHistoryDetailsModel
|
||||
|
|
@ -23,11 +24,13 @@ internal class TangemPayTxHistoryDetailsComponent(
|
|||
|
||||
@Composable
|
||||
override fun BottomSheet() {
|
||||
TangemPayTxHistoryDetailsContent(state = model.uiState)
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
TangemPayTxHistoryDetailsContent(state = state)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val transaction: TangemPayTxHistoryItem,
|
||||
val isBalanceHidden: Boolean,
|
||||
val userWalletId: UserWalletId,
|
||||
val onDismiss: () -> Unit,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -22,5 +22,8 @@ internal sealed class TangemPayDetailsNavigation {
|
|||
) : TangemPayDetailsNavigation()
|
||||
|
||||
@Serializable
|
||||
data class TransactionDetails(val transaction: TangemPayTxHistoryItem) : TangemPayDetailsNavigation()
|
||||
data class TransactionDetails(
|
||||
val transaction: TangemPayTxHistoryItem,
|
||||
val isBalanceHidden: Boolean,
|
||||
) : TangemPayDetailsNavigation()
|
||||
}
|
||||
|
|
@ -41,7 +41,6 @@ internal class TangemPayDetailsStateFactory(
|
|||
),
|
||||
)
|
||||
}
|
||||
|
||||
return TangemPayDetailsUM(
|
||||
topBarConfig = TangemPayDetailsTopBarConfig(
|
||||
onBackClick = onBack,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import com.tangem.core.ui.extensions.TextReference
|
|||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
internal data class TangemPayTxHistoryDetailsUM(
|
||||
val isBalanceHidden: Boolean,
|
||||
val title: TextReference,
|
||||
val iconState: ImageReference,
|
||||
val transactionTitle: TextReference,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.tangem.core.decompose.di.ModelScoped
|
|||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.domain.pay.TangemPaySwapDataFactory
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.features.tangempay.components.TangemPayAddFundsComponent
|
||||
import com.tangem.features.tangempay.entity.TangemPayAddFundsUM
|
||||
import com.tangem.features.tangempay.model.transformers.TangemPayAddFundsUMConverter
|
||||
|
|
@ -17,6 +18,7 @@ internal class TangemPayAddFundsModel @Inject constructor(
|
|||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val tangemPaySwapDataFactory: TangemPaySwapDataFactory,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<TangemPayAddFundsComponent.Params>()
|
||||
|
|
@ -24,7 +26,11 @@ internal class TangemPayAddFundsModel @Inject constructor(
|
|||
val uiState: TangemPayAddFundsUM = getInitialState()
|
||||
|
||||
private fun getInitialState(): TangemPayAddFundsUM {
|
||||
val userWallet = requireNotNull(
|
||||
getUserWalletUseCase(params.walletId).getOrNull(),
|
||||
) { "User wallet not found for id: ${params.walletId}" }
|
||||
val data = tangemPaySwapDataFactory.create(
|
||||
userWallet = userWallet,
|
||||
depositAddress = params.depositAddress,
|
||||
chainId = params.chainId,
|
||||
cryptoBalance = params.cryptoBalance,
|
||||
|
|
|
|||
|
|
@ -18,14 +18,15 @@ import com.tangem.core.ui.extensions.resourceReference
|
|||
import com.tangem.core.ui.message.SnackbarMessage
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.models.TokenReceiveConfig
|
||||
import com.tangem.domain.pay.TangemPayTopUpData
|
||||
import com.tangem.domain.pay.TangemPaySwapDataFactory
|
||||
import com.tangem.domain.pay.TangemPayTopUpData
|
||||
import com.tangem.domain.pay.model.TangemPayCardBalance
|
||||
import com.tangem.domain.pay.repository.CustomerOrderRepository
|
||||
import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
|
||||
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
|
||||
import com.tangem.domain.visa.model.TangemPayCardFrozenState
|
||||
import com.tangem.domain.visa.model.TangemPayTxHistoryItem
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.features.tangempay.TangemPayConstants
|
||||
import com.tangem.features.tangempay.components.AddFundsListener
|
||||
import com.tangem.features.tangempay.components.TangemPayDetailsContainerComponent
|
||||
|
|
@ -55,7 +56,7 @@ import kotlinx.coroutines.launch
|
|||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Suppress("LongParameterList", "LargeClass")
|
||||
@Stable
|
||||
@ModelScoped
|
||||
internal class TangemPayDetailsModel @Inject constructor(
|
||||
|
|
@ -71,6 +72,7 @@ internal class TangemPayDetailsModel @Inject constructor(
|
|||
private val txHistoryUpdateListener: TangemPayTxHistoryUpdateListener,
|
||||
private val tangemPaySwapDataFactory: TangemPaySwapDataFactory,
|
||||
private val orderRepository: CustomerOrderRepository,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
) : Model(), TangemPayTxHistoryUiActions, TangemPayDetailIntents, AddFundsListener {
|
||||
|
||||
private val params: TangemPayDetailsContainerComponent.Params = paramsContainer.require()
|
||||
|
|
@ -234,7 +236,11 @@ internal class TangemPayDetailsModel @Inject constructor(
|
|||
if (hasActiveWithdrawal) {
|
||||
showBottomSheetError(TangemPayDetailsErrorType.WithdrawInProgress)
|
||||
} else {
|
||||
val userWallet = requireNotNull(
|
||||
getUserWalletUseCase(params.userWalletId).getOrNull(),
|
||||
) { "User wallet not found: ${params.userWalletId}" }
|
||||
val data = tangemPaySwapDataFactory.create(
|
||||
userWallet = userWallet,
|
||||
depositAddress = depositAddress,
|
||||
chainId = params.config.chainId,
|
||||
cryptoBalance = currentBalance.cryptoBalance,
|
||||
|
|
@ -367,7 +373,12 @@ internal class TangemPayDetailsModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onTransactionClick(item: TangemPayTxHistoryItem) {
|
||||
bottomSheetNavigation.activate(TangemPayDetailsNavigation.TransactionDetails(item))
|
||||
bottomSheetNavigation.activate(
|
||||
configuration = TangemPayDetailsNavigation.TransactionDetails(
|
||||
transaction = item,
|
||||
isBalanceHidden = uiState.value.isBalanceHidden,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override fun onClickTermsAndLimits() {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ 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.navigation.url.UrlOpener
|
||||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.feedback.GetWalletMetaInfoUseCase
|
||||
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
|
|
@ -14,39 +15,56 @@ import com.tangem.features.tangempay.components.txHistory.TangemPayTxHistoryDeta
|
|||
import com.tangem.features.tangempay.entity.TangemPayTxHistoryDetailsUM
|
||||
import com.tangem.features.tangempay.model.transformers.TangemPayTxHistoryDetailsConverter
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@Stable
|
||||
@ModelScoped
|
||||
@Suppress("LongParameterList")
|
||||
internal class TangemPayTxHistoryDetailsModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val getUserWalletsUseCase: GetWalletsUseCase,
|
||||
private val getWalletMetaInfoUseCase: GetWalletMetaInfoUseCase,
|
||||
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||
private val urlOpener: UrlOpener,
|
||||
private val balanceHidingSettings: GetBalanceHidingSettingsUseCase,
|
||||
paramsContainer: ParamsContainer,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<TangemPayTxHistoryDetailsComponent.Params>()
|
||||
val uiState: TangemPayTxHistoryDetailsUM = TangemPayTxHistoryDetailsConverter.convert(
|
||||
TangemPayTxHistoryDetailsConverter.Input(
|
||||
item = params.transaction,
|
||||
onExplorerClick = ::openExplorer,
|
||||
onDisputeClick = ::dispute,
|
||||
onDismiss = ::dismiss,
|
||||
),
|
||||
)
|
||||
val uiState: StateFlow<TangemPayTxHistoryDetailsUM>
|
||||
field = MutableStateFlow(
|
||||
value = TangemPayTxHistoryDetailsConverter.convert(
|
||||
value = TangemPayTxHistoryDetailsConverter.Input(
|
||||
item = params.transaction,
|
||||
isBalanceHidden = params.isBalanceHidden,
|
||||
onExplorerClick = ::openExplorer,
|
||||
onDisputeClick = ::dispute,
|
||||
onDismiss = ::dismiss,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
init {
|
||||
subscribeToBalanceHiding()
|
||||
}
|
||||
|
||||
fun dismiss() {
|
||||
params.onDismiss()
|
||||
}
|
||||
|
||||
fun openExplorer(txHash: String?) {
|
||||
private fun subscribeToBalanceHiding() {
|
||||
balanceHidingSettings.isBalanceHidden()
|
||||
.onEach { isBalanceHidden -> uiState.update { it.copy(isBalanceHidden = isBalanceHidden) } }
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun openExplorer(txHash: String?) {
|
||||
txHash?.let(urlOpener::openUrlExternalBrowser)
|
||||
}
|
||||
|
||||
fun dispute() {
|
||||
private fun dispute() {
|
||||
modelScope.launch {
|
||||
val userWalletId = params.userWalletId
|
||||
val userWallet = getUserWalletsUseCase.invokeSync()
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.core.ui.extensions.themedColor
|
|||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.domain.visa.model.TangemPayCardFrozenState
|
||||
import com.tangem.features.tangempay.details.impl.R
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsBalanceBlockState
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsTopBarMenuItem
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsTopBarMenuItemType.FreezeCard
|
||||
import com.tangem.features.tangempay.entity.TangemPayDetailsTopBarMenuItemType.UnfreezeCard
|
||||
|
|
@ -31,9 +32,20 @@ internal class TangemPayFreezeUnfreezeStateTransformer(
|
|||
it.type == FreezeCard || it.type == UnfreezeCard
|
||||
}
|
||||
val dropdownMenuItems = createUpdatedMenuItems(filteredItems?.toPersistentList())
|
||||
val balanceBlockState = if (prevState.balanceBlockState is TangemPayDetailsBalanceBlockState.Content) {
|
||||
val actionButtons = prevState.balanceBlockState.actionButtons.map {
|
||||
it.copy(isEnabled = cardFrozenState == TangemPayCardFrozenState.Unfrozen)
|
||||
}
|
||||
prevState.balanceBlockState.copy(
|
||||
actionButtons = actionButtons.toPersistentList(),
|
||||
)
|
||||
} else {
|
||||
prevState.balanceBlockState
|
||||
}
|
||||
return prevState.copy(
|
||||
topBarConfig = prevState.topBarConfig.copy(items = dropdownMenuItems),
|
||||
cardFrozenState = converter.convert(cardFrozenState),
|
||||
balanceBlockState = balanceBlockState,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ internal object TangemPayTxHistoryDetailsConverter :
|
|||
override fun convert(value: Input): TangemPayTxHistoryDetailsUM {
|
||||
val transaction = value.item
|
||||
return TangemPayTxHistoryDetailsUM(
|
||||
isBalanceHidden = value.isBalanceHidden,
|
||||
title = transaction.extractDate(),
|
||||
iconState = transaction.extractIcon(),
|
||||
transactionTitle = transaction.extractTransactionTitle(),
|
||||
|
|
@ -254,6 +255,7 @@ internal object TangemPayTxHistoryDetailsConverter :
|
|||
|
||||
data class Input(
|
||||
val item: TangemPayTxHistoryItem,
|
||||
val isBalanceHidden: Boolean,
|
||||
val onExplorerClick: (String?) -> Unit,
|
||||
val onDisputeClick: () -> Unit,
|
||||
val onDismiss: () -> Unit,
|
||||
|
|
|
|||
|
|
@ -99,19 +99,6 @@ internal fun TangemPayDetailsScreen(
|
|||
}
|
||||
else -> Unit
|
||||
}
|
||||
item(
|
||||
key = TangemPayDetailsBalanceBlockState::class.java,
|
||||
content = {
|
||||
TangemPayDetailsBalanceBlock(
|
||||
modifier = modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||
.padding(top = 12.dp)
|
||||
.fillMaxWidth(),
|
||||
state = state.balanceBlockState,
|
||||
isBalanceHidden = state.isBalanceHidden,
|
||||
)
|
||||
},
|
||||
)
|
||||
if (state.addToWalletBlockState != null) {
|
||||
item(
|
||||
key = AddToWalletBlockState::class.java,
|
||||
|
|
@ -125,6 +112,19 @@ internal fun TangemPayDetailsScreen(
|
|||
},
|
||||
)
|
||||
}
|
||||
item(
|
||||
key = TangemPayDetailsBalanceBlockState::class.java,
|
||||
content = {
|
||||
TangemPayDetailsBalanceBlock(
|
||||
modifier = modifier
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16)
|
||||
.padding(top = 12.dp)
|
||||
.fillMaxWidth(),
|
||||
state = state.balanceBlockState,
|
||||
isBalanceHidden = state.isBalanceHidden,
|
||||
)
|
||||
},
|
||||
)
|
||||
with(txHistoryComponent) { txHistoryContent(listState = listState, state = txHistoryState) }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ internal fun TangemPayTxHistoryDetailsContent(state: TangemPayTxHistoryDetailsUM
|
|||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
text = state.transactionAmount,
|
||||
text = state.transactionAmount.orMaskWithStars(state.isBalanceHidden),
|
||||
style = TangemTheme.typography.head,
|
||||
color = state.transactionAmountColor.resolveReference(),
|
||||
)
|
||||
|
|
@ -158,6 +158,7 @@ private fun TangemPayTxHistoryDetailsContentPreview(
|
|||
private class TangemPayTxHistoryDetailsUMProvider : CollectionPreviewParameterProvider<TangemPayTxHistoryDetailsUM>(
|
||||
listOf(
|
||||
TangemPayTxHistoryDetailsUM(
|
||||
isBalanceHidden = true,
|
||||
title = stringReference("12 June • 12:40"),
|
||||
iconState = ImageReference.Res(R.drawable.ic_category_24),
|
||||
transactionTitle = stringReference("Starbucks"),
|
||||
|
|
@ -180,6 +181,7 @@ private class TangemPayTxHistoryDetailsUMProvider : CollectionPreviewParameterPr
|
|||
dismiss = {},
|
||||
),
|
||||
TangemPayTxHistoryDetailsUM(
|
||||
isBalanceHidden = true,
|
||||
title = stringReference("12 June • 12:40"),
|
||||
iconState = ImageReference.Res(R.drawable.ic_category_24),
|
||||
transactionTitle = stringReference("Starbucks"),
|
||||
|
|
@ -205,6 +207,7 @@ private class TangemPayTxHistoryDetailsUMProvider : CollectionPreviewParameterPr
|
|||
dismiss = {},
|
||||
),
|
||||
TangemPayTxHistoryDetailsUM(
|
||||
isBalanceHidden = false,
|
||||
title = stringReference("12 June • 12:40"),
|
||||
iconState = ImageReference.Res(R.drawable.ic_category_24),
|
||||
transactionTitle = stringReference("Starbucks"),
|
||||
|
|
@ -226,6 +229,7 @@ private class TangemPayTxHistoryDetailsUMProvider : CollectionPreviewParameterPr
|
|||
dismiss = {},
|
||||
),
|
||||
TangemPayTxHistoryDetailsUM(
|
||||
isBalanceHidden = false,
|
||||
title = stringReference("12 June • 12:40"),
|
||||
iconState = ImageReference.Res(R.drawable.ic_percent_24),
|
||||
transactionTitle = stringReference("Fee"),
|
||||
|
|
@ -248,6 +252,7 @@ private class TangemPayTxHistoryDetailsUMProvider : CollectionPreviewParameterPr
|
|||
dismiss = {},
|
||||
),
|
||||
TangemPayTxHistoryDetailsUM(
|
||||
isBalanceHidden = false,
|
||||
title = stringReference("12 June • 12:40"),
|
||||
iconState = ImageReference.Res(R.drawable.ic_arrow_down_24),
|
||||
transactionTitle = stringReference("Deposit"),
|
||||
|
|
@ -266,6 +271,7 @@ private class TangemPayTxHistoryDetailsUMProvider : CollectionPreviewParameterPr
|
|||
dismiss = {},
|
||||
),
|
||||
TangemPayTxHistoryDetailsUM(
|
||||
isBalanceHidden = false,
|
||||
title = stringReference("12 June • 12:40"),
|
||||
iconState = ImageReference.Res(R.drawable.ic_arrow_up_24),
|
||||
transactionTitle = stringReference("Withdrawal"),
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ dependencies {
|
|||
implementation(projects.core.decompose)
|
||||
implementation(projects.core.ui)
|
||||
|
||||
/** Domain */
|
||||
implementation(projects.domain.models)
|
||||
|
||||
/** Compose */
|
||||
implementation(deps.compose.runtime)
|
||||
}
|
||||
|
|
@ -2,15 +2,22 @@ package com.tangem.features.tangempay.components
|
|||
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
|
||||
interface TangemPayOnboardingComponent : ComposableContentComponent {
|
||||
|
||||
sealed class Params {
|
||||
|
||||
abstract val userWalletId: UserWalletId?
|
||||
|
||||
data class Deeplink(
|
||||
val deeplink: String,
|
||||
override val userWalletId: UserWalletId?,
|
||||
) : Params()
|
||||
|
||||
object ContinueOnboarding : Params()
|
||||
data class ContinueOnboarding(
|
||||
override val userWalletId: UserWalletId?,
|
||||
) : Params()
|
||||
}
|
||||
|
||||
interface Factory : ComponentFactory<Params, TangemPayOnboardingComponent>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ dependencies {
|
|||
|
||||
/** Domain */
|
||||
implementation(projects.domain.visa)
|
||||
implementation(projects.domain.wallets)
|
||||
|
||||
/** Data **/
|
||||
implementation(projects.data.visa)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
package com.tangem.features.tangempay.deeplink
|
||||
|
||||
import android.net.Uri
|
||||
import dagger.assisted.Assisted
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.features.tangempay.TangemPayFeatureToggles
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
|
||||
import com.tangem.features.tangempay.TangemPayFeatureToggles
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
|
||||
|
|
@ -12,11 +13,16 @@ internal class DefaultOnboardVisaDeepLinkHandler @AssistedInject constructor(
|
|||
@Assisted uri: Uri,
|
||||
appRouter: AppRouter,
|
||||
tangemPayFeatureToggles: TangemPayFeatureToggles,
|
||||
getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
|
||||
) : OnboardVisaDeepLinkHandler {
|
||||
|
||||
init {
|
||||
if (tangemPayFeatureToggles.isTangemPayEnabled) {
|
||||
val mode = AppRoute.TangemPayOnboarding.Mode.Deeplink(uri.toString())
|
||||
val userWallet = getSelectedWalletSyncUseCase.invoke().getOrNull()
|
||||
val mode = AppRoute.TangemPayOnboarding.Mode.Deeplink(
|
||||
deeplink = uri.toString(),
|
||||
userWalletId = userWallet?.walletId,
|
||||
)
|
||||
appRouter.push(AppRoute.TangemPayOnboarding(mode))
|
||||
} else {
|
||||
appRouter.push(AppRoute.Home())
|
||||
|
|
|
|||
|
|
@ -9,9 +9,13 @@ import com.tangem.core.decompose.model.ParamsContainer
|
|||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.data.pay.util.TangemPayWalletsManager
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||
import com.tangem.domain.pay.repository.OnboardingRepository
|
||||
import com.tangem.domain.pay.usecase.ProduceTangemPayInitialDataUseCase
|
||||
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
|
||||
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.features.tangempay.TangemPayConstants
|
||||
import com.tangem.features.tangempay.components.TangemPayOnboardingComponent
|
||||
import com.tangem.features.tangempay.model.transformers.TangemPayOnboardingButtonLoadingTransformer
|
||||
|
|
@ -37,6 +41,8 @@ internal class TangemPayOnboardingModel @Inject constructor(
|
|||
private val produceInitialDataUseCase: ProduceTangemPayInitialDataUseCase,
|
||||
private val urlOpener: UrlOpener,
|
||||
private val tangemPayWalletsManager: TangemPayWalletsManager,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
private val getSelectedWalletUseCase: GetSelectedWalletUseCase,
|
||||
) : Model() {
|
||||
|
||||
private val params = paramsContainer.require<TangemPayOnboardingComponent.Params>()
|
||||
|
|
@ -74,8 +80,9 @@ internal class TangemPayOnboardingModel @Inject constructor(
|
|||
|
||||
private suspend fun checkCustomerInfo() {
|
||||
// TODO implement selector
|
||||
val userWalletId = getUserWalletForPay(params.userWalletId)
|
||||
repository.getCustomerInfo(
|
||||
userWalletId = tangemPayWalletsManager.getDefaultWalletForTangemPayBlocking().walletId,
|
||||
userWalletId = userWalletId,
|
||||
)
|
||||
// selector
|
||||
.onRight { customerInfo ->
|
||||
|
|
@ -92,6 +99,24 @@ internal class TangemPayOnboardingModel @Inject constructor(
|
|||
.onLeft { back() }
|
||||
}
|
||||
|
||||
private fun getUserWalletForPay(userWalletId: UserWalletId?): UserWalletId {
|
||||
val userWallet = userWalletId?.let { getUserWalletUseCase(it).getOrNull() }
|
||||
return if (userWallet?.isMultiCurrency == true) {
|
||||
userWallet.walletId
|
||||
} else {
|
||||
tryGetSelectedWalletId()
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryGetSelectedWalletId(): UserWalletId {
|
||||
val selectedWallet = getSelectedWalletUseCase.sync().getOrNull()
|
||||
return if (selectedWallet?.isMultiCurrency == true) {
|
||||
selectedWallet.walletId
|
||||
} else {
|
||||
tangemPayWalletsManager.getDefaultWalletForTangemPayBlocking().walletId
|
||||
}
|
||||
}
|
||||
|
||||
private fun onTermsClick() {
|
||||
analytics.send(TangemPayAnalyticsEvents.ViewTermsClicked)
|
||||
urlOpener.openUrl(TangemPayConstants.TERMS_AND_LIMITS_LINK)
|
||||
|
|
@ -103,7 +128,7 @@ internal class TangemPayOnboardingModel @Inject constructor(
|
|||
uiState.transformerUpdate(TangemPayOnboardingButtonLoadingTransformer(isLoading = true))
|
||||
modelScope.launch {
|
||||
// TODO implement selector
|
||||
val userWalletId = tangemPayWalletsManager.getDefaultWalletForTangemPayBlocking().walletId
|
||||
val userWalletId = getUserWalletForPay(params.userWalletId)
|
||||
val result = produceInitialDataUseCase(userWalletId)
|
||||
if (result.isLeft()) {
|
||||
Timber.e("Error producing initial data: ${result.leftOrNull()?.message}")
|
||||
|
|
@ -135,7 +160,7 @@ internal class TangemPayOnboardingModel @Inject constructor(
|
|||
router.replaceAll(
|
||||
AppRoute.Wallet,
|
||||
AppRoute.Kyc(
|
||||
userWalletId = tangemPayWalletsManager.getDefaultWalletForTangemPayBlocking().walletId,
|
||||
userWalletId = getUserWalletForPay(params.userWalletId),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
package com.tangem.feature.wallet.child.wallet.model
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.model.MainScreenCustomerInfo
|
||||
import com.tangem.domain.pay.model.TangemPayCustomerInfoError
|
||||
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
internal class TangemPayMainInfoManager @Inject constructor(
|
||||
private val tangemPayMainScreenCustomerInfoUseCase: TangemPayMainScreenCustomerInfoUseCase,
|
||||
) {
|
||||
|
||||
val mainScreenCustomerInfo:
|
||||
StateFlow<Pair<UserWalletId, Either<TangemPayCustomerInfoError, MainScreenCustomerInfo>>?>
|
||||
field = MutableStateFlow(null)
|
||||
|
||||
suspend fun refreshTangemPayInfo(userWalletId: UserWalletId) {
|
||||
val info = tangemPayMainScreenCustomerInfoUseCase(userWalletId)
|
||||
mainScreenCustomerInfo.value = Pair(userWalletId, info)
|
||||
}
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@ import com.tangem.domain.nft.ObserveAndClearNFTCacheIfNeedUseCase
|
|||
import com.tangem.domain.notifications.GetIsHuaweiDeviceWithoutGoogleServicesUseCase
|
||||
import com.tangem.domain.notifications.repository.NotificationsRepository
|
||||
import com.tangem.domain.pay.repository.OnboardingRepository
|
||||
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
|
||||
import com.tangem.domain.settings.*
|
||||
import com.tangem.domain.tokens.RefreshMultiCurrencyWalletQuotesUseCase
|
||||
import com.tangem.domain.wallets.usecase.*
|
||||
|
|
@ -96,7 +97,7 @@ internal class WalletModel @Inject constructor(
|
|||
private val tangemPayOnboardingRepository: OnboardingRepository,
|
||||
private val yieldSupplyFeatureToggles: YieldSupplyFeatureToggles,
|
||||
private val accountsFeatureToggles: AccountsFeatureToggles,
|
||||
private val tangemPayMainInfoManager: TangemPayMainInfoManager,
|
||||
private val tangemPayMainScreenCustomerInfoUseCase: TangemPayMainScreenCustomerInfoUseCase,
|
||||
private val trackingContextProxy: TrackingContextProxy,
|
||||
val screenLifecycleProvider: ScreenLifecycleProvider,
|
||||
val innerWalletRouter: InnerWalletRouter,
|
||||
|
|
@ -375,7 +376,10 @@ internal class WalletModel @Inject constructor(
|
|||
}.distinctUntilChanged(),
|
||||
transform = ::Pair,
|
||||
).onEach { (inBackground, userWalletId) ->
|
||||
if (inBackground) return@onEach
|
||||
if (inBackground) {
|
||||
updateTangemPayJobHolder.cancel()
|
||||
return@onEach
|
||||
}
|
||||
|
||||
val savedCustomerInfo =
|
||||
tangemPayOnboardingRepository.getSavedCustomerInfo(userWalletId)
|
||||
|
|
@ -386,15 +390,15 @@ internal class WalletModel @Inject constructor(
|
|||
if (isShouldLaunchPeriodicUpdate) {
|
||||
updateTangemPayJobHolder.cancel()
|
||||
modelScope.launch {
|
||||
tangemPayMainInfoManager.refreshTangemPayInfo(userWalletId)
|
||||
tangemPayMainScreenCustomerInfoUseCase.fetch(userWalletId)
|
||||
while (isActive) {
|
||||
delay(TANGEM_PAY_UPDATE_INTERVAL)
|
||||
tangemPayMainInfoManager.refreshTangemPayInfo(userWalletId)
|
||||
tangemPayMainScreenCustomerInfoUseCase.fetch(userWalletId)
|
||||
}
|
||||
}.saveIn(updateTangemPayJobHolder)
|
||||
} else {
|
||||
// Don't refresh customer info periodically if the card was already issued, only update on swipe to refresh
|
||||
tangemPayMainInfoManager.refreshTangemPayInfo(userWalletId)
|
||||
tangemPayMainScreenCustomerInfoUseCase.fetch(userWalletId)
|
||||
}
|
||||
}.launchIn(modelScope)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ import com.tangem.domain.models.wallet.UserWalletId
|
|||
import com.tangem.domain.models.wallet.requireColdWallet
|
||||
import com.tangem.domain.pay.repository.OnboardingRepository
|
||||
import com.tangem.domain.pay.usecase.ProduceTangemPayInitialDataUseCase
|
||||
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.TangemPayMainInfoManager
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.features.tangempay.TangemPayFeatureToggles
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -43,7 +43,7 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
|
|||
private val getWalletMetainfoUseCase: GetWalletMetaInfoUseCase,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||
private val tangemPayInfoManager: TangemPayMainInfoManager,
|
||||
private val tangemPayMainScreenCustomerInfoUseCase: TangemPayMainScreenCustomerInfoUseCase,
|
||||
private val uiMessageSender: UiMessageSender,
|
||||
) : BaseWalletClickIntents(), TangemPayIntents {
|
||||
|
||||
|
|
@ -54,13 +54,13 @@ internal class TangemPayClickIntentsImplementor @Inject constructor(
|
|||
) {
|
||||
return
|
||||
}
|
||||
tangemPayInfoManager.refreshTangemPayInfo(userWalletId)
|
||||
tangemPayMainScreenCustomerInfoUseCase.fetch(userWalletId)
|
||||
}
|
||||
|
||||
override fun onRefreshPayToken(userWalletId: UserWalletId) {
|
||||
modelScope.launch {
|
||||
produceInitialDataTangemPay.invoke(userWalletId)
|
||||
tangemPayInfoManager.refreshTangemPayInfo(userWalletId)
|
||||
tangemPayMainScreenCustomerInfoUseCase.fetch(userWalletId)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ package com.tangem.feature.wallet.child.wallet.model.intents
|
|||
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
|
||||
import com.tangem.common.ui.tokens.TokenItemStateConverter.ApySource
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.staking.StakingBalance
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
|
|
@ -50,7 +50,12 @@ internal interface WalletContentClickIntents {
|
|||
|
||||
fun onTokenItemLongClick(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
|
||||
fun onApyLabelClick(userWalletId: UserWalletId, currencyStatus: CryptoCurrencyStatus, apy: String)
|
||||
fun onApyLabelClick(
|
||||
userWalletId: UserWalletId,
|
||||
currencyStatus: CryptoCurrencyStatus,
|
||||
apySource: ApySource,
|
||||
apy: String,
|
||||
)
|
||||
|
||||
fun onAccountExpandClick(account: Account)
|
||||
|
||||
|
|
@ -162,11 +167,17 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onApyLabelClick(userWalletId: UserWalletId, currencyStatus: CryptoCurrencyStatus, apy: String) {
|
||||
val navigationAction = if (currencyStatus.currency is CryptoCurrency.Token) {
|
||||
NavigationAction.YieldSupply(currencyStatus.value.yieldSupplyStatus?.isActive == true)
|
||||
} else {
|
||||
NavigationAction.Staking
|
||||
override fun onApyLabelClick(
|
||||
userWalletId: UserWalletId,
|
||||
currencyStatus: CryptoCurrencyStatus,
|
||||
apySource: ApySource,
|
||||
apy: String,
|
||||
) {
|
||||
val navigationAction = when (apySource) {
|
||||
ApySource.STAKING -> NavigationAction.Staking
|
||||
ApySource.YIELD_SUPPLY -> {
|
||||
NavigationAction.YieldSupply(currencyStatus.value.yieldSupplyStatus?.isActive == true)
|
||||
}
|
||||
}
|
||||
|
||||
sendApyLabelClickAnalytics(navigationAction, currencyStatus)
|
||||
|
|
|
|||
|
|
@ -111,8 +111,14 @@ internal class DefaultWalletRouter @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
override fun openTangemPayOnboarding() {
|
||||
router.push(AppRoute.TangemPayOnboarding(AppRoute.TangemPayOnboarding.Mode.ContinueOnboarding))
|
||||
override fun openTangemPayOnboarding(userWalletId: UserWalletId) {
|
||||
router.push(
|
||||
AppRoute.TangemPayOnboarding(
|
||||
AppRoute.TangemPayOnboarding.Mode.ContinueOnboarding(
|
||||
userWalletId = userWalletId,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override fun openTangemPayDetails(userWalletId: UserWalletId, config: TangemPayDetailsConfig) {
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ internal interface InnerWalletRouter {
|
|||
|
||||
fun openTokenReceiveBottomSheet(tokenReceiveConfig: TokenReceiveConfig)
|
||||
|
||||
fun openTangemPayOnboarding()
|
||||
fun openTangemPayOnboarding(userWalletId: UserWalletId)
|
||||
|
||||
fun openTangemPayDetails(userWalletId: UserWalletId, config: TangemPayDetailsConfig)
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ internal sealed class TangemPayState {
|
|||
|
||||
object Empty : TangemPayState()
|
||||
|
||||
data object Loading : TangemPayState()
|
||||
|
||||
data class Progress(
|
||||
val title: TextReference,
|
||||
val description: TextReference,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.TangemPayState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
|
||||
|
||||
internal class TangemPayLoadingStateTransformer(userWalletId: UserWalletId) : WalletStateTransformer(userWalletId) {
|
||||
override fun transform(prevState: WalletState): WalletState {
|
||||
return if (prevState is WalletState.MultiCurrency.Content) {
|
||||
prevState.copy(tangemPayState = TangemPayState.Loading)
|
||||
} else {
|
||||
prevState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -49,9 +49,15 @@ internal class TokenListStateConverter(
|
|||
clickIntents.onTokenItemLongClick(selectedWallet.walletId, currencyStatus)
|
||||
}
|
||||
|
||||
private val onApyLabelClick: (currencyStatus: CryptoCurrencyStatus, apy: String) -> Unit =
|
||||
{ currencyStatus, apy ->
|
||||
clickIntents.onApyLabelClick(selectedWallet.walletId, currencyStatus, apy)
|
||||
private val onApyLabelClick:
|
||||
(currencyStatus: CryptoCurrencyStatus, apySource: TokenItemStateConverter.ApySource, apy: String) -> Unit =
|
||||
{ currencyStatus, apySource, apy ->
|
||||
clickIntents.onApyLabelClick(
|
||||
userWalletId = selectedWallet.walletId,
|
||||
currencyStatus = currencyStatus,
|
||||
apySource = apySource,
|
||||
apy = apy,
|
||||
)
|
||||
}
|
||||
|
||||
private fun tokenStatusConverter(accountId: AccountId? = null) = TokenItemStateConverter(
|
||||
|
|
@ -60,7 +66,7 @@ internal class TokenListStateConverter(
|
|||
stakingApyMap = stakingApyMap,
|
||||
onItemClick = { _, status -> onTokenClick(accountId, status) },
|
||||
onItemLongClick = { _, status -> onTokenLongClick(accountId, status) },
|
||||
onApyLabelClick = { status, apy -> onApyLabelClick(status, apy) },
|
||||
onApyLabelClick = { status, apySource, apy -> onApyLabelClick(status, apySource, apy) },
|
||||
)
|
||||
|
||||
override fun convert(value: WalletTokensListState): WalletTokensListState {
|
||||
|
|
|
|||
|
|
@ -2,24 +2,24 @@ package com.tangem.feature.wallet.presentation.wallet.subscribers
|
|||
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.pay.model.MainCustomerInfoContentState
|
||||
import com.tangem.domain.pay.model.MainScreenCustomerInfo
|
||||
import com.tangem.domain.pay.model.TangemPayCustomerInfoError
|
||||
import com.tangem.domain.pay.repository.TangemPayCardDetailsRepository
|
||||
import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
|
||||
import com.tangem.domain.visa.model.TangemPayCardFrozenState
|
||||
import com.tangem.feature.wallet.child.wallet.model.TangemPayMainInfoManager
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletTangemPayAnalyticsEventSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateController
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.TangemPayHiddenStateTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.TangemPayRefreshNeededStateTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.TangemPayUnavailableStateTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.TangemPayUpdateInfoStateTransformer
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.transformers.*
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import timber.log.Timber
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
|
|
@ -29,7 +29,7 @@ internal class TangemPayMainSubscriber @AssistedInject constructor(
|
|||
private val clickIntents: WalletClickIntents,
|
||||
private val innerWalletRouter: InnerWalletRouter,
|
||||
private val cardDetailsRepository: TangemPayCardDetailsRepository,
|
||||
private val tangemPayMainInfoManager: TangemPayMainInfoManager,
|
||||
private val tangemPayMainScreenCustomerInfoUseCase: TangemPayMainScreenCustomerInfoUseCase,
|
||||
private val analytics: WalletTangemPayAnalyticsEventSender,
|
||||
) : WalletSubscriber() {
|
||||
|
||||
|
|
@ -38,11 +38,10 @@ internal class TangemPayMainSubscriber @AssistedInject constructor(
|
|||
}
|
||||
|
||||
private fun subscribeOnTangemPayInfoUpdates(): Flow<*> {
|
||||
return tangemPayMainInfoManager.mainScreenCustomerInfo
|
||||
.filterNotNull()
|
||||
.filter { it.first == userWallet.walletId }
|
||||
return tangemPayMainScreenCustomerInfoUseCase(userWalletId = userWallet.walletId)
|
||||
.distinctUntilChanged()
|
||||
.onEach { (userWalletId, mainInfoData) ->
|
||||
.onEach { mainInfoData ->
|
||||
val userWalletId = userWallet.walletId
|
||||
mainInfoData.onLeft { tangemPayError ->
|
||||
when (tangemPayError) {
|
||||
TangemPayCustomerInfoError.RefreshNeededError -> {
|
||||
|
|
@ -66,13 +65,23 @@ internal class TangemPayMainSubscriber @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
}
|
||||
}.onRight { data ->
|
||||
updateTangemPay(data, userWalletId)
|
||||
analytics.send(customerInfo = data)
|
||||
}
|
||||
}.onRight { contentState -> handleContentState(state = contentState) }
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun handleContentState(state: MainCustomerInfoContentState) {
|
||||
val userWalletId = userWallet.walletId
|
||||
when (state) {
|
||||
MainCustomerInfoContentState.Loading -> stateController.update(
|
||||
transformer = TangemPayLoadingStateTransformer(userWalletId),
|
||||
)
|
||||
is MainCustomerInfoContentState.Content -> {
|
||||
updateTangemPay(data = state.info, userWalletId = userWalletId)
|
||||
analytics.send(customerInfo = state.info)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun updateTangemPay(data: MainScreenCustomerInfo, userWalletId: UserWalletId) {
|
||||
val cardFrozenState =
|
||||
data.info.productInstance?.cardId?.let { cardDetailsRepository.cardFrozenStateSync(it) }
|
||||
|
|
@ -82,7 +91,7 @@ internal class TangemPayMainSubscriber @AssistedInject constructor(
|
|||
userWalletId = userWalletId,
|
||||
value = data,
|
||||
cardFrozenState = cardFrozenState,
|
||||
onClickKyc = innerWalletRouter::openTangemPayOnboarding,
|
||||
onClickKyc = { innerWalletRouter.openTangemPayOnboarding(userWalletId) },
|
||||
onIssuingCard = clickIntents::onIssuingCardClicked,
|
||||
onIssuingFailed = clickIntents::onIssuingFailedClicked,
|
||||
openDetails = { config ->
|
||||
|
|
|
|||
|
|
@ -224,9 +224,9 @@ private fun WalletContent(
|
|||
contentType = selectedWallet.tangemPayState::class.java,
|
||||
) {
|
||||
TangemPayMainScreenBlock(
|
||||
selectedWallet.tangemPayState,
|
||||
state = selectedWallet.tangemPayState,
|
||||
isBalanceHidden = state.isHidingMode,
|
||||
itemModifier,
|
||||
modifier = itemModifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.ui.components.visa
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.components.CircleShimmer
|
||||
import com.tangem.core.ui.components.RectangleShimmer
|
||||
import com.tangem.core.ui.components.SpacerWMax
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Composable
|
||||
internal fun TangemPayLoadingScreenBlock(modifier: Modifier = Modifier) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.background(color = TangemTheme.colors.background.primary, shape = TangemTheme.shapes.roundedCornersXMedium)
|
||||
.padding(horizontal = 12.dp, vertical = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
CircleShimmer(modifier = Modifier.size(36.dp))
|
||||
Column(
|
||||
modifier = Modifier.padding(start = 12.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
RectangleShimmer(modifier = Modifier.padding(vertical = 4.dp).sizeIn(minWidth = 70.dp, minHeight = 12.dp))
|
||||
RectangleShimmer(modifier = Modifier.padding(vertical = 2.dp).sizeIn(minWidth = 52.dp, minHeight = 12.dp))
|
||||
}
|
||||
SpacerWMax()
|
||||
Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
|
||||
RectangleShimmer(modifier = Modifier.padding(vertical = 4.dp).sizeIn(minWidth = 40.dp, minHeight = 12.dp))
|
||||
RectangleShimmer(modifier = Modifier.padding(vertical = 2.dp).sizeIn(minWidth = 40.dp, minHeight = 12.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ internal fun TangemPayMainScreenBlock(state: TangemPayState, isBalanceHidden: Bo
|
|||
is TangemPayState.RefreshNeeded -> TangemPayRefreshBlock(state, modifier)
|
||||
is TangemPayState.TemporaryUnavailable -> TangemPayUnavailableBlock(state, modifier)
|
||||
is TangemPayState.FailedIssue -> TangemPayFailedIssueState(state, modifier)
|
||||
TangemPayState.Loading -> TangemPayLoadingScreenBlock(modifier)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -32,6 +33,8 @@ internal fun TangemPayMainScreenBlock(state: TangemPayState, isBalanceHidden: Bo
|
|||
private fun TangemPayMainScreenBlockPreview() {
|
||||
TangemThemePreview {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8)) {
|
||||
TangemPayMainScreenBlock(state = TangemPayState.Loading, isBalanceHidden = false)
|
||||
|
||||
TangemPayMainScreenBlock(
|
||||
Progress(
|
||||
title = TextReference.Res(R.string.tangempay_kyc_in_progress_notification_title),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue