Updated on 2026-08-14
This commit is contained in:
commit
00e9e964ab
28 changed files with 346 additions and 226 deletions
|
|
@ -59,7 +59,7 @@ class DialogManager : StoreSubscriber<GlobalState> {
|
|||
return
|
||||
}
|
||||
val context = context ?: return
|
||||
if (dialog != null && dialog == state.dialog) return
|
||||
if (dialog != null) return
|
||||
|
||||
dialog = when (state.dialog) {
|
||||
is AppDialog.SimpleOkDialog -> SimpleOkDialog.create(state.dialog, context)
|
||||
|
|
@ -101,7 +101,7 @@ class DialogManager : StoreSubscriber<GlobalState> {
|
|||
is WalletConnectDialog.ApproveWcSession ->
|
||||
ApproveWcSessionDialog.create(state.dialog.session, state.dialog.networks, context)
|
||||
is WalletConnectDialog.ChooseNetwork ->
|
||||
ChooseNetworkDialog.create(state.dialog.networks, context)
|
||||
ChooseNetworkDialog.create(state.dialog.session, state.dialog.networks, context)
|
||||
is WalletConnectDialog.ClipboardOrScanQr ->
|
||||
ClipboardOrScanQrDialog.create(state.dialog.clipboardUri, context)
|
||||
is WalletConnectDialog.RequestTransaction ->
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import com.tangem.tap.currenciesRepository
|
|||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
|
||||
import com.tangem.tap.features.send.redux.SendAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.network.exchangeServices.CardExchangeRules
|
||||
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
|
||||
import com.tangem.tap.network.exchangeServices.mercuryo.MercuryoApi
|
||||
import com.tangem.tap.network.exchangeServices.mercuryo.MercuryoService
|
||||
|
|
@ -23,11 +24,11 @@ import com.tangem.tap.preferencesStorage
|
|||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import java.util.Locale
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.DispatchFunction
|
||||
import org.rekotlin.Middleware
|
||||
import java.util.*
|
||||
|
||||
class GlobalMiddleware {
|
||||
companion object {
|
||||
|
|
@ -62,9 +63,11 @@ private fun handleAction(action: Action, appState: () -> AppState?, dispatch: Di
|
|||
}
|
||||
}
|
||||
is GlobalAction.RestoreAppCurrency -> {
|
||||
store.dispatch(GlobalAction.RestoreAppCurrency.Success(
|
||||
preferencesStorage.fiatCurrenciesPrefStorage.getAppCurrency()
|
||||
))
|
||||
store.dispatch(
|
||||
GlobalAction.RestoreAppCurrency.Success(
|
||||
preferencesStorage.fiatCurrenciesPrefStorage.getAppCurrency(),
|
||||
),
|
||||
)
|
||||
}
|
||||
is GlobalAction.HideWarningMessage -> {
|
||||
store.state.globalState.warningManager?.let {
|
||||
|
|
@ -107,7 +110,13 @@ private fun handleAction(action: Action, appState: () -> AppState?, dispatch: Di
|
|||
secret = mercuryoSecret,
|
||||
)
|
||||
val sellService = MoonPayService(moonPayKey, moonPaySecretKey)
|
||||
val exchangeManager = CurrencyExchangeManager(buyService, sellService)
|
||||
val cardProvider = { store.state.globalState.scanResponse?.card }
|
||||
|
||||
val exchangeManager = CurrencyExchangeManager(
|
||||
buyService = buyService,
|
||||
sellService = sellService,
|
||||
primaryRules = CardExchangeRules(cardProvider),
|
||||
)
|
||||
store.dispatchOnMain(GlobalAction.ExchangeManager.Init.Success(exchangeManager))
|
||||
store.dispatchOnMain(GlobalAction.ExchangeManager.Update)
|
||||
}
|
||||
|
|
@ -127,7 +136,7 @@ private fun handleAction(action: Action, appState: () -> AppState?, dispatch: Di
|
|||
store.state.globalState.analyticsHandlers,
|
||||
currenciesRepository,
|
||||
action.additionalBlockchainsToDerive,
|
||||
action.messageResId
|
||||
action.messageResId,
|
||||
)
|
||||
withMainContext {
|
||||
store.dispatch(GlobalAction.ScanFailsCounter.ChooseBehavior(result))
|
||||
|
|
@ -150,15 +159,15 @@ private fun handleAction(action: Action, appState: () -> AppState?, dispatch: Di
|
|||
is Result.Success -> {
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.FetchUserCountry.Success(
|
||||
countryCode = result.data.code.lowercase()
|
||||
)
|
||||
countryCode = result.data.code.lowercase(),
|
||||
),
|
||||
)
|
||||
}
|
||||
is Result.Failure -> {
|
||||
store.dispatchOnMain(
|
||||
GlobalAction.FetchUserCountry.Success(
|
||||
countryCode = Locale.getDefault().country.lowercase()
|
||||
)
|
||||
countryCode = Locale.getDefault().country.lowercase(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,16 +68,15 @@ class ScanProductTask(
|
|||
when (processorResult) {
|
||||
is CompletionResult.Success -> ScanTask().run(session) { scanTaskResult ->
|
||||
when (scanTaskResult) {
|
||||
is CompletionResult.Success -> callback(
|
||||
CompletionResult.Success(
|
||||
processorResult.data
|
||||
is CompletionResult.Success -> {
|
||||
// it need because processorResult.data.card doesn't contains attestation result
|
||||
// and CardWallet.derivedKeys
|
||||
val processorScanResponseWithNewCard = processorResult.data.copy(
|
||||
card = scanTaskResult.data
|
||||
)
|
||||
)
|
||||
is CompletionResult.Failure -> callback(
|
||||
CompletionResult.Failure(
|
||||
scanTaskResult.error
|
||||
)
|
||||
)
|
||||
callback(CompletionResult.Success(processorScanResponseWithNewCard))
|
||||
}
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(scanTaskResult.error))
|
||||
}
|
||||
}
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(processorResult.error))
|
||||
|
|
@ -102,11 +101,11 @@ private class ScanNoteProcessor : ProductCommandProcessor<ScanResponse> {
|
|||
callback(
|
||||
CompletionResult.Success(
|
||||
ScanResponse(
|
||||
card,
|
||||
ProductType.Note,
|
||||
session.environment.walletData
|
||||
)
|
||||
)
|
||||
card = card,
|
||||
productType = ProductType.Note,
|
||||
walletData = session.environment.walletData,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -117,19 +116,17 @@ private class ScanWalletProcessor(
|
|||
) : ProductCommandProcessor<ScanResponse> {
|
||||
|
||||
var primaryCard: PrimaryCard? = null
|
||||
|
||||
override fun proceed(
|
||||
card: Card,
|
||||
session: CardSession,
|
||||
callback: (result: CompletionResult<ScanResponse>) -> Unit
|
||||
callback: (result: CompletionResult<ScanResponse>) -> Unit,
|
||||
) {
|
||||
createMissingWalletsIfNeeded(card, session, callback)
|
||||
}
|
||||
|
||||
private fun createMissingWalletsIfNeeded(
|
||||
card: Card,
|
||||
session: CardSession,
|
||||
callback: (result: CompletionResult<ScanResponse>) -> Unit
|
||||
callback: (result: CompletionResult<ScanResponse>) -> Unit,
|
||||
) {
|
||||
if (card.wallets.isEmpty() || card.firmwareVersion < FirmwareVersion.MultiWalletAvailable) {
|
||||
startLinkingForBackupIfNeeded(card, session, callback)
|
||||
|
|
@ -146,18 +143,12 @@ private class ScanWalletProcessor(
|
|||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
PreflightReadTask(
|
||||
PreflightReadMode.FullCardRead,
|
||||
card.cardId
|
||||
readMode = PreflightReadMode.FullCardRead,
|
||||
cardId = card.cardId
|
||||
).run(session) { readResult ->
|
||||
when (readResult) {
|
||||
is CompletionResult.Success -> {
|
||||
startLinkingForBackupIfNeeded(card, session, callback)
|
||||
}
|
||||
is CompletionResult.Failure -> callback(
|
||||
CompletionResult.Failure(
|
||||
readResult.error
|
||||
)
|
||||
)
|
||||
is CompletionResult.Success -> startLinkingForBackupIfNeeded(card, session, callback)
|
||||
is CompletionResult.Failure -> callback(CompletionResult.Failure(readResult.error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -165,18 +156,14 @@ private class ScanWalletProcessor(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun startLinkingForBackupIfNeeded(
|
||||
card: Card,
|
||||
session: CardSession,
|
||||
callback: (result: CompletionResult<ScanResponse>) -> Unit
|
||||
callback: (result: CompletionResult<ScanResponse>) -> Unit,
|
||||
) {
|
||||
val activationIsFinished =
|
||||
preferencesStorage.usedCardsPrefStorage.isActivationFinished(card.cardId)
|
||||
val activationIsFinished = preferencesStorage.usedCardsPrefStorage.isActivationFinished(card.cardId)
|
||||
|
||||
if (card.backupStatus == Card.BackupStatus.NoBackup &&
|
||||
!activationIsFinished && card.wallets.isNotEmpty()
|
||||
) {
|
||||
if (card.backupStatus == Card.BackupStatus.NoBackup && !activationIsFinished && card.wallets.isNotEmpty()) {
|
||||
StartPrimaryCardLinkingTask().run(session) { linkingResult ->
|
||||
when (linkingResult) {
|
||||
is CompletionResult.Success -> {
|
||||
|
|
@ -192,11 +179,10 @@ private class ScanWalletProcessor(
|
|||
deriveKeysIfNeeded(card, session, callback)
|
||||
}
|
||||
}
|
||||
|
||||
private fun deriveKeysIfNeeded(
|
||||
card: Card,
|
||||
session: CardSession,
|
||||
callback: (result: CompletionResult<ScanResponse>) -> Unit
|
||||
callback: (result: CompletionResult<ScanResponse>) -> Unit,
|
||||
) {
|
||||
scope.launch {
|
||||
val derivations = collectDerivations(card)
|
||||
|
|
@ -235,12 +221,13 @@ private class ScanWalletProcessor(
|
|||
private suspend fun getBlockchainsToDerive(card: Card): List<BlockchainNetwork> {
|
||||
val currenciesRepository = currenciesRepository ?: return emptyList()
|
||||
|
||||
val cardCurrencies = currenciesRepository.loadSavedCurrencies(card.cardId, card.settings.isHDWalletAllowed).toMutableList()
|
||||
val cardCurrencies = currenciesRepository
|
||||
.loadSavedCurrencies(card.cardId, card.settings.isHDWalletAllowed).toMutableList()
|
||||
|
||||
val blockchainsToDerive = cardCurrencies.ifEmpty {
|
||||
mutableListOf(
|
||||
BlockchainNetwork(Blockchain.Bitcoin, card),
|
||||
BlockchainNetwork(Blockchain.Ethereum, card)
|
||||
BlockchainNetwork(Blockchain.Ethereum, card),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -248,7 +235,7 @@ private class ScanWalletProcessor(
|
|||
blockchainsToDerive.addAll(
|
||||
listOf(
|
||||
BlockchainNetwork(Blockchain.Ethereum, card),
|
||||
BlockchainNetwork(Blockchain.EthereumTestnet, card)
|
||||
BlockchainNetwork(Blockchain.EthereumTestnet, card),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
@ -305,52 +292,44 @@ private class ScanTwinProcessor : ProductCommandProcessor<ScanResponse> {
|
|||
is CompletionResult.Success -> {
|
||||
val publicKey = card.getSingleWallet()?.publicKey
|
||||
if (publicKey == null) {
|
||||
callback(
|
||||
CompletionResult.Success(
|
||||
ScanResponse(
|
||||
card,
|
||||
ProductType.Twins,
|
||||
null
|
||||
)
|
||||
)
|
||||
)
|
||||
return@run
|
||||
}
|
||||
val verified =
|
||||
TwinsHelper.verifyTwinPublicKey(readDataResult.data.issuerData, publicKey)
|
||||
if (verified) {
|
||||
val twinPublicKey = readDataResult.data.issuerData.sliceArray(0 until 65)
|
||||
val walletData = session.environment.walletData
|
||||
val response = ScanResponse(
|
||||
card,
|
||||
ProductType.Twins,
|
||||
walletData,
|
||||
twinPublicKey.toHexString()
|
||||
card = card,
|
||||
productType = ProductType.Twins,
|
||||
walletData = null,
|
||||
)
|
||||
callback(CompletionResult.Success(response))
|
||||
return@run
|
||||
}
|
||||
|
||||
val verified = TwinsHelper.verifyTwinPublicKey(readDataResult.data.issuerData, publicKey)
|
||||
val response = if (verified) {
|
||||
val twinPublicKey = readDataResult.data.issuerData.sliceArray(0 until 65)
|
||||
val walletData = session.environment.walletData
|
||||
ScanResponse(
|
||||
card = card,
|
||||
productType = ProductType.Twins,
|
||||
walletData = walletData,
|
||||
secondTwinPublicKey = twinPublicKey.toHexString(),
|
||||
)
|
||||
} else {
|
||||
callback(
|
||||
CompletionResult.Success(
|
||||
ScanResponse(
|
||||
card,
|
||||
ProductType.Twins,
|
||||
null
|
||||
)
|
||||
)
|
||||
ScanResponse(
|
||||
card = card,
|
||||
productType = ProductType.Twins,
|
||||
walletData = null,
|
||||
)
|
||||
}
|
||||
callback(CompletionResult.Success(response))
|
||||
}
|
||||
is CompletionResult.Failure ->
|
||||
is CompletionResult.Failure -> {
|
||||
callback(CompletionResult.Success(ScanResponse(card, ProductType.Twins, null)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun Card.getCurvesForNonCreatedWallets(): List<EllipticCurve> {
|
||||
val curvesPresent = wallets.map { it.curve }.toSet()
|
||||
val curvesForNonCreatedWallets = supportedCurves
|
||||
.subtract(curvesPresent + EllipticCurve.Secp256r1)
|
||||
val curvesForNonCreatedWallets = supportedCurves.subtract(curvesPresent + EllipticCurve.Secp256r1)
|
||||
return curvesForNonCreatedWallets.toList()
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@ import com.tangem.common.CompletionResult
|
|||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.tap.common.extensions.dispatchNotification
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
|
|
@ -42,7 +43,8 @@ object DemoHelper {
|
|||
WalletAction.TradeCryptoAction.Buy::class.java,
|
||||
WalletAction.TradeCryptoAction.Sell::class.java,
|
||||
BackupAction.StartBackup::class.java,
|
||||
WalletAction.ExploreAddress::class.java
|
||||
WalletAction.ExploreAddress::class.java,
|
||||
DetailsAction.ResetToFactory.Start::class.java,
|
||||
)
|
||||
|
||||
fun isDemoCard(scanResponse: ScanResponse): Boolean = isDemoCardId(scanResponse.card.cardId)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ sealed class DetailsAction : Action {
|
|||
object ReCreateTwinsWallet : DetailsAction()
|
||||
|
||||
sealed class ResetToFactory : DetailsAction() {
|
||||
object Start : ResetToFactory()
|
||||
object Proceed : ResetToFactory()
|
||||
data class Confirm(val confirmed: Boolean) : ResetToFactory()
|
||||
object Failure : ResetToFactory()
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ 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.currenciesRepository
|
||||
import com.tangem.tap.features.disclaimer.redux.DisclaimerAction
|
||||
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
|
||||
import com.tangem.tap.features.wallet.models.hasSendableAmountsOrPendingTransactions
|
||||
|
|
@ -22,70 +22,74 @@ import com.tangem.tap.tangemSdkManager
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.Middleware
|
||||
|
||||
class DetailsMiddleware {
|
||||
private val eraseWalletMiddleware = EraseWalletMiddleware()
|
||||
private val manageSecurityMiddleware = ManageSecurityMiddleware()
|
||||
private val managePrivacyMiddleware = ManagePrivacyMiddleware()
|
||||
val detailsMiddleware: Middleware<AppState> = { _, _ ->
|
||||
val detailsMiddleware: Middleware<AppState> = { _, state ->
|
||||
{ next ->
|
||||
{ action ->
|
||||
when (action) {
|
||||
is DetailsAction.ResetToFactory -> eraseWalletMiddleware.handle(action)
|
||||
is DetailsAction.ManageSecurity -> manageSecurityMiddleware.handle(action)
|
||||
is DetailsAction.AppSettings -> managePrivacyMiddleware.handle(action)
|
||||
is DetailsAction.ShowDisclaimer -> {
|
||||
val uri = store.state.detailsState.cardTermsOfUseUrl
|
||||
if (uri != null) {
|
||||
store.dispatch(NavigationAction.OpenDocument(uri))
|
||||
} else {
|
||||
store.dispatch(DisclaimerAction.ShowAcceptedDisclaimer)
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer))
|
||||
}
|
||||
handleAction(state, action)
|
||||
next(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAction(state: () -> AppState?, action: Action) {
|
||||
if (DemoHelper.tryHandle(state, action)) return
|
||||
|
||||
when (action) {
|
||||
is DetailsAction.ResetToFactory -> eraseWalletMiddleware.handle(action)
|
||||
is DetailsAction.ManageSecurity -> manageSecurityMiddleware.handle(action)
|
||||
is DetailsAction.AppSettings -> managePrivacyMiddleware.handle(action)
|
||||
is DetailsAction.ShowDisclaimer -> {
|
||||
val uri = store.state.detailsState.cardTermsOfUseUrl
|
||||
if (uri != null) {
|
||||
store.dispatch(NavigationAction.OpenDocument(uri))
|
||||
}
|
||||
}
|
||||
is DetailsAction.ReCreateTwinsWallet -> {
|
||||
val wallet =
|
||||
store.state.walletState.walletManagers.map { it.wallet }.firstOrNull()
|
||||
if (wallet == null) {
|
||||
store.dispatch(TwinCardsAction.SetMode(CreateTwinWalletMode.RecreateWallet))
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingTwins))
|
||||
} else {
|
||||
if (wallet.hasSendableAmountsOrPendingTransactions()) {
|
||||
val walletIsNotEmpty =
|
||||
store.state.globalState.resources.strings.walletIsNotEmpty
|
||||
store.dispatchNotification(walletIsNotEmpty)
|
||||
} else {
|
||||
store.dispatch(TwinCardsAction.SetMode(CreateTwinWalletMode.RecreateWallet))
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingTwins))
|
||||
}
|
||||
is DetailsAction.ReCreateTwinsWallet -> {
|
||||
val wallet =
|
||||
store.state.walletState.walletManagers.map { it.wallet }.firstOrNull()
|
||||
if (wallet == null) {
|
||||
store.dispatch(TwinCardsAction.SetMode(CreateTwinWalletMode.RecreateWallet))
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingTwins))
|
||||
} else {
|
||||
if (wallet.hasSendableAmountsOrPendingTransactions()) {
|
||||
val walletIsNotEmpty =
|
||||
store.state.globalState.resources.strings.walletIsNotEmpty
|
||||
store.dispatchNotification(walletIsNotEmpty)
|
||||
} else {
|
||||
store.dispatch(TwinCardsAction.SetMode(CreateTwinWalletMode.RecreateWallet))
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingTwins))
|
||||
}
|
||||
}
|
||||
}
|
||||
is DetailsAction.CreateBackup -> {
|
||||
store.state.detailsState.scanResponse?.let {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingWallet))
|
||||
store.dispatch(
|
||||
GlobalAction.Onboarding.Start(
|
||||
it,
|
||||
fromHomeScreen = false,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
DetailsAction.ScanCard -> {
|
||||
scope.launch {
|
||||
when (val result = tangemSdkManager.scanCard()) {
|
||||
is CompletionResult.Success -> {
|
||||
val card = result.data
|
||||
store.dispatchOnMain(DetailsAction.PrepareCardSettingsData(card))
|
||||
}
|
||||
}
|
||||
is DetailsAction.CreateBackup -> {
|
||||
store.state.detailsState.scanResponse?.let {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingWallet))
|
||||
store.dispatch(
|
||||
GlobalAction.Onboarding.Start(
|
||||
it,
|
||||
fromHomeScreen = false,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
DetailsAction.ScanCard -> {
|
||||
scope.launch {
|
||||
when (val result = tangemSdkManager.scanCard()) {
|
||||
is CompletionResult.Success -> {
|
||||
val card = result.data
|
||||
store.dispatchOnMain(DetailsAction.PrepareCardSettingsData(card))
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
}
|
||||
}
|
||||
is CompletionResult.Failure -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
next(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -93,6 +97,9 @@ class DetailsMiddleware {
|
|||
class EraseWalletMiddleware {
|
||||
fun handle(action: DetailsAction.ResetToFactory) {
|
||||
when (action) {
|
||||
is DetailsAction.ResetToFactory.Start -> {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.ResetToFactory))
|
||||
}
|
||||
is DetailsAction.ResetToFactory.Proceed -> {
|
||||
val card = store.state.detailsState.cardSettingsState?.card ?: return
|
||||
if (card.isTangemTwins()) {
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ sealed class WalletConnectAction : Action {
|
|||
val session: WalletConnectSession,
|
||||
) : WalletConnectAction()
|
||||
|
||||
data class SelectNetwork(val networks: List<Blockchain>) : WalletConnectAction()
|
||||
data class SelectNetwork(val session: WalletConnectSession, val networks: List<Blockchain>) : WalletConnectAction()
|
||||
data class ChooseNetwork(val blockchain: Blockchain) : WalletConnectAction()
|
||||
data class UpdateBlockchain(
|
||||
val updatedSession: WalletConnectSession,
|
||||
|
|
|
|||
|
|
@ -62,7 +62,14 @@ class WalletConnectMiddleware {
|
|||
}
|
||||
}
|
||||
is WalletConnectAction.SelectNetwork -> {
|
||||
store.dispatch(GlobalAction.ShowDialog(WalletConnectDialog.ChooseNetwork(action.networks)))
|
||||
store.dispatch(
|
||||
GlobalAction.ShowDialog(
|
||||
WalletConnectDialog.ChooseNetwork(
|
||||
session = action.session,
|
||||
networks = action.networks,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
is WalletConnectAction.ChooseNetwork -> {
|
||||
val data = state()?.walletConnectState?.newSessionData ?: return
|
||||
|
|
|
|||
|
|
@ -95,10 +95,12 @@ sealed class WalletConnectDialog : StateDialog {
|
|||
object OpeningSessionRejected : WalletConnectDialog()
|
||||
object SessionTimeout : WalletConnectDialog()
|
||||
data class ApproveWcSession(
|
||||
val session: WalletConnectSession, val networks: List<Blockchain>,
|
||||
val session: WalletConnectSession,
|
||||
val networks: List<Blockchain>,
|
||||
) : WalletConnectDialog()
|
||||
|
||||
data class ChooseNetwork(
|
||||
val session: WalletConnectSession,
|
||||
val networks: List<Blockchain>,
|
||||
) : WalletConnectDialog()
|
||||
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ package com.tangem.tap.features.details.ui.cardsettings
|
|||
import com.tangem.domain.common.getTwinCardIdForUser
|
||||
import com.tangem.domain.common.isTangemTwins
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.details.redux.CardSettingsState
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import org.rekotlin.Store
|
||||
|
|
@ -64,7 +62,7 @@ class CardSettingsViewModel(private val store: Store<AppState>) {
|
|||
store.dispatch(DetailsAction.ManageSecurity.ChangeAccessCode)
|
||||
}
|
||||
is CardInfo.ResetToFactorySettings -> {
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.ResetToFactory))
|
||||
store.dispatch(DetailsAction.ResetToFactory.Start)
|
||||
}
|
||||
is CardInfo.SecurityMode -> {
|
||||
store.dispatch(DetailsAction.ManageSecurity.OpenSecurity)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import androidx.compose.material.Text
|
|||
import androidx.compose.material.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
|
@ -32,42 +33,61 @@ fun SettingsScreensScaffold(
|
|||
content: @Composable () -> Unit,
|
||||
background: @Composable (() -> Unit)? = null,
|
||||
fab: @Composable (() -> Unit)? = null,
|
||||
titleRes: Int,
|
||||
backgroundColor: Color = colorResource(id = R.color.background_primary),
|
||||
titleRes: Int? = null,
|
||||
onBackClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
BackHandler(true, onBackClick)
|
||||
|
||||
Scaffold(
|
||||
topBar = { EmptyTopBarWithNavigation(onBackClick = onBackClick) },
|
||||
topBar = {
|
||||
EmptyTopBarWithNavigation(
|
||||
onBackClick = onBackClick,
|
||||
backgroundColor = backgroundColor,
|
||||
)
|
||||
},
|
||||
modifier = modifier.systemBarsPadding(),
|
||||
backgroundColor = colorResource(id = R.color.background_primary),
|
||||
backgroundColor = backgroundColor,
|
||||
floatingActionButton = { fab?.invoke() },
|
||||
) {
|
||||
if (titleRes != null) {
|
||||
Box(modifier = modifier.fillMaxSize()) {
|
||||
background?.invoke()
|
||||
|
||||
Box(modifier = modifier.fillMaxSize()) {
|
||||
|
||||
background?.invoke()
|
||||
|
||||
Column(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = titleRes),
|
||||
modifier = modifier.padding(start = 20.dp, end = 20.dp, bottom = 52.dp),
|
||||
style = TangemTypography.headline1,
|
||||
color = colorResource(id = R.color.text_primary_1),
|
||||
)
|
||||
content()
|
||||
Column(modifier = modifier.fillMaxWidth()) {
|
||||
Text(
|
||||
text = stringResource(id = titleRes),
|
||||
modifier = modifier.padding(start = 20.dp, end = 20.dp, bottom = 52.dp),
|
||||
style = TangemTypography.headline1,
|
||||
color = colorResource(id = R.color.text_primary_1),
|
||||
)
|
||||
content()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
content()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ScreenTitle(
|
||||
titleRes: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = titleRes),
|
||||
modifier = modifier.padding(start = 20.dp, end = 20.dp),
|
||||
style = TangemTypography.headline1,
|
||||
color = colorResource(id = R.color.text_primary_1),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun EmptyTopBarWithNavigation(
|
||||
onBackClick: () -> Unit,
|
||||
backgroundColor: Color = colorResource(id = R.color.background_primary),
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
TopAppBar(
|
||||
|
|
@ -81,7 +101,7 @@ fun EmptyTopBarWithNavigation(
|
|||
)
|
||||
}
|
||||
},
|
||||
backgroundColor = colorResource(id = R.color.background_primary),
|
||||
backgroundColor = backgroundColor,
|
||||
elevation = 0.dp,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,9 +10,10 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
|
|
@ -24,6 +25,7 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.tap.common.compose.TangemTypography
|
||||
import com.tangem.tap.features.details.ui.common.ScreenTitle
|
||||
import com.tangem.tap.features.details.ui.common.SettingsScreensScaffold
|
||||
import com.tangem.wallet.R
|
||||
|
||||
|
|
@ -34,10 +36,7 @@ fun DetailsScreen(
|
|||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
SettingsScreensScaffold(
|
||||
content = {
|
||||
Content(state = state, modifier = modifier)
|
||||
},
|
||||
titleRes = R.string.details_title,
|
||||
content = { Content(state = state, modifier = modifier) },
|
||||
onBackClick = onBackPressed,
|
||||
)
|
||||
}
|
||||
|
|
@ -50,37 +49,32 @@ fun Content(
|
|||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.padding(bottom = 40.dp),
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = 40.dp)
|
||||
.weight(1f),
|
||||
) {
|
||||
items(state.elements) {
|
||||
if (it == SettingsElement.WalletConnect) {
|
||||
WalletConnectDetailsItem(
|
||||
onItemsClick = state.onItemsClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
DetailsItem(
|
||||
item = it,
|
||||
appCurrency = state.appCurrency,
|
||||
onItemsClick = state.onItemsClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
ScreenTitle(titleRes = R.string.details_title, modifier.padding(bottom = 52.dp))
|
||||
state.elements.map {
|
||||
if (it == SettingsElement.WalletConnect) {
|
||||
WalletConnectDetailsItem(
|
||||
onItemsClick = state.onItemsClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
DetailsItem(
|
||||
item = it,
|
||||
appCurrency = state.appCurrency,
|
||||
onItemsClick = state.onItemsClick,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = modifier.weight(1f))
|
||||
TangemSocialAccounts(state.tangemLinks, state.onSocialNetworkClick)
|
||||
Spacer(modifier = Modifier.size(12.dp))
|
||||
Text(
|
||||
text = "${stringResource(id = state.appNameRes)} ${state.tangemVersion}",
|
||||
style = TangemTypography.caption,
|
||||
color = colorResource(id = R.color.text_tertiary),
|
||||
modifier = modifier.padding(start = 16.dp, end = 16.dp),
|
||||
modifier = modifier.padding(start = 16.dp, end = 16.dp, bottom = 40.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ enum class SettingsElement(
|
|||
AppCurrency(R.drawable.ic_currency, R.string.details_row_title_currency),
|
||||
AppSettings(R.drawable.ic_settings, R.string.app_settings_title),
|
||||
LinkMoreCards(R.drawable.ic_more_cards, R.string.details_row_title_create_backup),
|
||||
TermsOfService(R.drawable.ic_text, R.string.disclaimer_title),
|
||||
TermsOfService(R.drawable.ic_text, R.string.disclaimer_title), // General Terms of Service of the App
|
||||
TermsOfUse(R.drawable.ic_text, R.string.details_row_title_card_tou), // Terms of Use for S2C cards only
|
||||
PrivacyPolicy(R.drawable.ic_lock, R.string.details_row_privacy_policy),
|
||||
;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tap.features.details.ui.details
|
||||
|
||||
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
|
||||
import com.tangem.tap.common.feedback.FeedbackEmail
|
||||
import com.tangem.tap.common.feedback.SupportInfo
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
|
|
@ -9,6 +10,7 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
|
|||
import com.tangem.tap.domain.extensions.isMultiwalletAllowed
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import com.tangem.tap.features.details.redux.DetailsState
|
||||
import com.tangem.tap.features.disclaimer.redux.DisclaimerAction
|
||||
import com.tangem.tap.features.home.LocaleRegionProvider
|
||||
import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
|
|
@ -30,6 +32,7 @@ class DetailsViewModel(private val store: Store<AppState>) {
|
|||
}
|
||||
SettingsElement.AppSettings -> null // TODO: until we implement settings from this screen
|
||||
SettingsElement.AppCurrency -> if (state.scanResponse?.card?.isMultiwalletAllowed != true) it else null
|
||||
SettingsElement.TermsOfUse -> if (state.scanResponse?.card?.isStart2Coin == true) it else null
|
||||
else -> it
|
||||
}
|
||||
}
|
||||
|
|
@ -72,6 +75,10 @@ class DetailsViewModel(private val store: Store<AppState>) {
|
|||
store.dispatch(DetailsAction.CreateBackup)
|
||||
}
|
||||
SettingsElement.TermsOfService -> {
|
||||
store.dispatch(DisclaimerAction.ShowAcceptedDisclaimer)
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer))
|
||||
}
|
||||
SettingsElement.TermsOfUse -> {
|
||||
store.dispatch(DetailsAction.ShowDisclaimer)
|
||||
}
|
||||
SettingsElement.PrivacyPolicy -> {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
package com.tangem.tap.features.details.ui.resetcard
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.Icon
|
||||
|
|
@ -15,6 +18,7 @@ import androidx.compose.material.IconToggleButton
|
|||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
|
@ -22,6 +26,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.tap.common.compose.TangemTypography
|
||||
import com.tangem.tap.features.details.ui.common.DetailsMainButton
|
||||
import com.tangem.tap.features.details.ui.common.ScreenTitle
|
||||
import com.tangem.tap.features.details.ui.common.SettingsScreensScaffold
|
||||
import com.tangem.wallet.R
|
||||
|
||||
|
|
@ -31,14 +36,18 @@ fun ResetCardScreen(
|
|||
onBackPressed: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
||||
SettingsScreensScaffold(
|
||||
content = { ResetCardView(state = state, modifier = modifier) },
|
||||
background =
|
||||
{ Image(painter = painterResource(id = R.drawable.ic_reset_background), contentDescription = "") },
|
||||
titleRes = R.string.reset_card_to_factory_navigation_title,
|
||||
onBackClick = onBackPressed,
|
||||
)
|
||||
Box(modifier = modifier.background(colorResource(id = R.color.background_primary))) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_reset_background),
|
||||
contentDescription = "",
|
||||
modifier = modifier.offset(y = (-16).dp),
|
||||
)
|
||||
SettingsScreensScaffold(
|
||||
content = { ResetCardView(state = state, modifier = modifier) },
|
||||
onBackClick = onBackPressed,
|
||||
backgroundColor = Color.Transparent,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -51,7 +60,16 @@ fun ResetCardView(
|
|||
.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.Bottom,
|
||||
) {
|
||||
|
||||
Box(
|
||||
modifier = modifier,
|
||||
) {
|
||||
ScreenTitle(titleRes = R.string.reset_card_to_factory_navigation_title)
|
||||
}
|
||||
Spacer(
|
||||
modifier = modifier
|
||||
.defaultMinSize(20.dp)
|
||||
.weight(1f),
|
||||
)
|
||||
Text(
|
||||
text = stringResource(id = R.string.common_attention),
|
||||
modifier = modifier.padding(start = 20.dp, end = 20.dp),
|
||||
|
|
|
|||
|
|
@ -6,10 +6,12 @@ import androidx.compose.foundation.layout.Row
|
|||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.selection.selectable
|
||||
import androidx.compose.material.RadioButton
|
||||
import androidx.compose.material.RadioButtonDefaults
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -45,7 +47,8 @@ fun SecurityModeOptions(
|
|||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.padding(bottom = 28.dp),
|
||||
.padding(bottom = 28.dp)
|
||||
.offset(y = (-16).dp),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
state.availableOptions.map {
|
||||
|
|
@ -85,12 +88,16 @@ fun SecurityOption(
|
|||
.selectable(
|
||||
selected = selected, onClick = { state.onNewModeSelected(option) },
|
||||
)
|
||||
.padding(start = 20.dp, end = 20.dp, bottom = 32.dp),
|
||||
.padding(start = 20.dp, end = 20.dp, top = 16.dp, bottom = 16.dp),
|
||||
) {
|
||||
|
||||
RadioButton(
|
||||
selected = selected, onClick = null,
|
||||
modifier = modifier.padding(end = 20.dp),
|
||||
colors = RadioButtonDefaults.colors(
|
||||
unselectedColor = colorResource(id = R.color.icon_secondary),
|
||||
selectedColor = colorResource(id = R.color.icon_accent),
|
||||
),
|
||||
)
|
||||
|
||||
Column {
|
||||
|
|
|
|||
|
|
@ -52,11 +52,9 @@ fun WalletConnectScreen(
|
|||
}
|
||||
},
|
||||
fab = {
|
||||
AddSessionFab(
|
||||
onAddSession = {
|
||||
state.onAddSession(context.getFromClipboard()?.toString())
|
||||
},
|
||||
)
|
||||
if (!state.isLoading) {
|
||||
AddSessionFab(onAddSession = { state.onAddSession(context.getFromClipboard()?.toString()) })
|
||||
}
|
||||
},
|
||||
titleRes = R.string.wallet_connect_title,
|
||||
onBackClick = onBackPressed,
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ class ApproveWcSessionDialog {
|
|||
}
|
||||
if (networks.size > 1) {
|
||||
setNeutralButton(context.getText(R.string.wallet_connect_select_network)) { _, _ ->
|
||||
store.dispatch(WalletConnectAction.SelectNetwork(networks))
|
||||
store.dispatch(GlobalAction.HideDialog(WalletConnectDialog.ApproveWcSession(session, networks)))
|
||||
store.dispatch(WalletConnectAction.SelectNetwork(session = session, networks = networks))
|
||||
}
|
||||
}
|
||||
setNegativeButton(context.getText(R.string.common_reject)) { _, _ ->
|
||||
|
|
|
|||
|
|
@ -5,22 +5,26 @@ import androidx.appcompat.app.AlertDialog
|
|||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
||||
object ChooseNetworkDialog {
|
||||
fun create(
|
||||
blockchains: List<Blockchain>,
|
||||
session: WalletConnectSession,
|
||||
networks: List<Blockchain>,
|
||||
context: Context,
|
||||
): AlertDialog {
|
||||
return AlertDialog.Builder(context)
|
||||
.setTitle(context.getString(R.string.wallet_connect_select_network))
|
||||
.setNegativeButton(context.getString(R.string.common_cancel)) { _, _ -> /* no-op */ }
|
||||
.setNegativeButton(context.getString(R.string.common_cancel)) { _, _ ->
|
||||
store.dispatch(WalletConnectAction.FailureEstablishingSession(session.session))
|
||||
}
|
||||
.setOnDismissListener {
|
||||
store.dispatch(GlobalAction.HideDialog())
|
||||
}
|
||||
.setSingleChoiceItems(blockchains.map { it.fullName }.toTypedArray(), 0) { _, which ->
|
||||
blockchains.getOrNull(which)?.let { selectedBlockchain ->
|
||||
.setSingleChoiceItems(networks.map { it.fullName }.toTypedArray(), 0) { _, which ->
|
||||
networks.getOrNull(which)?.let { selectedBlockchain ->
|
||||
store.dispatch(
|
||||
WalletConnectAction.ChooseNetwork(
|
||||
blockchain = selectedBlockchain,
|
||||
|
|
|
|||
|
|
@ -219,8 +219,10 @@ private fun sendTransaction(
|
|||
return@launch
|
||||
}
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
withMainContext {
|
||||
dispatch(SendAction.ChangeSendButtonState(ButtonState.ENABLED))
|
||||
tangemSdk.config.linkedTerminal = isLinkedTerminal
|
||||
|
||||
when (sendResult) {
|
||||
is SimpleResult.Success -> {
|
||||
store.state.globalState.analyticsHandlers?.triggerEvent(
|
||||
|
|
@ -260,12 +262,12 @@ private fun sendTransaction(
|
|||
card = card,
|
||||
)
|
||||
|
||||
val error = (sendResult.error as? BlockchainSdkError) ?: return@withContext
|
||||
val error = (sendResult.error as? BlockchainSdkError) ?: return@withMainContext
|
||||
|
||||
when (error) {
|
||||
is BlockchainSdkError.WrappedTangemError -> {
|
||||
val tangemSdkError = (error.tangemError as? TangemSdkError) ?: return@withContext
|
||||
if (tangemSdkError is TangemSdkError.UserCancelled) return@withContext
|
||||
val tangemSdkError = (error.tangemError as? TangemSdkError) ?: return@withMainContext
|
||||
if (tangemSdkError is TangemSdkError.UserCancelled) return@withMainContext
|
||||
|
||||
dispatch(SendAction.Dialog.SendTransactionFails.CardSdkError(tangemSdkError))
|
||||
}
|
||||
|
|
@ -294,7 +296,6 @@ private fun sendTransaction(
|
|||
}
|
||||
}
|
||||
}
|
||||
dispatch(SendAction.ChangeSendButtonState(ButtonState.ENABLED))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
package com.tangem.tap.network.exchangeServices
|
||||
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
|
||||
import com.tangem.tap.features.demo.isDemoCard
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class CardExchangeRules(
|
||||
val cardProvider: () -> Card?,
|
||||
) : ExchangeRules {
|
||||
|
||||
override fun isBuyAllowed(): Boolean {
|
||||
val card = cardProvider() ?: return false
|
||||
|
||||
return when {
|
||||
card.isDemoCard() -> false
|
||||
card.isStart2Coin -> false
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
override fun isSellAllowed(): Boolean {
|
||||
val card = cardProvider() ?: return false
|
||||
|
||||
return when {
|
||||
card.isDemoCard() -> false
|
||||
card.isStart2Coin -> false
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
override fun availableForBuy(currency: Currency): Boolean {
|
||||
val card = cardProvider() ?: return false
|
||||
|
||||
return when {
|
||||
card.isDemoCard() -> false
|
||||
card.isStart2Coin -> false
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
override fun availableForSell(currency: Currency): Boolean {
|
||||
val card = cardProvider() ?: return false
|
||||
|
||||
return when {
|
||||
card.isDemoCard() -> false
|
||||
card.isStart2Coin -> false
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@ import java.math.BigDecimal
|
|||
class CurrencyExchangeManager(
|
||||
private val buyService: ExchangeService,
|
||||
private val sellService: ExchangeService,
|
||||
private val primaryRules: ExchangeRules,
|
||||
) : ExchangeService, ExchangeUrlBuilder {
|
||||
|
||||
override suspend fun update() {
|
||||
|
|
@ -30,10 +31,16 @@ class CurrencyExchangeManager(
|
|||
sellService.update()
|
||||
}
|
||||
|
||||
override fun isBuyAllowed(): Boolean = buyService.isBuyAllowed()
|
||||
override fun isSellAllowed(): Boolean = sellService.isSellAllowed()
|
||||
override fun availableForBuy(currency: Currency): Boolean = buyService.availableForBuy(currency)
|
||||
override fun availableForSell(currency: Currency): Boolean = sellService.availableForSell(currency)
|
||||
override fun isBuyAllowed(): Boolean = primaryRules.isBuyAllowed() && buyService.isBuyAllowed()
|
||||
override fun isSellAllowed(): Boolean = primaryRules.isSellAllowed() && sellService.isSellAllowed()
|
||||
|
||||
override fun availableForBuy(currency: Currency): Boolean {
|
||||
return primaryRules.availableForBuy(currency) && buyService.availableForBuy(currency)
|
||||
}
|
||||
|
||||
override fun availableForSell(currency: Currency): Boolean {
|
||||
return primaryRules.availableForSell(currency) && sellService.availableForSell(currency)
|
||||
}
|
||||
|
||||
override fun getUrl(
|
||||
action: Action,
|
||||
|
|
|
|||
|
|
@ -3,8 +3,11 @@ package com.tangem.tap.network.exchangeServices
|
|||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
|
||||
interface ExchangeService {
|
||||
interface ExchangeService: ExchangeRules {
|
||||
suspend fun update()
|
||||
}
|
||||
|
||||
interface ExchangeRules {
|
||||
fun isBuyAllowed(): Boolean
|
||||
fun isSellAllowed(): Boolean
|
||||
fun availableForBuy(currency: Currency):Boolean
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@
|
|||
<string name="token_details_hide_token">Hide token</string>
|
||||
<string name="token_details_hide_alert_title">Hide %s</string>
|
||||
<string name="token_details_hide_alert_hide">Hide</string>
|
||||
<string name="token_details_hide_alert_message">You hide the token from the main screen, but you can add it back at any time.</string>
|
||||
<string name="token_details_hide_alert_message">You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page.</string>
|
||||
<string name="token_details_unable_hide_alert_title">Unable to hide %s</string>
|
||||
<string name="token_details_unable_hide_alert_message">The %s token is the main currency on the %s network and cannot be hidden as long as you have other tokens on this network in the list.</string>
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@
|
|||
<string name="token_details_hide_token">Hide token</string>
|
||||
<string name="token_details_hide_alert_title">Hide %s</string>
|
||||
<string name="token_details_hide_alert_hide">Hide</string>
|
||||
<string name="token_details_hide_alert_message">You hide the token from the main screen, but you can add it back at any time.</string>
|
||||
<string name="token_details_hide_alert_message">You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page.</string>
|
||||
<string name="token_details_unable_hide_alert_title">Unable to hide %s</string>
|
||||
<string name="token_details_unable_hide_alert_message">The %s token is the main currency on the %s network and cannot be hidden as long as you have other tokens on this network in the list.</string>
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@
|
|||
<string name="token_details_hide_token">Hide token</string>
|
||||
<string name="token_details_hide_alert_title">Hide %s</string>
|
||||
<string name="token_details_hide_alert_hide">Hide</string>
|
||||
<string name="token_details_hide_alert_message">You hide the token from the main screen, but you can add it back at any time.</string>
|
||||
<string name="token_details_hide_alert_message">You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page.</string>
|
||||
<string name="token_details_unable_hide_alert_title">Unable to hide %s</string>
|
||||
<string name="token_details_unable_hide_alert_message">The %s token is the main currency on the %s network and cannot be hidden as long as you have other tokens on this network in the list.</string>
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@
|
|||
<string name="token_details_hide_token">Скрыть токен</string>
|
||||
<string name="token_details_hide_alert_title">Скрыть %s</string>
|
||||
<string name="token_details_hide_alert_hide">Скрыть</string>
|
||||
<string name="token_details_hide_alert_message">Вы скрываете токен с главного экрана, но в любой момент сможете добавить его обратно.</string>
|
||||
<string name="token_details_hide_alert_message">Вы скрываете токен с главного экрана, но в любой момент сможете добавить его обратно через страницу управления токенами.</string>
|
||||
<string name="token_details_unable_hide_alert_title">Невозможно скрыть %s</string>
|
||||
|
||||
<string name="token_details_unable_hide_alert_message">Токен %s является основной валютой в сети %s и не может быть скрыт до тех пор, пока у вас в списке есть другие токены этой сети.</string>
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@
|
|||
<string name="token_details_hide_token">Hide token</string>
|
||||
<string name="token_details_hide_alert_title">Hide %s</string>
|
||||
<string name="token_details_hide_alert_hide">Hide</string>
|
||||
<string name="token_details_hide_alert_message">You are about to hide this token from the main screen. You can add it back anytime.</string>
|
||||
<string name="token_details_hide_alert_message">You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page.</string>
|
||||
<string name="token_details_unable_hide_alert_title">Unable to hide %s</string>
|
||||
<string name="token_details_unable_hide_alert_message">The %s token is the main currency on the %s network and cannot be hidden as long as you have other tokens on this network in the list.</string>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue