Updated on 2026-08-14

This commit is contained in:
Tangem 2023-04-13 22:52:21 +09:00
parent 4ea919aed9
commit 6455a837b7
8 changed files with 99 additions and 47 deletions

View file

@ -1,22 +1,15 @@
package com.tangem.tap.domain.model.builders
import com.tangem.common.extensions.toHexString
import com.tangem.common.services.Result
import com.tangem.domain.common.CardDTO
import com.tangem.domain.common.ProductType
import com.tangem.domain.common.SaltPayWorkaround
import com.tangem.domain.common.ScanResponse
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
import com.tangem.domain.common.TwinCardNumber
import com.tangem.domain.common.TwinsHelper
import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.operations.attestation.TangemApi
import com.tangem.tap.domain.model.UserWallet
import com.tangem.tap.features.wallet.redux.Artwork
import com.tangem.tap.domain.userWalletList.GetCardImageUseCase
class UserWalletBuilder(
private val scanResponse: ScanResponse,
private val onlineCardVerifier: OnlineCardVerifier = OnlineCardVerifier(),
private val getCardImageUseCase: GetCardImageUseCase = GetCardImageUseCase(),
) {
private var backupCardsIds: Set<String> = emptySet()
@ -50,7 +43,7 @@ class UserWalletBuilder(
UserWallet(
walletId = it,
name = userWalletName,
artworkUrl = loadArtworkUrl(card.cardId, card.cardPublicKey),
artworkUrl = getCardImageUseCase.invoke(card.cardId, card.cardPublicKey),
cardsInWallet = backupCardsIds.plus(card.cardId),
scanResponse = this,
isMultiCurrency = cardTypesResolver.isMultiwalletAllowed(),
@ -58,37 +51,4 @@ class UserWalletBuilder(
}
}
}
private suspend fun loadArtworkUrl(cardId: String, cardPublicKey: ByteArray): String {
return when (val result = onlineCardVerifier.getCardInfo(cardId, cardPublicKey)) {
is Result.Success -> {
val artworkId = result.data.artwork?.id
if (artworkId.isNullOrEmpty()) {
getFallbackArtworkUrl(cardId)
} else {
getUrlForArtwork(cardId, cardPublicKey.toHexString(), artworkId)
}
}
is Result.Failure -> getFallbackArtworkUrl(cardId)
}
}
private fun getFallbackArtworkUrl(cardId: String): String {
return when {
cardId.startsWith(Artwork.SERGIO_CARD_ID) -> Artwork.SERGIO_CARD_URL
cardId.startsWith(Artwork.MARTA_CARD_ID) -> Artwork.MARTA_CARD_URL
SaltPayWorkaround.isSaltPayCardId(cardId) -> Artwork.SALT_PAY_URL
else -> when (TwinsHelper.getTwinCardNumber(cardId)) {
TwinCardNumber.First -> Artwork.TWIN_CARD_1
TwinCardNumber.Second -> Artwork.TWIN_CARD_2
else -> Artwork.DEFAULT_IMG_URL
}
}
}
private fun getUrlForArtwork(cardId: String, cardPublicKeyHex: String, artworkId: String): String {
return TangemApi.Companion.BaseUrl.VERIFY.url + TangemApi.ARTWORK +
"?artworkId=$artworkId&CID=$cardId&publicKey=$cardPublicKeyHex"
}
}

View file

@ -0,0 +1,59 @@
package com.tangem.tap.domain.userWalletList
import com.tangem.common.extensions.toHexString
import com.tangem.common.services.Result
import com.tangem.domain.common.SaltPayWorkaround
import com.tangem.domain.common.TwinCardNumber
import com.tangem.domain.common.TwinsHelper
import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.operations.attestation.TangemApi
import com.tangem.tap.features.wallet.redux.Artwork
/**
* Use case for getting card image url
*
* @property verifier REST API
*
[REDACTED_AUTHOR]
*/
class GetCardImageUseCase(private val verifier: OnlineCardVerifier = OnlineCardVerifier()) {
/**
* Get card image url
*
* @param cardId card id
* @param cardPublicKey card public key
*/
suspend operator fun invoke(cardId: String, cardPublicKey: ByteArray): String {
return when (val result = verifier.getCardInfo(cardId, cardPublicKey)) {
is Result.Success -> {
val artworkId = result.data.artwork?.id
if (artworkId.isNullOrEmpty()) {
getFallbackArtworkUrl(cardId)
} else {
getUrlForArtwork(cardId, cardPublicKey.toHexString(), artworkId)
}
}
is Result.Failure -> getFallbackArtworkUrl(cardId)
}
}
private fun getFallbackArtworkUrl(cardId: String): String {
return when {
cardId.startsWith(Artwork.SERGIO_CARD_ID) -> Artwork.SERGIO_CARD_URL
cardId.startsWith(Artwork.MARTA_CARD_ID) -> Artwork.MARTA_CARD_URL
SaltPayWorkaround.isSaltPayCardId(cardId) -> Artwork.SALT_PAY_URL
else -> when (TwinsHelper.getTwinCardNumber(cardId)) {
TwinCardNumber.First -> Artwork.TWIN_CARD_1
TwinCardNumber.Second -> Artwork.TWIN_CARD_2
else -> Artwork.DEFAULT_IMG_URL
}
}
}
private fun getUrlForArtwork(cardId: String, cardPublicKeyHex: String, artworkId: String): String {
return TangemApi.Companion.BaseUrl.VERIFY.url + TangemApi.ARTWORK +
"?artworkId=$artworkId&CID=$cardId&publicKey=$cardPublicKeyHex"
}
}

View file

@ -55,7 +55,10 @@ interface UserWalletsListManager {
* @return [CompletionResult.Success] with updated [UserWallet]
* or [CompletionResult.Failure] with [NoSuchElementException] if [UserWallet] with [userWalletId] not found
* */
suspend fun update(userWalletId: UserWalletId, update: (UserWallet) -> UserWallet): CompletionResult<UserWallet>
suspend fun update(
userWalletId: UserWalletId,
update: suspend (UserWallet) -> UserWallet,
): CompletionResult<UserWallet>
/**
* Delete saved [UserWallet]s with provided [UserWalletId]s

View file

@ -120,7 +120,7 @@ internal class BiometricUserWalletsListManager(
override suspend fun update(
userWalletId: UserWalletId,
update: (UserWallet) -> UserWallet,
update: suspend (UserWallet) -> UserWallet,
): CompletionResult<UserWallet> {
return get(userWalletId)
.map { storedUserWallet ->

View file

@ -58,7 +58,7 @@ internal class RuntimeUserWalletsListManager : UserWalletsListManager {
override suspend fun update(
userWalletId: UserWalletId,
update: (UserWallet) -> UserWallet,
update: suspend (UserWallet) -> UserWallet,
): CompletionResult<UserWallet> = catching {
val wallet = state.value.userWallet
?.takeIf { it.walletId == userWalletId }

View file

@ -4,6 +4,7 @@ import android.content.Context
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.address.AddressType
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.domain.common.util.UserWalletId
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.redux.NotificationAction
import com.tangem.tap.domain.TapError
@ -130,4 +131,8 @@ sealed class WalletAction : Action {
data class WalletStoresChanged(val walletStores: List<WalletStoreModel>) : WalletAction()
data class TotalFiatBalanceChanged(val balance: TotalFiatBalance) : WalletAction()
data class UpdateUserWalletArtwork(val walletId: UserWalletId) : WalletAction()
data class SetArtworkUrl(val url: String) : WalletAction()
}

View file

@ -5,6 +5,7 @@ import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.address.AddressType
import com.tangem.common.CompletionResult
import com.tangem.common.doOnSuccess
import com.tangem.common.extensions.guard
import com.tangem.core.analytics.Analytics
import com.tangem.datasource.connection.NetworkConnectionManager
@ -25,6 +26,7 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.model.WalletDataModel
import com.tangem.tap.domain.model.WalletStoreModel
import com.tangem.tap.domain.userWalletList.GetCardImageUseCase
import com.tangem.tap.domain.userWalletList.lockIfLockable
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.home.redux.HomeAction
@ -133,6 +135,9 @@ class WalletMiddleware {
Timber.e("Unable to load/refresh wallets data, no user wallet selected")
return
}
store.dispatch(WalletAction.UpdateUserWalletArtwork(selectedWallet.walletId))
scope.launch {
globalState.tapWalletManager.loadData(
userWallet = selectedWallet,
@ -205,6 +210,23 @@ class WalletMiddleware {
is WalletAction.ChangeSelectedAddress -> {
changeSelectedWalletAddress(action.type, walletState)
}
is WalletAction.UpdateUserWalletArtwork -> {
scope.launch {
userWalletsListManager
.update(
userWalletId = action.walletId,
update = { userWallet ->
userWallet.copy(
artworkUrl = GetCardImageUseCase().invoke(
cardId = userWallet.cardId,
cardPublicKey = userWallet.scanResponse.card.cardPublicKey,
),
)
},
)
.doOnSuccess { store.dispatch(WalletAction.SetArtworkUrl(it.artworkUrl)) }
}
}
}
}

View file

@ -113,8 +113,11 @@ private fun internalReduce(action: Action, state: AppState, appStateHolder: AppS
)
}
is WalletAction.UpdateCanSaveUserWallets -> {
newState = newState.copy(canSaveUserWallets = action.canSaveUserWallets)
}
is WalletAction.SetArtworkUrl -> {
newState = newState.copy(
canSaveUserWallets = action.canSaveUserWallets,
cardImage = Artwork(artworkId = action.url, artwork = newState.cardImage?.artwork),
)
}
else -> Unit