Updated on 2026-08-14
This commit is contained in:
commit
024e67e7b7
43 changed files with 267 additions and 179 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit cf6ea50867477f97655da9da47ff94987e8f3354
|
||||
Subproject commit a1658496e777b611fc990ef2bc1a1a1fd48bc1e6
|
||||
|
|
@ -132,7 +132,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
store.dispatch(NavigationAction.ActivityDestroyed)
|
||||
store.dispatch(NavigationAction.ActivityDestroyed(WeakReference(this)))
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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>>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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 -> {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 -> {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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) },
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ fun CurrencyIconView.load(
|
|||
currencyTextView = null,
|
||||
token = null,
|
||||
blockchain = currency.blockchain,
|
||||
getLocalImage = true,
|
||||
).load()
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,6 @@
|
|||
android:background="@color/backgroundLightGray"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:fitsSystemWindows="true"
|
||||
android:focusableInTouchMode="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue