Updated on 2026-08-14
This commit is contained in:
commit
86eef602fd
69 changed files with 715 additions and 570 deletions
|
|
@ -18,12 +18,22 @@ class NavBarInsetsFragmentLifecycleCallback : FragmentLifecycleCallbacks() {
|
|||
savedInstanceState: Bundle?,
|
||||
) {
|
||||
if (v is ComposeView) return
|
||||
|
||||
ViewCompat.setOnApplyWindowInsetsListener(v) { view, windowInsets ->
|
||||
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars())
|
||||
view.updatePadding(
|
||||
bottom = insets.bottom,
|
||||
)
|
||||
val statusBarInsets = windowInsets.getInsets(WindowInsetsCompat.Type.statusBars())
|
||||
val navigationBarInsets = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars())
|
||||
|
||||
if (view.fitsSystemWindows) {
|
||||
view.updatePadding(
|
||||
top = statusBarInsets.top,
|
||||
bottom = navigationBarInsets.bottom,
|
||||
)
|
||||
} else {
|
||||
view.updatePadding(
|
||||
bottom = navigationBarInsets.bottom,
|
||||
)
|
||||
}
|
||||
windowInsets
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,11 +122,19 @@ class TapApplication : Application(), ImageLoaderFactory {
|
|||
)
|
||||
val logWriter = TangemLogCollector(
|
||||
levels = logLevels,
|
||||
messageFormatter = LogFormat.StairsFormatter()
|
||||
messageFormatter = LogFormat.StairsFormatter(),
|
||||
)
|
||||
Log.addLogger(logWriter)
|
||||
|
||||
store.dispatch(GlobalAction.SetFeedbackManager(FeedbackManager(infoHolder, logWriter)))
|
||||
store.dispatch(
|
||||
GlobalAction.SetFeedbackManager(
|
||||
FeedbackManager(
|
||||
infoHolder = infoHolder,
|
||||
logCollector = logWriter,
|
||||
preferencesStorage = preferencesStorage,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun initAppsFlyer() {
|
||||
|
|
|
|||
|
|
@ -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 ->
|
||||
|
|
|
|||
|
|
@ -116,6 +116,8 @@ fun BigDecimal.formatAmountAsSpannedString(
|
|||
val integer = amount.substringBefore('.')
|
||||
val reminder = amount.substringAfter('.')
|
||||
|
||||
// test formatter log Log.e("TEST ", BigDecimal("1234567890987654321.1234567890987654321").formatWithSpaces())
|
||||
|
||||
return buildSpannedString {
|
||||
append(integer)
|
||||
append('.')
|
||||
|
|
@ -136,7 +138,7 @@ fun BigDecimal.formatWithSpaces(): String {
|
|||
var index: Int = integerStr.length
|
||||
while (0 < index) {
|
||||
if (index <= 3) {
|
||||
packets.add(0, integerStr)
|
||||
packets.add(integerStr)
|
||||
break
|
||||
}
|
||||
index -= 3
|
||||
|
|
@ -145,7 +147,7 @@ fun BigDecimal.formatWithSpaces(): String {
|
|||
}
|
||||
|
||||
return buildString {
|
||||
packets.forEachIndexed { index, packet ->
|
||||
packets.reversed().forEachIndexed { index, packet ->
|
||||
append(packet)
|
||||
if (index != packets.lastIndex) append(' ')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,6 +76,6 @@ fun Store<*>.dispatchDialogShow(dialog: StateDialog) {
|
|||
|
||||
fun Store<*>.dispatchDialogHide() {
|
||||
scope.launch(Dispatchers.Main) {
|
||||
store.dispatch(GlobalAction.HideDialog())
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,9 +22,8 @@ import timber.log.Timber
|
|||
*/
|
||||
suspend fun WalletManager.safeUpdate(): Result<Wallet> = try {
|
||||
val scanResponse = store.state.globalState.scanResponse
|
||||
?: error("Scan response must not be null")
|
||||
|
||||
if (scanResponse.isDemoCard() || TestActions.testAmountInjectionForWalletManagerEnabled) {
|
||||
if (scanResponse?.isDemoCard() == true || TestActions.testAmountInjectionForWalletManagerEnabled) {
|
||||
delay(500)
|
||||
TestActions.testAmountInjectionForWalletManagerEnabled = false
|
||||
Result.Success(wallet)
|
||||
|
|
@ -52,11 +51,10 @@ suspend fun WalletManager.safeUpdate(): Result<Wallet> = try {
|
|||
|
||||
fun WalletManager?.getToUpUrl(): String? {
|
||||
val globalState = store.state.globalState
|
||||
val exchangeManager = globalState.exchangeManager ?: return null
|
||||
val wallet = this?.wallet ?: return null
|
||||
|
||||
val defaultAddress = wallet.address
|
||||
return exchangeManager.getUrl(
|
||||
return globalState.exchangeManager.getUrl(
|
||||
action = CurrencyExchangeManager.Action.Buy,
|
||||
blockchain = wallet.blockchain,
|
||||
cryptoCurrencyName = wallet.blockchain.currency,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.tap.common.feature
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface Feature {
|
||||
fun featureIsSwitchedOn():Boolean
|
||||
}
|
||||
|
|
@ -1,12 +1,14 @@
|
|||
package com.tangem.tap.common.feedback
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import com.tangem.domain.common.TapWorkarounds
|
||||
import com.tangem.tap.common.extensions.sendEmail
|
||||
import com.tangem.tap.common.log.TangemLogCollector
|
||||
import com.tangem.tap.common.zendesk.ZendeskConfig
|
||||
import com.tangem.tap.foregroundActivityObserver
|
||||
import com.tangem.tap.logConfig
|
||||
import com.tangem.tap.persistence.PreferencesStorage
|
||||
import com.tangem.tap.withForegroundActivity
|
||||
import com.tangem.wallet.R
|
||||
import com.zendesk.logger.Logger
|
||||
|
|
@ -14,6 +16,8 @@ import timber.log.Timber
|
|||
import zendesk.chat.Chat
|
||||
import zendesk.chat.ChatConfiguration
|
||||
import zendesk.chat.ChatEngine
|
||||
import zendesk.chat.ChatProvidersConfiguration
|
||||
import zendesk.chat.VisitorInfo
|
||||
import zendesk.configurations.Configuration
|
||||
import zendesk.messaging.MessagingActivity
|
||||
import java.io.File
|
||||
|
|
@ -26,6 +30,7 @@ import java.io.StringWriter
|
|||
class FeedbackManager(
|
||||
val infoHolder: AdditionalFeedbackInfo,
|
||||
private val logCollector: TangemLogCollector,
|
||||
private val preferencesStorage: PreferencesStorage,
|
||||
) {
|
||||
fun initChat(
|
||||
context: Context,
|
||||
|
|
@ -58,6 +63,7 @@ class FeedbackManager(
|
|||
fun openChat(feedbackData: FeedbackData) {
|
||||
feedbackData.prepare(infoHolder)
|
||||
foregroundActivityObserver.withForegroundActivity { activity ->
|
||||
setChatVisitorInfo()
|
||||
setChatVisitorNote(activity, feedbackData)
|
||||
showMessagingActivity(activity)
|
||||
}
|
||||
|
|
@ -82,6 +88,20 @@ class FeedbackManager(
|
|||
}
|
||||
}
|
||||
|
||||
private fun setChatVisitorInfo() {
|
||||
if (preferencesStorage.chatFirstLaunchTime == null) {
|
||||
preferencesStorage.chatFirstLaunchTime = System.currentTimeMillis()
|
||||
}
|
||||
val chatUserId = (preferencesStorage.chatFirstLaunchTime.toString() + Build.MODEL).hashCode()
|
||||
val visitorInfo = VisitorInfo.builder()
|
||||
.withName("User $chatUserId")
|
||||
.build()
|
||||
|
||||
Chat.INSTANCE.chatProvidersConfiguration = ChatProvidersConfiguration.builder()
|
||||
.withVisitorInfo(visitorInfo)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun setChatVisitorNote(
|
||||
context: Context,
|
||||
feedbackData: FeedbackData,
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ sealed class GlobalAction : Action {
|
|||
|
||||
// dialogs
|
||||
data class ShowDialog(val stateDialog: StateDialog) : GlobalAction()
|
||||
data class HideDialog(val stateDialog: StateDialog? = null) : GlobalAction()
|
||||
object HideDialog : GlobalAction()
|
||||
|
||||
sealed class Onboarding {
|
||||
data class Start(val scanResponse: ScanResponse?, val fromHomeScreen: Boolean = true) : GlobalAction()
|
||||
|
|
|
|||
|
|
@ -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,11 +68,7 @@ fun globalReducer(action: Action, state: AppState): GlobalState {
|
|||
globalState.copy(dialog = action.stateDialog)
|
||||
}
|
||||
is GlobalAction.HideDialog -> {
|
||||
if (action.stateDialog == null || action.stateDialog == globalState.dialog) {
|
||||
globalState.copy(dialog = null)
|
||||
} else {
|
||||
globalState
|
||||
}
|
||||
globalState.copy(dialog = null)
|
||||
}
|
||||
is GlobalAction.ExchangeManager.Init.Success -> {
|
||||
globalState.copy(exchangeManager = action.exchangeManager)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ data class GlobalState(
|
|||
val appCurrency: FiatCurrency = FiatCurrency.Default,
|
||||
val scanCardFailsCounter: Int = 0,
|
||||
val dialog: StateDialog? = null,
|
||||
val exchangeManager: CurrencyExchangeManager? = null,
|
||||
val exchangeManager: CurrencyExchangeManager = CurrencyExchangeManager.dummy(),
|
||||
val resources: AndroidResources = AndroidResources(),
|
||||
val analyticsHandlers: AnalyticsHandler? = null,
|
||||
val userCountryCode: String? = null,
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class SimpleCancelableAlertDialog {
|
|||
setNegativeButton(context.getText(secondaryButtonRes)) { _, _ -> secondaryButtonAction()}
|
||||
}
|
||||
setOnDismissListener {
|
||||
store.dispatch(GlobalAction.HideDialog())
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
}
|
||||
}.create()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -7,13 +7,14 @@ import com.tangem.blockchain.common.TransactionData
|
|||
import com.tangem.blockchain.common.TransactionSender
|
||||
import com.tangem.blockchain.common.TransactionSigner
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchain.common.toBlockchainCustomError
|
||||
import com.tangem.blockchain.common.toBlockchainSdkError
|
||||
import com.tangem.blockchain.extensions.Result
|
||||
import com.tangem.blockchain.extensions.SimpleResult
|
||||
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)
|
||||
|
|
@ -482,7 +484,7 @@ class DemoTransactionSender(
|
|||
publicKey = walletManager.wallet.publicKey
|
||||
)
|
||||
return when (signerResponse) {
|
||||
is CompletionResult.Success -> SimpleResult.Failure(Exception(ID).toBlockchainCustomError())
|
||||
is CompletionResult.Success -> SimpleResult.Failure(Exception(ID).toBlockchainSdkError())
|
||||
is CompletionResult.Failure -> SimpleResult.fromTangemSdkError(signerResponse.error)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ 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.WalletConnectDialog
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
|
@ -23,18 +22,22 @@ class ApproveWcSessionDialog {
|
|||
setTitle(context.getString(R.string.wallet_connect))
|
||||
setMessage(message)
|
||||
setPositiveButton(context.getText(R.string.common_start)) { _, _ ->
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
store.dispatch(WalletConnectAction.ChooseNetwork(session.wallet.blockchain!!))
|
||||
}
|
||||
if (networks.size > 1) {
|
||||
setNeutralButton(context.getText(R.string.wallet_connect_select_network)) { _, _ ->
|
||||
store.dispatch(WalletConnectAction.SelectNetwork(networks))
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
store.dispatch(WalletConnectAction.SelectNetwork(session = session, networks = networks))
|
||||
}
|
||||
}
|
||||
setNegativeButton(context.getText(R.string.common_reject)) { _, _ ->
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
store.dispatch(WalletConnectAction.FailureEstablishingSession(session.session))
|
||||
}
|
||||
setOnDismissListener {
|
||||
store.dispatch(GlobalAction.HideDialog(WalletConnectDialog.ApproveWcSession(session, networks)))
|
||||
setOnCancelListener {
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
store.dispatch(WalletConnectAction.FailureEstablishingSession(session.session))
|
||||
}
|
||||
}.create()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ class BnbTransactionDialog {
|
|||
store.dispatch(WalletConnectAction.RejectRequest(session, sessionId))
|
||||
}
|
||||
setOnDismissListener {
|
||||
store.dispatch(GlobalAction.HideDialog())
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
}
|
||||
}.create()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,28 +5,32 @@ 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 */ }
|
||||
.setOnDismissListener {
|
||||
store.dispatch(GlobalAction.HideDialog())
|
||||
.setNegativeButton(context.getString(R.string.common_cancel)) { _, _ ->
|
||||
store.dispatch(WalletConnectAction.FailureEstablishingSession(session.session))
|
||||
}
|
||||
.setSingleChoiceItems(blockchains.map { it.fullName }.toTypedArray(), 0) { _, which ->
|
||||
blockchains.getOrNull(which)?.let { selectedBlockchain ->
|
||||
.setOnDismissListener {
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
}
|
||||
.setSingleChoiceItems(networks.map { it.fullName }.toTypedArray(), 0) { _, which ->
|
||||
networks.getOrNull(which)?.let { selectedBlockchain ->
|
||||
store.dispatch(
|
||||
WalletConnectAction.ChooseNetwork(
|
||||
blockchain = selectedBlockchain,
|
||||
),
|
||||
)
|
||||
store.dispatch(GlobalAction.HideDialog())
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
}
|
||||
}
|
||||
.create()
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class ClipboardOrScanQrDialog {
|
|||
store.dispatch(NavigationAction.NavigateTo(AppScreen.QrScan))
|
||||
}
|
||||
setOnDismissListener {
|
||||
store.dispatch(GlobalAction.HideDialog())
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
}
|
||||
}.create()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ class PersonalSignDialog {
|
|||
store.dispatch(WalletConnectAction.RejectRequest(data.session, data.id))
|
||||
}
|
||||
setOnDismissListener {
|
||||
store.dispatch(GlobalAction.HideDialog())
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
}
|
||||
}.create()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ class TransactionDialog {
|
|||
store.dispatch(WalletConnectAction.RejectRequest(data.session, data.id))
|
||||
}
|
||||
setOnDismissListener {
|
||||
store.dispatch(GlobalAction.HideDialog())
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
}
|
||||
}.create()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ data class OnboardingNoteState(
|
|||
get() = steps.indexOf(currentStep)
|
||||
|
||||
val isBuyAllowed: Boolean by ReadOnlyProperty<Any, Boolean> { _, _ ->
|
||||
store.state.globalState.exchangeManager?.availableForBuy(walletBalance.currency) ?: false
|
||||
store.state.globalState.exchangeManager.availableForBuy(walletBalance.currency)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,19 @@ import com.tangem.common.extensions.guard
|
|||
import com.tangem.domain.common.ScanResponse
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.extensions.dispatchDialogShow
|
||||
import com.tangem.tap.common.extensions.dispatchErrorNotification
|
||||
import com.tangem.tap.common.extensions.dispatchOpenUrl
|
||||
import com.tangem.tap.common.extensions.getAddressData
|
||||
import com.tangem.tap.common.extensions.getToUpUrl
|
||||
import com.tangem.tap.common.extensions.onCardScanned
|
||||
import com.tangem.tap.common.postUi
|
||||
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.currenciesRepository
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.extensions.makePrimaryWalletManager
|
||||
import com.tangem.tap.domain.twins.TwinCardsManager
|
||||
|
|
@ -265,7 +271,8 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
|
|||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
|
||||
}
|
||||
CreateTwinWalletMode.RecreateWallet -> {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
currenciesRepository.removeCurrencies(scanResponse.card.cardId)
|
||||
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ data class TwinCardsState(
|
|||
get() = currentStep == TwinCardsStep.CreateSecondWallet || currentStep == TwinCardsStep.CreateThirdWallet
|
||||
|
||||
val isBuyAllowed: Boolean by ReadOnlyProperty<Any, Boolean> { _, _ ->
|
||||
store.state.globalState.exchangeManager?.availableForBuy(walletBalance.currency) ?: false
|
||||
store.state.globalState.exchangeManager.availableForBuy(walletBalance.currency)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class AddMoreBackupCardsDialog {
|
|||
|
||||
}
|
||||
setOnDismissListener {
|
||||
store.dispatch(GlobalAction.HideDialog())
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
}
|
||||
}.create()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ class BackupInProgressDialog {
|
|||
setTitle(R.string.alert_title)
|
||||
setMessage(R.string.onboarding_backup_exit_warning)
|
||||
setPositiveButton(R.string.warning_button_ok) { _, _ ->
|
||||
store.dispatch(GlobalAction.HideDialog())
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
}
|
||||
setOnDismissListener {
|
||||
store.dispatch(GlobalAction.HideDialog())
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
}
|
||||
}.create()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class ConfirmDiscardingBackupDialog {
|
|||
store.dispatch(BackupAction.DiscardSavedBackup)
|
||||
}
|
||||
setOnDismissListener {
|
||||
store.dispatch(GlobalAction.HideDialog())
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
}
|
||||
setCancelable(false)
|
||||
}.create()
|
||||
|
|
|
|||
|
|
@ -15,11 +15,11 @@ class UnfinishedBackupFoundDialog {
|
|||
setTitle(R.string.alert_title)
|
||||
setMessage(R.string.welcome_interrupted_backup_alert_message)
|
||||
setPositiveButton(R.string.welcome_interrupted_backup_alert_resume) { _, _ ->
|
||||
store.dispatch(GlobalAction.HideDialog())
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
store.dispatch(BackupAction.ResumeBackup)
|
||||
}
|
||||
setNegativeButton(R.string.welcome_interrupted_backup_alert_discard) { _, _ ->
|
||||
store.dispatch(GlobalAction.HideDialog())
|
||||
store.dispatch(GlobalAction.HideDialog)
|
||||
store.dispatch(GlobalAction.ShowDialog(BackupDialog.ConfirmDiscardingBackup))
|
||||
}
|
||||
setCancelable(false)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,18 @@ package com.tangem.tap.features.send.redux.reducers
|
|||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.tap.common.CurrencyConverter
|
||||
import com.tangem.tap.common.entities.IndeterminateProgressButton
|
||||
import com.tangem.tap.features.send.redux.*
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdActionUi
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction
|
||||
import com.tangem.tap.features.send.redux.AmountAction
|
||||
import com.tangem.tap.features.send.redux.AmountActionUi
|
||||
import com.tangem.tap.features.send.redux.FeeAction
|
||||
import com.tangem.tap.features.send.redux.FeeActionUi
|
||||
import com.tangem.tap.features.send.redux.PrepareSendScreen
|
||||
import com.tangem.tap.features.send.redux.ReceiptAction
|
||||
import com.tangem.tap.features.send.redux.ReleaseSendState
|
||||
import com.tangem.tap.features.send.redux.SendAction
|
||||
import com.tangem.tap.features.send.redux.SendScreenAction
|
||||
import com.tangem.tap.features.send.redux.TransactionExtrasAction
|
||||
import com.tangem.tap.features.send.redux.states.ExternalTransactionData
|
||||
import com.tangem.tap.features.send.redux.states.IdStateHolder
|
||||
import com.tangem.tap.features.send.redux.states.SendState
|
||||
|
|
@ -46,7 +57,8 @@ private class SendReducer : SendInternalReducer {
|
|||
sendState.copy(sendButtonState = IndeterminateProgressButton(action.state))
|
||||
}
|
||||
is SendAction.Dialog.TezosWarningDialog -> sendState.copy(dialog = action)
|
||||
is SendAction.Dialog.SendTransactionFails -> sendState.copy(dialog = action)
|
||||
is SendAction.Dialog.SendTransactionFails.CardSdkError -> sendState.copy(dialog = action)
|
||||
is SendAction.Dialog.SendTransactionFails.BlockchainSdkError -> sendState.copy(dialog = action)
|
||||
is SendAction.Dialog.Hide -> sendState.copy(dialog = null)
|
||||
is SendAction.Warnings.Set -> sendState.copy(sendWarningsList = action.warningList)
|
||||
is SendAction.SendSpecificTransaction ->
|
||||
|
|
|
|||
|
|
@ -7,9 +7,6 @@ import com.tangem.blockchain.common.Wallet
|
|||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.domain.common.extensions.canHandleToken
|
||||
import com.tangem.domain.common.extensions.toCoinId
|
||||
import com.tangem.domain.features.addCustomToken.CustomCurrency
|
||||
import com.tangem.tap.common.entities.Button
|
||||
import com.tangem.tap.common.extensions.toQrCode
|
||||
import com.tangem.tap.common.redux.global.CryptoCurrencyName
|
||||
|
|
@ -17,12 +14,18 @@ import com.tangem.tap.common.toggleWidget.WidgetState
|
|||
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
|
||||
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
|
||||
import com.tangem.tap.features.wallet.models.*
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.models.PendingTransaction
|
||||
import com.tangem.tap.features.wallet.models.TotalBalance
|
||||
import com.tangem.tap.features.wallet.models.WalletRent
|
||||
import com.tangem.tap.features.wallet.models.WalletWarning
|
||||
import com.tangem.tap.features.wallet.models.hasPendingTransactions
|
||||
import com.tangem.tap.features.wallet.models.hasSendableAmounts
|
||||
import com.tangem.tap.features.wallet.models.isSendableAmount
|
||||
import com.tangem.tap.features.wallet.redux.reducers.calculateTotalFiatAmount
|
||||
import com.tangem.tap.features.wallet.redux.reducers.findProgressState
|
||||
import com.tangem.tap.features.wallet.ui.BalanceStatus
|
||||
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
|
||||
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
|
||||
import com.tangem.tap.store
|
||||
import org.rekotlin.StateType
|
||||
import java.math.BigDecimal
|
||||
|
|
@ -48,21 +51,15 @@ data class WalletState(
|
|||
|
||||
// if you do not delegate - the application crashes on startup,
|
||||
// because twinCardsState has not been created yet
|
||||
val twinCardsState: TwinCardsState by ReadOnlyProperty<Any, TwinCardsState> { thisRef, property ->
|
||||
val twinCardsState: TwinCardsState by ReadOnlyProperty<Any, TwinCardsState> { _, _ ->
|
||||
store.state.twinCardsState
|
||||
}
|
||||
|
||||
val isTangemTwins: Boolean
|
||||
get() = store.state.globalState.scanResponse?.isTangemTwins() == true
|
||||
|
||||
val primaryWallet: WalletData? = wallets.firstOrNull()
|
||||
?.walletsData?.firstOrNull()
|
||||
val primaryWalletManager: WalletManager? =
|
||||
if (wallets.isNotEmpty()) wallets[0].walletManager else null
|
||||
|
||||
val shouldShowDetails: Boolean =
|
||||
primaryWallet?.currencyData?.status != BalanceStatus.EmptyCard &&
|
||||
primaryWallet?.currencyData?.status != BalanceStatus.UnknownBlockchain
|
||||
val isExchangeServiceFeatureOn: Boolean
|
||||
get() = store.state.globalState.exchangeManager.featureIsSwitchedOn()
|
||||
|
||||
val blockchains: List<Blockchain>
|
||||
get() = wallets.mapNotNull { it.walletManager?.wallet?.blockchain }
|
||||
|
|
@ -76,6 +73,14 @@ data class WalletState(
|
|||
val walletManagers: List<WalletManager>
|
||||
get() = wallets.mapNotNull { it.walletManager }
|
||||
|
||||
val primaryWallet: WalletData? = wallets.firstOrNull()?.walletsData?.firstOrNull()
|
||||
|
||||
val primaryWalletManager: WalletManager? = if (wallets.isNotEmpty()) wallets[0].walletManager else null
|
||||
|
||||
val shouldShowDetails: Boolean =
|
||||
primaryWallet?.currencyData?.status != BalanceStatus.EmptyCard &&
|
||||
primaryWallet?.currencyData?.status != BalanceStatus.UnknownBlockchain
|
||||
|
||||
fun getWalletManager(currency: Currency?): WalletManager? {
|
||||
if (currency?.blockchain == null) return null
|
||||
return getWalletStore(currency)?.walletManager
|
||||
|
|
@ -248,32 +253,6 @@ data class WalletState(
|
|||
return updatedWallets + remainingWallets
|
||||
}
|
||||
|
||||
fun updateTradeCryptoState(
|
||||
exchangeManager: CurrencyExchangeManager?,
|
||||
walletData: WalletData
|
||||
): WalletData {
|
||||
return walletData.copy(
|
||||
tradeCryptoState = TradeCryptoState.from(
|
||||
exchangeManager,
|
||||
walletData
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun updateTradeCryptoState(
|
||||
exchangeManager: CurrencyExchangeManager?,
|
||||
walletDataList: List<WalletData>
|
||||
): List<WalletData> {
|
||||
return walletDataList.map {
|
||||
it.copy(
|
||||
tradeCryptoState = TradeCryptoState.from(
|
||||
exchangeManager,
|
||||
it
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateTotalBalance(): WalletState {
|
||||
val walletsData = this.wallets
|
||||
.flatMap(WalletStore::walletsData)
|
||||
|
|
@ -352,39 +331,24 @@ data class Artwork(
|
|||
}
|
||||
}
|
||||
|
||||
data class TradeCryptoState(
|
||||
val isAvailableToSell: () -> Boolean = { false },
|
||||
val isAvailableToBuy: () -> Boolean = { false },
|
||||
) {
|
||||
companion object {
|
||||
fun from(
|
||||
exchangeManager: CurrencyExchangeManager?,
|
||||
walletData: WalletData
|
||||
): TradeCryptoState {
|
||||
val exchanger = exchangeManager ?: return walletData.tradeCryptoState
|
||||
val currency = walletData.currency
|
||||
|
||||
return TradeCryptoState(
|
||||
isAvailableToSell = { exchanger.availableForSell(currency) },
|
||||
isAvailableToBuy = { exchanger.availableForBuy(currency) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class WalletData(
|
||||
val pendingTransactions: List<PendingTransaction> = emptyList(),
|
||||
val hashesCountVerified: Boolean? = null,
|
||||
val walletAddresses: WalletAddresses? = null,
|
||||
val currencyData: BalanceWidgetData = BalanceWidgetData(),
|
||||
val updatingWallet: Boolean = false,
|
||||
val tradeCryptoState: TradeCryptoState = TradeCryptoState(),
|
||||
val fiatRateString: String? = null,
|
||||
val fiatRate: BigDecimal? = null,
|
||||
val mainButton: WalletMainButton = WalletMainButton.SendButton(false),
|
||||
val currency: Currency,
|
||||
val walletRent: WalletRent? = null,
|
||||
) {
|
||||
val isAvailableToBuy: Boolean
|
||||
get() = store.state.globalState.exchangeManager.availableForBuy(currency)
|
||||
|
||||
val isAvailableToSell: Boolean
|
||||
get() = store.state.globalState.exchangeManager.availableForSell(currency)
|
||||
|
||||
fun shouldShowMultipleAddress(): Boolean {
|
||||
val listOfAddresses = walletAddresses?.list ?: return false
|
||||
return listOfAddresses.size > 1
|
||||
|
|
|
|||
|
|
@ -37,20 +37,18 @@ 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
|
||||
}
|
||||
|
||||
val selectedWalletData = store.state.walletState.getSelectedWalletData() ?: return
|
||||
val exchangeManager = store.state.globalState.exchangeManager ?: return
|
||||
val card = store.state.globalState.scanResponse?.card ?: return
|
||||
val appCurrency = store.state.globalState.appCurrency
|
||||
|
||||
val addresses = selectedWalletData.walletAddresses?.list.orEmpty()
|
||||
if (addresses.isEmpty()) return
|
||||
|
||||
val exchangeManager = store.state.globalState.exchangeManager
|
||||
val appCurrency = store.state.globalState.appCurrency
|
||||
val currency = selectedWalletData.currency
|
||||
|
||||
if (currency is Currency.Token && currency.blockchain.isTestnet()) {
|
||||
|
|
@ -81,15 +79,13 @@ class TradeCryptoMiddleware {
|
|||
|
||||
private fun proceedSellAction() {
|
||||
val selectedWalletData = store.state.walletState.getSelectedWalletData() ?: return
|
||||
val exchangeManager = store.state.globalState.exchangeManager ?: return
|
||||
val appCurrency = store.state.globalState.appCurrency
|
||||
|
||||
val appCurrency = store.state.globalState.appCurrency
|
||||
val addresses = selectedWalletData.walletAddresses?.list.orEmpty()
|
||||
if (addresses.isEmpty()) return
|
||||
|
||||
val currency = selectedWalletData.currency
|
||||
|
||||
exchangeManager.getUrl(
|
||||
store.state.globalState.exchangeManager.getUrl(
|
||||
action = CurrencyExchangeManager.Action.Sell,
|
||||
blockchain = currency.blockchain,
|
||||
cryptoCurrencyName = currency.currencySymbol,
|
||||
|
|
@ -100,8 +96,8 @@ class TradeCryptoMiddleware {
|
|||
|
||||
private fun preconfigureAndOpenSendScreen(action: WalletAction.TradeCryptoAction.SendCrypto) {
|
||||
val selectedWalletData = store.state.walletState.getSelectedWalletData() ?: return
|
||||
val walletManager =
|
||||
store.state.walletState.getWalletManager(selectedWalletData.currency)
|
||||
|
||||
val walletManager = store.state.walletState.getWalletManager(selectedWalletData.currency)
|
||||
store.dispatchOnMain(PrepareSendScreen(
|
||||
coinAmount = walletManager?.wallet?.amounts?.get(AmountType.Coin),
|
||||
coinRate = selectedWalletData.fiatRate,
|
||||
|
|
@ -116,11 +112,10 @@ class TradeCryptoMiddleware {
|
|||
}
|
||||
|
||||
private fun openReceiptUrl(transactionId: String) {
|
||||
val exchangeManager = store.state.globalState.exchangeManager ?: return
|
||||
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo())
|
||||
exchangeManager.getSellCryptoReceiptUrl(CurrencyExchangeManager.Action.Sell, transactionId)?.let {
|
||||
store.dispatchOnMain(NavigationAction.OpenUrl(it))
|
||||
}
|
||||
store.state.globalState.exchangeManager.getSellCryptoReceiptUrl(
|
||||
action = CurrencyExchangeManager.Action.Sell,
|
||||
transactionId = transactionId,
|
||||
)?.let { store.dispatchOnMain(NavigationAction.OpenUrl(it)) }
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,9 @@ import com.tangem.tap.features.wallet.models.Currency
|
|||
import com.tangem.tap.features.wallet.models.filterByToken
|
||||
import com.tangem.tap.features.wallet.models.getPendingTransactions
|
||||
import com.tangem.tap.features.wallet.models.removeUnknownTransactions
|
||||
import com.tangem.tap.features.wallet.redux.*
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
import com.tangem.tap.features.wallet.redux.WalletMainButton
|
||||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN
|
||||
import com.tangem.tap.features.wallet.ui.BalanceStatus
|
||||
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
|
||||
|
|
@ -38,8 +40,6 @@ class OnWalletLoadedReducer {
|
|||
val walletData = walletState.getWalletData(blockchainNetwork) ?: return walletState
|
||||
|
||||
val fiatCurrency = store.state.globalState.appCurrency
|
||||
val exchangeManager = store.state.globalState.exchangeManager
|
||||
|
||||
val coinAmountValue = wallet.amounts[AmountType.Coin]?.value
|
||||
val formattedAmount = coinAmountValue?.toFormattedCurrencyString(
|
||||
wallet.blockchain.decimals(),
|
||||
|
|
@ -71,7 +71,6 @@ class OnWalletLoadedReducer {
|
|||
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
|
||||
mainButton = WalletMainButton.SendButton(isCoinSendButtonEnabled),
|
||||
currency = Currency.fromBlockchainNetwork(blockchainNetwork),
|
||||
tradeCryptoState = TradeCryptoState.from(exchangeManager, walletData),
|
||||
)
|
||||
|
||||
val tokens = wallet.getTokens().mapNotNull { token ->
|
||||
|
|
@ -104,7 +103,6 @@ class OnWalletLoadedReducer {
|
|||
),
|
||||
pendingTransactions = tokenPendingTransactions.removeUnknownTransactions(),
|
||||
mainButton = WalletMainButton.SendButton(isTokenSendButtonEnabled),
|
||||
tradeCryptoState = TradeCryptoState.from(exchangeManager, tokenWalletData),
|
||||
)
|
||||
}
|
||||
val newWallets = tokens + newWalletData
|
||||
|
|
@ -118,8 +116,6 @@ class OnWalletLoadedReducer {
|
|||
if (wallet.blockchain != walletState.primaryBlockchain) return walletState
|
||||
|
||||
val fiatCurrencyName = store.state.globalState.appCurrency.code
|
||||
val exchangeManager = store.state.globalState.exchangeManager
|
||||
|
||||
val token = wallet.getFirstToken()
|
||||
val tokenData = if (token != null) {
|
||||
val tokenAmount = wallet.getTokenAmount(token)
|
||||
|
|
@ -167,7 +163,6 @@ class OnWalletLoadedReducer {
|
|||
),
|
||||
pendingTransactions = pendingTransactions.removeUnknownTransactions(),
|
||||
mainButton = WalletMainButton.SendButton(sendButtonEnabled),
|
||||
tradeCryptoState = TradeCryptoState.from(exchangeManager, walletState.primaryWallet),
|
||||
)
|
||||
val wallets = listOfNotNull(walletData)
|
||||
val updatedStore = walletState.getWalletStore(walletData?.currency)?.updateWallets(wallets)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,11 @@ import com.tangem.blockchain.common.Wallet
|
|||
import com.tangem.common.extensions.mapNotNullValues
|
||||
import com.tangem.domain.common.TwinCardNumber
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.extensions.toFiatRateString
|
||||
import com.tangem.tap.common.extensions.toFiatString
|
||||
import com.tangem.tap.common.extensions.toFiatValue
|
||||
import com.tangem.tap.common.extensions.toFormattedCurrencyString
|
||||
import com.tangem.tap.common.extensions.toFormattedFiatValue
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.extensions.getArtworkUrl
|
||||
|
|
@ -14,10 +18,18 @@ import com.tangem.tap.domain.getFirstToken
|
|||
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.models.WalletRent
|
||||
import com.tangem.tap.features.wallet.redux.*
|
||||
import com.tangem.tap.features.wallet.redux.AddressData
|
||||
import com.tangem.tap.features.wallet.redux.Artwork
|
||||
import com.tangem.tap.features.wallet.redux.ErrorType
|
||||
import com.tangem.tap.features.wallet.redux.ProgressState
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletAddresses
|
||||
import com.tangem.tap.features.wallet.redux.WalletData
|
||||
import com.tangem.tap.features.wallet.redux.WalletMainButton
|
||||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
import com.tangem.tap.features.wallet.redux.WalletStore
|
||||
import com.tangem.tap.features.wallet.ui.BalanceStatus
|
||||
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
|
||||
import com.tangem.tap.store
|
||||
import org.rekotlin.Action
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
|
@ -35,7 +47,6 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
|
||||
if (action !is WalletAction) return state.walletState
|
||||
|
||||
val exchangeManager = store.state.globalState.exchangeManager
|
||||
var newState = state.walletState
|
||||
|
||||
when (action) {
|
||||
|
|
@ -144,10 +155,6 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
currencySymbol = walletData.currencyData.currencySymbol,
|
||||
),
|
||||
mainButton = WalletMainButton.SendButton(false),
|
||||
tradeCryptoState = TradeCryptoState.from(
|
||||
exchangeManager,
|
||||
walletData
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
@ -171,13 +178,9 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
currencySymbol = wallet.currencyData.currencySymbol,
|
||||
),
|
||||
mainButton = WalletMainButton.SendButton(false),
|
||||
tradeCryptoState = TradeCryptoState.from(exchangeManager, wallet)
|
||||
)
|
||||
}
|
||||
val wallets = newState.updateTradeCryptoState(
|
||||
exchangeManager,
|
||||
newState.replaceSomeWallets(newWallets)
|
||||
)
|
||||
val wallets = newState.replaceSomeWallets(newWallets)
|
||||
val walletStore = newState.getWalletStore(action.blockchain)?.updateWallets(wallets)
|
||||
newState = newState.updateWalletStore(walletStore)
|
||||
}
|
||||
|
|
@ -210,14 +213,9 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
)
|
||||
)
|
||||
}
|
||||
var updatedWalletStore = newState.getWalletStore(action.blockchain)
|
||||
val updatedWalletStore = newState.getWalletStore(action.blockchain)
|
||||
?.updateWallets(listOfNotNull(walletData))
|
||||
|
||||
updatedWalletStore =
|
||||
updatedWalletStore?.updateWallets(
|
||||
newState.updateTradeCryptoState(exchangeManager, updatedWalletStore.walletsData)
|
||||
)
|
||||
|
||||
newState = newState.updateWalletStore(updatedWalletStore)
|
||||
}
|
||||
|
||||
|
|
@ -248,11 +246,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
|
|||
)
|
||||
)
|
||||
}
|
||||
val updatedWallets =
|
||||
newState.updateTradeCryptoState(
|
||||
exchangeManager,
|
||||
walletStore!!.updateWallets(listOfNotNull(newWalletData) + tokenWallets).walletsData
|
||||
)
|
||||
val updatedWallets = walletStore!!.updateWallets(listOfNotNull(newWalletData) + tokenWallets).walletsData
|
||||
|
||||
newState = newState.updateWalletsData(updatedWallets)
|
||||
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
|||
setupAddressCard(selectedWallet)
|
||||
setupNoInternetHandling(state)
|
||||
setupBalanceData(selectedWallet.currencyData)
|
||||
setupButtons(selectedWallet)
|
||||
setupButtons(selectedWallet, state.isExchangeServiceFeatureOn)
|
||||
|
||||
handleCurrencyIcon(selectedWallet)
|
||||
handleWarnings(selectedWallet)
|
||||
|
|
@ -186,7 +186,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
|||
)
|
||||
}
|
||||
|
||||
private fun setupButtons(selectedWallet: WalletData) = with(binding) {
|
||||
private fun setupButtons(selectedWallet: WalletData, isExchangeServiceFeatureOn: Boolean) = with(binding) {
|
||||
lWalletDetails.btnCopy.setOnClickListener {
|
||||
selectedWallet.walletAddresses?.selectedAddress?.address?.let { addressString ->
|
||||
store.dispatch(WalletAction.CopyAddress(addressString, requireContext()))
|
||||
|
|
@ -199,8 +199,9 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details),
|
|||
}
|
||||
|
||||
rowButtons.updateButtonsVisibility(
|
||||
buyAllowed = selectedWallet.tradeCryptoState.isAvailableToBuy(),
|
||||
sellAllowed = selectedWallet.tradeCryptoState.isAvailableToSell(),
|
||||
exchangeServiceFeatureOn = isExchangeServiceFeatureOn,
|
||||
buyAllowed = selectedWallet.isAvailableToBuy,
|
||||
sellAllowed = selectedWallet.isAvailableToSell,
|
||||
sendAllowed = selectedWallet.mainButton.enabled,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,22 +114,26 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
|
||||
override fun newState(state: WalletState) {
|
||||
if (activity == null || view == null) return
|
||||
|
||||
val isSaltPay = store.state.globalState.scanResponse?.card?.isSaltPay == true
|
||||
|
||||
when {
|
||||
isSaltPay -> {
|
||||
isSaltPay && (walletView !is SaltPaySingleWalletView) -> {
|
||||
walletView = SaltPaySingleWalletView()
|
||||
walletView.changeWalletView(this, binding)
|
||||
}
|
||||
state.isMultiwalletAllowed &&
|
||||
state.primaryWallet?.currencyData?.status != BalanceStatus.EmptyCard &&
|
||||
walletView is SingleWalletView -> {
|
||||
state.isMultiwalletAllowed && state.primaryWallet?.currencyData?.status != BalanceStatus.EmptyCard &&
|
||||
walletView !is MultiWalletView -> {
|
||||
walletView = MultiWalletView()
|
||||
walletView.changeWalletView(this, binding)
|
||||
}
|
||||
!state.isMultiwalletAllowed && walletView is MultiWalletView -> {
|
||||
!state.isMultiwalletAllowed && walletView !is SingleWalletView -> {
|
||||
walletView = SingleWalletView()
|
||||
walletView.changeWalletView(this, binding)
|
||||
}
|
||||
else -> {} // we keep the same view unless we scan a card that requires a different view
|
||||
}
|
||||
|
||||
walletView.changeWalletView(this, binding)
|
||||
walletView.onNewState(state)
|
||||
|
||||
if (!state.shouldShowDetails) {
|
||||
|
|
|
|||
|
|
@ -14,9 +14,8 @@ internal class WalletDetailsButtonsRow @JvmOverloads constructor(
|
|||
) : LinearLayout(context, attrs, defStyleAttr) {
|
||||
private val binding = ViewWalletDetailsButtonsRowBinding.inflate(
|
||||
LayoutInflater.from(context),
|
||||
this
|
||||
this,
|
||||
)
|
||||
|
||||
var onBuyClick: (() -> Unit)? = null
|
||||
var onSellClick: (() -> Unit)? = null
|
||||
var onTradeClick: (() -> Unit)? = null
|
||||
|
|
@ -34,10 +33,13 @@ internal class WalletDetailsButtonsRow @JvmOverloads constructor(
|
|||
}
|
||||
|
||||
fun updateButtonsVisibility(
|
||||
exchangeServiceFeatureOn: Boolean,
|
||||
buyAllowed: Boolean,
|
||||
sellAllowed: Boolean,
|
||||
sendAllowed: Boolean
|
||||
sendAllowed: Boolean,
|
||||
) = with(binding) {
|
||||
containerExchangeButtons.isVisible = exchangeServiceFeatureOn
|
||||
|
||||
btnBuy.isVisible = (buyAllowed && !sellAllowed) || (!buyAllowed && !sellAllowed)
|
||||
btnBuy.isEnabled = buyAllowed
|
||||
btnSell.isVisible = !buyAllowed && sellAllowed
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tap.features.wallet.ui.wallet
|
||||
|
||||
import android.widget.Button
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.tangem.common.card.Card
|
||||
|
|
@ -20,11 +21,11 @@ import com.tangem.tap.features.wallet.redux.WalletState
|
|||
import com.tangem.tap.features.wallet.ui.BalanceStatus
|
||||
import com.tangem.tap.features.wallet.ui.WalletFragment
|
||||
import com.tangem.tap.features.wallet.ui.adapters.WalletAdapter
|
||||
import com.tangem.tap.features.wallet.ui.view.WalletDetailsButtonsRow
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.databinding.FragmentWalletBinding
|
||||
|
||||
|
||||
class MultiWalletView : WalletView() {
|
||||
private lateinit var walletsAdapter: WalletAdapter
|
||||
override fun changeWalletView(fragment: WalletFragment, binding: FragmentWalletBinding) {
|
||||
|
|
@ -33,14 +34,12 @@ class MultiWalletView : WalletView() {
|
|||
showMultiWalletView(binding)
|
||||
}
|
||||
|
||||
|
||||
private fun showMultiWalletView(binding: FragmentWalletBinding) = with(binding) {
|
||||
tvTwinCardNumber.hide()
|
||||
rvPendingTransaction.hide()
|
||||
lCardBalance.root.hide()
|
||||
lAddress.root.hide()
|
||||
lButtonsShort.root.hide()
|
||||
lButtonsLong.root.hide()
|
||||
rowButtons.hide()
|
||||
lSingleWalletBalance.root.hide()
|
||||
rvMultiwallet.show()
|
||||
btnAddToken.show()
|
||||
|
|
@ -59,7 +58,6 @@ class MultiWalletView : WalletView() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onViewCreated() {
|
||||
setupWalletsRecyclerView()
|
||||
}
|
||||
|
|
@ -86,17 +84,17 @@ class MultiWalletView : WalletView() {
|
|||
TokensAction.LoadCurrencies(
|
||||
supportedBlockchains = currenciesRepository.getBlockchains(
|
||||
card.firmwareVersion,
|
||||
card.isTestCard
|
||||
card.isTestCard,
|
||||
),
|
||||
scanResponse = store.state.globalState.scanResponse
|
||||
)
|
||||
scanResponse = store.state.globalState.scanResponse,
|
||||
),
|
||||
)
|
||||
store.dispatch(TokensAction.AllowToAddTokens(true))
|
||||
store.dispatch(
|
||||
TokensAction.SetAddedCurrencies(
|
||||
wallets = state.walletsData,
|
||||
derivationStyle = card.derivationStyle
|
||||
)
|
||||
derivationStyle = card.derivationStyle,
|
||||
),
|
||||
)
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.AddTokens))
|
||||
}
|
||||
|
|
@ -105,7 +103,7 @@ class MultiWalletView : WalletView() {
|
|||
|
||||
private fun handleBackupWarning(
|
||||
binding: FragmentWalletBinding,
|
||||
showBackupWarning: Boolean
|
||||
showBackupWarning: Boolean,
|
||||
) = with(binding.lWalletBackupWarning) {
|
||||
root.isVisible = showBackupWarning
|
||||
root.setOnClickListener {
|
||||
|
|
@ -128,11 +126,11 @@ class MultiWalletView : WalletView() {
|
|||
veilBalance.unVeil()
|
||||
}
|
||||
tvProcessing.animateVisibility(
|
||||
show = totalBalance.state == ProgressState.Error
|
||||
show = totalBalance.state == ProgressState.Error,
|
||||
)
|
||||
|
||||
tvBalance.text = totalBalance.fiatAmount.formatAmountAsSpannedString(
|
||||
currencySymbol = totalBalance.fiatCurrency.symbol
|
||||
currencySymbol = totalBalance.fiatCurrency.symbol,
|
||||
)
|
||||
tvCurrencyName.text = totalBalance.fiatCurrency.code
|
||||
|
||||
|
|
@ -145,14 +143,14 @@ class MultiWalletView : WalletView() {
|
|||
private fun handleErrorStates(
|
||||
state: WalletState,
|
||||
binding: FragmentWalletBinding,
|
||||
fragment: WalletFragment
|
||||
fragment: WalletFragment,
|
||||
) {
|
||||
when (state.primaryWallet?.currencyData?.status) {
|
||||
BalanceStatus.EmptyCard -> {
|
||||
showErrorState(
|
||||
binding,
|
||||
fragment.getText(R.string.wallet_error_empty_card),
|
||||
fragment.getString(R.string.wallet_error_empty_card_subtitle)
|
||||
fragment.getString(R.string.wallet_error_empty_card_subtitle),
|
||||
)
|
||||
configureButtonsForEmptyWalletState(binding)
|
||||
}
|
||||
|
|
@ -160,7 +158,7 @@ class MultiWalletView : WalletView() {
|
|||
showErrorState(
|
||||
binding,
|
||||
fragment.getText(R.string.wallet_error_unsupported_blockchain),
|
||||
fragment.getString(R.string.wallet_error_unsupported_blockchain_subtitle)
|
||||
fragment.getString(R.string.wallet_error_unsupported_blockchain_subtitle),
|
||||
)
|
||||
}
|
||||
else -> { /* no-op */
|
||||
|
|
@ -184,9 +182,21 @@ class MultiWalletView : WalletView() {
|
|||
|
||||
private fun configureButtonsForEmptyWalletState(binding: FragmentWalletBinding) =
|
||||
with(binding) {
|
||||
lButtonsLong.root.show()
|
||||
lButtonsLong.btnConfirmLong.setOnClickListener { store.dispatch(WalletAction.CreateWallet) }
|
||||
lButtonsLong.btnConfirmLong.text =
|
||||
fragment?.getText(R.string.wallet_button_create_wallet)
|
||||
rowButtons.btnBuy.hide()
|
||||
rowButtons.btnSell.hide()
|
||||
rowButtons.btnTrade.hide()
|
||||
rowButtons.show()
|
||||
|
||||
rowButtons.btnSend.text = fragment?.getText(R.string.wallet_button_create_wallet)
|
||||
rowButtons.onSendClick = { store.dispatch(WalletAction.CreateWallet) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val WalletDetailsButtonsRow.btnBuy: Button
|
||||
get() = this.findViewById(R.id.btn_buy)
|
||||
private val WalletDetailsButtonsRow.btnSell: Button
|
||||
get() = this.findViewById(R.id.btn_sell)
|
||||
private val WalletDetailsButtonsRow.btnTrade: Button
|
||||
get() = this.findViewById(R.id.btn_trade)
|
||||
private val WalletDetailsButtonsRow.btnSend: Button
|
||||
get() = this.findViewById(R.id.btn_send)
|
||||
|
|
@ -2,7 +2,6 @@ package com.tangem.tap.features.wallet.ui.wallet
|
|||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.tangem.domain.common.TwinCardNumber
|
||||
import com.tangem.tap.common.extensions.beginDelayedTransition
|
||||
|
|
@ -12,11 +11,15 @@ import com.tangem.tap.common.extensions.show
|
|||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.models.PendingTransaction
|
||||
import com.tangem.tap.features.wallet.redux.*
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletData
|
||||
import com.tangem.tap.features.wallet.redux.WalletMainButton
|
||||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
import com.tangem.tap.features.wallet.ui.BalanceWidget
|
||||
import com.tangem.tap.features.wallet.ui.MultipleAddressUiHelper
|
||||
import com.tangem.tap.features.wallet.ui.WalletFragment
|
||||
import com.tangem.tap.features.wallet.ui.adapters.PendingTransactionsAdapter
|
||||
import com.tangem.tap.features.wallet.ui.view.WalletDetailsButtonsRow
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.databinding.FragmentWalletBinding
|
||||
|
|
@ -55,7 +58,7 @@ class SingleWalletView : WalletView() {
|
|||
state.primaryWallet ?: return
|
||||
|
||||
setupTwinCards(state.twinCardsState, binding)
|
||||
setupButtons(state.primaryWallet, state.isTangemTwins, binding)
|
||||
setupButtons(state.primaryWallet, binding, state.isExchangeServiceFeatureOn)
|
||||
setupAddressCard(state.primaryWallet, binding)
|
||||
showPendingTransactionsIfPresent(state.primaryWallet.pendingTransactions)
|
||||
setupBalance(state, state.primaryWallet)
|
||||
|
|
@ -79,9 +82,7 @@ class SingleWalletView : WalletView() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setupTwinCards(
|
||||
twinCardsState: TwinCardsState?, binding: FragmentWalletBinding,
|
||||
) = with(binding) {
|
||||
private fun setupTwinCards(twinCardsState: TwinCardsState?, binding: FragmentWalletBinding) = with(binding) {
|
||||
twinCardsState?.cardNumber?.let { cardNumber ->
|
||||
tvTwinCardNumber.show()
|
||||
val number = when (cardNumber) {
|
||||
|
|
@ -97,92 +98,47 @@ class SingleWalletView : WalletView() {
|
|||
}
|
||||
|
||||
private fun setupButtons(
|
||||
state: WalletData, isTwinsWallet: Boolean, binding: FragmentWalletBinding,
|
||||
walletData: WalletData,
|
||||
binding: FragmentWalletBinding,
|
||||
isExchangeServiceFeatureEnabled: Boolean,
|
||||
) = with(binding) {
|
||||
setupButtonsType(state, binding)
|
||||
val tradeState = state.tradeCryptoState
|
||||
val btnConfirm = if (tradeState.isAvailableToSell() || tradeState.isAvailableToBuy()) {
|
||||
lButtonsShort.btnConfirm
|
||||
} else {
|
||||
lButtonsLong.btnConfirmLong
|
||||
}
|
||||
|
||||
setupConfirmButton(state, btnConfirm, isTwinsWallet)
|
||||
setupRowButtons(walletData, rowButtons, isExchangeServiceFeatureEnabled)
|
||||
|
||||
lAddress.btnCopy.setOnClickListener {
|
||||
state.walletAddresses?.selectedAddress?.address?.let { addressString ->
|
||||
walletData.walletAddresses?.selectedAddress?.address?.let { addressString ->
|
||||
store.dispatch(WalletAction.CopyAddress(addressString, fragment!!.requireContext()))
|
||||
}
|
||||
}
|
||||
lAddress.btnShowQr.setOnClickListener {
|
||||
state.walletAddresses?.selectedAddress?.let { selectedAddress ->
|
||||
walletData.walletAddresses?.selectedAddress?.let { selectedAddress ->
|
||||
store.dispatch(
|
||||
WalletAction.DialogAction.QrCode(
|
||||
currency = state.currency,
|
||||
currency = walletData.currency,
|
||||
selectedAddress = selectedAddress,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
setupTradeButton(binding, state.tradeCryptoState)
|
||||
}
|
||||
|
||||
private fun setupTradeButton(binding: FragmentWalletBinding, tradeCryptoState: TradeCryptoState) {
|
||||
val allowedToBuy = tradeCryptoState.isAvailableToBuy()
|
||||
val allowedToSell = tradeCryptoState.isAvailableToSell()
|
||||
val action = when {
|
||||
allowedToBuy && !allowedToSell -> WalletAction.TradeCryptoAction.Buy()
|
||||
!allowedToBuy && allowedToSell -> WalletAction.TradeCryptoAction.Sell
|
||||
allowedToBuy && allowedToSell -> WalletAction.DialogAction.ChooseTradeActionDialog
|
||||
else -> null
|
||||
}
|
||||
val text = when {
|
||||
allowedToBuy && !allowedToSell -> R.string.wallet_button_buy
|
||||
!allowedToBuy && allowedToSell -> R.string.wallet_button_sell
|
||||
allowedToBuy && allowedToSell -> R.string.wallet_button_trade
|
||||
else -> R.string.wallet_button_trade
|
||||
}
|
||||
val icon = when {
|
||||
allowedToBuy && !allowedToSell -> R.drawable.ic_arrow_up
|
||||
!allowedToBuy && allowedToSell -> R.drawable.ic_arrow_down
|
||||
allowedToBuy && allowedToSell -> R.drawable.ic_arrows_up_down
|
||||
else -> null
|
||||
}
|
||||
with(binding) {
|
||||
lButtonsShort.btnTrade.text = fragment?.getText(text)
|
||||
icon?.let { lButtonsShort.btnTrade.setIconResource(it) }
|
||||
lButtonsShort.btnTrade.setOnClickListener { if (action != null) store.dispatch(action) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupButtonsType(state: WalletData, binding: FragmentWalletBinding) = with(binding) {
|
||||
if (state.tradeCryptoState.isAvailableToSell() || state.tradeCryptoState.isAvailableToBuy()) {
|
||||
lButtonsLong.root.hide()
|
||||
lButtonsShort.root.show()
|
||||
} else {
|
||||
lButtonsLong.root.show()
|
||||
lButtonsShort.root.hide()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupConfirmButton(
|
||||
state: WalletData, btnConfirm: Button, isTwinsWallet: Boolean,
|
||||
private fun setupRowButtons(
|
||||
walletData: WalletData,
|
||||
rowButtons: WalletDetailsButtonsRow,
|
||||
isExchangeServiceFeatureEnabled: Boolean,
|
||||
) {
|
||||
val buttonTitle = when (state.mainButton) {
|
||||
is WalletMainButton.SendButton -> R.string.wallet_button_send
|
||||
is WalletMainButton.CreateWalletButton -> {
|
||||
if (!isTwinsWallet) {
|
||||
R.string.wallet_button_create_wallet
|
||||
} else {
|
||||
R.string.wallet_button_create_twin_wallet
|
||||
}
|
||||
}
|
||||
}
|
||||
btnConfirm.text = fragment?.getString(buttonTitle)
|
||||
btnConfirm.isEnabled = state.mainButton.enabled
|
||||
btnConfirm.setOnClickListener {
|
||||
when (state.mainButton) {
|
||||
rowButtons.updateButtonsVisibility(
|
||||
exchangeServiceFeatureOn = isExchangeServiceFeatureEnabled,
|
||||
buyAllowed = walletData.isAvailableToBuy,
|
||||
sellAllowed = walletData.isAvailableToSell,
|
||||
sendAllowed = walletData.mainButton.enabled,
|
||||
)
|
||||
|
||||
rowButtons.onBuyClick = { store.dispatch(WalletAction.TradeCryptoAction.Buy()) }
|
||||
rowButtons.onSendClick = { store.dispatch(WalletAction.TradeCryptoAction.Sell) }
|
||||
rowButtons.onTradeClick = { store.dispatch(WalletAction.DialogAction.ChooseTradeActionDialog) }
|
||||
|
||||
rowButtons.onSendClick = {
|
||||
when (walletData.mainButton) {
|
||||
is WalletMainButton.SendButton -> store.dispatch(WalletAction.Send())
|
||||
is WalletMainButton.CreateWalletButton -> store.dispatch(WalletAction.CreateWallet)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
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 featureIsSwitchedOn(): Boolean {
|
||||
val card = cardProvider() ?: return false
|
||||
|
||||
return !card.isStart2Coin
|
||||
}
|
||||
|
||||
override fun isBuyAllowed(): Boolean {
|
||||
val card = cardProvider() ?: return false
|
||||
|
||||
return when {
|
||||
card.isDemoCard() -> true
|
||||
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() -> true
|
||||
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,17 +23,26 @@ import java.math.BigDecimal
|
|||
class CurrencyExchangeManager(
|
||||
private val buyService: ExchangeService,
|
||||
private val sellService: ExchangeService,
|
||||
private val primaryRules: ExchangeRules,
|
||||
) : ExchangeService, ExchangeUrlBuilder {
|
||||
|
||||
override fun featureIsSwitchedOn(): Boolean = primaryRules.featureIsSwitchedOn()
|
||||
|
||||
override suspend fun update() {
|
||||
buyService.update()
|
||||
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,
|
||||
|
|
@ -67,6 +76,14 @@ class CurrencyExchangeManager(
|
|||
}
|
||||
|
||||
enum class Action { Buy, Sell }
|
||||
|
||||
companion object {
|
||||
fun dummy(): CurrencyExchangeManager = CurrencyExchangeManager(
|
||||
buyService = ExchangeService.dummy(),
|
||||
sellService = ExchangeService.dummy(),
|
||||
primaryRules = ExchangeRules.dummy(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun CurrencyExchangeManager.buyErc20TestnetTokens(
|
||||
|
|
|
|||
|
|
@ -1,16 +1,44 @@
|
|||
package com.tangem.tap.network.exchangeServices
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.tap.common.feature.Feature
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
|
||||
interface ExchangeService {
|
||||
suspend fun update()
|
||||
interface Exchanger {
|
||||
fun isBuyAllowed(): Boolean
|
||||
fun isSellAllowed(): Boolean
|
||||
fun availableForBuy(currency: Currency):Boolean
|
||||
fun availableForSell(currency: Currency):Boolean
|
||||
}
|
||||
|
||||
interface ExchangeService: Feature, Exchanger {
|
||||
suspend fun update()
|
||||
|
||||
companion object {
|
||||
fun dummy(): ExchangeService = object : ExchangeService {
|
||||
override fun featureIsSwitchedOn(): Boolean = false
|
||||
override suspend fun update() {}
|
||||
override fun isBuyAllowed(): Boolean = false
|
||||
override fun isSellAllowed(): Boolean = false
|
||||
override fun availableForBuy(currency: Currency): Boolean = false
|
||||
override fun availableForSell(currency: Currency): Boolean = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface ExchangeRules: Feature, Exchanger {
|
||||
|
||||
companion object {
|
||||
fun dummy(): ExchangeRules = object : ExchangeRules {
|
||||
override fun featureIsSwitchedOn(): Boolean = false
|
||||
override fun isBuyAllowed(): Boolean = false
|
||||
override fun isSellAllowed(): Boolean = false
|
||||
override fun availableForBuy(currency: Currency): Boolean = false
|
||||
override fun availableForSell(currency: Currency): Boolean = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface ExchangeUrlBuilder {
|
||||
fun getUrl(
|
||||
action: CurrencyExchangeManager.Action,
|
||||
|
|
|
|||
|
|
@ -4,15 +4,6 @@ import com.squareup.moshi.Json
|
|||
import retrofit2.http.GET
|
||||
import retrofit2.http.Path
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
|
||||
|
||||
|
||||
private val CurrenciesUrl = "https://api.mercuryo.io/v1.6/lib/currencies"
|
||||
|
||||
|
||||
|
||||
interface MercuryoApi {
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ class MercuryoService(
|
|||
private val blockchainsAvailableToBuy = mutableListOf<Blockchain>()
|
||||
private val tokensAvailableToBy = mutableMapOf<String, MutableList<Blockchain>>()
|
||||
|
||||
override fun featureIsSwitchedOn(): Boolean = true
|
||||
|
||||
override suspend fun update() {
|
||||
when (val result = performRequest { api.currencies(apiVersion) }) {
|
||||
is Result.Success -> {
|
||||
|
|
@ -130,6 +132,7 @@ class MercuryoService(
|
|||
private fun blockchainFromCurrencyName(currencyName: String): Blockchain? = when (currencyName) {
|
||||
"BNB" -> Blockchain.BSC
|
||||
"ETH" -> Blockchain.Ethereum
|
||||
"ADA" -> Blockchain.CardanoShelley
|
||||
else -> Blockchain.values().find { it.currency.lowercase() == currencyName.lowercase() }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ class MoonPayService(
|
|||
|
||||
private var status: MoonPayStatus? = null
|
||||
|
||||
override fun featureIsSwitchedOn(): Boolean = true
|
||||
|
||||
override suspend fun update() {
|
||||
withIOContext {
|
||||
performRequest {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import android.content.Context
|
|||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import com.tangem.common.json.MoshiJsonConverter
|
||||
import java.util.Calendar
|
||||
import java.util.*
|
||||
|
||||
|
||||
class PreferencesStorage(applicationContext: Application) {
|
||||
|
|
@ -25,6 +25,10 @@ class PreferencesStorage(applicationContext: Application) {
|
|||
fiatCurrenciesPrefStorage.migrate()
|
||||
}
|
||||
|
||||
var chatFirstLaunchTime: Long?
|
||||
get() = preferences.getLong(CHAT_FIRST_LAUNCH_KEY, 0).takeIf { it != 0L }
|
||||
set(value) = preferences.edit { putLong(CHAT_FIRST_LAUNCH_KEY, value ?: 0) }
|
||||
|
||||
fun getCountOfLaunches(): Int = preferences.getInt(APP_LAUNCH_COUNT_KEY, 1)
|
||||
|
||||
@Deprecated("Use UsedCardsPrefStorage instead")
|
||||
|
|
@ -58,6 +62,7 @@ class PreferencesStorage(applicationContext: Application) {
|
|||
private const val DISCLAIMER_ACCEPTED_KEY = "disclaimerAccepted"
|
||||
private const val TWINS_ONBOARDING_SHOWN_KEY = "twinsOnboardingShown"
|
||||
private const val APP_LAUNCH_COUNT_KEY = "launchCount"
|
||||
private const val CHAT_FIRST_LAUNCH_KEY = "chatFirstLaunchKey"
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
android:layout_height="match_parent"
|
||||
android:background="@color/backgroundWhite"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:fitsSystemWindows="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="74dp">
|
||||
android:paddingBottom="92dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_card"
|
||||
|
|
@ -93,6 +93,17 @@
|
|||
app:barrierDirection="bottom"
|
||||
app:constraint_referenced_ids="iv_card,tv_twin_card_number" />
|
||||
|
||||
<include
|
||||
android:id="@+id/l_wallet_backup_warning"
|
||||
layout="@layout/layout_wallet_backup_warning"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toBottomOf="@id/rv_warning_messages" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_warning_messages"
|
||||
android:layout_width="match_parent"
|
||||
|
|
@ -105,17 +116,6 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/barrier" />
|
||||
|
||||
<include
|
||||
android:id="@+id/l_wallet_backup_warning"
|
||||
layout="@layout/layout_wallet_backup_warning"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toBottomOf="@id/rv_warning_messages" />
|
||||
|
||||
<include
|
||||
android:id="@+id/l_card_total_balance"
|
||||
layout="@layout/layout_card_total_balance"
|
||||
|
|
@ -176,32 +176,6 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/rv_pending_transaction" />
|
||||
|
||||
<include
|
||||
android:id="@+id/l_buttons_long"
|
||||
layout="@layout/layout_wallet_long_buttons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/l_address"
|
||||
app:layout_constraintVertical_bias="1" />
|
||||
|
||||
<include
|
||||
android:id="@+id/l_buttons_short"
|
||||
layout="@layout/layout_wallet_short_buttons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="30dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/l_address"
|
||||
app:layout_constraintVertical_bias="1" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_add_token"
|
||||
style="@style/BaseTapButton"
|
||||
|
|
@ -213,6 +187,7 @@
|
|||
android:elevation="4dp"
|
||||
android:text="@string/main_manage_tokens"
|
||||
android:textSize="16sp"
|
||||
android:visibility="gone"
|
||||
app:cornerRadius="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/rv_multiwallet"
|
||||
|
|
@ -222,4 +197,14 @@
|
|||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<com.tangem.tap.features.wallet.ui.view.WalletDetailsButtonsRow
|
||||
android:id="@+id/row_buttons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="32dp" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
|
|
|||
|
|
@ -1,50 +1,56 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:parentTag="android.widget.LinearLayout"
|
||||
tools:orientation="horizontal">
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:orientation="horizontal"
|
||||
tools:parentTag="android.widget.LinearLayout">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
<FrameLayout
|
||||
android:id="@+id/container_exchange_buttons"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_trade"
|
||||
style="@style/TapButtonWithIcon"
|
||||
android:layout_width="0dp"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:layout_weight="1"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/wallet_button_trade"
|
||||
android:visibility="gone"
|
||||
app:icon="@drawable/ic_arrows_up_down"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_buy"
|
||||
style="@style/TapButtonWithIcon"
|
||||
android:layout_width="0dp"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:layout_weight="1"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/wallet_button_buy"
|
||||
android:visibility="gone"
|
||||
app:icon="@drawable/ic_arrow_up" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_sell"
|
||||
style="@style/TapButtonWithIcon"
|
||||
android:layout_width="0dp"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:layout_weight="1"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/wallet_button_sell"
|
||||
android:visibility="gone"
|
||||
app:icon="@drawable/ic_arrow_down" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_send"
|
||||
style="@style/TapButtonWithIcon"
|
||||
android:layout_width="0dp"
|
||||
android:layout_marginStart="6dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/wallet_button_send"
|
||||
app:icon="@drawable/ic_send" />
|
||||
android:id="@+id/btn_send"
|
||||
style="@style/TapButtonWithIcon"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/wallet_button_send"
|
||||
app:icon="@drawable/ic_send" />
|
||||
|
||||
</merge>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -100,7 +100,7 @@
|
|||
<string name="wallet_connect_subtitle">Подключение к Dapps</string>
|
||||
|
||||
<string name="details_row_title_create_backup">Добавить еще карты</string>
|
||||
<string name="details_row_title_create_backup_footer">Вы можете объеденить до трех карт в одним кошельке. Это можно сделать только один раз.</string>
|
||||
<string name="details_row_title_create_backup_footer">Вы можете объединить до трех карт в одном кошельке. Это можно сделать только один раз.</string>
|
||||
|
||||
<string name="details_row_privacy_policy">Privacy policy</string>
|
||||
|
||||
|
|
|
|||
7
app/src/main/res/values-v29/zendesk_styles.xml
Normal file
7
app/src/main/res/values-v29/zendesk_styles.xml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="ZendeskTheme" parent="ZendeskSdkTheme.Light">
|
||||
<item name="android:forceDarkAllowed">false</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ ext.versions = [
|
|||
kotlin : '1.6.10',
|
||||
build_gradle : '7.1.3',
|
||||
tangem_card_sdk : 'develop-159',
|
||||
tangem_blockchain_sdk: 'develop-100',
|
||||
tangem_blockchain_sdk: 'develop-105',
|
||||
// tangem_blockchain_sdk: '0.0.1',
|
||||
]
|
||||
|
||||
|
|
@ -10,4 +10,4 @@ ext.environmentConfig = [
|
|||
environment : "ENVIRONMENT",
|
||||
testActionEnabled: "TEST_ACTION_ENABLED",
|
||||
logEnabled : "LOG_ENABLED",
|
||||
]
|
||||
]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue