Updated on 2026-08-14

This commit is contained in:
Tangem 2022-12-09 11:58:47 +04:00
commit 024e67e7b7
43 changed files with 267 additions and 179 deletions

@ -1 +1 @@
Subproject commit cf6ea50867477f97655da9da47ff94987e8f3354
Subproject commit a1658496e777b611fc990ef2bc1a1a1fd48bc1e6

View file

@ -132,7 +132,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
}
override fun onDestroy() {
store.dispatch(NavigationAction.ActivityDestroyed)
store.dispatch(NavigationAction.ActivityDestroyed(WeakReference(this)))
super.onDestroy()
}

View file

@ -153,7 +153,7 @@ class DialogManager : StoreSubscriber<GlobalState> {
primaryButtonAction = state.dialog.onOk,
)
is WalletDialog.RussianCardholdersWarningDialog ->
RussianCardholdersWarningBottomSheetDialog(context)
RussianCardholdersWarningBottomSheetDialog(context, state.dialog.topUpUrl)
else -> null
}
dialog?.show()

View file

@ -165,6 +165,7 @@ private fun handleAction(action: Action, appState: () -> AppState?, dispatch: Di
}
is GlobalAction.ScanCard -> {
scope.launch {
tangemSdkManager.changeDisplayedCardIdNumbersCount(null)
val result = tangemSdkManager.scanProduct(
store.state.globalState.analyticsHandler,
userTokensRepository,

View file

@ -1,7 +1,7 @@
package com.tangem.tap.common.redux.navigation
import android.net.Uri
import androidx.fragment.app.FragmentActivity
import androidx.appcompat.app.AppCompatActivity
import org.rekotlin.Action
import java.lang.ref.WeakReference
@ -9,7 +9,7 @@ sealed class NavigationAction : Action {
data class NavigateTo(
val screen: AppScreen,
val fragmentShareTransition: FragmentShareTransition? = null,
val addToBackstack: Boolean = true
val addToBackstack: Boolean = true,
) : NavigationAction()
data class PopBackTo(val screen: AppScreen? = null) : NavigationAction()
@ -20,6 +20,6 @@ sealed class NavigationAction : Action {
data class Share(val data: String) : NavigationAction()
data class ActivityCreated(val activity: WeakReference<FragmentActivity>) : NavigationAction()
object ActivityDestroyed : NavigationAction()
data class ActivityCreated(val activity: WeakReference<AppCompatActivity>) : NavigationAction()
data class ActivityDestroyed(val activity: WeakReference<AppCompatActivity>) : NavigationAction()
}

View file

@ -25,7 +25,14 @@ private fun internalReduce(action: Action, state: AppState): NavigationState {
state.navigationState.copy(backStack = navState.backStack.subList(0, index))
}
is NavigationAction.ActivityCreated -> navState.copy(activity = navigationAction.activity)
is NavigationAction.ActivityDestroyed -> navState.copy(activity = null)
is NavigationAction.ActivityDestroyed -> {
when {
// Destroy the activity if it invoked for the same activity. Prevents overwriting to null if there is a
// new scan from the background [REDACTED_TASK_KEY]
navState.activity?.get() == navigationAction.activity.get() -> navState.copy(activity = null)
else -> navState
}
}
else -> navState
}
}

View file

@ -1,12 +1,12 @@
package com.tangem.tap.common.redux.navigation
import androidx.fragment.app.FragmentActivity
import androidx.appcompat.app.AppCompatActivity
import org.rekotlin.StateType
import java.lang.ref.WeakReference
data class NavigationState(
val backStack: List<AppScreen> = listOf(AppScreen.Home),
val activity: WeakReference<FragmentActivity>? = null
val activity: WeakReference<AppCompatActivity>? = null,
) : StateType
enum class AppScreen {

View file

@ -20,21 +20,18 @@ class RatesRepository {
val throttledResult = coinsList.filter { throttler.isStillThrottled(it) }.map {
Pair(it, throttler.geValue(it))
}
if (throttledResult.isNotEmpty()) {
return handleFiatRatesResult(throttledResult.toMap())
}
val currenciesToUpdate = coinsList.filter { !throttler.isStillThrottled(it) }
val coinIds = currenciesToUpdate.mapNotNull { it.coinId }.distinct()
if (coinIds.isEmpty()) return EMPTY_RESULT
if (coinIds.isEmpty()) return handleFiatRatesResult(throttledResult.toMap())
return when (val result = tangemTechService.rates(currencyId, coinIds)) {
is Result.Success -> {
val ratesResultList: Map<String, Result<BigDecimal>> = result.data.rates.mapValues {
Result.Success(it.value.toBigDecimal())
}
val updatedCurrencies = mutableMapOf<Currency, Result<BigDecimal>?>()
currenciesToUpdate.forEach { currency ->
val updatedCurrencies = throttledResult.toMap().toMutableMap()
coinsList.forEach { currency ->
ratesResultList[currency.coinId]?.let {
updatedCurrencies[currency] = it
throttler.updateThrottlingTo(currency)
@ -65,13 +62,6 @@ class RatesRepository {
fun clear() {
throttler.clear()
}
companion object {
val EMPTY_RESULT = Result.Success(Pair(
mutableMapOf<Currency, BigDecimal>(),
mutableMapOf<Currency, Throwable>()
))
}
}
typealias RatesResult = Pair<MutableMap<Currency, BigDecimal>, MutableMap<Currency, Throwable>>

View file

@ -182,8 +182,9 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
return withContext(Dispatchers.Main) { result }
}
fun changeDisplayedCardIdNumbersCount(scanResponse: ScanResponse) {
fun changeDisplayedCardIdNumbersCount(scanResponse: ScanResponse?) {
tangemSdk.config.cardIdDisplayFormat = when {
scanResponse == null -> CardIdDisplayFormat.Full
scanResponse.isTangemTwins() -> CardIdDisplayFormat.LastLuhn(4)
scanResponse.isSaltPay() -> CardIdDisplayFormat.None
else -> CardIdDisplayFormat.Full

View file

@ -4,12 +4,14 @@ import com.tangem.common.card.Card
import com.tangem.common.card.CardWallet
import com.tangem.common.card.EllipticCurve
import com.tangem.common.card.FirmwareVersion
import com.tangem.common.extensions.calculateSha256
import com.tangem.common.extensions.toHexString
import com.tangem.common.services.Result
import com.tangem.domain.common.TapWorkarounds.isSaltPay
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
import com.tangem.domain.common.TapWorkarounds.isTangemNote
import com.tangem.domain.common.TwinCardNumber
import com.tangem.domain.common.extensions.calculateHmacSha256
import com.tangem.domain.common.getTwinCardNumber
import com.tangem.domain.common.isTangemTwin
import com.tangem.operations.attestation.CardVerifyAndGetInfo
@ -88,4 +90,19 @@ fun Card.getArtworkUrl(artworkId: String?): String? {
cardId.startsWith(Artwork.MARTA_CARD_ID) -> Artwork.MARTA_CARD_URL
else -> null
}
}
fun Card.getUserWalletId(): String {
val walletPublicKey = this.wallets.firstOrNull()?.publicKey ?: return ""
return UserWalletId(walletPublicKey).stringValue
}
class UserWalletId(val walletPublicKey: ByteArray) {
val stringValue: String = calculateUserId(walletPublicKey)
private fun calculateUserId(walletPublicKey: ByteArray): String {
val message = "UserWalletID".toByteArray()
val keyHash = walletPublicKey.calculateSha256()
return message.calculateHmacSha256(keyHash).toHexString()
}
}

View file

@ -3,14 +3,12 @@ package com.tangem.tap.domain.tokens
import android.content.Context
import com.tangem.blockchain.common.DerivationStyle
import com.tangem.common.card.Card
import com.tangem.common.extensions.calculateSha256
import com.tangem.common.extensions.toHexString
import com.tangem.common.services.Result
import com.tangem.domain.common.extensions.calculateHmacSha256
import com.tangem.network.api.tangemTech.TangemTechService
import com.tangem.network.api.tangemTech.UserTokensResponse
import com.tangem.tap.common.AndroidFileReader
import com.tangem.tap.domain.NoDataError
import com.tangem.tap.domain.extensions.getUserWalletId
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.wallet.models.Currency
@ -26,7 +24,7 @@ class UserTokensRepository(
private val networkService: UserTokensNetworkService,
) {
suspend fun getUserTokens(card: Card): List<Currency> {
val userId = card.getUserId()
val userId = card.getUserWalletId()
if (DemoHelper.isDemoCardId(card.cardId)) {
return loadTokensOffline(card, userId).ifEmpty { loadDemoCurrencies() }
}
@ -38,7 +36,7 @@ class UserTokensRepository(
return when (val networkResult = networkService.getUserTokens(userId)) {
is Result.Success -> {
val tokens = networkResult.data.tokens.mapNotNull { Currency.fromTokenResponse(it) }
storageService.saveUserTokens(card.getUserId(), tokens.toUserTokensResponse())
storageService.saveUserTokens(card.getUserWalletId(), tokens.toUserTokensResponse())
tokens.distinct()
}
is Result.Failure -> {
@ -49,14 +47,14 @@ class UserTokensRepository(
suspend fun saveUserTokens(card: Card, tokens: List<Currency>) {
val userTokens = tokens.toUserTokensResponse()
networkService.saveUserTokens(card.getUserId(), userTokens)
storageService.saveUserTokens(card.getUserId(), userTokens)
networkService.saveUserTokens(card.getUserWalletId(), userTokens)
storageService.saveUserTokens(card.getUserWalletId(), userTokens)
}
suspend fun removeUserTokens(card: Card) {
val userTokens = emptyList<Currency>().toUserTokensResponse()
networkService.saveUserTokens(card.getUserId(), userTokens)
storageService.saveUserTokens(card.getUserId(), userTokens)
networkService.saveUserTokens(card.getUserWalletId(), userTokens)
storageService.saveUserTokens(card.getUserWalletId(), userTokens)
}
private fun List<Currency>.toUserTokensResponse(): UserTokensResponse {
@ -69,7 +67,7 @@ class UserTokensRepository(
}
suspend fun loadBlockchainsToDerive(card: Card): List<BlockchainNetwork> {
val userId = card.getUserId()
val userId = card.getUserWalletId()
val blockchainNetworks = loadTokensOffline(card, userId).toBlockchainNetworks()
if (DemoHelper.isDemoCardId(card.cardId)) {
@ -113,11 +111,6 @@ class UserTokensRepository(
return storageService.getUserTokens(userId) ?: storageService.getUserTokens(card)
}
private fun Card.getUserId(): String {
val walletPublicKey = this.wallets.firstOrNull()?.publicKey ?: return ""
return UserWalletId(walletPublicKey).stringValue
}
companion object {
const val SORT_DEFAULT_VALUE = "manual"
const val GROUP_DEFAULT_VALUE = "none"
@ -131,16 +124,4 @@ class UserTokensRepository(
return UserTokensRepository(storageService, networkService)
}
}
}
data class UserWalletId(
val walletPublicKey: ByteArray,
) {
val stringValue: String = calculateUserId(walletPublicKey)
private fun calculateUserId(walletPublicKey: ByteArray): String {
val message = "UserWalletID".toByteArray()
val keyHash = walletPublicKey.calculateSha256()
return message.calculateHmacSha256(keyHash).toHexString()
}
}

View file

@ -44,6 +44,8 @@ import kotlin.collections.set
class WalletConnectManager {
private var cardId: String? = null
private val okHttpClient: OkHttpClient by lazy {
OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)
@ -138,6 +140,7 @@ class WalletConnectManager {
fun restoreSessions(scanResponse: ScanResponse) {
val walletPublicKey = scanResponse.card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 }?.publicKey
?: return
if (scanResponse.card.backupStatus?.isActive != true) cardId = scanResponse.card.cardId
val sessions = walletConnectRepository.loadSavedSessions()
// filter sessions for this particular card
.filter { it.wallet.walletPublicKey.contentEquals(walletPublicKey) }
@ -223,10 +226,10 @@ class WalletConnectManager {
}
private fun onSessionClosed(session: WCSession) {
store.state.globalState.analyticsHandler?.logWcEvent(
store.state.globalState.analyticsHandler.logWcEvent(
AnalyticsAnOld.WcAnalyticsEvent.Session(
AnalyticsAnOld.WcSessionEvent.Disconnect, sessions[session]?.peerMeta?.url
)
AnalyticsAnOld.WcSessionEvent.Disconnect, sessions[session]?.peerMeta?.url,
),
)
sessions.remove(session)
walletConnectRepository.removeSession(session)
@ -272,13 +275,13 @@ class WalletConnectManager {
val activeData = sessions[session]
val data = activeData?.transactionData ?: return
scope.launch {
val hash = WalletConnectSdkHelper().completeTransaction(data).guard {
val hash = WalletConnectSdkHelper().completeTransaction(data, cardId).guard {
sessions[data.session.session] = activeData.copy(transactionData = null)
store.dispatchOnMain(
WalletConnectAction.RejectRequest(
data.session.session,
data.id
)
data.id,
),
)
return@launch
}
@ -292,12 +295,12 @@ class WalletConnectManager {
) {
val activeData = sessions[sessionData] ?: return
scope.launch {
val hash = WalletConnectSdkHelper().signBnbTransaction(data, activeData).guard {
val hash = WalletConnectSdkHelper().signBnbTransaction(data, activeData, cardId).guard {
store.dispatchOnMain(
WalletConnectAction.RejectRequest(
sessionData,
id
)
id,
),
)
return@launch
}
@ -330,14 +333,14 @@ class WalletConnectManager {
val activeData = sessions[session]
val data = activeData?.personalSignData ?: return
scope.launch {
val hash = WalletConnectSdkHelper().signPersonalMessage(data.hash, activeData.wallet)
val hash = WalletConnectSdkHelper().signPersonalMessage(data.hash, activeData.wallet, cardId)
.guard {
sessions[data.session.session] = activeData.copy(transactionData = null)
store.dispatchOnMain(
WalletConnectAction.RejectRequest(
data.session.session,
data.id
)
data.id,
),
)
return@launch
}
@ -372,7 +375,7 @@ class WalletConnectManager {
),
)
}
store.state.globalState.analyticsHandler?.logWcEvent(
store.state.globalState.analyticsHandler.logWcEvent(
AnalyticsAnOld.WcAnalyticsEvent.Session(
AnalyticsAnOld.WcSessionEvent.Connect, peer.url,
),
@ -387,10 +390,10 @@ class WalletConnectManager {
}
client.onEthSendTransaction = { id: Long, transaction: WCEthereumTransaction ->
Timber.d("onEthSendTransaction: $transaction")
store.state.globalState.analyticsHandler?.logWcEvent(
store.state.globalState.analyticsHandler.logWcEvent(
AnalyticsAnOld.WcAnalyticsEvent.Action(
AnalyticsAnOld.WcAction.SendTransaction
)
AnalyticsAnOld.WcAction.SendTransaction,
),
)
sessions[client.session]?.toWalletConnectSession()?.let { sessionData ->
store.dispatchOnMain(
@ -405,10 +408,10 @@ class WalletConnectManager {
}
client.onEthSignTransaction = { id: Long, transaction: WCEthereumTransaction ->
Timber.d("onEthSignTransaction: $transaction")
store.state.globalState.analyticsHandler?.logWcEvent(
store.state.globalState.analyticsHandler.logWcEvent(
AnalyticsAnOld.WcAnalyticsEvent.Action(
AnalyticsAnOld.WcAction.SignTransaction
)
AnalyticsAnOld.WcAction.SignTransaction,
),
)
sessions[client.session]?.toWalletConnectSession()?.let { sessionData ->
store.dispatchOnMain(
@ -423,10 +426,10 @@ class WalletConnectManager {
}
client.onEthSign = { id: Long, message: WCEthereumSignMessage ->
Timber.d("onEthSign: $message")
store.state.globalState.analyticsHandler?.logWcEvent(
store.state.globalState.analyticsHandler.logWcEvent(
AnalyticsAnOld.WcAnalyticsEvent.Action(
AnalyticsAnOld.WcAction.PersonalSign
)
AnalyticsAnOld.WcAction.PersonalSign,
),
)
sessions[client.session]?.toWalletConnectSession()?.let { sessionData ->
store.dispatchOnMain(
@ -499,10 +502,10 @@ class WalletConnectManager {
val message = EthSignHelper.tryToParseEthTypedMessage(request)
if (message != null) {
Timber.d("onEthSign_v4: $message")
store.state.globalState.analyticsHandler?.logWcEvent(
store.state.globalState.analyticsHandler.logWcEvent(
AnalyticsAnOld.WcAnalyticsEvent.Action(
AnalyticsAnOld.WcAction.PersonalSign
)
AnalyticsAnOld.WcAction.PersonalSign,
),
)
sessions[client.session]?.toWalletConnectSession()?.let { sessionData ->
store.dispatchOnMain(

View file

@ -126,24 +126,24 @@ class WalletConnectSdkHelper {
)
}
suspend fun completeTransaction(data: WcTransactionData): String? {
suspend fun completeTransaction(data: WcTransactionData, cardId: String?): String? {
return when (data.type) {
WcTransactionType.EthSendTransaction -> sendTransaction(data)
WcTransactionType.EthSignTransaction -> signTransaction(data)
WcTransactionType.EthSendTransaction -> sendTransaction(data, cardId)
WcTransactionType.EthSignTransaction -> signTransaction(data, cardId)
}
}
private suspend fun sendTransaction(data: WcTransactionData): String? {
private suspend fun sendTransaction(data: WcTransactionData, cardId: String?): String? {
val result = (data.walletManager as TransactionSender).send(
transactionData = data.transaction,
signer = CommonSigner(tangemSdk)
signer = CommonSigner(tangemSdk, cardId),
)
return when (result) {
SimpleResult.Success -> {
HEX_PREFIX + data.walletManager.wallet.recentTransactions.last().hash
}
is SimpleResult.Failure -> {
store.state.globalState.analyticsHandler?.handleBlockchainSdkErrorEvent(
store.state.globalState.analyticsHandler.handleBlockchainSdkErrorEvent(
result.error,
AnalyticsAnOld.ActionToLog.WalletConnectTransaction,
)
@ -153,27 +153,27 @@ class WalletConnectSdkHelper {
}
}
private suspend fun signTransaction(data: WcTransactionData): String? {
private suspend fun signTransaction(data: WcTransactionData, cardId: String?): String? {
val dataToSign = EthereumUtils.buildTransactionToSign(
transactionData = data.transaction,
nonce = null,
blockchain = data.walletManager.wallet.blockchain,
gasLimit = null
gasLimit = null,
) ?: return null
val command = SignHashCommand(
hash = dataToSign.hash,
walletPublicKey = data.walletManager.wallet.publicKey.seedKey,
derivationPath = data.walletManager.wallet.publicKey.derivationPath
derivationPath = data.walletManager.wallet.publicKey.derivationPath,
)
val result = tangemSdkManager.runTaskAsync(command, initialMessage = Message())
val result = tangemSdkManager.runTaskAsync(command, initialMessage = Message(), cardId = cardId)
return when (result) {
is CompletionResult.Success -> {
HEX_PREFIX + result.data
}
is CompletionResult.Failure -> {
(result.error as? TangemSdkError)?.let { error ->
store.state.globalState.analyticsHandler?.handleCardSdkErrorEvent(
store.state.globalState.analyticsHandler.handleCardSdkErrorEvent(
error,
AnalyticsAnOld.ActionToLog.WalletConnectSign,
)
@ -184,13 +184,13 @@ class WalletConnectSdkHelper {
}
}
suspend fun signBnbTransaction(data: ByteArray, session: WalletConnectActiveData): String? {
suspend fun signBnbTransaction(data: ByteArray, session: WalletConnectActiveData, cardId: String?): String? {
val command = SignHashCommand(
hash = data,
walletPublicKey = session.wallet.walletPublicKey ?: return null,
derivationPath = session.wallet.derivationPath
derivationPath = session.wallet.derivationPath,
)
val result = tangemSdkManager.runTaskAsync(command, initialMessage = Message())
val result = tangemSdkManager.runTaskAsync(command, initialMessage = Message(), cardId = cardId)
return when (result) {
is CompletionResult.Success -> {
val key = session.wallet.derivedPublicKey?.toDecompressedPublicKey()
@ -202,7 +202,7 @@ class WalletConnectSdkHelper {
}
is CompletionResult.Failure -> {
(result.error as? TangemSdkError)?.let { error ->
store.state.globalState.analyticsHandler?.handleCardSdkErrorEvent(
store.state.globalState.analyticsHandler.handleCardSdkErrorEvent(
error,
AnalyticsAnOld.ActionToLog.WalletConnectTransaction,
)
@ -266,10 +266,10 @@ class WalletConnectSdkHelper {
}.joinToString("")
}
suspend fun signPersonalMessage(hashToSign: ByteArray, wallet: WalletForSession): String? {
suspend fun signPersonalMessage(hashToSign: ByteArray, wallet: WalletForSession, cardId: String?): String? {
val key = wallet.derivedPublicKey ?: wallet.walletPublicKey
val command = SignHashCommand(hashToSign, wallet.walletPublicKey!!, wallet.derivationPath)
return when (val result = tangemSdkManager.runTaskAsync(command)) {
return when (val result = tangemSdkManager.runTaskAsync(command, cardId)) {
is CompletionResult.Success -> {
val hash = result.data.signature
return EthereumUtils.prepareSignedMessageData(
@ -278,7 +278,7 @@ class WalletConnectSdkHelper {
}
is CompletionResult.Failure -> {
(result.error as? TangemSdkError)?.let { error ->
store.state.globalState.analyticsHandler?.handleCardSdkErrorEvent(
store.state.globalState.analyticsHandler.handleCardSdkErrorEvent(
error,
AnalyticsAnOld.ActionToLog.WalletConnectSign,
)

View file

@ -5,12 +5,15 @@ import com.tangem.common.core.TangemSdkError
import com.tangem.domain.common.TapWorkarounds.isTangemTwins
import com.tangem.tap.common.analytics.AnalyticsAnOld
import com.tangem.tap.common.analytics.AnalyticsParamAnOld
import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.extensions.dispatchNotification
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.extensions.getUserWalletId
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
@ -18,6 +21,7 @@ import com.tangem.tap.features.wallet.models.hasSendableAmountsOrPendingTransact
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.tap.tangemSdkManager
import com.tangem.wallet.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@ -78,7 +82,18 @@ class DetailsMiddleware {
when (val result = tangemSdkManager.scanCard()) {
is CompletionResult.Success -> {
val card = result.data
store.dispatchOnMain(DetailsAction.PrepareCardSettingsData(card))
if (card.getUserWalletId() ==
store.state.globalState.scanResponse?.card?.getUserWalletId()
) {
store.dispatchOnMain(DetailsAction.PrepareCardSettingsData(card))
} else {
store.dispatchDialogShow(
AppDialog.SimpleOkDialogRes(
headerId = R.string.common_warning,
messageId = R.string.error_wrong_wallet_tapped,
),
)
}
}
is CompletionResult.Failure -> {
}
@ -110,7 +125,7 @@ class DetailsMiddleware {
}
is CompletionResult.Failure -> {
(result.error as? TangemSdkError)?.let { error ->
store.state.globalState.analyticsHandler?.handleCardSdkErrorEvent(
store.state.globalState.analyticsHandler.handleCardSdkErrorEvent(
error,
AnalyticsAnOld.ActionToLog.PurgeWallet,
card = store.state.detailsState.scanResponse?.card,
@ -154,7 +169,7 @@ class DetailsMiddleware {
}
is CompletionResult.Failure -> {
(result.error as? TangemSdkError)?.let { error ->
store.state.globalState.analyticsHandler?.handleCardSdkErrorEvent(
store.state.globalState.analyticsHandler.handleCardSdkErrorEvent(
error = error,
action = AnalyticsAnOld.ActionToLog.ChangeSecOptions,
params = mapOf(

View file

@ -16,6 +16,7 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.features.home.compose.StoriesScreen
import com.tangem.tap.features.home.redux.HomeAction
import com.tangem.tap.features.home.redux.HomeState
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction
import com.tangem.tap.features.tokens.redux.TokensAction
import com.tangem.tap.store
import org.rekotlin.StoreSubscriber
@ -37,6 +38,8 @@ class HomeFragment : Fragment(), StoreSubscriber<HomeState> {
): View? {
val context = container?.context ?: return null
store.dispatch(BackupAction.CheckForUnfinishedBackup)
composeView = ComposeView(context).apply {
setContent {
AppCompatTheme {

View file

@ -35,7 +35,6 @@ import com.tangem.tap.features.onboarding.OnboardingHelper
import com.tangem.tap.features.onboarding.OnboardingSaltPayHelper
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsStep
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction
import com.tangem.tap.features.onboarding.products.wallet.saltPay.SaltPayExceptionHandler
import com.tangem.tap.features.onboarding.products.wallet.saltPay.redux.OnboardingSaltPayAction
import com.tangem.tap.features.onboarding.products.wallet.saltPay.redux.OnboardingSaltPayState
@ -73,7 +72,6 @@ private fun handleHomeAction(appState: () -> AppState?, action: Action, dispatch
store.dispatch(GlobalAction.RestoreAppCurrency)
store.dispatch(GlobalAction.ExchangeManager.Init)
store.dispatch(GlobalAction.FetchUserCountry)
store.dispatch(BackupAction.CheckForUnfinishedBackup)
}
is HomeAction.ShouldScanCardOnResume -> {
if (action.shouldScanCard) {
@ -107,7 +105,7 @@ private fun handleHomeAction(appState: () -> AppState?, action: Action, dispatch
RUSSIA_COUNTRY_CODE, BELARUS_COUNTRY_CODE -> store.dispatchOpenUrl(BUY_WALLET_URL)
else -> store.dispatch(NavigationAction.NavigateTo(AppScreen.Shop))
}
store.state.globalState.analyticsHandler?.handleAnalyticsEvent(
store.state.globalState.analyticsHandler.handleAnalyticsEvent(
event = AnalyticsEventAnOld.GET_CARD,
params = mapOf(AnalyticsParamAnOld.SOURCE.param to GetCardSourceParamsAnOld.WELCOME.param),
)

View file

@ -6,6 +6,7 @@ import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE
import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.extensions.dispatchErrorNotification
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.dispatchOpenUrl
import com.tangem.tap.common.extensions.getAddressData
import com.tangem.tap.common.extensions.getToUpUrl
@ -20,8 +21,10 @@ import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.extensions.hasWallets
import com.tangem.tap.domain.extensions.makePrimaryWalletManager
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.tap.tangemSdkManager
@ -152,6 +155,10 @@ private fun handleNoteAction(appState: () -> AppState?, action: Action, dispatch
}
is OnboardingNoteAction.TopUp -> {
val topUpUrl = noteState.walletManager?.getToUpUrl() ?: return
if (globalState.userCountryCode == RUSSIA_COUNTRY_CODE) {
store.dispatchOnMain(WalletAction.DialogAction.RussianCardholdersWarningDialog(topUpUrl))
return
}
store.dispatchOpenUrl(topUpUrl)
}
OnboardingNoteAction.Done -> {

View file

@ -20,8 +20,8 @@ import com.tangem.network.api.paymentology.RegistrationResponse
import com.tangem.network.api.paymentology.tryExtractError
import com.tangem.operations.attestation.AttestWalletKeyResponse
import com.tangem.tap.common.extensions.safeUpdate
import com.tangem.tap.domain.extensions.UserWalletId
import com.tangem.tap.domain.getFirstToken
import com.tangem.tap.domain.tokens.UserWalletId
import com.tangem.tap.features.onboarding.products.wallet.saltPay.message.SaltPayActivationError
import java.math.BigDecimal

View file

@ -7,6 +7,7 @@ import com.tangem.tap.features.send.redux.SendScreenAction
import com.tangem.tap.features.send.redux.states.FeeState
import com.tangem.tap.features.send.redux.states.FeeType
import com.tangem.tap.features.send.redux.states.SendState
import com.tangem.tap.features.wallet.redux.ProgressState
/**
[REDACTED_AUTHOR]
@ -36,7 +37,7 @@ class FeeReducer : SendInternalReducer {
private fun handleAction(action: FeeAction, sendState: SendState, state: FeeState): SendState {
val result = when (action) {
is FeeAction.RequestFee -> {
state
state.copy(progressState = ProgressState.Loading)
}
is FeeAction.ChangeLayoutVisibility -> {
fun getVisibility(current: Boolean, proposed: Boolean?): Boolean = proposed ?: current
@ -68,12 +69,15 @@ class FeeReducer : SendInternalReducer {
currentFee = currentFee,
feeIsApproximate = isFeeApproximate(sendState),
)
}
}.copy(
progressState = ProgressState.Done,
)
}
FeeAction.FeeCalculation.ClearResult -> {
state.copy(
feeList = null,
currentFee = null,
progressState = ProgressState.Done,
)
}
}

View file

@ -1,6 +1,7 @@
package com.tangem.tap.features.send.redux.states
import com.tangem.blockchain.common.Amount
import com.tangem.tap.features.wallet.redux.ProgressState
import java.math.BigDecimal
/**
@ -20,6 +21,7 @@ data class FeeState(
val controlsLayoutIsVisible: Boolean = false,
val feeChipGroupIsVisible: Boolean = true,
val includeFeeSwitcherIsEnabled: Boolean = true,
val progressState: ProgressState = ProgressState.Done
) : SendScreenState {
override val stateId: StateId = StateId.FEE

View file

@ -11,6 +11,7 @@ import com.tangem.tap.common.extensions.beginDelayedTransition
import com.tangem.tap.common.extensions.enableError
import com.tangem.tap.common.extensions.getColor
import com.tangem.tap.common.extensions.getString
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.extensions.update
import com.tangem.tap.common.redux.getMessageString
@ -35,6 +36,7 @@ import com.tangem.tap.features.send.ui.SendFragment
import com.tangem.tap.features.send.ui.dialogs.RequestFeeErrorDialog
import com.tangem.tap.features.send.ui.dialogs.SendTransactionFailsDialog
import com.tangem.tap.features.send.ui.dialogs.TezosWarningDialog
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.features.wallet.redux.WalletState.Companion.ROUGH_SIGN
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN
import com.tangem.tap.features.wallet.ui.adapters.WarningMessagesAdapter
@ -62,7 +64,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
StateId.TRANSACTION_EXTRAS -> handleTransactionExtrasState(fg, state.transactionExtrasState)
StateId.AMOUNT -> handleAmountState(fg, state.amountState)
StateId.FEE -> handleFeeState(fg, state.feeState)
StateId.RECEIPT -> handleReceiptState(fg, state.receiptState)
StateId.RECEIPT -> handleReceiptState(fg, state.receiptState, state.feeState.progressState)
}
}
}
@ -263,7 +265,11 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
if (chipGroup.checkedChipId != chipId && chipId != View.NO_ID) chipGroup.check(chipId)
}
private fun handleReceiptState(fg: SendFragment, state: ReceiptState) = with(fg.binding.clReceiptContainer) {
private fun handleReceiptState(
fg: SendFragment,
state: ReceiptState,
feeProgressState: ProgressState,
) = with(fg.binding.clReceiptContainer) {
val mainLayout = clReceiptContainer as ViewGroup
val totalLayout = llTotalContainer.llTotal as ViewGroup
val totalTokenLayout = llTotalContainer.flTotalTokenCrypto as ViewGroup
@ -274,11 +280,22 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
return if (value == UNKNOWN_AMOUNT_SIGN) value else "$ROUGH_SIGN $value"
}
when (feeProgressState) {
ProgressState.Loading -> {
tvReceiptFeeValue.hide()
pbReceiptFee.show()
}
ProgressState.Done -> {
pbReceiptFee.hide()
tvReceiptFeeValue.show()
}
}
when (state.visibleTypeOfReceipt) {
ReceiptLayoutType.FIAT -> {
val receipt = state.fiat ?: return
llTotalContainer.tvTotalValue
totalLayout.show(true)
totalTokenLayout.show(false)
tvReceiptAmountValue.update("${receipt.amountFiat} ${receipt.symbols.fiat}")
@ -290,7 +307,6 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
receipt.willSentCrypto, receipt.symbols.crypto,
)
llTotalContainer.tvWillBeSentValue.update(willSent)
}
ReceiptLayoutType.CRYPTO -> {
val receipt = state.crypto ?: return

View file

@ -10,7 +10,6 @@ import com.tangem.common.extensions.toMapKey
import com.tangem.common.hdWallet.DerivationPath
import com.tangem.common.services.Result
import com.tangem.domain.DomainWrapped
import com.tangem.domain.common.KeyWalletPublicKey
import com.tangem.domain.common.ScanResponse
import com.tangem.domain.common.TapWorkarounds.derivationStyle
import com.tangem.domain.common.TapWorkarounds.isTestCard
@ -205,19 +204,17 @@ class TokensMiddleware {
when (result) {
is CompletionResult.Success -> {
val newDerivedKeys = result.data.entries
val updatedDerivedKeys =
mutableMapOf<KeyWalletPublicKey, ExtendedPublicKeysMap>()
val oldDerivedKeys = scanResponse.derivedKeys
newDerivedKeys.forEach { entry ->
val derivationData = derivationDataList.find {
it.mapKeyOfWalletPublicKey == entry.key
} ?: return@forEach
updatedDerivedKeys[entry.key] =
ExtendedPublicKeysMap(derivationData.alreadyDerivedKeys + entry.value)
val walletKeys = (newDerivedKeys.keys + oldDerivedKeys.keys).toSet()
val updatedDerivedKeys = walletKeys.associateWith { walletKey ->
val oldDerivations = ExtendedPublicKeysMap(oldDerivedKeys[walletKey] ?: emptyMap())
val newDerivations = newDerivedKeys[walletKey] ?: ExtendedPublicKeysMap(emptyMap())
ExtendedPublicKeysMap(oldDerivations + newDerivations)
}
val updatedScanResponse = scanResponse.copy(
derivedKeys = updatedDerivedKeys
derivedKeys = updatedDerivedKeys,
)
store.dispatchOnMain(GlobalAction.SaveScanNoteResponse(updatedScanResponse))
delay(DELAY_SDK_DIALOG_CLOSE)
@ -318,14 +315,7 @@ class TokensMiddleware {
}
private fun removeCurrenciesIfNeeded(currencies: List<Currency>) {
if (currencies.isNotEmpty()) {
currencies.forEach { currency ->
store.dispatch(WalletAction.MultiWallet.RemoveWallet(
currency = currency,
fromScreen = AppScreen.AddTokens
))
}
}
if (currencies.isNotEmpty()) store.dispatch(WalletAction.MultiWallet.RemoveWallets(currencies))
}
private fun isNeedToDerive(scanResponse: ScanResponse, currency: Currency): Boolean {

View file

@ -11,7 +11,6 @@ import com.tangem.common.card.Card
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.redux.ErrorAction
import com.tangem.tap.common.redux.NotificationAction
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
@ -76,16 +75,14 @@ sealed class WalletAction : Action {
data class TokenLoaded(
val amount: Amount,
val token: Token,
val blockchain: BlockchainNetwork
val blockchain: BlockchainNetwork,
) : MultiWallet()
data class SelectWallet(val walletData: WalletData?) : MultiWallet()
data class TryToRemoveWallet(val currency: Currency) : MultiWallet()
data class RemoveWallet(
val currency: Currency,
val fromScreen: AppScreen,
) : MultiWallet()
data class RemoveWallet(val currency: Currency) : MultiWallet()
data class RemoveWallets(val currencies: List<Currency>) : MultiWallet()
data class SetPrimaryBlockchain(val blockchain: Blockchain) : MultiWallet()
data class SetPrimaryToken(val token: Token) : MultiWallet()
@ -157,7 +154,7 @@ sealed class WalletAction : Action {
object SignedHashesMultiWalletDialog : DialogAction()
object ChooseTradeActionDialog : DialogAction()
data class ChooseCurrency(val amounts: List<Amount>?) : DialogAction()
object RussianCardholdersWarningDialog : DialogAction()
data class RussianCardholdersWarningDialog(val topUpUrl: String? = null) : DialogAction()
object Hide : DialogAction()
}

View file

@ -109,12 +109,7 @@ class MultiWalletMiddleware {
WalletDialog.RemoveWalletDialog(
currencyTitle = currency.currencyName,
onOk = {
store.dispatch(
WalletAction.MultiWallet.RemoveWallet(
currency = currency,
fromScreen = AppScreen.WalletDetails,
),
)
store.dispatch(WalletAction.MultiWallet.RemoveWallet(currency))
store.dispatch(NavigationAction.PopBackTo())
},
),
@ -135,10 +130,17 @@ class MultiWalletMiddleware {
.filter { it.blockchain == currency.blockchain && it.derivationPath == currency.derivationPath }
}
scope.launch { userTokensRepository.saveUserTokens(card, currencies) }
if (action.fromScreen == AppScreen.AddTokens) {
store.dispatch(WalletAction.MultiWallet.SelectWallet(null))
}
is WalletAction.MultiWallet.RemoveWallets -> {
val card = globalState.scanResponse?.card.guard {
store.dispatchErrorNotification(TapError.UnsupportedState("card is NULL"))
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
return
}
var currencies = walletState?.currencies ?: emptyList()
currencies = currencies.filterNot { action.currencies.contains(it) }
scope.launch { userTokensRepository.saveUserTokens(card, currencies) }
store.dispatch(WalletAction.MultiWallet.SelectWallet(null))
}
is WalletAction.MultiWallet.ShowWalletBackupWarning -> Unit
is WalletAction.MultiWallet.BackupWallet -> {

View file

@ -37,7 +37,7 @@ class TradeCryptoMiddleware {
action: WalletAction.TradeCryptoAction.Buy,
) {
if (action.checkUserLocation && state()?.globalState?.userCountryCode == RUSSIA_COUNTRY_CODE) {
store.dispatchOnMain(WalletAction.DialogAction.RussianCardholdersWarningDialog)
store.dispatchOnMain(WalletAction.DialogAction.RussianCardholdersWarningDialog())
return
}

View file

@ -32,7 +32,7 @@ class WalletDialogsMiddleware {
)
}
is WalletAction.DialogAction.RussianCardholdersWarningDialog -> {
store.dispatchDialogShow(WalletDialog.RussianCardholdersWarningDialog)
store.dispatchDialogShow(WalletDialog.RussianCardholdersWarningDialog(action.topUpUrl))
}
is WalletAction.DialogAction.Hide -> {
store.dispatchDialogHide()

View file

@ -1,5 +1,6 @@
package com.tangem.tap.features.wallet.redux.middlewares
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.blockchain.blockchains.solana.RentProvider
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.AmountType
@ -14,8 +15,10 @@ import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.tap.common.analytics.AnalyticsAnOld
import com.tangem.tap.common.extensions.copyToClipboard
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
import com.tangem.tap.common.extensions.dispatchErrorNotification
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.dispatchOpenUrl
import com.tangem.tap.common.extensions.dispatchToastNotification
import com.tangem.tap.common.extensions.onCardScanned
import com.tangem.tap.common.extensions.shareText
import com.tangem.tap.common.extensions.stripZeroPlainString
@ -23,6 +26,7 @@ import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.failedRates
import com.tangem.tap.domain.loadedRates
import com.tangem.tap.features.demo.DemoHelper
@ -37,11 +41,13 @@ import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletData
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.WalletStore
import com.tangem.tap.network.NetworkConnectivity
import com.tangem.tap.network.NetworkStateChanged
import com.tangem.tap.preferencesStorage
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.tap.tangemSdkManager
import com.tangem.wallet.R
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.launch
@ -115,10 +121,9 @@ class WalletMiddleware {
true,
),
)
store.dispatch(WalletAction.LoadWallet.Success(
action.wallet,
action.blockchain
))
store.dispatch(
action = WalletAction.LoadWallet.Success(action.wallet, action.blockchain)
)
}
}
store.dispatch(WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline)
@ -186,10 +191,10 @@ class WalletMiddleware {
}
is CompletionResult.Failure -> {
(result.error as? TangemSdkError)?.let { error ->
store.state.globalState.analyticsHandler?.handleCardSdkErrorEvent(
store.state.globalState.analyticsHandler.handleCardSdkErrorEvent(
error,
AnalyticsAnOld.ActionToLog.CreateWallet,
card = store.state.detailsState.scanResponse?.card
card = store.state.detailsState.scanResponse?.card,
)
}
}
@ -249,10 +254,22 @@ class WalletMiddleware {
store.dispatchOpenUrl(action.exploreUrl)
}
is WalletAction.Send -> {
if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) {
store.dispatchErrorNotification(TapError.NoInternetConnection)
return
}
val newAction = prepareSendAction(action.amount, store.state.walletState)
store.dispatch(newAction)
if (newAction is PrepareSendScreen) {
store.dispatch(NavigationAction.NavigateTo(AppScreen.Send))
if (newAction is PrepareSendScreen && newAction.walletManager == null) {
store.dispatch(NavigationAction.PopBackTo(screen = AppScreen.Home))
FirebaseCrashlytics.getInstance().recordException(
IllegalStateException("PrepareSendScreen: walletManager is null")
)
store.dispatchToastNotification(R.string.internal_error_wallet_manager_not_found)
} else {
store.dispatch(newAction)
if (newAction is PrepareSendScreen) {
store.dispatch(NavigationAction.NavigateTo(AppScreen.Send))
}
}
}
}
@ -359,11 +376,13 @@ class WalletMiddleware {
val currency = walletManager.wallet.blockchain.currency
if (show) {
dispatchOnMain(WalletAction.SetWalletRent(
wallet = walletManager.wallet,
minRent = ("${rentProvider.rentAmount().stripZeroPlainString()} $currency"),
rentExempt = ("${rentExempt.stripZeroPlainString()} $currency")
))
dispatchOnMain(
WalletAction.SetWalletRent(
wallet = walletManager.wallet,
minRent = ("${rentProvider.rentAmount().stripZeroPlainString()} $currency"),
rentExempt = ("${rentExempt.stripZeroPlainString()} $currency")
)
)
} else {
dispatchOnMain(WalletAction.RemoveWalletRent(walletManager.wallet))
}

View file

@ -31,5 +31,5 @@ sealed interface WalletDialog : StateDialog {
val titleRes: Int = R.string.token_details_unable_hide_alert_title
}
object RussianCardholdersWarningDialog : WalletDialog
data class RussianCardholdersWarningDialog(val topUpUrl: String?) : WalletDialog
}

View file

@ -160,6 +160,11 @@ class MultiWalletReducer {
is WalletAction.MultiWallet.RemoveWallet -> {
state.removeWallet(state.getWalletData(action.currency))
}
is WalletAction.MultiWallet.RemoveWallets -> {
var updatedState = state
action.currencies.forEach { updatedState = updatedState.removeWallet(state.getWalletData(it)) }
updatedState
}
is WalletAction.MultiWallet.SetPrimaryBlockchain ->
state.copy(primaryBlockchain = action.blockchain)

View file

@ -88,7 +88,7 @@ class OnWalletLoadedReducer {
val tokenFiatAmountFormatted = tokenFiatAmount?.toFormattedFiatValue(fiatCurrency.symbol)
?: UNKNOWN_AMOUNT_SIGN
val isTokenSendButtonEnabled = newWalletData.shouldEnableTokenSendButton()
val isTokenSendButtonEnabled = tokenWalletData?.shouldEnableTokenSendButton() == true
&& pendingTransactions.isEmpty()
tokenWalletData?.copy(
currencyData = tokenWalletData.currencyData.copy(

View file

@ -149,14 +149,16 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
binding.srlWalletDetails.setOnRefreshListener {
if (selectedWallet.currencyData.status != BalanceStatus.Loading) {
store.dispatch(WalletAction.LoadWallet(
blockchain = BlockchainNetwork(
selectedWallet.currency.blockchain,
selectedWallet.currency.derivationPath,
emptyList()
)
)
store.dispatch(
WalletAction.LoadWallet(
blockchain = BlockchainNetwork(
selectedWallet.currency.blockchain,
selectedWallet.currency.derivationPath,
emptyList(),
),
),
)
store.dispatch(WalletAction.LoadFiatRate(coinsList = listOf(selectedWallet.currency)))
}
}

View file

@ -5,12 +5,16 @@ import android.os.Bundle
import android.view.LayoutInflater
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.tangem.tap.common.extensions.dispatchDialogHide
import com.tangem.tap.common.extensions.dispatchOpenUrl
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.store
import com.tangem.wallet.databinding.DialogRussiansCardholdersWarningBinding
class RussianCardholdersWarningBottomSheetDialog(context: Context) : BottomSheetDialog(context) {
class RussianCardholdersWarningBottomSheetDialog(
context: Context, private val topUpUrl: String?,
) : BottomSheetDialog
(context) {
private var binding: DialogRussiansCardholdersWarningBinding? = null
@ -29,7 +33,11 @@ class RussianCardholdersWarningBottomSheetDialog(context: Context) : BottomSheet
}
binding?.btnYes?.setOnClickListener {
store.dispatch(WalletAction.TradeCryptoAction.Buy(checkUserLocation = false))
if (topUpUrl != null) {
store.dispatchOpenUrl(topUpUrl)
} else {
store.dispatch(WalletAction.TradeCryptoAction.Buy(checkUserLocation = false))
}
dismiss()
}
binding?.btnNo?.setOnClickListener {

View file

@ -24,6 +24,7 @@ class CurrencyIconRequest(
private val currencyTextView: TextView?,
private val token: Token?,
private val blockchain: Blockchain,
private val getLocalImage: Boolean = false,
) {
fun load() {
when {
@ -84,7 +85,7 @@ class CurrencyIconRequest(
crossinline onError: (Blockchain) -> Unit = {},
) {
currencyImageView.loadIcon(
data = getIconUrl(blockchain.toNetworkId()),
data = if (getLocalImage) blockchain.getRoundIconRes() else getIconUrl(blockchain.toNetworkId()),
placeholderRes = blockchain.getRoundIconRes(),
onStart = { onStart(blockchain) },
onSuccess = { onSuccess(blockchain) },

View file

@ -68,6 +68,7 @@ fun CurrencyIconView.load(
currencyTextView = null,
token = null,
blockchain = currency.blockchain,
getLocalImage = true,
).load()
}
}

View file

@ -8,7 +8,6 @@
android:background="@color/backgroundLightGray"
android:clipChildren="false"
android:clipToPadding="false"
android:fitsSystemWindows="true"
android:focusableInTouchMode="true"
android:orientation="vertical">

View file

@ -22,10 +22,10 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:textAllCaps="true"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="75.00 usd" />
<TextView
@ -39,16 +39,30 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvReceiptAmount" />
<ProgressBar
android:id="@+id/pbReceiptFee"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center"
android:elevation="18dp"
android:indeterminate="true"
android:indeterminateTint="@color/accent"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/tvReceiptFee"
app:layout_constraintEnd_toEndOf="@+id/tvReceiptFeeValue"
app:layout_constraintTop_toTopOf="@+id/tvReceiptFee" />
<TextView
android:id="@+id/tvReceiptFeeValue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/tvReceiptFee"
android:textAllCaps="true"
android:textColor="@color/darkGray1"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/tvReceiptFee"
tools:text="0.03 usd" />
<View

View file

@ -153,4 +153,5 @@
<string name="onboarding_subtitle_kyc_retry">Please check you email for further instructions</string>
<string name="onboarding_exit_alert_title">Do you want to exit the activation process?</string>
<string name="onboarding_exit_alert_message">In this case, you will need to start from the beginning.</string>
<string name="error_wrong_wallet_tapped">You have used a card from another wallet. Tap the card associated with this wallet</string>
</resources>

View file

@ -153,4 +153,5 @@
<string name="onboarding_subtitle_kyc_retry">Please check you email for further instructions</string>
<string name="onboarding_exit_alert_title">Do you want to exit the activation process?</string>
<string name="onboarding_exit_alert_message">In this case, you will need to start from the beginning.</string>
<string name="error_wrong_wallet_tapped">You have used a card from another wallet. Tap the card associated with this wallet</string>
</resources>

View file

@ -153,4 +153,5 @@
<string name="onboarding_subtitle_kyc_retry">Please check you email for further instructions</string>
<string name="onboarding_exit_alert_title">Do you want to exit the activation process?</string>
<string name="onboarding_exit_alert_message">In this case, you will need to start from the beginning.</string>
<string name="error_wrong_wallet_tapped">You have used a card from another wallet. Tap the card associated with this wallet</string>
</resources>

View file

@ -151,4 +151,6 @@
<string name="onboarding_subtitle_kyc_retry">Более подробная информация отправлена на ваш адрес электронной почты.</string>
<string name="onboarding_exit_alert_title">Вы хотите выйти из процесса активации?</string>
<string name="onboarding_exit_alert_message">В этом случае вам будет необходимо начать процесс заново.</string>
<string name="error_wrong_wallet_tapped">Вы использовали карту от другого кошелька. Приложите карту, связанную с этим кошельком.</string>
<string name="internal_error_wallet_manager_not_found">Внутренняя ошибка: не удается найти менеджер кошельков</string>
</resources>

View file

@ -153,4 +153,6 @@
<string name="onboarding_subtitle_kyc_retry">Please check you email for further instructions</string>
<string name="onboarding_exit_alert_title">Do you want to exit the activation process?</string>
<string name="onboarding_exit_alert_message">In this case, you will need to start from the beginning.</string>
<string name="error_wrong_wallet_tapped">You have used a card from another wallet. Tap the card associated with this wallet</string>
<string name="internal_error_wallet_manager_not_found">Internal error: wallet manager not found</string>
</resources>

View file

@ -3,7 +3,7 @@ ext.versions = [
build_gradle : '7.1.3',
tangem_card_sdk : 'develop-165',
// tangem_card_sdk : '0.0.1',
tangem_blockchain_sdk: 'develop-130',
tangem_blockchain_sdk: 'develop-139',
// tangem_blockchain_sdk: '0.0.1',
]

View file

@ -44,8 +44,7 @@ fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? {
"polkadot" -> Blockchain.Polkadot
"polkadot/test" -> Blockchain.PolkadotTestnet
"kusama" -> Blockchain.Kusama
// "optimistic-ethereum" -> Blockchain.Optimism
"optimistic-ethereum" -> null //TODO: Optimism is disabled until next release
"optimistic-ethereum" -> Blockchain.Optimism
"optimistic-ethereum/test" -> Blockchain.OptimismTestnet
"dash" -> Blockchain.Dash
"sxdai" -> Blockchain.SaltPay
@ -144,6 +143,5 @@ fun Blockchain.isSupportedInApp(): Boolean {
}
private val excludedBlockchains = listOf(
Blockchain.Optimism, // TODO: remove when fee calculation is fixed
Blockchain.SaltPay,
)