From 376c14d5598c49af102e65e885b2c8c1c108af84 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 5 Jul 2022 10:48:30 +0400 Subject: [PATCH 01/28] Updated on 2026-08-14 --- .../wallet/redux/OnboardingWalletMiddleware.kt | 7 ++++++- .../tangem/tap/features/wallet/redux/WalletAction.kt | 12 +++++------- .../redux/middlewares/MultiWalletMiddleware.kt | 7 +++---- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt index 88121813ad..1cb8587935 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt @@ -91,7 +91,12 @@ private fun handleWalletAction(action: Action) { BlockchainNetwork(Blockchain.Bitcoin, result.data.card), BlockchainNetwork(Blockchain.Ethereum, result.data.card) ) - store.dispatchOnMain(WalletAction.MultiWallet.SaveCurrencies(blockchainNetworks)) + store.dispatchOnMain( + WalletAction.MultiWallet.SaveCurrencies( + blockchainNetworks = blockchainNetworks, + cardId = result.data.card.cardId + ) + ) onboardingManager.activationStarted(updatedResponse.card.cardId) store.dispatch(OnboardingWalletAction.ProceedBackup) } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt index edc6be9c6e..295d786c94 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt @@ -1,11 +1,7 @@ package com.tangem.tap.features.wallet.redux import android.content.Context -import com.tangem.blockchain.common.Amount -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.Token -import com.tangem.blockchain.common.Wallet -import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.common.* import com.tangem.blockchain.common.address.AddressType import com.tangem.common.card.Card import com.tangem.tap.common.entities.FiatCurrency @@ -16,8 +12,8 @@ import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.features.wallet.models.Currency import com.tangem.wallet.R -import java.math.BigDecimal import org.rekotlin.Action +import java.math.BigDecimal sealed class WalletAction : Action { @@ -64,7 +60,9 @@ sealed class WalletAction : Action { MultiWallet() data class AddToken(val token: Token, val blockchain: BlockchainNetwork) : MultiWallet() - data class SaveCurrencies(val blockchainNetworks: List) : MultiWallet() + data class SaveCurrencies( + val blockchainNetworks: List, val cardId: String? = null + ) : MultiWallet() // object FindTokensInUse : MultiWallet() // object FindBlockchainsInUse : MultiWallet() diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt index e3af462d5d..8618bb40a6 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt @@ -21,10 +21,10 @@ import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.features.wallet.redux.models.WalletDialog import com.tangem.tap.scope import com.tangem.tap.store -import java.math.BigDecimal import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import java.math.BigDecimal class MultiWalletMiddleware { fun handle( @@ -76,9 +76,8 @@ class MultiWalletMiddleware { ) } is WalletAction.MultiWallet.SaveCurrencies -> { - globalState.scanResponse?.card?.cardId?.let { - currenciesRepository.saveCurrencies(it, action.blockchainNetworks) - } + val cardId = action.cardId ?: globalState.scanResponse?.card?.cardId ?: return + currenciesRepository.saveCurrencies(cardId, action.blockchainNetworks) } is WalletAction.MultiWallet.TryToRemoveWallet -> { val walletState = store.state.walletState From 0342b819da2401bb9a685b334aa8fabe774f170c Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 5 Jul 2022 10:49:02 +0400 Subject: [PATCH 02/28] Updated on 2026-08-14 --- .../products/wallet/redux/OnboardingWalletMiddleware.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt index 1cb8587935..391d65cb96 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt @@ -277,7 +277,11 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) BlockchainNetwork(Blockchain.Bitcoin, result.data), BlockchainNetwork(Blockchain.Ethereum, result.data) ) - store.dispatchOnMain(WalletAction.MultiWallet.SaveCurrencies(blockchainNetworks)) + store.dispatchOnMain( + WalletAction.MultiWallet.SaveCurrencies( + blockchainNetworks = blockchainNetworks, cardId = result.data.cardId + ) + ) if (backupService.currentState == BackupService.State.Finished) { store.dispatchOnMain(BackupAction.FinishBackup) } else { From 1ccc05447b0008aa325d56551a27e1724251fb1f Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 5 Jul 2022 15:55:20 +0300 Subject: [PATCH 03/28] Updated on 2026-08-14 --- app/build.gradle | 6 +++--- dependencies.gradle | 8 +++++--- domain/build.gradle | 6 +++--- network/build.gradle | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index abf8cda691..c73412c88f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -93,10 +93,10 @@ dependencies { implementation 'com.google.android.play:core-ktx:1.8.1' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' - implementation 'com.tangem:blockchain:develop-93' + implementation "com.tangem:blockchain:${versions.tamgem_blockchain_sdk}" // implementation 'com.tangem:blockchain:0.0.1' - implementation 'com.tangem.tangem-sdk-kotlin:core:develop-154' - implementation 'com.tangem.tangem-sdk-kotlin:android:develop-154' + implementation "com.tangem.tangem-sdk-kotlin:core:${versions.tamgem_card_sdk}" + implementation "com.tangem.tangem-sdk-kotlin:android:${versions.tamgem_card_sdk}" // WebView implementation "androidx.browser:browser:1.3.0" diff --git a/dependencies.gradle b/dependencies.gradle index d463106208..45200c5dcf 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -1,10 +1,12 @@ ext.versions = [ - kotlin : '1.6.10', - build_gradle: '7.1.3', + kotlin : '1.6.10', + build_gradle : '7.1.3', + tamgem_card_sdk : 'develop-154', + tamgem_blockchain_sdk: 'develop-93', ] ext.environmentConfig = [ - environment: "ENVIRONMENT", + environment : "ENVIRONMENT", testActionEnabled: "TEST_ACTION_ENABLED", logEnabled : "LOG_ENABLED", ] \ No newline at end of file diff --git a/domain/build.gradle b/domain/build.gradle index 5a54411b94..5e9fc9c665 100644 --- a/domain/build.gradle +++ b/domain/build.gradle @@ -59,10 +59,10 @@ dependencies { implementation implementation(project(path: ':network')) implementation implementation(project(path: ':common')) - implementation 'com.tangem:blockchain:develop-93' + implementation "com.tangem:blockchain:${versions.tamgem_blockchain_sdk}" // implementation 'com.tangem:blockchain:0.0.1' - implementation 'com.tangem.tangem-sdk-kotlin:core:develop-154' - implementation 'com.tangem.tangem-sdk-kotlin:android:develop-154' + implementation "com.tangem.tangem-sdk-kotlin:core:${versions.tamgem_card_sdk}" + implementation "com.tangem.tangem-sdk-kotlin:android:${versions.tamgem_card_sdk}" // Kotlin coroutines implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2' diff --git a/network/build.gradle b/network/build.gradle index b90ff59980..dce3040c09 100644 --- a/network/build.gradle +++ b/network/build.gradle @@ -12,7 +12,7 @@ dependencies { implementation implementation(project(path: ':common')) // Tangem sdk's - implementation 'com.tangem.tangem-sdk-kotlin:core:develop-142' + implementation "com.tangem.tangem-sdk-kotlin:core:${versions.tamgem_card_sdk}" // Kotlin coroutines implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2' From 320f068f00f3604f235595b678ea772eda0916fd Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 5 Jul 2022 15:56:26 +0300 Subject: [PATCH 04/28] Updated on 2026-08-14 --- app/build.gradle | 1 - domain/build.gradle | 1 - 2 files changed, 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index c73412c88f..791cb96330 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -94,7 +94,6 @@ dependencies { coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' implementation "com.tangem:blockchain:${versions.tamgem_blockchain_sdk}" -// implementation 'com.tangem:blockchain:0.0.1' implementation "com.tangem.tangem-sdk-kotlin:core:${versions.tamgem_card_sdk}" implementation "com.tangem.tangem-sdk-kotlin:android:${versions.tamgem_card_sdk}" diff --git a/domain/build.gradle b/domain/build.gradle index 5e9fc9c665..d7a8832fd4 100644 --- a/domain/build.gradle +++ b/domain/build.gradle @@ -60,7 +60,6 @@ dependencies { implementation implementation(project(path: ':common')) implementation "com.tangem:blockchain:${versions.tamgem_blockchain_sdk}" -// implementation 'com.tangem:blockchain:0.0.1' implementation "com.tangem.tangem-sdk-kotlin:core:${versions.tamgem_card_sdk}" implementation "com.tangem.tangem-sdk-kotlin:android:${versions.tamgem_card_sdk}" From ee1cf2a65408ba518026bb68fd7e4bf9bfd7edd3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 8 Jul 2022 11:45:37 +0300 Subject: [PATCH 05/28] Updated on 2026-08-14 --- app/src/main/assets/tangem-app-config | 2 +- .../wallet/ui/images/CurrencyIconRequest.kt | 136 +++++++++++++----- 2 files changed, 98 insertions(+), 40 deletions(-) diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index 7f058ec409..f50133547b 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit 7f058ec409b4eaaf8688ecd4bf944ef8d58d3564 +Subproject commit f50133547b5e19fb7ad32e64370573d60f6791fd diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconRequest.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconRequest.kt index 99051bc72d..fb75bc6474 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconRequest.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconRequest.kt @@ -1,12 +1,9 @@ package com.tangem.tap.features.wallet.ui.images -import android.graphics.PorterDuff -import android.graphics.PorterDuffColorFilter import android.widget.ImageView import android.widget.TextView import androidx.constraintlayout.utils.widget.ImageFilterView import coil.imageLoader -import coil.load import coil.request.ImageRequest import coil.transform.RoundedCornersTransformation import com.tangem.blockchain.common.Blockchain @@ -29,57 +26,112 @@ fun loadCurrencyIcon( token: Token?, blockchain: Blockchain, ) { - when { - token == null -> currencyImageView.loadIcon( - iconUrl = getIconUrl(blockchain.toNetworkId()), - placeholderRes = blockchain.getRoundIconRes(), + CurrencyIconLoader( + currencyImageView = currencyImageView, + currencyTextView = currencyTextView, + token = token, + blockchain = blockchain + ) + .load() +} + +private class CurrencyIconLoader( + private val currencyImageView: ImageFilterView, + private val currencyTextView: TextView, + private val token: Token?, + private val blockchain: Blockchain, +) { + fun load() { + when { + token == null && blockchain.isTestnet() -> loadTestnetBlockchainIcon() + token == null -> loadBlockchainIcon() + blockchain.isTestnet() -> loadTestnetTokenIcon() + else -> loadTokenIcon() + } + } + + private fun loadBlockchainIcon() { + loadBlockchainIconBase( onStart = { - if (blockchain.isTestnet()) { - currencyImageView.saturation = 0f - } else { - currencyImageView.colorFilter = null - } + currencyImageView.colorFilter = null } ) - token.symbol == QCX -> currencyImageView.load(R.drawable.ic_qcx) - token.symbol == VOYR -> currencyImageView.load(R.drawable.ic_voyr) - else -> currencyImageView.loadIcon( - iconUrl = getTokenIconUrl(token, blockchain), + } + + private fun loadTestnetBlockchainIcon() { + loadBlockchainIconBase( + onStart = { + currencyImageView.saturation = 0f + } + ) + } + + private fun loadTokenIcon() { + loadTokenIconBase( + onStart = { + currencyImageView.setColorFilter(it.getColor()) + }, + onSuccess = { + currencyImageView.colorFilter = null + } + ) + } + + private fun loadTestnetTokenIcon() { + loadTokenIconBase( + onStart = { + currencyImageView.saturation = 0f + } + ) + } + + private inline fun loadBlockchainIconBase( + crossinline onStart: (Blockchain) -> Unit = {}, + crossinline onSuccess: (Blockchain) -> Unit = {}, + crossinline onError: (Blockchain) -> Unit = {}, + ) { + currencyImageView.loadIcon( + data = getIconUrl(blockchain.toNetworkId()), + placeholderRes = blockchain.getRoundIconRes(), + onStart = { onStart(blockchain) }, + onSuccess = { onSuccess(blockchain) }, + onError = { onError(blockchain) }, + ) + } + + private inline fun loadTokenIconBase( + crossinline onStart: (Token) -> Unit = {}, + crossinline onSuccess: (Token) -> Unit = {}, + crossinline onError: (Token) -> Unit = {}, + ) { + if (token == null) return + + currencyImageView.loadIcon( + data = getTokenIcon(token, blockchain), placeholderRes = R.drawable.shape_circle, onStart = { currencyTextView.text = token.symbol.take(1) currencyTextView.setTextColor(token.getTextColor()) - - if (blockchain.isTestnet()) { - currencyImageView.saturation = 0f - } - }, - onError = { - currencyImageView.colorFilter = PorterDuffColorFilter( - /* color = */ - token.getColor(), - /* mode = */ - PorterDuff.Mode.SRC_ATOP, - ) + onStart(token) }, onSuccess = { - if (!blockchain.isTestnet()) { - currencyImageView.colorFilter = null - } - } + currencyTextView.text = null + onSuccess(token) + }, + onError = { onError(token) }, ) } } private inline fun ImageView.loadIcon( - iconUrl: String?, + data: Any?, placeholderRes: Int, crossinline onStart: () -> Unit = {}, crossinline onSuccess: () -> Unit = {}, crossinline onError: () -> Unit = {}, ) { ImageRequest.Builder(context) - .data(iconUrl) + .data(data) .placeholder(placeholderRes) .error(placeholderRes) .fallback(placeholderRes) @@ -101,9 +153,15 @@ private inline fun ImageView.loadIcon( .also(context.imageLoader::enqueue) } -private fun getTokenIconUrl(token: Token, blockchain: Blockchain): String? { - return token.id?.let(::getIconUrl) - ?: token.getCustomIconUrl() - ?: IconsUtil.getTokenIconUri(blockchain, token) - ?.toString() +private fun getTokenIcon(token: Token, blockchain: Blockchain): Any? { + return when (token.symbol) { + QCX -> R.drawable.ic_qcx + VOYR -> R.drawable.ic_voyr + else -> { + token.id?.let(::getIconUrl) + ?: token.getCustomIconUrl() + ?: IconsUtil.getTokenIconUri(blockchain, token) + ?.toString() + } + } } \ No newline at end of file From 17066df7412040e1eacfbfd0785a7ea398c978ca Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 8 Jul 2022 12:57:20 +0300 Subject: [PATCH 06/28] Updated on 2026-08-14 --- app/src/main/assets/tangem-app-config | 2 +- .../tap/common/extensions/WalletManager.kt | 4 +- .../tap/common/redux/global/GlobalAction.kt | 11 +- .../common/redux/global/GlobalMidlleware.kt | 180 ++++++++++-------- .../tap/common/redux/global/GlobalReducer.kt | 4 +- .../tap/common/redux/global/GlobalState.kt | 2 +- .../configurable/config/ConfigManager.kt | 6 +- .../domain/configurable/config/JsonModels.kt | 2 +- .../extensions/CurrencyExchangeManager.kt | 57 ------ .../tap/features/home/redux/HomeMiddleware.kt | 2 +- .../features/onboarding/OnboardingManager.kt | 2 +- .../note/redux/OnboardingNoteState.kt | 5 +- .../products/twins/redux/TwinCardsState.kt | 5 +- .../tap/features/wallet/redux/WalletState.kt | 15 +- .../middlewares/TradeCryptoMiddleware.kt | 10 +- .../redux/reducers/OnWalletLoadedReducer.kt | 4 +- .../wallet/redux/reducers/WalletReducer.kt | 23 +-- .../wallet/ui/WalletDetailsFragment.kt | 26 +-- .../wallet/ui/wallet/SingleWalletView.kt | 20 +- .../CurrencyExchangeManager.kt | 69 ++----- .../exchangeServices/ExchangeService.kt | 29 +++ .../exchangeServices/mercuryo/MercuryoApi.kt | 57 ++++++ .../mercuryo/MercuryoService.kt | 127 ++++++++++++ .../moonpay/MoonPayService.kt | 52 ++--- .../exchangeServices/onramper/OnnramperApi.kt | 83 -------- .../onramper/OnramperService.kt | 116 ----------- 26 files changed, 407 insertions(+), 506 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/domain/extensions/CurrencyExchangeManager.kt create mode 100644 app/src/main/java/com/tangem/tap/network/exchangeServices/ExchangeService.kt create mode 100644 app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoApi.kt create mode 100644 app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt delete mode 100644 app/src/main/java/com/tangem/tap/network/exchangeServices/onramper/OnnramperApi.kt delete mode 100644 app/src/main/java/com/tangem/tap/network/exchangeServices/onramper/OnramperService.kt diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index 7f058ec409..feec295055 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit 7f058ec409b4eaaf8688ecd4bf944ef8d58d3564 +Subproject commit feec2950551a1dbabbb018b446b25e17442db21f diff --git a/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt b/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt index d671b60a29..40acdd1b7d 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt @@ -49,11 +49,11 @@ suspend fun WalletManager.safeUpdate(): Result = try { fun WalletManager?.getToUpUrl(): String? { val globalState = store.state.globalState - val currencyExchangeManager = globalState.currencyExchangeManager ?: return null + val exchangeManager = globalState.exchangeManager ?: return null val wallet = this?.wallet ?: return null val defaultAddress = wallet.address - return currencyExchangeManager.getUrl( + return exchangeManager.getUrl( action = CurrencyExchangeManager.Action.Buy, blockchain = wallet.blockchain, cryptoCurrencyName = wallet.blockchain.currency, diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt index b871b7853f..540209a39c 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt @@ -15,7 +15,6 @@ import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager import com.tangem.tap.features.details.redux.SecurityOption import com.tangem.tap.features.feedback.EmailData import com.tangem.tap.features.feedback.FeedbackManager -import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager import org.rekotlin.Action sealed class GlobalAction : Action { @@ -77,7 +76,13 @@ sealed class GlobalAction : Action { data class SendFeedback(val emailData: EmailData) : GlobalAction() data class UpdateFeedbackInfo(val walletManagers: List) : GlobalAction() - object InitCurrencyExchangeManager : GlobalAction() { - data class Success(val exchangeManager: CurrencyExchangeManager) : GlobalAction() + object ExchangeManager : GlobalAction() { + object Init : GlobalAction() { + data class Success( + val exchangeManager: com.tangem.tap.network.exchangeServices.CurrencyExchangeManager, + ) : GlobalAction() + } + + object Update : GlobalAction() } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt index 1e104b5dd4..9777538858 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt @@ -2,9 +2,11 @@ package com.tangem.tap.common.redux.global import com.tangem.common.CompletionResult import com.tangem.common.core.TangemSdkError +import com.tangem.common.extensions.guard import com.tangem.common.extensions.ifNotNull import com.tangem.domain.common.extensions.withMainContext import com.tangem.tap.* +import com.tangem.tap.common.extensions.dispatchDebugErrorNotification import com.tangem.tap.common.extensions.dispatchDialogShow import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.AppDialog @@ -13,9 +15,12 @@ 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.CurrencyExchangeManager +import com.tangem.tap.network.exchangeServices.mercuryo.MercuryoApi +import com.tangem.tap.network.exchangeServices.mercuryo.MercuryoService import com.tangem.tap.network.exchangeServices.moonpay.MoonPayService -import com.tangem.tap.network.exchangeServices.onramper.OnramperService import kotlinx.coroutines.launch +import org.rekotlin.Action +import org.rekotlin.DispatchFunction import org.rekotlin.Middleware class GlobalMiddleware { @@ -24,94 +29,105 @@ class GlobalMiddleware { } } -private val globalMiddlewareHandler: Middleware = { _, appState -> +private val globalMiddlewareHandler: Middleware = { dispatch, appState -> { nextDispatch -> { action -> - when (action) { - is GlobalAction.ScanFailsCounter.ChooseBehavior -> { - when (action.result) { - is CompletionResult.Success -> store.dispatch(GlobalAction.ScanFailsCounter.Reset) + handleAction(action, appState, dispatch) + nextDispatch(action) + } + } +} + +private fun handleAction(action: Action, appState: () -> AppState?, dispatch: DispatchFunction) { + when (action) { + is GlobalAction.ScanFailsCounter.ChooseBehavior -> { + when (action.result) { + is CompletionResult.Success -> store.dispatch(GlobalAction.ScanFailsCounter.Reset) + is CompletionResult.Failure -> { + if (action.result.error is TangemSdkError.UserCancelled) { + store.dispatch(GlobalAction.ScanFailsCounter.Increment) + if (store.state.globalState.scanCardFailsCounter >= 2) { + store.dispatchDialogShow(AppDialog.ScanFailsDialog) + } + } else { + store.dispatch(GlobalAction.ScanFailsCounter.Reset) + } + } + } + } + is GlobalAction.RestoreAppCurrency -> { + store.dispatch(GlobalAction.RestoreAppCurrency.Success( + preferencesStorage.fiatCurrenciesPrefStorage.getAppCurrency() + )) + } + is GlobalAction.HideWarningMessage -> { + store.state.globalState.warningManager?.let { + if (it.hideWarning(action.warning)) { + if (WarningMessagesManager.isAlreadySignedHashesWarning(action.warning)) { + //TODO: No appropriate warningMessage identification. Make it better later + store.dispatch(WalletAction.Warnings.CheckHashesCount.SaveCardId) + } + + store.dispatch(WalletAction.Warnings.Update) + store.dispatch(SendAction.Warnings.Update) + } + } + } + is GlobalAction.SendFeedback -> { + store.state.globalState.feedbackManager?.send(action.emailData) + } + is GlobalAction.UpdateWalletSignedHashes -> { + store.dispatch(WalletAction.Warnings.CheckRemainingSignatures(action.remainingSignatures)) + } + is GlobalAction.UpdateFeedbackInfo -> { + store.state.globalState.feedbackManager?.infoHolder + ?.setWalletsInfo(action.walletManagers) + } + is GlobalAction.ExchangeManager.Init -> { + val config = appState()?.globalState?.configManager?.config + ifNotNull( + config?.mercuryoWidgetId, + config?.moonPayApiKey, + config?.moonPayApiSecretKey, + ) { mercuryoWidgetId, moonPayKey, moonPaySecretKey -> + scope.launch { + val buyService = MercuryoService(MercuryoApi.API_VERSION, mercuryoWidgetId) + val sellService = MoonPayService(moonPayKey, moonPaySecretKey) + val exchangeManager = CurrencyExchangeManager(buyService, sellService) + store.dispatchOnMain(GlobalAction.ExchangeManager.Init.Success(exchangeManager)) + store.dispatchOnMain(GlobalAction.ExchangeManager.Update) + } + } + } + is GlobalAction.ExchangeManager.Init.Success -> {} + is GlobalAction.ExchangeManager.Update -> { + val exchangeManager = appState()?.globalState?.exchangeManager.guard { + store.dispatchDebugErrorNotification("exchangeManager is not initialized") + return + } + scope.launch { exchangeManager.update() } + } + is GlobalAction.ScanCard -> { + scope.launch { + val result = tangemSdkManager.scanProduct( + store.state.globalState.analyticsHandlers, + currenciesRepository, + action.additionalBlockchainsToDerive, + action.messageResId + ) + withMainContext { + store.dispatch(GlobalAction.ScanFailsCounter.ChooseBehavior(result)) + when (result) { + is CompletionResult.Success -> { + tangemSdkManager.changeDisplayedCardIdNumbersCount(result.data) + action.onSuccess?.invoke(result.data) + } is CompletionResult.Failure -> { - if (action.result.error is TangemSdkError.UserCancelled) { - store.dispatch(GlobalAction.ScanFailsCounter.Increment) - if (store.state.globalState.scanCardFailsCounter >= 2) { - store.dispatchDialogShow(AppDialog.ScanFailsDialog) - } - } else { - store.dispatch(GlobalAction.ScanFailsCounter.Reset) - } - } - } - } - is GlobalAction.RestoreAppCurrency -> { - store.dispatch(GlobalAction.RestoreAppCurrency.Success( - preferencesStorage.fiatCurrenciesPrefStorage.getAppCurrency() - )) - } - is GlobalAction.HideWarningMessage -> { - store.state.globalState.warningManager?.let { - if (it.hideWarning(action.warning)) { - if (WarningMessagesManager.isAlreadySignedHashesWarning(action.warning)) { - //TODO: No appropriate warningMessage identification. Make it better later - store.dispatch(WalletAction.Warnings.CheckHashesCount.SaveCardId) - } - - store.dispatch(WalletAction.Warnings.Update) - store.dispatch(SendAction.Warnings.Update) - } - } - } - - is GlobalAction.SendFeedback -> { - store.state.globalState.feedbackManager?.send(action.emailData) - } - is GlobalAction.UpdateWalletSignedHashes -> { - store.dispatch(WalletAction.Warnings.CheckRemainingSignatures(action.remainingSignatures)) - } - is GlobalAction.UpdateFeedbackInfo -> { - store.state.globalState.feedbackManager?.infoHolder - ?.setWalletsInfo(action.walletManagers) - } - is GlobalAction.InitCurrencyExchangeManager -> { - val config = appState()?.globalState?.configManager?.config - ifNotNull( - config?.onramperApiKey, - config?.moonPayApiKey, - config?.moonPayApiSecretKey, - ) { onramperKey, moonPayKey, moonPaySecretKey -> - scope.launch { - val onramper = OnramperService(onramperKey) - val moonPay = MoonPayService(moonPayKey, moonPaySecretKey) - val exchangeManager = CurrencyExchangeManager(onramper, moonPay) - exchangeManager.getStatus() - store.dispatchOnMain(GlobalAction.InitCurrencyExchangeManager.Success(exchangeManager)) - } - } - } - is GlobalAction.ScanCard -> { - scope.launch { - val result = tangemSdkManager.scanProduct( - store.state.globalState.analyticsHandlers, - currenciesRepository, - action.additionalBlockchainsToDerive, - action.messageResId - ) - withMainContext { - store.dispatch(GlobalAction.ScanFailsCounter.ChooseBehavior(result)) - when (result) { - is CompletionResult.Success -> { - tangemSdkManager.changeDisplayedCardIdNumbersCount(result.data) - action.onSuccess?.invoke(result.data) - } - is CompletionResult.Failure -> { - action.onFailure?.invoke(result.error) - } - } + action.onFailure?.invoke(result.error) } } } } - nextDispatch(action) } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt index 67e54a6042..681d1f758a 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt @@ -70,8 +70,8 @@ fun globalReducer(action: Action, state: AppState): GlobalState { is GlobalAction.HideDialog -> { globalState.copy(dialog = null) } - is GlobalAction.InitCurrencyExchangeManager.Success -> { - globalState.copy(currencyExchangeManager = action.exchangeManager) + is GlobalAction.ExchangeManager.Init.Success -> { + globalState.copy(exchangeManager = action.exchangeManager) } is GlobalAction.SetIfCardVerifiedOnline -> globalState.copy(cardVerifiedOnline = action.verified) diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt index 75504f3ef2..48591606c1 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt @@ -25,7 +25,7 @@ data class GlobalState( val appCurrency: FiatCurrency = FiatCurrency.Default, val scanCardFailsCounter: Int = 0, val dialog: StateDialog? = null, - val currencyExchangeManager: CurrencyExchangeManager? = null, + val exchangeManager: CurrencyExchangeManager? = null, val resources: AndroidResources = AndroidResources(), val analyticsHandlers: AnalyticsHandler? = null, ) : StateType diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt index cb587f277b..7cddf2598a 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt @@ -11,8 +11,8 @@ import com.tangem.tap.domain.configurable.Loader data class Config( val coinMarketCapKey: String = "f6622117-c043-47a0-8975-9d673ce484de", val moonPayApiKey: String = "pk_test_kc90oYTANy7UQdBavDKGfL4K9l6VEPE", - val onramperApiKey: String = "pk_test_Ix2aCF3ej_5tcDKkBR7MChIvf5Nb0oPORPQ3Oal5G8I0", val moonPayApiSecretKey: String = "sk_test_V8w4M19LbDjjYOt170s0tGuvXAgyEb1C", + val mercuryoWidgetId: String = "", val appsFlyerDevKey: String = "", val blockchainSdkConfig: BlockchainSdkConfig = BlockchainSdkConfig(), val isSendingToPayIdEnabled: Boolean = true, @@ -82,8 +82,8 @@ class ConfigManager( config = config.copy( coinMarketCapKey = values.coinMarketCapKey, moonPayApiKey = values.moonPayApiKey, - onramperApiKey = values.onramperApiKey, moonPayApiSecretKey = values.moonPayApiSecretKey, + mercuryoWidgetId = values.mercuryoWidgetId, blockchainSdkConfig = BlockchainSdkConfig( blockchairApiKey = values.blockchairApiKey, blockchairAuthorizationToken = values.blockchairAuthorizationToken, @@ -96,8 +96,8 @@ class ConfigManager( defaultConfig = defaultConfig.copy( coinMarketCapKey = values.coinMarketCapKey, moonPayApiKey = values.moonPayApiKey, - onramperApiKey = values.onramperApiKey, moonPayApiSecretKey = values.moonPayApiSecretKey, + mercuryoWidgetId = values.mercuryoWidgetId, blockchainSdkConfig = BlockchainSdkConfig( blockchairApiKey = values.blockchairApiKey, blockchairAuthorizationToken = values.blockchairAuthorizationToken, diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt b/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt index 41d1ea2022..d7be703460 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt @@ -15,8 +15,8 @@ class FeatureModel( class ConfigValueModel( val coinMarketCapKey: String, + val mercuryoWidgetId: String, val moonPayApiKey: String, - val onramperApiKey: String, val moonPayApiSecretKey: String, val blockchairApiKey: String?, val blockchairAuthorizationToken: String?, diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/CurrencyExchangeManager.kt b/app/src/main/java/com/tangem/tap/domain/extensions/CurrencyExchangeManager.kt deleted file mode 100644 index 23be9f4aaf..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/extensions/CurrencyExchangeManager.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.tangem.tap.domain.extensions - -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.Blockchain.Arbitrum -import com.tangem.tap.features.wallet.redux.Currency -import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager -import com.tangem.tap.network.exchangeServices.CurrencyExchangeStatus -import com.tangem.tap.store - -/** -[REDACTED_AUTHOR] - */ -fun CurrencyExchangeManager.buyIsAllowed(currency: Currency): Boolean { - return this.status?.buyIsAllowed(currency) ?: false -} - -fun CurrencyExchangeManager.sellIsAllowed(currency: Currency): Boolean { - return this.status?.sellIsAllowed(currency) ?: false -} - -fun CurrencyExchangeStatus.buyIsAllowed(currency: Currency): Boolean { - if (store.state.globalState.configManager?.config?.isTopUpEnabled == false) return false - if (currency.blockchain == Arbitrum) return false - if (!isBuyAllowed) return false - - //TODO: temporary, for the 3.32 release, unlock all buy button - return true - - return when (currency) { - is Currency.Blockchain -> { - val blockchain = currency.blockchain - when { - blockchain.isTestnet() -> blockchain.getTestnetTopUpUrl() != null - blockchain == Blockchain.Unknown -> false - else -> availableToBuy.contains(currency.currencySymbol) - } - } - is Currency.Token -> false - } -} - -fun CurrencyExchangeStatus.sellIsAllowed(currency: Currency): Boolean { - if (store.state.globalState.configManager?.config?.isTopUpEnabled == false) return false - if (!isSellAllowed) return false - - return when (currency) { - is Currency.Blockchain -> { - val blockchain = currency.blockchain - when { - blockchain.isTestnet() -> false - blockchain == Blockchain.Unknown || currency.blockchain == Blockchain.BSC -> false - else -> availableToSell.contains(currency.currencySymbol) - } - } - is Currency.Token -> false - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt index e4472de543..8a3878556d 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt @@ -41,7 +41,7 @@ private val homeMiddleware: Middleware = { dispatch, state -> when (action) { is HomeAction.Init -> { store.dispatch(GlobalAction.RestoreAppCurrency) - store.dispatch(GlobalAction.InitCurrencyExchangeManager) + store.dispatch(GlobalAction.ExchangeManager.Init) store.dispatch(HomeAction.SetTermsOfUseState(preferencesStorage.wasDisclaimerAccepted())) } is HomeAction.ShouldScanCardOnResume -> { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingManager.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingManager.kt index f0873595ff..a539cefefd 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingManager.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingManager.kt @@ -83,7 +83,7 @@ class OnboardingManager( data class OnboardingWalletBalance( val value: BigDecimal = BigDecimal.ZERO, - val currency: Currency.Blockchain = Currency.Blockchain(Blockchain.Unknown, null), + val currency: Currency = Currency.Blockchain(Blockchain.Unknown, null), val hasIncomingTransaction: Boolean = false, val state: ProgressState, val error: TapError? = null, diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt index 77b12445f7..a9ebb0e757 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteState.kt @@ -2,7 +2,6 @@ package com.tangem.tap.features.onboarding.products.note.redux import com.tangem.blockchain.common.WalletManager import com.tangem.tap.domain.TapError -import com.tangem.tap.domain.extensions.buyIsAllowed import com.tangem.tap.features.onboarding.OnboardingWalletBalance import com.tangem.tap.store import org.rekotlin.StateType @@ -26,8 +25,8 @@ data class OnboardingNoteState( val progress: Int get() = steps.indexOf(currentStep) - val isBuyAllowed: Boolean by ReadOnlyProperty { thisRef, property -> - store.state.globalState.currencyExchangeManager?.buyIsAllowed(walletBalance.currency) ?: false + val isBuyAllowed: Boolean by ReadOnlyProperty { _, _ -> + store.state.globalState.exchangeManager?.availableForBuy(walletBalance.currency) ?: false } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsState.kt index fc24dbd461..19cc8ad307 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsState.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsState.kt @@ -3,7 +3,6 @@ package com.tangem.tap.features.onboarding.products.twins.redux import com.tangem.blockchain.common.WalletManager import com.tangem.domain.common.TwinCardNumber import com.tangem.tap.domain.TapError -import com.tangem.tap.domain.extensions.buyIsAllowed import com.tangem.tap.domain.twins.TwinCardsManager import com.tangem.tap.features.onboarding.OnboardingWalletBalance import com.tangem.tap.store @@ -56,8 +55,8 @@ data class TwinCardsState( val showAlert: Boolean get() = currentStep == TwinCardsStep.CreateSecondWallet || currentStep == TwinCardsStep.CreateThirdWallet - val isBuyAllowed: Boolean by ReadOnlyProperty { thisRef, property -> - store.state.globalState.currencyExchangeManager?.buyIsAllowed(walletBalance.currency) ?: false + val isBuyAllowed: Boolean by ReadOnlyProperty { _, _ -> + store.state.globalState.exchangeManager?.availableForBuy(walletBalance.currency) ?: false } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt index b4502b9267..26224eb41e 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt @@ -14,8 +14,6 @@ import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.common.redux.global.CryptoCurrencyName import com.tangem.tap.common.toggleWidget.WidgetState import com.tangem.tap.domain.configurable.warningMessage.WarningMessage -import com.tangem.tap.domain.extensions.buyIsAllowed -import com.tangem.tap.domain.extensions.sellIsAllowed import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState import com.tangem.tap.features.tokens.redux.TokenWithBlockchain @@ -62,7 +60,7 @@ data class WalletState( val shouldShowDetails: Boolean = primaryWallet?.currencyData?.status != BalanceStatus.EmptyCard && - primaryWallet?.currencyData?.status != BalanceStatus.UnknownBlockchain + primaryWallet?.currencyData?.status != BalanceStatus.UnknownBlockchain val blockchains: List get() = wallets.mapNotNull { it.walletManager?.wallet?.blockchain } @@ -363,18 +361,21 @@ data class Artwork( } data class TradeCryptoState( - val sellingAllowed: Boolean = false, - val buyingAllowed: Boolean = false, + val isAvailableToSell: () -> Boolean = { false }, + val isAvailableToBuy: () -> Boolean = { false }, ) { companion object { fun from( exchangeManager: CurrencyExchangeManager?, walletData: WalletData ): TradeCryptoState { - val status = exchangeManager ?: return walletData.tradeCryptoState + val exchanger = exchangeManager ?: return walletData.tradeCryptoState val currency = walletData.currency - return TradeCryptoState(status.sellIsAllowed(currency), status.buyIsAllowed(currency)) + return TradeCryptoState( + isAvailableToSell = { exchanger.availableForSell(currency) }, + isAvailableToBuy = { exchanger.availableForBuy(currency) }, + ) } } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt index 3e4210fe42..3ff2589348 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt @@ -13,7 +13,7 @@ import com.tangem.tap.features.send.redux.SendAction import com.tangem.tap.features.wallet.redux.Currency import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager -import com.tangem.tap.network.exchangeServices.buyErc20Tokens +import com.tangem.tap.network.exchangeServices.buyErc20TestnetTokens import com.tangem.tap.scope import com.tangem.tap.store import kotlinx.coroutines.launch @@ -33,7 +33,7 @@ class TradeCryptoMiddleware { private fun startExchange(action: WalletAction.TradeCryptoAction) { val selectedWalletData = store.state.walletState.getSelectedWalletData() - val exchangeManager = store.state.globalState.currencyExchangeManager ?: return + val exchangeManager = store.state.globalState.exchangeManager ?: return val addresses = selectedWalletData?.walletAddresses ?: return if (addresses.list.isEmpty()) return val appCurrency = store.state.globalState.appCurrency @@ -53,11 +53,11 @@ class TradeCryptoMiddleware { ) { val walletManager = store.state.walletState.getWalletManager(currency) if (walletManager !is EthereumWalletManager) { - store.dispatchDebugErrorNotification("Testnet tokens available only for the ETH") + store.dispatchDebugErrorNotification("Testnet tokens available only for the Ethereum") return } - scope.launch { exchangeManager.buyErc20Tokens(walletManager, currency.token) } + scope.launch { exchangeManager.buyErc20TestnetTokens(walletManager, currency.token) } return } @@ -89,7 +89,7 @@ class TradeCryptoMiddleware { } private fun openReceiptUrl(transactionId: String) { - val exchangeManager = store.state.globalState.currencyExchangeManager ?: return + val exchangeManager = store.state.globalState.exchangeManager ?: return store.dispatchOnMain(NavigationAction.PopBackTo()) exchangeManager.getSellCryptoReceiptUrl(CurrencyExchangeManager.Action.Sell, transactionId)?.let { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/OnWalletLoadedReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/OnWalletLoadedReducer.kt index 2e03a3f88d..50f4a1cec0 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/OnWalletLoadedReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/OnWalletLoadedReducer.kt @@ -37,7 +37,7 @@ class OnWalletLoadedReducer { val walletData = walletState.getWalletData(blockchainNetwork) ?: return walletState val fiatCurrency = store.state.globalState.appCurrency - val exchangeManager = store.state.globalState.currencyExchangeManager + val exchangeManager = store.state.globalState.exchangeManager val coinAmountValue = wallet.amounts[AmountType.Coin]?.value val formattedAmount = coinAmountValue?.toFormattedCurrencyString( @@ -117,7 +117,7 @@ class OnWalletLoadedReducer { if (wallet.blockchain != walletState.primaryBlockchain) return walletState val fiatCurrencyName = store.state.globalState.appCurrency.code - val exchangeManager = store.state.globalState.currencyExchangeManager + val exchangeManager = store.state.globalState.exchangeManager val token = wallet.getFirstToken() val tokenData = if (token != null) { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt index 04fec99524..3e39e7ed42 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt @@ -6,34 +6,19 @@ 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.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.extensions.* import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.TapError import com.tangem.tap.domain.extensions.getArtworkUrl import com.tangem.tap.domain.getFirstToken import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.features.wallet.models.WalletRent -import com.tangem.tap.features.wallet.redux.AddressData -import com.tangem.tap.features.wallet.redux.Artwork -import com.tangem.tap.features.wallet.redux.Currency -import com.tangem.tap.features.wallet.redux.ErrorType -import com.tangem.tap.features.wallet.redux.ProgressState -import com.tangem.tap.features.wallet.redux.TradeCryptoState -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.redux.* import com.tangem.tap.features.wallet.ui.BalanceStatus import com.tangem.tap.features.wallet.ui.BalanceWidgetData import com.tangem.tap.store -import java.math.BigDecimal import org.rekotlin.Action +import java.math.BigDecimal class WalletReducer { companion object { @@ -49,7 +34,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState { if (action !is WalletAction) return state.walletState - val exchangeManager = store.state.globalState.currencyExchangeManager + val exchangeManager = store.state.globalState.exchangeManager var newState = state.walletState when (action) { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt index 997c042f15..d5bd095cbc 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt @@ -1,11 +1,7 @@ package com.tangem.tap.features.wallet.ui import android.os.Bundle -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem -import android.view.View -import android.view.ViewGroup +import android.view.* import android.widget.TextView import androidx.activity.OnBackPressedCallback import androidx.annotation.ColorRes @@ -18,25 +14,13 @@ import by.kirich1409.viewbindingdelegate.viewBinding import com.tangem.tangem_sdk_new.extensions.dpToPx import com.tangem.tap.common.SnackbarHandler import com.tangem.tap.common.TestActions -import com.tangem.tap.common.extensions.appendIfNotNull -import com.tangem.tap.common.extensions.beginDelayedTransition -import com.tangem.tap.common.extensions.fitChipsByGroupWidth -import com.tangem.tap.common.extensions.getColor -import com.tangem.tap.common.extensions.getString -import com.tangem.tap.common.extensions.hide -import com.tangem.tap.common.extensions.show -import com.tangem.tap.common.extensions.toQrCode +import com.tangem.tap.common.extensions.* import com.tangem.tap.common.recyclerView.SpaceItemDecoration import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.features.onboarding.getQRReceiveMessage import com.tangem.tap.features.wallet.models.PendingTransaction -import com.tangem.tap.features.wallet.redux.Currency -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.WalletData -import com.tangem.tap.features.wallet.redux.WalletState +import com.tangem.tap.features.wallet.redux.* import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN import com.tangem.tap.features.wallet.ui.adapters.PendingTransactionsAdapter import com.tangem.tap.features.wallet.ui.adapters.WalletDetailWarningMessagesAdapter @@ -200,8 +184,8 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), } rowButtons.updateButtonsVisibility( - buyAllowed = selectedWallet.tradeCryptoState.buyingAllowed, - sellAllowed = selectedWallet.tradeCryptoState.sellingAllowed, + buyAllowed = selectedWallet.tradeCryptoState.isAvailableToBuy(), + sellAllowed = selectedWallet.tradeCryptoState.isAvailableToSell(), sendAllowed = selectedWallet.mainButton.enabled, ) } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt index b7e0a307a5..19815ccfe7 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt @@ -11,12 +11,7 @@ import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState import com.tangem.tap.features.wallet.models.PendingTransaction -import com.tangem.tap.features.wallet.redux.Currency -import com.tangem.tap.features.wallet.redux.TradeCryptoState -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.redux.* import com.tangem.tap.features.wallet.ui.BalanceWidget import com.tangem.tap.features.wallet.ui.MultipleAddressUiHelper import com.tangem.tap.features.wallet.ui.WalletFragment @@ -122,9 +117,8 @@ class SingleWalletView : WalletView { setupButtonsType(state, binding) - val btnConfirm = if (state.tradeCryptoState.sellingAllowed || - state.tradeCryptoState.buyingAllowed - ) { + val tradeState = state.tradeCryptoState + val btnConfirm = if (tradeState.isAvailableToSell() || tradeState.isAvailableToBuy()) { lButtonsShort.btnConfirm } else { lButtonsLong.btnConfirmLong @@ -152,8 +146,8 @@ class SingleWalletView : WalletView { } private fun setupTradeButton(binding: FragmentWalletBinding, tradeCryptoState: TradeCryptoState) { - val allowedToBuy = tradeCryptoState.buyingAllowed - val allowedToSell = tradeCryptoState.sellingAllowed + val allowedToBuy = tradeCryptoState.isAvailableToBuy() + val allowedToSell = tradeCryptoState.isAvailableToSell() val action = when { allowedToBuy && !allowedToSell -> WalletAction.TradeCryptoAction.Buy !allowedToBuy && allowedToSell -> WalletAction.TradeCryptoAction.Sell @@ -180,9 +174,7 @@ class SingleWalletView : WalletView { } private fun setupButtonsType(state: WalletData, binding: FragmentWalletBinding) = with(binding) { - if (state.tradeCryptoState.sellingAllowed || - state.tradeCryptoState.buyingAllowed - ) { + if (state.tradeCryptoState.isAvailableToSell() || state.tradeCryptoState.isAvailableToBuy()) { lButtonsLong.root.hide() lButtonsShort.root.show() } else { diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt index 7a89edc4b0..745ee01053 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt @@ -11,6 +11,7 @@ import com.tangem.tap.common.extensions.safeUpdate import com.tangem.tap.common.redux.global.CryptoCurrencyName import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.TangemSigner +import com.tangem.tap.features.wallet.redux.Currency import com.tangem.tap.store import com.tangem.tap.tangemSdk import java.math.BigDecimal @@ -18,57 +19,20 @@ import java.math.BigDecimal /** [REDACTED_AUTHOR] */ -interface ExchangeService { - suspend fun isBuyAllowed(): Boolean - suspend fun availableToBuy(): List - suspend fun isSellAllowed(): Boolean - suspend fun availableToSell(): List -} - -interface ExchangeUrlBuilder { - fun getUrl( - action: CurrencyExchangeManager.Action, - blockchain: Blockchain, - cryptoCurrencyName: CryptoCurrencyName, - fiatCurrencyName: String, - walletAddress: String, - ): String? - - fun getSellCryptoReceiptUrl(action: CurrencyExchangeManager.Action, transactionId: String): String? - - companion object { - const val SCHEME = "https" - const val URL_SELL = "sell.moonpay.com" - const val SUCCESS_URL = "tangem://success.tangem.com" - } -} - class CurrencyExchangeManager( - private val onramperService: ExchangeService, - private val moonPayService: ExchangeService, + private val buyService: ExchangeService, + private val sellService: ExchangeService, ) : ExchangeService, ExchangeUrlBuilder { - var status: CurrencyExchangeStatus? = null - private set - - suspend fun getStatus(): CurrencyExchangeStatus { - val isBuyAllowed = isBuyAllowed() - val isSellAllowed = isSellAllowed() - val availableToBuy = availableToBuy() - val availableToSell = availableToSell() - status = CurrencyExchangeStatus( - isBuyAllowed, - isSellAllowed, - availableToBuy, - availableToSell, - ) - return status!! + override suspend fun update() { + buyService.update() + sellService.update() } - override suspend fun isBuyAllowed(): Boolean = onramperService.isBuyAllowed() - override suspend fun availableToBuy(): List = onramperService.availableToBuy() - override suspend fun isSellAllowed(): Boolean = moonPayService.isSellAllowed() - override suspend fun availableToSell(): List = moonPayService.availableToSell() + 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 getUrl( action: Action, @@ -96,22 +60,15 @@ class CurrencyExchangeManager( private fun getExchangeUrlBuilder(action: Action): ExchangeUrlBuilder { return when (action) { - Action.Buy -> onramperService - Action.Sell -> moonPayService + Action.Buy -> buyService + Action.Sell -> sellService } as ExchangeUrlBuilder } enum class Action { Buy, Sell } } -data class CurrencyExchangeStatus( - val isBuyAllowed: Boolean, - val isSellAllowed: Boolean, - val availableToBuy: List, - val availableToSell: List, -) - -suspend fun CurrencyExchangeManager.buyErc20Tokens(walletManager: EthereumWalletManager, token: Token) { +suspend fun CurrencyExchangeManager.buyErc20TestnetTokens(walletManager: EthereumWalletManager, token: Token) { walletManager.safeUpdate() val amountToSend = Amount(walletManager.wallet.blockchain) diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/ExchangeService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/ExchangeService.kt new file mode 100644 index 0000000000..e08e007029 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/ExchangeService.kt @@ -0,0 +1,29 @@ +package com.tangem.tap.network.exchangeServices + +import com.tangem.blockchain.common.Blockchain +import com.tangem.tap.features.wallet.redux.Currency + +interface ExchangeService { + suspend fun update() + fun isBuyAllowed(): Boolean + fun isSellAllowed(): Boolean + fun availableForBuy(currency: Currency):Boolean + fun availableForSell(currency: Currency):Boolean +} + +interface ExchangeUrlBuilder { + fun getUrl( + action: CurrencyExchangeManager.Action, + blockchain: Blockchain, + cryptoCurrencyName: String, + fiatCurrencyName: String, + walletAddress: String, + ): String? + + fun getSellCryptoReceiptUrl(action: CurrencyExchangeManager.Action, transactionId: String): String? + + companion object { + const val SCHEME = "https" + const val SUCCESS_URL = "tangem://success.tangem.com" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoApi.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoApi.kt new file mode 100644 index 0000000000..45494e0114 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoApi.kt @@ -0,0 +1,57 @@ +package com.tangem.tap.network.exchangeServices.mercuryo + +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 { + + @GET("{apiVersion}/lib/currencies") + suspend fun currencies( + @Path("apiVersion") apiVersion: String, + ): MercuryoCurrenciesResponse + + companion object { + const val BASE_URL = "https://api.mercuryo.io/" + const val API_VERSION = "v1.6" + } +} + +data class MercuryoCurrenciesResponse( + val status: Int, + val data: Data, +) { + data class Data( + val fiat: List, + val crypto: List, + val config: Config + ) + + data class Config( + val base: Map, + @Json(name = "has_withdrawal_fee") + val hasWithdrawalFee: Map, + @Json(name = "display_options") + val displayOptions: Map, + val icons: Map, + ) + + data class DisplayOption( + @Json(name = "fullname") + val fullName: String, + @Json(name = "total_digits") + val totalDigits: Int, + @Json(name = "display_digits") + val displayDigits: Int, + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt new file mode 100644 index 0000000000..9760849ed6 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt @@ -0,0 +1,127 @@ +package com.tangem.tap.network.exchangeServices.mercuryo + +import android.net.Uri +import com.tangem.blockchain.common.Blockchain +import com.tangem.common.services.Result +import com.tangem.common.services.performRequest +import com.tangem.network.common.createRetrofitInstance +import com.tangem.tap.common.extensions.urlEncode +import com.tangem.tap.common.redux.global.CryptoCurrencyName +import com.tangem.tap.features.wallet.redux.Currency +import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager +import com.tangem.tap.network.exchangeServices.ExchangeService +import com.tangem.tap.network.exchangeServices.ExchangeUrlBuilder + +/** +[REDACTED_AUTHOR] + */ +class MercuryoService( + private val apiVersion: String, + private val mercuryoWidgetId: String, +) : ExchangeService, ExchangeUrlBuilder { + + private val api: MercuryoApi = createRetrofitInstance(MercuryoApi.BASE_URL) + .create(MercuryoApi::class.java) + + private val blockchainsAvailableToBuy = mutableListOf() + private val tokensAvailableToBy = mutableMapOf>() + + override suspend fun update() { + when (val result = performRequest { api.currencies(apiVersion) }) { + is Result.Success -> { + val response = result.data + if (response.status == 200) { + // all currencies which can be bought + val currenciesAvailableToBy = response.data.crypto + // tokens which can be bought only from specific blockchain network + val supportedTokensWithNetwork = response.data.config.base + + currenciesAvailableToBy.forEach { currencyName -> + val blockchain = blockchainFromCurrencyName(currencyName) + if (blockchain == null) { + // suppose its a token + supportedTokensWithNetwork[currencyName]?.let { + blockchainFromCurrencyName(it) + }?.let { blockchainNetwork -> + val supportedInBlockchainsNetwork = tokensAvailableToBy[currencyName] + ?: mutableListOf() + supportedInBlockchainsNetwork.add(blockchainNetwork) + tokensAvailableToBy[currencyName] = supportedInBlockchainsNetwork + } + } else { + blockchainsAvailableToBuy.add(blockchain) + } + } + } + } + is Result.Failure -> { + blockchainsAvailableToBuy.clear() + tokensAvailableToBy.clear() + } + } + } + + override fun isBuyAllowed(): Boolean = true + + override fun isSellAllowed(): Boolean = false + + override fun availableForBuy(currency: Currency): Boolean { + if (!isBuyAllowed()) return false + + // blockchains which cant be defined by mercuryo service + val unsupportedBlockchains = listOf(Blockchain.Unknown, Blockchain.Binance, Blockchain.Arbitrum) + val blockchain = currency.blockchain + + return when (currency) { + is Currency.Blockchain -> { + when { + blockchain.isTestnet() -> blockchain.getTestnetTopUpUrl() != null + unsupportedBlockchains.contains(blockchain) -> false + else -> { + blockchainsAvailableToBuy.contains(currency.blockchain) + } + } + } + is Currency.Token -> { + val supportedInBlockchains = tokensAvailableToBy[currency.currencySymbol] ?: return false + supportedInBlockchains.contains(currency.blockchain) + } + } + } + + override fun availableForSell(currency: Currency): Boolean = false + + override fun getUrl( + action: CurrencyExchangeManager.Action, + blockchain: Blockchain, + cryptoCurrencyName: CryptoCurrencyName, + fiatCurrencyName: String, + walletAddress: String + ): String? { + if (action == CurrencyExchangeManager.Action.Sell) throw UnsupportedOperationException() + + val builder = Uri.Builder() + .scheme(ExchangeUrlBuilder.SCHEME) + .authority("exchange.mercuryo.io") + .appendQueryParameter("widget_id", mercuryoWidgetId) + .appendQueryParameter("type", action.name.lowercase()) + .appendQueryParameter("currency", cryptoCurrencyName) + .appendQueryParameter("address", walletAddress.urlEncode()) + .appendQueryParameter("fix_currency", "true") + .appendQueryParameter("return_url", ExchangeUrlBuilder.SUCCESS_URL) + + val url = builder.build().toString() + return url + } + + override fun getSellCryptoReceiptUrl( + action: CurrencyExchangeManager.Action, + transactionId: String + ): String? = null + + private fun blockchainFromCurrencyName(currencyName: String): Blockchain? = when (currencyName) { + "BNB" -> Blockchain.BSC + "ETH" -> Blockchain.Ethereum + else -> Blockchain.values().find { it.currency.lowercase() == currencyName.lowercase() } + } +} diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt index 54fd9c558e..161cfc568a 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt @@ -5,15 +5,15 @@ import android.util.Base64 import com.tangem.blockchain.common.Blockchain import com.tangem.common.services.Result import com.tangem.common.services.performRequest +import com.tangem.domain.common.extensions.withIOContext import com.tangem.network.common.createRetrofitInstance import com.tangem.tap.common.extensions.urlEncode import com.tangem.tap.common.redux.global.CryptoCurrencyName +import com.tangem.tap.features.wallet.redux.Currency import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager import com.tangem.tap.network.exchangeServices.ExchangeService import com.tangem.tap.network.exchangeServices.ExchangeUrlBuilder import com.tangem.tap.network.exchangeServices.ExchangeUrlBuilder.Companion.SCHEME -import com.tangem.tap.network.exchangeServices.ExchangeUrlBuilder.Companion.URL_SELL -import kotlinx.coroutines.coroutineScope import javax.crypto.Mac import javax.crypto.spec.SecretKeySpec @@ -29,14 +29,14 @@ class MoonPayService( private var status: MoonPayStatus? = null - private suspend fun updateStatus() { - try { - coroutineScope { + override suspend fun update() { + withIOContext { + performRequest { val userStatusResult = performRequest { api.getUserStatus(apiKey) } - if (userStatusResult is Result.Failure) return@coroutineScope userStatusResult + if (userStatusResult is Result.Failure) return@performRequest val currenciesResult = performRequest { api.getCurrencies(apiKey) } - if (currenciesResult is Result.Failure) return@coroutineScope currenciesResult + if (currenciesResult is Result.Failure) return@performRequest val userStatus = (userStatusResult as Result.Success).data val currencies = (currenciesResult as Result.Success).data @@ -65,29 +65,31 @@ class MoonPayService( status = MoonPayStatus(currenciesToSell, userStatus, currencies) } - } catch (error: Error) { - status = null - Result.Failure(error) } } - override suspend fun isBuyAllowed(): Boolean = false + override fun isBuyAllowed(): Boolean = false - override suspend fun availableToBuy(): List = listOf() - - override suspend fun isSellAllowed(): Boolean { - refreshStatus() + override fun isSellAllowed(): Boolean { return status?.responseUserStatus?.isSellAllowed ?: false } - override suspend fun availableToSell(): List { - refreshStatus() - return status?.availableToSell ?: emptyList() - } + override fun availableForBuy(currency: Currency): Boolean = false - private suspend fun refreshStatus() { - if (status == null) { - updateStatus() + override fun availableForSell(currency: Currency): Boolean { + val availableForSell = status?.availableForSell ?: return false + if (!isSellAllowed()) return false + + return when (currency) { + is Currency.Blockchain -> { + val blockchain = currency.blockchain + when { + blockchain.isTestnet() -> false + blockchain == Blockchain.Unknown || currency.blockchain == Blockchain.BSC -> false + else -> availableForSell.contains(currency.currencySymbol) + } + } + is Currency.Token -> false } } @@ -133,10 +135,14 @@ class MoonPayService( val sha256encoded = sha256Hmac.doFinal(data.toByteArray()) return Base64.encodeToString(sha256encoded, Base64.NO_WRAP) } + + companion object { + const val URL_SELL = "sell.moonpay.com" + } } private data class MoonPayStatus( - val availableToSell: List, + val availableForSell: List, val responseUserStatus: MoonPayUserStatus, val responseCurrencies: List ) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/onramper/OnnramperApi.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/onramper/OnnramperApi.kt deleted file mode 100644 index 30a65ed706..0000000000 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/onramper/OnnramperApi.kt +++ /dev/null @@ -1,83 +0,0 @@ -package com.tangem.tap.network.exchangeServices.onramper - -import com.squareup.moshi.JsonClass -import retrofit2.http.GET -import retrofit2.http.Path - -interface OnramperApi { - @GET("gateways") - suspend fun gateways(): GatewaysResponse - - @GET("rate/{fromCurrency}/{toCurrency}/{paymentMethod}/{amount}") - suspend fun rate( - @Path("fromCurrency") fromCurrency: String, - @Path("toCurrency") toCurrency: String, - @Path("paymentMethod") paymentMethod: String, - @Path("amount") amount: Int, - ): RateResponse - - companion object { - val BASE_URL = "https://onramper.tech/" - } -} - -@JsonClass(generateAdapter = true) -data class GatewaysResponse( - val gateways: List -) - -@JsonClass(generateAdapter = true) -data class OnramperGateway( - val identifier: String, - val paymentMethods: List, - val fiatCurrencies: List, - val cryptoCurrencies: List -) - -@JsonClass(generateAdapter = true) -data class OnramperCurrency( - val id: String, - val code: String, - val precision: Int -) - -@JsonClass(generateAdapter = true) -data class RateResponse( - val identifier: String, - val duration: OnramperDuration, - val available: Boolean, - val error: OnramperError? = null, - val rate: Double? = null, - val fees: Double? = null, - val requiredKYC: List? = null, - val receivedCrypto: Double? = null, - val nextStep: OnramperNextStep? = null, -) - -@JsonClass(generateAdapter = true) -data class OnramperNextStep( - val type: String, - val url: String, - val message: String, - val extraData: List -) - -@JsonClass(generateAdapter = true) -data class OnramperExtraData( - val type: String, - val name: String, - val humanName: String, -) - -@JsonClass(generateAdapter = true) -data class OnramperDuration( - val seconds: Long, - val message: String -) - -@JsonClass(generateAdapter = true) -data class OnramperError( - val type: String, - val message: String, - val limit: Double -) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/onramper/OnramperService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/onramper/OnramperService.kt deleted file mode 100644 index 94bdae395e..0000000000 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/onramper/OnramperService.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.tangem.tap.network.exchangeServices.onramper - -import android.net.Uri -import com.tangem.blockchain.common.Blockchain -import com.tangem.common.services.Result -import com.tangem.common.services.performRequest -import com.tangem.network.common.AddHeaderInterceptor -import com.tangem.network.common.createRetrofitInstance -import com.tangem.tap.common.extensions.urlEncode -import com.tangem.tap.common.redux.global.CryptoCurrencyName -import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager -import com.tangem.tap.network.exchangeServices.ExchangeService -import com.tangem.tap.network.exchangeServices.ExchangeUrlBuilder -import com.tangem.tap.network.exchangeServices.ExchangeUrlBuilder.Companion.SCHEME -import com.tangem.tap.network.exchangeServices.ExchangeUrlBuilder.Companion.SUCCESS_URL -import kotlinx.coroutines.coroutineScope -import java.util.* - -/** -[REDACTED_AUTHOR] - */ -class OnramperService( - val apiKey: String -) : ExchangeService, ExchangeUrlBuilder { - - private val api: OnramperApi by lazy { - createRetrofitInstance( - baseUrl = OnramperApi.BASE_URL, - interceptors = listOf( - AddHeaderInterceptor(mapOf("Authorization" to "Basic $apiKey")), - ) - ).create(OnramperApi::class.java) - } - - private var status: OnramperStatus? = null - - private suspend fun updateStatus() { - try { - coroutineScope { - val result = performRequest { api.gateways() } - if (result is Result.Failure) return@coroutineScope result - - val response = (result as Result.Success).data - val currenciesToBuy = extractCurrenciesToBuy(response).sorted() - val status = OnramperStatus(currenciesToBuy, response) - this@OnramperService.status = status - } - } catch (error: Error) { - status = null - Result.Failure(error) - } - } - - private fun extractCurrenciesToBuy(response: GatewaysResponse): List { - return response.gateways.map { gateway -> - gateway.cryptoCurrencies.map { currency -> currency.code } - }.flatten().toMutableSet().toList() - } - - override suspend fun isBuyAllowed(): Boolean { - refreshStatus() - return status != null - } - - override suspend fun availableToBuy(): List { - refreshStatus() - return status?.availableToBuy ?: emptyList() - } - - private suspend fun refreshStatus() { - if (status == null) { - updateStatus() - } - } - - override suspend fun isSellAllowed(): Boolean = false - - override suspend fun availableToSell(): List = listOf() - - override fun getUrl( - action: CurrencyExchangeManager.Action, - blockchain: Blockchain, - cryptoCurrencyName: CryptoCurrencyName, - fiatCurrency: String, - walletAddress: String, - ): String? { - var languageCode = Locale.getDefault().language - if (languageCode.isEmpty()) languageCode = "en" - - val builder = Uri.Builder() - .scheme(SCHEME) - .authority("widget.onramper.com") - .appendQueryParameter("apiKey", this.apiKey.urlEncode()) - .appendQueryParameter("defaultCrypto", cryptoCurrencyName) - .appendQueryParameter("wallets", "${blockchain.currency}:$walletAddress".urlEncode()) - .appendQueryParameter("redirectURL", SUCCESS_URL) - .appendQueryParameter("defaultFiat", fiatCurrency) - .appendQueryParameter("language", languageCode) - - status?.apply { - val gateways = responseGateways.gateways.joinToString(",") { it.identifier }.urlEncode() - builder.appendQueryParameter("onlyGateways", gateways) - - } - - val url = builder.build().toString() - return url - } - - override fun getSellCryptoReceiptUrl(action: CurrencyExchangeManager.Action, transactionId: String): String? = null -} - -private data class OnramperStatus( - val availableToBuy: List, - val responseGateways: GatewaysResponse -) \ No newline at end of file From c4241a2ee402994b51b80fae87eab9b7eaa11d16 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 8 Jul 2022 21:04:03 +0300 Subject: [PATCH 07/28] Updated on 2026-08-14 --- app/src/main/assets/tangem-app-config | 2 +- .../tangem/tap/common/redux/global/GlobalMidlleware.kt | 2 +- .../tap/domain/configurable/config/ConfigManager.kt | 2 ++ .../tangem/tap/domain/configurable/config/JsonModels.kt | 1 + .../network/exchangeServices/mercuryo/MercuryoService.kt | 9 +++++++++ 5 files changed, 14 insertions(+), 2 deletions(-) diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index feec295055..59ee0bff06 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit feec2950551a1dbabbb018b446b25e17442db21f +Subproject commit 59ee0bff06ee54f3202421c28f7fb6e3d421dad8 diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt index 9777538858..2a12c999bf 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt @@ -91,7 +91,7 @@ private fun handleAction(action: Action, appState: () -> AppState?, dispatch: Di config?.moonPayApiSecretKey, ) { mercuryoWidgetId, moonPayKey, moonPaySecretKey -> scope.launch { - val buyService = MercuryoService(MercuryoApi.API_VERSION, mercuryoWidgetId) + val buyService = MercuryoService(MercuryoApi.API_VERSION, mercuryoWidgetId, "") val sellService = MoonPayService(moonPayKey, moonPaySecretKey) val exchangeManager = CurrencyExchangeManager(buyService, sellService) store.dispatchOnMain(GlobalAction.ExchangeManager.Init.Success(exchangeManager)) diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt index 7cddf2598a..d0e7bb9584 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt @@ -13,6 +13,7 @@ data class Config( val moonPayApiKey: String = "pk_test_kc90oYTANy7UQdBavDKGfL4K9l6VEPE", val moonPayApiSecretKey: String = "sk_test_V8w4M19LbDjjYOt170s0tGuvXAgyEb1C", val mercuryoWidgetId: String = "", + val mercuryoSecret: String = "", val appsFlyerDevKey: String = "", val blockchainSdkConfig: BlockchainSdkConfig = BlockchainSdkConfig(), val isSendingToPayIdEnabled: Boolean = true, @@ -98,6 +99,7 @@ class ConfigManager( moonPayApiKey = values.moonPayApiKey, moonPayApiSecretKey = values.moonPayApiSecretKey, mercuryoWidgetId = values.mercuryoWidgetId, + mercuryoSecret = values.mercuryoSecret, blockchainSdkConfig = BlockchainSdkConfig( blockchairApiKey = values.blockchairApiKey, blockchairAuthorizationToken = values.blockchairAuthorizationToken, diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt b/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt index d7be703460..f9e053ee98 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt @@ -16,6 +16,7 @@ class FeatureModel( class ConfigValueModel( val coinMarketCapKey: String, val mercuryoWidgetId: String, + val mercuryoSecret: String, val moonPayApiKey: String, val moonPayApiSecretKey: String, val blockchairApiKey: String?, diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt index 9760849ed6..a0068b9ce1 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt @@ -2,6 +2,8 @@ package com.tangem.tap.network.exchangeServices.mercuryo import android.net.Uri import com.tangem.blockchain.common.Blockchain +import com.tangem.common.extensions.calculateSha512 +import com.tangem.common.extensions.toHexString import com.tangem.common.services.Result import com.tangem.common.services.performRequest import com.tangem.network.common.createRetrofitInstance @@ -18,6 +20,7 @@ import com.tangem.tap.network.exchangeServices.ExchangeUrlBuilder class MercuryoService( private val apiVersion: String, private val mercuryoWidgetId: String, + private val secret: String, ) : ExchangeService, ExchangeUrlBuilder { private val api: MercuryoApi = createRetrofitInstance(MercuryoApi.BASE_URL) @@ -107,6 +110,7 @@ class MercuryoService( .appendQueryParameter("type", action.name.lowercase()) .appendQueryParameter("currency", cryptoCurrencyName) .appendQueryParameter("address", walletAddress.urlEncode()) + .appendQueryParameter("signature", signature(walletAddress).urlEncode()) .appendQueryParameter("fix_currency", "true") .appendQueryParameter("return_url", ExchangeUrlBuilder.SUCCESS_URL) @@ -114,6 +118,11 @@ class MercuryoService( return url } + private fun signature(address: String): String { + return (address + secret).calculateSha512().toHexString().lowercase() + } + + override fun getSellCryptoReceiptUrl( action: CurrencyExchangeManager.Action, transactionId: String From bf31658f026d9d7048094371fcc9383a357aa42c Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 8 Jul 2022 21:15:20 +0300 Subject: [PATCH 08/28] Updated on 2026-08-14 --- .../tangem/tap/common/redux/global/GlobalMidlleware.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt index 2a12c999bf..ea77ce5ed9 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt @@ -87,11 +87,16 @@ private fun handleAction(action: Action, appState: () -> AppState?, dispatch: Di val config = appState()?.globalState?.configManager?.config ifNotNull( config?.mercuryoWidgetId, + config?.mercuryoSecret, config?.moonPayApiKey, config?.moonPayApiSecretKey, - ) { mercuryoWidgetId, moonPayKey, moonPaySecretKey -> + ) { mercuryoWidgetId, mercuryoSecret, moonPayKey, moonPaySecretKey -> scope.launch { - val buyService = MercuryoService(MercuryoApi.API_VERSION, mercuryoWidgetId, "") + val buyService = MercuryoService( + apiVersion = MercuryoApi.API_VERSION, + mercuryoWidgetId = mercuryoWidgetId, + secret = mercuryoSecret, + ) val sellService = MoonPayService(moonPayKey, moonPaySecretKey) val exchangeManager = CurrencyExchangeManager(buyService, sellService) store.dispatchOnMain(GlobalAction.ExchangeManager.Init.Success(exchangeManager)) From 712f53991be8d8aa117d38372fb7c7506752a89f Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 5 Jul 2022 11:26:36 +0300 Subject: [PATCH 09/28] Updated on 2026-08-14 --- .../com/tangem/tap/common/DialogManager.kt | 3 + .../tap/common/redux/global/GlobalState.kt | 1 + .../tap/features/wallet/redux/WalletAction.kt | 5 +- .../tap/features/wallet/redux/WalletState.kt | 18 ++- .../middlewares/TradeCryptoMiddleware.kt | 65 ++++++---- .../middlewares/WalletDialogsMiddleware.kt | 5 +- .../wallet/ui/WalletDetailsFragment.kt | 2 +- .../ChooseTradeActionBottomSheetDialog.kt | 2 +- ...sianCardholdersWarningBottomSheetDialog.kt | 39 ++++++ .../wallet/ui/wallet/SingleWalletView.kt | 2 +- .../main/res/drawable/ic_cross_rounded_24.xml | 9 ++ app/src/main/res/drawable/img_flag_ru_24.xml | 18 +++ .../dialog_russians_cardholders_warning.xml | 114 ++++++++++++++++++ app/src/main/res/values-ru/strings_final.xml | 18 +++ app/src/main/res/values/strings_final.xml | 19 +++ 15 files changed, 289 insertions(+), 31 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/RussianCardholdersWarningBottomSheetDialog.kt create mode 100644 app/src/main/res/drawable/ic_cross_rounded_24.xml create mode 100644 app/src/main/res/drawable/img_flag_ru_24.xml create mode 100644 app/src/main/res/layout/dialog_russians_cardholders_warning.xml diff --git a/app/src/main/java/com/tangem/tap/common/DialogManager.kt b/app/src/main/java/com/tangem/tap/common/DialogManager.kt index 8af2818c0a..9af6c88b29 100644 --- a/app/src/main/java/com/tangem/tap/common/DialogManager.kt +++ b/app/src/main/java/com/tangem/tap/common/DialogManager.kt @@ -22,6 +22,7 @@ import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.UnfinishedB import com.tangem.tap.features.wallet.redux.WalletDialog import com.tangem.tap.features.wallet.ui.dialogs.AmountToSendBottomSheetDialog import com.tangem.tap.features.wallet.ui.dialogs.ChooseTradeActionBottomSheetDialog +import com.tangem.tap.features.wallet.ui.dialogs.RussianCardholdersWarningBottomSheetDialog import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog import com.tangem.tap.features.wallet.ui.dialogs.SignedHashesWarningDialog import com.tangem.tap.features.wallet.ui.dialogs.SimpleOkDialog @@ -115,6 +116,8 @@ class DialogManager : StoreSubscriber { AmountToSendBottomSheetDialog(context, state.dialog) is WalletDialog.SignedHashesMultiWalletDialog -> SignedHashesWarningDialog.create(context) + is WalletDialog.RussianCardholdersWarningDialog -> + RussianCardholdersWarningBottomSheetDialog(context) else -> null } dialog?.show() diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt index 48591606c1..54142bb9af 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt @@ -28,6 +28,7 @@ data class GlobalState( val exchangeManager: CurrencyExchangeManager? = null, val resources: AndroidResources = AndroidResources(), val analyticsHandlers: AnalyticsHandler? = null, + val userCountry: String? = null, ) : StateType diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt index 769d28a8cd..c90af2af53 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt @@ -145,6 +145,7 @@ sealed class WalletAction : Action { object SignedHashesMultiWalletDialog : DialogAction() object ChooseTradeActionDialog : DialogAction() data class ChooseCurrency(val amounts: List?) : DialogAction() + object RussianCardholdersWarningDialog : DialogAction() object Hide : DialogAction() } @@ -155,8 +156,10 @@ sealed class WalletAction : Action { object EmptyWallet : WalletAction() sealed class TradeCryptoAction : WalletAction() { - object Buy : TradeCryptoAction() object Sell : TradeCryptoAction() + data class Buy( + val checkUserLocation: Boolean = true, + ) : TradeCryptoAction() data class FinishSelling(val transactionId: String) : TradeCryptoAction() data class SendCrypto( val currencyId: String, diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt index 26224eb41e..1bbf3366a6 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt @@ -1,7 +1,12 @@ package com.tangem.tap.features.wallet.redux import android.graphics.Bitmap -import com.tangem.blockchain.common.* +import com.tangem.blockchain.common.Amount +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.DerivationStyle +import com.tangem.blockchain.common.Token +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 @@ -17,15 +22,21 @@ 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.tokens.redux.TokenWithBlockchain -import com.tangem.tap.features.wallet.models.* +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 +import org.rekotlin.StateType import kotlin.properties.ReadOnlyProperty data class WalletState( @@ -317,6 +328,7 @@ sealed interface WalletDialog : StateDialog { val currenciesList: List, val currentAppCurrency: FiatCurrency, ) : WalletDialog + object RussianCardholdersWarningDialog : WalletDialog } enum class ProgressState : WidgetState { Loading, Refreshing, Done, Error } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt index 3ff2589348..636baf5aaf 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt @@ -24,33 +24,34 @@ class TradeCryptoMiddleware { if (DemoHelper.tryHandle(state, action)) return when (action) { - is WalletAction.TradeCryptoAction.Buy -> startExchange(action) - is WalletAction.TradeCryptoAction.Sell -> startExchange(action) + is WalletAction.TradeCryptoAction.Buy -> proceedBuyAction(state, action) + is WalletAction.TradeCryptoAction.Sell -> proceedSellAction() is WalletAction.TradeCryptoAction.SendCrypto -> preconfigureAndOpenSendScreen(action) is WalletAction.TradeCryptoAction.FinishSelling -> openReceiptUrl(action.transactionId) } } - private fun startExchange(action: WalletAction.TradeCryptoAction) { - val selectedWalletData = store.state.walletState.getSelectedWalletData() - val exchangeManager = store.state.globalState.exchangeManager ?: return - val addresses = selectedWalletData?.walletAddresses ?: return - if (addresses.list.isEmpty()) return - val appCurrency = store.state.globalState.appCurrency - - val defaultAddress = addresses.list[0].address - val currency = selectedWalletData.currency - val currencySymbol = selectedWalletData.currency.currencySymbol - - val exchangeAction = if (action is WalletAction.TradeCryptoAction.Buy) { - CurrencyExchangeManager.Action.Buy - } else { - CurrencyExchangeManager.Action.Sell + private fun proceedBuyAction( + state: () -> AppState?, + action: WalletAction.TradeCryptoAction.Buy, + ) { + if (action.checkUserLocation && state()?.globalState?.userCountry == "RU") { + store.dispatchOnMain( + WalletAction.DialogAction.RussianCardholdersWarningDialog + ) + return } - if (exchangeAction == CurrencyExchangeManager.Action.Buy && - currency is Currency.Token && currency.blockchain.isTestnet() - ) { + val selectedWalletData = store.state.walletState.getSelectedWalletData() ?: return + val exchangeManager = store.state.globalState.exchangeManager ?: return + val appCurrency = store.state.globalState.appCurrency + + val addresses = selectedWalletData.walletAddresses?.list.orEmpty() + if (addresses.isEmpty()) return + + val currency = selectedWalletData.currency + + if (currency is Currency.Token && currency.blockchain.isTestnet()) { val walletManager = store.state.walletState.getWalletManager(currency) if (walletManager !is EthereumWalletManager) { store.dispatchDebugErrorNotification("Testnet tokens available only for the Ethereum") @@ -62,13 +63,31 @@ class TradeCryptoMiddleware { } exchangeManager.getUrl( - action = exchangeAction, + action = CurrencyExchangeManager.Action.Buy, blockchain = currency.blockchain, - cryptoCurrencyName = currencySymbol, + cryptoCurrencyName = currency.currencySymbol, fiatCurrencyName = appCurrency.code, - walletAddress = defaultAddress + walletAddress = addresses[0].address )?.let { store.dispatchOnMain(NavigationAction.OpenUrl(it)) } + } + private fun proceedSellAction() { + val selectedWalletData = store.state.walletState.getSelectedWalletData() ?: return + val exchangeManager = store.state.globalState.exchangeManager ?: return + val appCurrency = store.state.globalState.appCurrency + + val addresses = selectedWalletData.walletAddresses?.list.orEmpty() + if (addresses.isEmpty()) return + + val currency = selectedWalletData.currency + + exchangeManager.getUrl( + action = CurrencyExchangeManager.Action.Sell, + blockchain = currency.blockchain, + cryptoCurrencyName = currency.currencySymbol, + fiatCurrencyName = appCurrency.code, + walletAddress = addresses[0].address + )?.let { store.dispatchOnMain(NavigationAction.OpenUrl(it)) } } private fun preconfigureAndOpenSendScreen(action: WalletAction.TradeCryptoAction.SendCrypto) { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletDialogsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletDialogsMiddleware.kt index e495c55c94..e760c446f2 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletDialogsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletDialogsMiddleware.kt @@ -27,10 +27,13 @@ class WalletDialogsMiddleware { is WalletAction.DialogAction.ChooseCurrency -> { store.dispatchDialogShow( WalletDialog.SelectAmountToSendDialog( - amounts = action.amounts + amounts = action.amounts ) ) } + is WalletAction.DialogAction.RussianCardholdersWarningDialog -> { + store.dispatchDialogShow(WalletDialog.RussianCardholdersWarningDialog) + } is WalletAction.DialogAction.Hide -> { store.dispatchDialogHide() } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt index d5bd095cbc..1cf3a038b2 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt @@ -91,7 +91,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), private fun setupButtons() = with(binding) { rowButtons.onBuyClick = { - store.dispatch(WalletAction.TradeCryptoAction.Buy) + store.dispatch(WalletAction.TradeCryptoAction.Buy()) } rowButtons.onSellClick = { store.dispatch(WalletAction.TradeCryptoAction.Sell) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ChooseTradeActionBottomSheetDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ChooseTradeActionBottomSheetDialog.kt index 1c65315d3a..397231a58c 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ChooseTradeActionBottomSheetDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ChooseTradeActionBottomSheetDialog.kt @@ -27,7 +27,7 @@ class ChooseTradeActionBottomSheetDialog(context: Context) : BottomSheetDialog(c } binding!!.dialogBtnBuy.setOnClickListener { - store.dispatch(WalletAction.TradeCryptoAction.Buy) + store.dispatch(WalletAction.TradeCryptoAction.Buy()) dismiss() } binding!!.dialogBtnSell.setOnClickListener { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/RussianCardholdersWarningBottomSheetDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/RussianCardholdersWarningBottomSheetDialog.kt new file mode 100644 index 0000000000..a3b84e3a48 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/RussianCardholdersWarningBottomSheetDialog.kt @@ -0,0 +1,39 @@ +package com.tangem.tap.features.wallet.ui.dialogs + +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import com.google.android.material.bottomsheet.BottomSheetDialog +import com.tangem.tap.common.extensions.dispatchDialogHide +import com.tangem.tap.features.wallet.redux.WalletAction +import com.tangem.tap.store +import com.tangem.wallet.databinding.DialogRussiansCardholdersWarningBinding + +class RussianCardholdersWarningBottomSheetDialog(context: Context) : BottomSheetDialog(context) { + + private var binding: DialogRussiansCardholdersWarningBinding? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = DialogRussiansCardholdersWarningBinding + .inflate(LayoutInflater.from(context)) + .also { setContentView(it.root) } + } + + override fun show() { + super.show() + setOnDismissListener { + binding = null + store.dispatchDialogHide() + } + + binding?.btnYes?.setOnClickListener { + // TODO: Open instruction WebView + dismiss() + } + binding?.btnNo?.setOnClickListener { + store.dispatch(WalletAction.TradeCryptoAction.Buy(checkUserLocation = false)) + dismiss() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt index 19815ccfe7..7c431c33b7 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt @@ -149,7 +149,7 @@ class SingleWalletView : WalletView { val allowedToBuy = tradeCryptoState.isAvailableToBuy() val allowedToSell = tradeCryptoState.isAvailableToSell() val action = when { - allowedToBuy && !allowedToSell -> WalletAction.TradeCryptoAction.Buy + allowedToBuy && !allowedToSell -> WalletAction.TradeCryptoAction.Buy() !allowedToBuy && allowedToSell -> WalletAction.TradeCryptoAction.Sell allowedToBuy && allowedToSell -> WalletAction.DialogAction.ChooseTradeActionDialog else -> null diff --git a/app/src/main/res/drawable/ic_cross_rounded_24.xml b/app/src/main/res/drawable/ic_cross_rounded_24.xml new file mode 100644 index 0000000000..ad1cab5a84 --- /dev/null +++ b/app/src/main/res/drawable/ic_cross_rounded_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/img_flag_ru_24.xml b/app/src/main/res/drawable/img_flag_ru_24.xml new file mode 100644 index 0000000000..deff0dc4bb --- /dev/null +++ b/app/src/main/res/drawable/img_flag_ru_24.xml @@ -0,0 +1,18 @@ + + + + + + + + diff --git a/app/src/main/res/layout/dialog_russians_cardholders_warning.xml b/app/src/main/res/layout/dialog_russians_cardholders_warning.xml new file mode 100644 index 0000000000..8f2196c2d5 --- /dev/null +++ b/app/src/main/res/layout/dialog_russians_cardholders_warning.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-ru/strings_final.xml b/app/src/main/res/values-ru/strings_final.xml index 201a1684ae..7b022e0aa3 100644 --- a/app/src/main/res/values-ru/strings_final.xml +++ b/app/src/main/res/values-ru/strings_final.xml @@ -50,4 +50,22 @@ В сумме учтены не все монеты Управление токенами Нет цены + + Скрыть токен + Скрыть %s + Скрыть + Вы скрываете токен с главного экрана, но в любой момент сможете добавить его обратно. + Невозможно скрыть %s + Токен %s является основной валютой в сети %s и не может быть скрыт, до тех пор пока у вас в списке есть другие токены этой сети. + + Сеть %s не найдена. Пожалуйста, добавьте её и попробуйте заново. + Сеть %s + + Бэкап кошелька не был произведен + Чтобы защитить свои активы, мы советуем вам выполнить эту процедуру + + Карты банков РФ в данный момент не принимаются + У вас есть карта банка другой страны или платежной системы UnionPay? + Да + Нет diff --git a/app/src/main/res/values/strings_final.xml b/app/src/main/res/values/strings_final.xml index d59d31ff0e..3a16ec6ea0 100644 --- a/app/src/main/res/values/strings_final.xml +++ b/app/src/main/res/values/strings_final.xml @@ -50,4 +50,23 @@ The amount does not include some of your funds Manage tokens No rate + + Hide token + Hide %s + Hide + You are about to hide this token from the main screen. You can add it back anytime. + Unable to hide %s + 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. + + %s network not found. Please, add it first and try again. + %s network + + Your wallet has not been backed up + To protect your assets, we advise you to carry out this procedure + Remove token + + Russian bank cards are not accepted at the moment + Do you have a bank card of another country or a UnionPay card? + Yes + No From 787c3362658ad56ecd80d7f20772209503f93497 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 5 Jul 2022 22:14:44 +0300 Subject: [PATCH 10/28] Updated on 2026-08-14 --- .../RussianCardholdersWarningBottomSheetDialog.kt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/RussianCardholdersWarningBottomSheetDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/RussianCardholdersWarningBottomSheetDialog.kt index a3b84e3a48..a41dce9228 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/RussianCardholdersWarningBottomSheetDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/RussianCardholdersWarningBottomSheetDialog.kt @@ -5,6 +5,7 @@ import android.os.Bundle import android.view.LayoutInflater import com.google.android.material.bottomsheet.BottomSheetDialog import com.tangem.tap.common.extensions.dispatchDialogHide +import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.store import com.tangem.wallet.databinding.DialogRussiansCardholdersWarningBinding @@ -28,12 +29,16 @@ class RussianCardholdersWarningBottomSheetDialog(context: Context) : BottomSheet } binding?.btnYes?.setOnClickListener { - // TODO: Open instruction WebView - dismiss() - } - binding?.btnNo?.setOnClickListener { store.dispatch(WalletAction.TradeCryptoAction.Buy(checkUserLocation = false)) dismiss() } + binding?.btnNo?.setOnClickListener { + store.dispatch(NavigationAction.OpenUrl(INSTRUCTION_URL)) + dismiss() + } + } + + companion object { + private const val INSTRUCTION_URL = "https://tangem.com/howtobuy.html" } } \ No newline at end of file From 50c3fe85b66f1d753543b2fc9f599488ccc46b04 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 6 Jul 2022 15:10:06 +0300 Subject: [PATCH 11/28] Updated on 2026-08-14 --- app/src/main/assets/tangem-app-config | 2 +- .../tap/common/redux/global/GlobalAction.kt | 4 +++ .../common/redux/global/GlobalMidlleware.kt | 29 ++++++++++++++++++- .../tap/common/redux/global/GlobalReducer.kt | 4 ++- .../tap/common/redux/global/GlobalState.kt | 2 +- .../tap/features/home/RegionProvider.kt | 6 ++-- .../tap/features/home/redux/HomeMiddleware.kt | 8 +++-- .../middlewares/TradeCryptoMiddleware.kt | 3 +- .../network/api/tangemTech/Responses.kt | 6 +++- .../network/api/tangemTech/TangemTechApi.kt | 2 ++ .../api/tangemTech/TangemTechService.kt | 4 +++ 11 files changed, 59 insertions(+), 11 deletions(-) diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index 59ee0bff06..f50133547b 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit 59ee0bff06ee54f3202421c28f7fb6e3d421dad8 +Subproject commit f50133547b5e19fb7ad32e64370573d60f6791fd diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt index 540209a39c..a82d5ba94b 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt @@ -85,4 +85,8 @@ sealed class GlobalAction : Action { object Update : GlobalAction() } + + object FetchUserCountry : GlobalAction() { + data class Success(val countryCode: String) : GlobalAction() + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt index ea77ce5ed9..7f61d63022 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt @@ -4,13 +4,14 @@ import com.tangem.common.CompletionResult import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.guard import com.tangem.common.extensions.ifNotNull +import com.tangem.common.services.Result import com.tangem.domain.common.extensions.withMainContext -import com.tangem.tap.* import com.tangem.tap.common.extensions.dispatchDebugErrorNotification import com.tangem.tap.common.extensions.dispatchDialogShow import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.AppState +import com.tangem.tap.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 @@ -18,6 +19,11 @@ import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager import com.tangem.tap.network.exchangeServices.mercuryo.MercuryoApi import com.tangem.tap.network.exchangeServices.mercuryo.MercuryoService import com.tangem.tap.network.exchangeServices.moonpay.MoonPayService +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 @@ -134,5 +140,26 @@ private fun handleAction(action: Action, appState: () -> AppState?, dispatch: Di } } } + is GlobalAction.FetchUserCountry -> { + scope.launch { + val techService = store.state.domainNetworks.tangemTechService + when (val result = techService.userCountry()) { + is Result.Success -> { + store.dispatch( + GlobalAction.FetchUserCountry.Success( + countryCode = result.data.code.lowercase() + ) + ) + } + is Result.Failure -> { + store.dispatch( + GlobalAction.FetchUserCountry.Success( + countryCode = Locale.getDefault().country.lowercase() + ) + ) + } + } + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt index 681d1f758a..449d47ae10 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt @@ -75,7 +75,9 @@ fun globalReducer(action: Action, state: AppState): GlobalState { } is GlobalAction.SetIfCardVerifiedOnline -> globalState.copy(cardVerifiedOnline = action.verified) - + is GlobalAction.FetchUserCountry.Success -> globalState.copy( + userCountryCode = action.countryCode + ) else -> globalState } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt index 54142bb9af..8588256411 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt @@ -28,7 +28,7 @@ data class GlobalState( val exchangeManager: CurrencyExchangeManager? = null, val resources: AndroidResources = AndroidResources(), val analyticsHandlers: AnalyticsHandler? = null, - val userCountry: String? = null, + val userCountryCode: String? = null, ) : StateType diff --git a/app/src/main/java/com/tangem/tap/features/home/RegionProvider.kt b/app/src/main/java/com/tangem/tap/features/home/RegionProvider.kt index 17a5076984..22cac042e5 100644 --- a/app/src/main/java/com/tangem/tap/features/home/RegionProvider.kt +++ b/app/src/main/java/com/tangem/tap/features/home/RegionProvider.kt @@ -44,5 +44,7 @@ class TelephonyManagerRegionProvider(context: Context) : RegionProvider { } class LocaleRegionProvider : RegionProvider { - override fun getRegion(): String? = Locale.current.region -} \ No newline at end of file + override fun getRegion(): String = Locale.current.region +} + +const val RUSSIA_COUNTRY_CODE = "ru" \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt index 8a3878556d..0edb0a435b 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt @@ -15,6 +15,7 @@ import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.FragmentShareTransition import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE import com.tangem.tap.features.home.redux.HomeMiddleware.Companion.BUY_WALLET_URL import com.tangem.tap.features.onboarding.OnboardingHelper import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction @@ -35,7 +36,7 @@ class HomeMiddleware { } } -private val homeMiddleware: Middleware = { dispatch, state -> +private val homeMiddleware: Middleware = { _, _ -> { next -> { action -> when (action) { @@ -43,6 +44,7 @@ private val homeMiddleware: Middleware = { dispatch, state -> store.dispatch(GlobalAction.RestoreAppCurrency) store.dispatch(GlobalAction.ExchangeManager.Init) store.dispatch(HomeAction.SetTermsOfUseState(preferencesStorage.wasDisclaimerAccepted())) + store.dispatch(GlobalAction.FetchUserCountry) } is HomeAction.ShouldScanCardOnResume -> { if (action.shouldScanCard) { @@ -55,8 +57,8 @@ private val homeMiddleware: Middleware = { dispatch, state -> // store.dispatch(NavigationAction.NavigateTo(AppScreen.AddCustomTokens)) } is HomeAction.GoToShop -> { - when (action.regionProvider.getRegion()?.toLowerCase()) { - "ru" -> store.dispatchOpenUrl(BUY_WALLET_URL) + when (action.regionProvider.getRegion()?.lowercase()) { + RUSSIA_COUNTRY_CODE -> store.dispatchOpenUrl(BUY_WALLET_URL) else -> store.dispatch(NavigationAction.NavigateTo(AppScreen.Shop)) } store.state.globalState.analyticsHandlers?.triggerEvent( diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt index 636baf5aaf..ebbaae8538 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt @@ -8,6 +8,7 @@ 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.demo.DemoHelper +import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE import com.tangem.tap.features.send.redux.PrepareSendScreen import com.tangem.tap.features.send.redux.SendAction import com.tangem.tap.features.wallet.redux.Currency @@ -35,7 +36,7 @@ class TradeCryptoMiddleware { state: () -> AppState?, action: WalletAction.TradeCryptoAction.Buy, ) { - if (action.checkUserLocation && state()?.globalState?.userCountry == "RU") { + if (action.checkUserLocation && state()?.globalState?.userCountryCode == RUSSIA_COUNTRY_CODE) { store.dispatchOnMain( WalletAction.DialogAction.RussianCardholdersWarningDialog ) diff --git a/network/src/main/java/com/tangem/network/api/tangemTech/Responses.kt b/network/src/main/java/com/tangem/network/api/tangemTech/Responses.kt index c21fb92726..9a4b2cd8b1 100644 --- a/network/src/main/java/com/tangem/network/api/tangemTech/Responses.kt +++ b/network/src/main/java/com/tangem/network/api/tangemTech/Responses.kt @@ -43,4 +43,8 @@ data class CurrenciesResponse(val currencies: List) { val unit: String, // $, €, ₽ val type: String, ) : TangemTechResponse -} \ No newline at end of file +} + +data class GeoResponse( + val code: String +) : TangemTechResponse \ No newline at end of file diff --git a/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechApi.kt b/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechApi.kt index 0081f4ee24..80558732dd 100644 --- a/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechApi.kt +++ b/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechApi.kt @@ -27,4 +27,6 @@ interface TangemTechApi { @GET("currencies") suspend fun currencies(): CurrenciesResponse + @GET("geo") + suspend fun geo(): GeoResponse } \ No newline at end of file diff --git a/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechService.kt b/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechService.kt index 5790bdac3a..3bf3bd232e 100644 --- a/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechService.kt +++ b/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechService.kt @@ -47,6 +47,10 @@ class TangemTechService { } } + suspend fun userCountry(): Result = withContext(Dispatchers.IO) { + performRequest { api.geo() } + } + suspend fun currencies(): Result = withContext(Dispatchers.IO) { performRequest { api.currencies() } } From 752e022d1478bac49bcc0a62eec33b9356333364 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 11 Jul 2022 11:09:27 +0300 Subject: [PATCH 12/28] Updated on 2026-08-14 --- app/src/main/res/values-de/strings.xml | 3 +-- app/src/main/res/values-fr/strings.xml | 3 +-- app/src/main/res/values-it/strings.xml | 3 +-- app/src/main/res/values-ru/strings.xml | 2 +- app/src/main/res/values-ru/strings_final.xml | 1 - app/src/main/res/values/strings.xml | 1 - 6 files changed, 4 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 97c8b2d0af..3e7c059c3a 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -1,6 +1,5 @@ - Nein Änderungen speichern Warnung Erneut versuchen @@ -196,4 +195,4 @@ RECHTLICHER HAFTUNGSAUSSCHLUSS Um %s XTZ reduzieren Nein, alles senden - \ No newline at end of file + diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 9e6777a914..069163f054 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -1,7 +1,6 @@ - Non Sauvegarder les modifications Alerte Réessayer @@ -188,4 +187,4 @@ Avertissement Réduire de% s XTZ Non, envoyer toute la somme - \ No newline at end of file + diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index a7011d4bcc..7ad1cddc70 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -1,7 +1,6 @@ - No Mantieni le modifiche Avviso Riprova @@ -195,4 +194,4 @@ Questa nota legale è stata modificata l\'ultima volta 01.10.2020. Riduci di %s XTZ No, invia l\'intero importo - \ No newline at end of file + diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 3712df8712..1e7e892972 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -1,7 +1,6 @@ Tangem - Нет Сохранить изменения Предупреждение Повторить попытку @@ -382,4 +381,5 @@ Привет, команда поддержки, Пожалуйста, расскажите нам больше о Вашей проблеме. Каждая деталь может быть полезной. Информация ниже не является обязательной. Вы можете стереть её, если хотите. + Нет diff --git a/app/src/main/res/values-ru/strings_final.xml b/app/src/main/res/values-ru/strings_final.xml index 7b022e0aa3..cd706ff8af 100644 --- a/app/src/main/res/values-ru/strings_final.xml +++ b/app/src/main/res/values-ru/strings_final.xml @@ -67,5 +67,4 @@ Карты банков РФ в данный момент не принимаются У вас есть карта банка другой страны или платежной системы UnionPay? Да - Нет diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 520b7bc183..5a05ac5fae 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2,7 +2,6 @@ Tangem - No Save changes Warning Retry From b336d9afb24501e8b73cb5d3939820eafdeecafb Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 8 Jul 2022 22:21:20 +0400 Subject: [PATCH 13/28] Updated on 2026-08-14 --- .../java/com/tangem/tap/features/home/HomeFragment.kt | 4 +++- .../java/com/tangem/tap/features/home/RegionProvider.kt | 3 ++- .../com/tangem/tap/features/home/redux/HomeAction.kt | 3 +-- .../com/tangem/tap/features/home/redux/HomeMiddleware.kt | 9 +++++---- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt index 2b26faeeba..c453cdadc4 100644 --- a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt @@ -38,7 +38,9 @@ class HomeFragment : Fragment(R.layout.fragment_home), StoreSubscriber = { _, _ -> // store.dispatch(NavigationAction.NavigateTo(AppScreen.AddCustomTokens)) } is HomeAction.GoToShop -> { - when (action.regionProvider.getRegion()?.lowercase()) { - RUSSIA_COUNTRY_CODE -> store.dispatchOpenUrl(BUY_WALLET_URL) + when (action.userCountryCode) { + RUSSIA_COUNTRY_CODE, BELARUS_COUNTRY_CODE -> + store.dispatchOpenUrl(BUY_WALLET_URL) else -> store.dispatch(NavigationAction.NavigateTo(AppScreen.Shop)) } store.state.globalState.analyticsHandlers?.triggerEvent( From c2aaadd3141c4b20fe774900a006e09bbb4b0ae2 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 11 Jul 2022 18:31:56 +0400 Subject: [PATCH 14/28] Updated on 2026-08-14 --- .../com/tangem/tap/domain/configurable/config/ConfigManager.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt index d0e7bb9584..52037f828c 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt @@ -85,6 +85,7 @@ class ConfigManager( moonPayApiKey = values.moonPayApiKey, moonPayApiSecretKey = values.moonPayApiSecretKey, mercuryoWidgetId = values.mercuryoWidgetId, + mercuryoSecret = values.mercuryoSecret, blockchainSdkConfig = BlockchainSdkConfig( blockchairApiKey = values.blockchairApiKey, blockchairAuthorizationToken = values.blockchairAuthorizationToken, From 5dff0e709cd7edba22091db123f9fb58ec3662d8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 11 Jul 2022 18:52:03 +0400 Subject: [PATCH 15/28] Updated on 2026-08-14 --- .../network/exchangeServices/mercuryo/MercuryoService.kt | 6 ++++-- .../com/tangem/domain/common/extensions/Blockchain.kt | 9 +++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt index a0068b9ce1..428b1bb0b3 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt @@ -6,6 +6,7 @@ import com.tangem.common.extensions.calculateSha512 import com.tangem.common.extensions.toHexString import com.tangem.common.services.Result import com.tangem.common.services.performRequest +import com.tangem.domain.common.extensions.removePrefixFromAddress import com.tangem.network.common.createRetrofitInstance import com.tangem.tap.common.extensions.urlEncode import com.tangem.tap.common.redux.global.CryptoCurrencyName @@ -103,14 +104,15 @@ class MercuryoService( ): String? { if (action == CurrencyExchangeManager.Action.Sell) throw UnsupportedOperationException() + val walletAddressCorrected = blockchain.removePrefixFromAddress(walletAddress) val builder = Uri.Builder() .scheme(ExchangeUrlBuilder.SCHEME) .authority("exchange.mercuryo.io") .appendQueryParameter("widget_id", mercuryoWidgetId) .appendQueryParameter("type", action.name.lowercase()) .appendQueryParameter("currency", cryptoCurrencyName) - .appendQueryParameter("address", walletAddress.urlEncode()) - .appendQueryParameter("signature", signature(walletAddress).urlEncode()) + .appendQueryParameter("address", walletAddressCorrected.urlEncode()) + .appendQueryParameter("signature", signature(walletAddressCorrected).urlEncode()) .appendQueryParameter("fix_currency", "true") .appendQueryParameter("return_url", ExchangeUrlBuilder.SUCCESS_URL) diff --git a/domain/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt b/domain/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt index b69ffb1c49..ec8fcce10c 100644 --- a/domain/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt +++ b/domain/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt @@ -1,5 +1,6 @@ package com.tangem.domain.common.extensions +import com.tangem.blockchain.blockchains.bitcoincash.cashaddr.CashAddr import com.tangem.blockchain.common.Blockchain fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? { @@ -104,4 +105,12 @@ fun Blockchain.toCoinId(): String { Blockchain.Tron, Blockchain.TronTestnet -> "tron" else -> "unknown" } +} + +fun Blockchain.removePrefixFromAddress(address: String): String { + return when (this) { + Blockchain.BitcoinCash -> address + .removePrefix(CashAddr.MAIN_NET_PREFIX).removePrefix(":") + else -> address + } } \ No newline at end of file From b4633c6072887dc5a77f9c607a8edf689e70f236 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 11 Jul 2022 19:01:26 +0300 Subject: [PATCH 16/28] Updated on 2026-08-14 --- .../wallet/ui/dialogs/ChooseTradeActionBottomSheetDialog.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ChooseTradeActionBottomSheetDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ChooseTradeActionBottomSheetDialog.kt index 397231a58c..3fc5905990 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ChooseTradeActionBottomSheetDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ChooseTradeActionBottomSheetDialog.kt @@ -27,12 +27,12 @@ class ChooseTradeActionBottomSheetDialog(context: Context) : BottomSheetDialog(c } binding!!.dialogBtnBuy.setOnClickListener { - store.dispatch(WalletAction.TradeCryptoAction.Buy()) dismiss() + store.dispatch(WalletAction.TradeCryptoAction.Buy()) } binding!!.dialogBtnSell.setOnClickListener { - store.dispatch(WalletAction.TradeCryptoAction.Sell) dismiss() + store.dispatch(WalletAction.TradeCryptoAction.Sell) } } } \ No newline at end of file From f274e2c5ddb93ee2d7473241118bb359ea467592 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 11 Jul 2022 21:45:47 +0400 Subject: [PATCH 17/28] Updated on 2026-08-14 --- app/src/main/assets/tangem-app-config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index f50133547b..59ee0bff06 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit f50133547b5e19fb7ad32e64370573d60f6791fd +Subproject commit 59ee0bff06ee54f3202421c28f7fb6e3d421dad8 From 699f261589c7b29e485c92562ad09954833ed72d Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 12 Jul 2022 11:08:13 +0400 Subject: [PATCH 18/28] Updated on 2026-08-14 --- .../network/exchangeServices/mercuryo/MercuryoService.kt | 9 +++------ .../com/tangem/domain/common/extensions/Blockchain.kt | 9 --------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt index 428b1bb0b3..2d446490ec 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt @@ -6,9 +6,7 @@ import com.tangem.common.extensions.calculateSha512 import com.tangem.common.extensions.toHexString import com.tangem.common.services.Result import com.tangem.common.services.performRequest -import com.tangem.domain.common.extensions.removePrefixFromAddress import com.tangem.network.common.createRetrofitInstance -import com.tangem.tap.common.extensions.urlEncode import com.tangem.tap.common.redux.global.CryptoCurrencyName import com.tangem.tap.features.wallet.redux.Currency import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager @@ -101,18 +99,17 @@ class MercuryoService( cryptoCurrencyName: CryptoCurrencyName, fiatCurrencyName: String, walletAddress: String - ): String? { + ): String { if (action == CurrencyExchangeManager.Action.Sell) throw UnsupportedOperationException() - val walletAddressCorrected = blockchain.removePrefixFromAddress(walletAddress) val builder = Uri.Builder() .scheme(ExchangeUrlBuilder.SCHEME) .authority("exchange.mercuryo.io") .appendQueryParameter("widget_id", mercuryoWidgetId) .appendQueryParameter("type", action.name.lowercase()) .appendQueryParameter("currency", cryptoCurrencyName) - .appendQueryParameter("address", walletAddressCorrected.urlEncode()) - .appendQueryParameter("signature", signature(walletAddressCorrected).urlEncode()) + .appendQueryParameter("address", walletAddress) + .appendQueryParameter("signature", signature(walletAddress)) .appendQueryParameter("fix_currency", "true") .appendQueryParameter("return_url", ExchangeUrlBuilder.SUCCESS_URL) diff --git a/domain/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt b/domain/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt index ec8fcce10c..b69ffb1c49 100644 --- a/domain/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt +++ b/domain/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt @@ -1,6 +1,5 @@ package com.tangem.domain.common.extensions -import com.tangem.blockchain.blockchains.bitcoincash.cashaddr.CashAddr import com.tangem.blockchain.common.Blockchain fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? { @@ -105,12 +104,4 @@ fun Blockchain.toCoinId(): String { Blockchain.Tron, Blockchain.TronTestnet -> "tron" else -> "unknown" } -} - -fun Blockchain.removePrefixFromAddress(address: String): String { - return when (this) { - Blockchain.BitcoinCash -> address - .removePrefix(CashAddr.MAIN_NET_PREFIX).removePrefix(":") - else -> address - } } \ No newline at end of file From 8a94c2cc5844ef1535d419c56ede85c418680d55 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 12 Jul 2022 14:48:28 +0400 Subject: [PATCH 19/28] Updated on 2026-08-14 --- .../com/tangem/tap/common/DialogManager.kt | 7 ---- .../tap/features/wallet/redux/WalletState.kt | 40 +------------------ .../wallet/redux/models/WalletDialog.kt | 2 + .../CurrencyExchangeManager.kt | 2 +- .../exchangeServices/ExchangeService.kt | 2 +- .../mercuryo/MercuryoService.kt | 2 +- .../moonpay/MoonPayService.kt | 2 +- 7 files changed, 8 insertions(+), 49 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/DialogManager.kt b/app/src/main/java/com/tangem/tap/common/DialogManager.kt index 7b67bb6555..70f4ae2d61 100644 --- a/app/src/main/java/com/tangem/tap/common/DialogManager.kt +++ b/app/src/main/java/com/tangem/tap/common/DialogManager.kt @@ -18,13 +18,6 @@ import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.ConfirmDisc import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.UnfinishedBackupFoundDialog import com.tangem.tap.features.wallet.redux.models.WalletDialog import com.tangem.tap.features.wallet.ui.dialogs.* -import com.tangem.tap.features.wallet.redux.WalletDialog -import com.tangem.tap.features.wallet.ui.dialogs.AmountToSendBottomSheetDialog -import com.tangem.tap.features.wallet.ui.dialogs.ChooseTradeActionBottomSheetDialog -import com.tangem.tap.features.wallet.ui.dialogs.RussianCardholdersWarningBottomSheetDialog -import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog -import com.tangem.tap.features.wallet.ui.dialogs.SignedHashesWarningDialog -import com.tangem.tap.features.wallet.ui.dialogs.SimpleOkDialog import com.tangem.tap.features.wallet.ui.wallet.CurrencySelectionDialog import com.tangem.tap.store import com.tangem.wallet.R diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt index a1e0220339..ba70c84e05 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt @@ -1,55 +1,30 @@ package com.tangem.tap.features.wallet.redux import android.graphics.Bitmap -import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token import com.tangem.blockchain.common.Wallet import com.tangem.blockchain.common.WalletManager -import com.tangem.blockchain.common.Amount -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.DerivationStyle -import com.tangem.blockchain.common.Token -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.tap.common.entities.Button -import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.extensions.toQrCode -import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.common.redux.global.CryptoCurrencyName 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.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.tokens.redux.TokenWithBlockchain -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.models.* 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 -import org.rekotlin.StateType import kotlin.properties.ReadOnlyProperty -import org.rekotlin.StateType data class WalletState( val state: ProgressState = ProgressState.Done, @@ -333,17 +308,6 @@ data class WalletState( } } -sealed interface WalletDialog : StateDialog { - data class SelectAmountToSendDialog(val amounts: List?) : WalletDialog - object SignedHashesMultiWalletDialog : WalletDialog - object ChooseTradeActionDialog : WalletDialog - data class CurrencySelectionDialog( - val currenciesList: List, - val currentAppCurrency: FiatCurrency, - ) : WalletDialog - object RussianCardholdersWarningDialog : WalletDialog -} - enum class ProgressState : WidgetState { Loading, Refreshing, Done, Error } enum class ErrorType { NoInternetConnection } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/models/WalletDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/models/WalletDialog.kt index a5a54c93f6..68718acd45 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/models/WalletDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/models/WalletDialog.kt @@ -30,4 +30,6 @@ sealed interface WalletDialog : StateDialog { val messageRes: Int = R.string.token_details_unable_hide_alert_message val titleRes: Int = R.string.token_details_unable_hide_alert_title } + + object RussianCardholdersWarningDialog : WalletDialog } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt index 745ee01053..811c9c0545 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt @@ -11,7 +11,7 @@ import com.tangem.tap.common.extensions.safeUpdate import com.tangem.tap.common.redux.global.CryptoCurrencyName import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.TangemSigner -import com.tangem.tap.features.wallet.redux.Currency +import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.store import com.tangem.tap.tangemSdk import java.math.BigDecimal diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/ExchangeService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/ExchangeService.kt index e08e007029..712fd2d5e0 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/ExchangeService.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/ExchangeService.kt @@ -1,7 +1,7 @@ package com.tangem.tap.network.exchangeServices import com.tangem.blockchain.common.Blockchain -import com.tangem.tap.features.wallet.redux.Currency +import com.tangem.tap.features.wallet.models.Currency interface ExchangeService { suspend fun update() diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt index 2d446490ec..97190aa9fd 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt @@ -8,7 +8,7 @@ import com.tangem.common.services.Result import com.tangem.common.services.performRequest import com.tangem.network.common.createRetrofitInstance import com.tangem.tap.common.redux.global.CryptoCurrencyName -import com.tangem.tap.features.wallet.redux.Currency +import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager import com.tangem.tap.network.exchangeServices.ExchangeService import com.tangem.tap.network.exchangeServices.ExchangeUrlBuilder diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt index 161cfc568a..fa96286e84 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/moonpay/MoonPayService.kt @@ -9,7 +9,7 @@ import com.tangem.domain.common.extensions.withIOContext import com.tangem.network.common.createRetrofitInstance import com.tangem.tap.common.extensions.urlEncode import com.tangem.tap.common.redux.global.CryptoCurrencyName -import com.tangem.tap.features.wallet.redux.Currency +import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager import com.tangem.tap.network.exchangeServices.ExchangeService import com.tangem.tap.network.exchangeServices.ExchangeUrlBuilder From 8c080fdb584d78e1b88bbc0a1826b97fb58420ae Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 7 Jul 2022 18:05:02 +0300 Subject: [PATCH 20/28] Updated on 2026-08-14 --- app/build.gradle | 7 +++++-- build.gradle | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 791cb96330..3f12b8e345 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -144,8 +144,11 @@ dependencies { implementation("com.squareup.okhttp3:okhttp") implementation("com.squareup.okhttp3:logging-interceptor") -// -// //Crypto + // Support + implementation("com.zendesk:support:5.0.9") + implementation("com.zendesk:messaging:5.2.4") + + //Crypto implementation "com.madgag.spongycastle:core:1.58.0.0" // animation diff --git a/build.gradle b/build.gradle index 23b9e4d0bc..64f57c2248 100644 --- a/build.gradle +++ b/build.gradle @@ -22,6 +22,7 @@ allprojects { mavenLocal() maven { url "https://nexus.tangem-tech.com/repository/maven-releases/" } maven { url 'https://jitpack.io' } + maven { url 'https://zendesk.jfrog.io/zendesk/repo' } } } From ba350d0de0c98362f6bb1b708504adf0328c685f Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 7 Jul 2022 19:56:44 +0300 Subject: [PATCH 21/28] Updated on 2026-08-14 --- .../tangem/tap/ForegroundActivityObserver.kt | 46 +++++++++++++++++++ .../java/com/tangem/tap/TapApplication.kt | 4 ++ 2 files changed, 50 insertions(+) create mode 100644 app/src/main/java/com/tangem/tap/ForegroundActivityObserver.kt diff --git a/app/src/main/java/com/tangem/tap/ForegroundActivityObserver.kt b/app/src/main/java/com/tangem/tap/ForegroundActivityObserver.kt new file mode 100644 index 0000000000..806769df5f --- /dev/null +++ b/app/src/main/java/com/tangem/tap/ForegroundActivityObserver.kt @@ -0,0 +1,46 @@ +package com.tangem.tap + +import android.app.Activity +import android.app.Application.ActivityLifecycleCallbacks +import android.os.Bundle +import java.util.WeakHashMap +import kotlin.reflect.KClass + +class ForegroundActivityObserver { + private val activities = WeakHashMap, Activity>() + + val foregroundActivity: Activity? + get() = activities.entries + .filterNot { it.value.isDestroyed } + .firstOrNull() + ?.value + + val callbacks get() = Callbacks() + + inner class Callbacks : ActivityLifecycleCallbacks { + override fun onActivityResumed(activity: Activity) { + activities[activity::class] = activity + } + + override fun onActivityDestroyed(activity: Activity) { + activities.remove(activity::class) + } + + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { + } + override fun onActivityStarted(activity: Activity) { + } + override fun onActivityPaused(activity: Activity) { + } + override fun onActivityStopped(activity: Activity) { + } + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) { + } + } +} + +fun ForegroundActivityObserver.withForegroundActivity( + block: (Activity) -> Unit +) { + foregroundActivity?.let { block(it) } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 2e6c7b534b..372fb621bf 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -40,6 +40,8 @@ val store = Store( ) val logConfig = LogConfig() +lateinit var foregroundActivityObserver: ForegroundActivityObserver + lateinit var preferencesStorage: PreferencesStorage lateinit var currenciesRepository: CurrenciesRepository lateinit var walletConnectRepository: WalletConnectRepository @@ -68,6 +70,8 @@ class TapApplication : Application(), ImageLoaderFactory { ) walletConnectRepository = WalletConnectRepository(this) + foregroundActivityObserver = ForegroundActivityObserver() + registerActivityLifecycleCallbacks(foregroundActivityObserver.callbacks) initFeedbackManager() loadConfigs() From 3f2e5700fb8b85431d6b6b34b101d6f7654026a3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 8 Jul 2022 13:31:54 +0300 Subject: [PATCH 22/28] Updated on 2026-08-14 --- .../java/com/tangem/tap/TapApplication.kt | 8 +- .../common/feedback/AdditionalFeedbackInfo.kt | 127 ++++++ .../tap/common/feedback/FeedbackData.kt | 95 +++++ .../common/feedback/FeedbackDataBuilder.kt | 81 ++++ .../tap/common/feedback/FeedbackManager.kt | 72 ++++ .../tap/common/log/TangemLogCollector.kt | 25 ++ .../tap/common/redux/global/GlobalAction.kt | 12 +- .../common/redux/global/GlobalMidlleware.kt | 2 +- .../tap/common/redux/global/GlobalState.kt | 2 +- .../features/details/ui/DetailsFragment.kt | 2 +- .../tap/features/feedback/FeedbackManager.kt | 380 ------------------ .../ui/dialogs/SendTransactionFailsDialog.kt | 2 +- .../ui/adapters/WarningMessagesAdapter.kt | 2 +- .../wallet/ui/dialogs/ScanFailsDialog.kt | 2 +- 14 files changed, 418 insertions(+), 394 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/feedback/AdditionalFeedbackInfo.kt create mode 100644 app/src/main/java/com/tangem/tap/common/feedback/FeedbackData.kt create mode 100644 app/src/main/java/com/tangem/tap/common/feedback/FeedbackDataBuilder.kt create mode 100644 app/src/main/java/com/tangem/tap/common/feedback/FeedbackManager.kt create mode 100644 app/src/main/java/com/tangem/tap/common/log/TangemLogCollector.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/feedback/FeedbackManager.kt diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 372fb621bf..61b576837d 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -12,7 +12,10 @@ import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder import com.tangem.domain.DomainLayer import com.tangem.network.common.MoshiConverter import com.tangem.tap.common.analytics.GlobalAnalyticsHandler +import com.tangem.tap.common.feedback.AdditionalFeedbackInfo +import com.tangem.tap.common.feedback.FeedbackManager import com.tangem.tap.common.images.createCoilImageLoader +import com.tangem.tap.common.log.TangemLogCollector import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.appReducer import com.tangem.tap.common.redux.global.GlobalAction @@ -24,9 +27,6 @@ import com.tangem.tap.domain.configurable.warningMessage.RemoteWarningLoader import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager import com.tangem.tap.domain.tokens.CurrenciesRepository import com.tangem.tap.domain.walletconnect.WalletConnectRepository -import com.tangem.tap.features.feedback.AdditionalEmailInfo -import com.tangem.tap.features.feedback.FeedbackManager -import com.tangem.tap.features.feedback.TangemLogCollector import com.tangem.tap.network.NetworkConnectivity import com.tangem.tap.persistence.PreferencesStorage import com.tangem.wallet.BuildConfig @@ -98,7 +98,7 @@ class TapApplication : Application(), ImageLoaderFactory { } private fun initFeedbackManager() { - val infoHolder = AdditionalEmailInfo() + val infoHolder = AdditionalFeedbackInfo() infoHolder.setAppVersion(this) val logWriter = TangemLogCollector() diff --git a/app/src/main/java/com/tangem/tap/common/feedback/AdditionalFeedbackInfo.kt b/app/src/main/java/com/tangem/tap/common/feedback/AdditionalFeedbackInfo.kt new file mode 100644 index 0000000000..ddc7a41b23 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/feedback/AdditionalFeedbackInfo.kt @@ -0,0 +1,127 @@ +package com.tangem.tap.common.feedback + +import android.content.Context +import android.content.pm.PackageManager +import android.os.Build +import com.tangem.blockchain.common.Amount +import com.tangem.blockchain.common.AmountType +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.Token +import com.tangem.blockchain.common.Wallet +import com.tangem.blockchain.common.WalletManager +import com.tangem.domain.common.ScanResponse +import com.tangem.tap.common.extensions.stripZeroPlainString + +class AdditionalFeedbackInfo { + class EmailWalletInfo( + var blockchain: Blockchain = Blockchain.Unknown, + var address: String = "", + var explorerLink: String = "", + var host: String = "", + var derivationPath: String = "", + ) + + var appVersion: String = "" + + // card + var cardId: String = "" + var cardFirmwareVersion: String = "" + var cardIssuer: String = "" + var cardBlockchain: String = "" + + // wallets + internal val walletsInfo = mutableListOf() + internal val tokens = mutableMapOf>() + internal var onSendErrorWalletInfo: EmailWalletInfo? = null + var signedHashesCount: String = "" + + // device + var phoneModel: String = Build.MODEL + var osVersion: String = Build.VERSION.SDK_INT.toString() + + // send error + var destinationAddress: String = "" + var amount: String = "" + var fee: String = "" + var token: String = "" + + fun setAppVersion(context: Context) { + try { + val pInfo = context.packageManager.getPackageInfo(context.packageName, 0) + appVersion = pInfo.versionName + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + } + } + + fun setCardInfo(data: ScanResponse) { + cardId = data.card.cardId + cardBlockchain = data.walletData?.blockchain ?: "" + cardFirmwareVersion = data.card.firmwareVersion.stringValue + cardIssuer = data.card.issuer.name + signedHashesCount = data.card.wallets + .joinToString("; ") { "${it.curve.curve} - ${it.totalSignedHashes}" } + } + + fun setWalletsInfo(walletManagers: List) { + walletsInfo.clear() + tokens.clear() + walletManagers.forEach { manager -> + walletsInfo.add( + EmailWalletInfo( + blockchain = manager.wallet.blockchain, + address = getAddress(manager.wallet), + explorerLink = getExploreUri(manager.wallet), + host = manager.currentHost, + derivationPath = manager.wallet.publicKey.derivationPath?.rawPath ?: "" + ) + ) + if (manager.cardTokens.isNotEmpty()) { + tokens[manager.wallet.blockchain] = manager.cardTokens + } + } + } + + fun updateOnSendError( + wallet: Wallet, + host: String, + amountToSend: Amount, + feeAmount: Amount, + destinationAddress: String, + ) { + onSendErrorWalletInfo = EmailWalletInfo( + blockchain = wallet.blockchain, + address = getAddress(wallet), + explorerLink = getExploreUri(wallet), + host = host, + derivationPath = wallet.publicKey.derivationPath?.rawPath ?: "" + ) + + this.destinationAddress = destinationAddress + amount = amountToSend.value?.stripZeroPlainString() ?: "0" + fee = feeAmount.value?.stripZeroPlainString() ?: "0" + token = if (amountToSend.type is AmountType.Token) amountToSend.currencySymbol else "" + } + + private fun getAddress(wallet: Wallet): String { + return if (wallet.addresses.size == 1) { + wallet.address + } else { + val addresses = wallet.addresses.joinToString(", ") { + "${it.type.javaClass.simpleName} - ${it.value}" + } + "Multiple address: $addresses" + } + } + + private fun getExploreUri(wallet: Wallet): String { + return if (wallet.addresses.size == 1) { + wallet.getExploreUrl(wallet.address) + } else { + val links = wallet.addresses.joinToString(", ") { + "${it.type.javaClass.simpleName} - ${wallet.getExploreUrl(it.value)}" + } + "Multiple explorers links: $links" + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/feedback/FeedbackData.kt b/app/src/main/java/com/tangem/tap/common/feedback/FeedbackData.kt new file mode 100644 index 0000000000..4750a444b3 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/feedback/FeedbackData.kt @@ -0,0 +1,95 @@ +package com.tangem.tap.common.feedback + +import android.content.Context +import com.tangem.domain.common.TapWorkarounds +import com.tangem.wallet.R + +interface FeedbackData { + val subjectResId: Int + val mainMessageResId: Int + + fun getDataCollectionMessageResId(): Int = R.string.feedback_data_collection_message + + fun prepare(infoHolder: AdditionalFeedbackInfo) {} + + fun createOptionalMessage(infoHolder: AdditionalFeedbackInfo): String + + fun joinTogether(context: Context, infoHolder: AdditionalFeedbackInfo): String { + return StringBuilder().apply { + append(context.getString(mainMessageResId)) + appendLine(3) + append(context.getString(getDataCollectionMessageResId())) + appendLine() + append(createOptionalMessage(infoHolder)) + }.toString() + } +} + +class RateCanBeBetterEmail : FeedbackData { + override val subjectResId: Int = R.string.feedback_subject_rate_negative + override val mainMessageResId: Int = R.string.feedback_preface_rate_negative + + override fun createOptionalMessage(infoHolder: AdditionalFeedbackInfo): String = FeedbackDataBuilder(infoHolder) + .appendCardInfo() + .appendLine() + .appendPhoneInfo() + .build() +} + +class ScanFailsEmail : FeedbackData { + + override val subjectResId: Int = R.string.feedback_subject_scan_failed + override val mainMessageResId: Int = R.string.feedback_preface_scan_failed + + override fun joinTogether(context: Context, infoHolder: AdditionalFeedbackInfo): String = StringBuilder().apply { + append(context.getString(mainMessageResId)) + appendLine(4) + append(createOptionalMessage(infoHolder)) + }.toString() + + override fun createOptionalMessage(infoHolder: AdditionalFeedbackInfo): String = FeedbackDataBuilder(infoHolder) + .appendPhoneInfo() + .build() +} + +class SendTransactionFailedEmail( + val error: String +) : FeedbackData { + + override val subjectResId: Int = R.string.feedback_subject_tx_failed + override val mainMessageResId: Int = R.string.feedback_preface_tx_failed + + override fun createOptionalMessage(infoHolder: AdditionalFeedbackInfo): String = FeedbackDataBuilder(infoHolder) + .appendCardInfo() + .appendDelimiter() + .appendTxFailedBlockchainInfo(error) + .appendLine() + .appendPhoneInfo() + .build() +} + +class FeedbackEmail : FeedbackData { + override val subjectResId: Int + get() = if (isS2CCard) s2cSubject else tangemSubject + override val mainMessageResId: Int + get() = if (isS2CCard) s2cMainMessage else tangemMainMessage + + private val tangemSubject: Int = R.string.feedback_subject_support_tangem + private val tangemMainMessage: Int = R.string.feedback_preface_support + + private val s2cSubject: Int = R.string.feedback_subject_support + private val s2cMainMessage: Int = R.string.feedback_preface_support + + private var isS2CCard = false + + override fun prepare(infoHolder: AdditionalFeedbackInfo) { + isS2CCard = TapWorkarounds.isStart2CoinIssuer(infoHolder.cardIssuer) + } + + override fun createOptionalMessage(infoHolder: AdditionalFeedbackInfo): String = FeedbackDataBuilder(infoHolder) + .appendCardInfo() + .appendWalletsInfo() + .appendLine() + .appendPhoneInfo() + .build() +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/feedback/FeedbackDataBuilder.kt b/app/src/main/java/com/tangem/tap/common/feedback/FeedbackDataBuilder.kt new file mode 100644 index 0000000000..9e5378e9e5 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/feedback/FeedbackDataBuilder.kt @@ -0,0 +1,81 @@ +package com.tangem.tap.common.feedback + +class FeedbackDataBuilder( + private val infoHolder: AdditionalFeedbackInfo +) { + val builder = StringBuilder() + + fun appendDelimiter(): FeedbackDataBuilder { + builder.appendDelimiter() + return this + } + + fun appendLine(count: Int = 1): FeedbackDataBuilder { + builder.appendLine(count) + return this + } + + fun appendCardInfo(): FeedbackDataBuilder { + builder.appendKeyValue("Card ID", infoHolder.cardId) + builder.appendKeyValue("Firmware version", infoHolder.cardFirmwareVersion) + builder.appendKeyValue("Card Blockchain", infoHolder.cardBlockchain) + builder.appendKeyValue("Signed hashes", infoHolder.signedHashesCount) + return this + } + + fun appendWalletsInfo(): FeedbackDataBuilder { + infoHolder.walletsInfo.forEach { + builder.appendDelimiter() + builder.appendKeyValue("Blockchain", it.blockchain.fullName) + builder.appendKeyValue("Host", it.host) + builder.appendKeyValue("Wallet address", it.address) + builder.appendKeyValue("Derivation path", it.derivationPath) + builder.appendKeyValue("Explorer link", it.explorerLink) + + infoHolder.tokens[it.blockchain]?.let { tokens -> + builder.append("Tokens:") + appendLine() + tokens.forEach { token -> + builder.appendKeyValue("Name", token.name) + builder.appendKeyValue("ID", token.id ?: "[custom token]") + builder.appendKeyValue("Contract address", token.contractAddress) + } + } + } + return this + } + + fun appendTxFailedBlockchainInfo(error: String): FeedbackDataBuilder { + val walletInfo = infoHolder.onSendErrorWalletInfo ?: AdditionalFeedbackInfo.EmailWalletInfo() + builder.appendKeyValue("Blockchain", walletInfo.blockchain.fullName) + builder.appendKeyValue("Derivation path", walletInfo.derivationPath) + builder.appendKeyValue("Host", walletInfo.host) + builder.appendKeyValue("Token", infoHolder.token) + builder.appendKeyValue("Error", error) + builder.appendDelimiter() + builder.appendKeyValue("Source address", walletInfo.address) + builder.appendKeyValue("Destination address", infoHolder.destinationAddress) + builder.appendKeyValue("Amount", infoHolder.amount) + builder.appendKeyValue("Fee", infoHolder.fee) + return this + } + + fun appendPhoneInfo(): FeedbackDataBuilder { + builder.appendKeyValue("Phone model", infoHolder.phoneModel) + builder.appendKeyValue("OS version", infoHolder.osVersion) + builder.appendKeyValue("App version", infoHolder.appVersion) + return this + } + + fun build(): String = builder.toString() +} + +private fun StringBuilder.appendKeyValue(key: String, value: String): StringBuilder { + return if (value.isNotBlank()) this.append("$key: $value\n") else this +} + +private fun StringBuilder.appendDelimiter(): StringBuilder = append("----------\n") + +private fun StringBuilder.appendLine(count: Int = 1): StringBuilder { + return append(List(count) { "\n" }.joinToString(separator = "")) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/feedback/FeedbackManager.kt b/app/src/main/java/com/tangem/tap/common/feedback/FeedbackManager.kt new file mode 100644 index 0000000000..3a72ece232 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/feedback/FeedbackManager.kt @@ -0,0 +1,72 @@ +package com.tangem.tap.common.feedback + +import android.app.Activity +import com.tangem.domain.common.TapWorkarounds +import com.tangem.tap.common.extensions.sendEmail +import com.tangem.tap.common.log.TangemLogCollector +import java.io.File +import java.io.FileWriter +import java.io.StringWriter +import timber.log.Timber + + +/** +[REDACTED_AUTHOR] + */ +class FeedbackManager( + val infoHolder: AdditionalFeedbackInfo, + private val logCollector: TangemLogCollector, +) { + + private lateinit var activity: Activity + + fun updateActivity(activity: Activity) { + this.activity = activity + } + + fun sendEmail(feedbackData: FeedbackData, onFail: ((Exception) -> Unit)? = null) { + if (!this::activity.isInitialized) return + + feedbackData.prepare(infoHolder) + val fileLog = if (feedbackData is ScanFailsEmail) createLogFile() else null + activity.sendEmail( + email = getSupportEmail(), + subject = activity.getString(feedbackData.subjectResId), + message = feedbackData.joinTogether(activity, infoHolder), + file = fileLog, + onFail = onFail + ) + } + + private fun getSupportEmail(): String { + return if (TapWorkarounds.isStart2CoinIssuer(infoHolder.cardIssuer)) { + S2C_SUPPORT_EMAIL + } else { + DEFAULT_SUPPORT_EMAIL + } + } + + private fun createLogFile(): File? { + return try { + val file = File(activity.filesDir, "logs.txt") + file.delete() + file.createNewFile() + + val stringWriter = StringWriter() + logCollector.getLogs().forEach { stringWriter.append(it) } + val fileWriter = FileWriter(file) + fileWriter.write(stringWriter.toString()) + fileWriter.close() + logCollector.clearLogs() + file + } catch (ex: Exception) { + Timber.e(ex, "Can't create a file for email attachment") + null + } + } + + companion object { + const val DEFAULT_SUPPORT_EMAIL = "support@tangem.com" + const val S2C_SUPPORT_EMAIL = "cardsupport@start2coin.com" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/log/TangemLogCollector.kt b/app/src/main/java/com/tangem/tap/common/log/TangemLogCollector.kt new file mode 100644 index 0000000000..c1ae08ebc2 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/log/TangemLogCollector.kt @@ -0,0 +1,25 @@ +package com.tangem.tap.common.log + +import com.tangem.Log +import com.tangem.TangemSdkLogger +import java.text.SimpleDateFormat +import java.util.Date + +class TangemLogCollector : TangemSdkLogger { + private val dateFormatter = SimpleDateFormat("HH:mm:ss.SSS") + private val logs = mutableListOf() + private val mutex = Object() + + override fun log(message: () -> String, level: Log.Level) { + val time = dateFormatter.format(Date()) + synchronized(mutex) { + logs.add("$time: ${message()}\n") + } + } + + fun getLogs(): List = synchronized(mutex) { logs.toList() } + + fun clearLogs() { + synchronized(mutex) { logs.clear() } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt index a82d5ba94b..a12a6d20bf 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt @@ -7,14 +7,18 @@ import com.tangem.common.core.TangemError import com.tangem.domain.common.ScanResponse import com.tangem.tap.common.analytics.GlobalAnalyticsHandler import com.tangem.tap.common.entities.FiatCurrency -import com.tangem.tap.common.redux.* +import com.tangem.tap.common.feedback.FeedbackData +import com.tangem.tap.common.feedback.FeedbackManager +import com.tangem.tap.common.redux.DebugErrorAction +import com.tangem.tap.common.redux.ErrorAction +import com.tangem.tap.common.redux.NotificationAction +import com.tangem.tap.common.redux.StateDialog +import com.tangem.tap.common.redux.ToastNotificationAction import com.tangem.tap.domain.TapError import com.tangem.tap.domain.configurable.config.ConfigManager import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager import com.tangem.tap.features.details.redux.SecurityOption -import com.tangem.tap.features.feedback.EmailData -import com.tangem.tap.features.feedback.FeedbackManager import org.rekotlin.Action sealed class GlobalAction : Action { @@ -73,7 +77,7 @@ sealed class GlobalAction : Action { data class SetFeedbackManager(val feedbackManager: FeedbackManager) : GlobalAction() data class SetAnanlyticHandlers(val analyticsHandlers: GlobalAnalyticsHandler) : GlobalAction() - data class SendFeedback(val emailData: EmailData) : GlobalAction() + data class SendFeedback(val feedbackData: FeedbackData) : GlobalAction() data class UpdateFeedbackInfo(val walletManagers: List) : GlobalAction() object ExchangeManager : GlobalAction() { diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt index 7f61d63022..55c9b20908 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt @@ -80,7 +80,7 @@ private fun handleAction(action: Action, appState: () -> AppState?, dispatch: Di } } is GlobalAction.SendFeedback -> { - store.state.globalState.feedbackManager?.send(action.emailData) + store.state.globalState.feedbackManager?.sendEmail(action.feedbackData) } is GlobalAction.UpdateWalletSignedHashes -> { store.dispatch(WalletAction.Warnings.CheckRemainingSignatures(action.remainingSignatures)) diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt index 8588256411..62e5217866 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt @@ -3,12 +3,12 @@ package com.tangem.tap.common.redux.global import com.tangem.domain.common.ScanResponse import com.tangem.tap.common.analytics.AnalyticsHandler import com.tangem.tap.common.entities.FiatCurrency +import com.tangem.tap.common.feedback.FeedbackManager import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.domain.PayIdManager import com.tangem.tap.domain.TapWalletManager import com.tangem.tap.domain.configurable.config.ConfigManager import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager -import com.tangem.tap.features.feedback.FeedbackManager import com.tangem.tap.features.onboarding.OnboardingManager import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager import org.rekotlin.StateType diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt index 2789b0069d..ce4eaf8018 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt @@ -9,6 +9,7 @@ import androidx.transition.TransitionInflater import by.kirich1409.viewbindingdelegate.viewBinding import com.tangem.domain.common.getTwinCardIdForUser import com.tangem.tap.common.extensions.show +import com.tangem.tap.common.feedback.FeedbackEmail import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction @@ -16,7 +17,6 @@ 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.details.redux.SecurityOption -import com.tangem.tap.features.feedback.FeedbackEmail import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.store import com.tangem.wallet.R diff --git a/app/src/main/java/com/tangem/tap/features/feedback/FeedbackManager.kt b/app/src/main/java/com/tangem/tap/features/feedback/FeedbackManager.kt deleted file mode 100644 index ad5a091c99..0000000000 --- a/app/src/main/java/com/tangem/tap/features/feedback/FeedbackManager.kt +++ /dev/null @@ -1,380 +0,0 @@ -package com.tangem.tap.features.feedback - -import android.app.Activity -import android.content.Context -import android.content.pm.PackageManager -import android.os.Build -import com.tangem.Log -import com.tangem.TangemSdkLogger -import com.tangem.blockchain.common.* -import com.tangem.domain.common.ScanResponse -import com.tangem.domain.common.TapWorkarounds -import com.tangem.tap.common.extensions.sendEmail -import com.tangem.tap.common.extensions.stripZeroPlainString -import com.tangem.wallet.R -import timber.log.Timber -import java.io.File -import java.io.FileWriter -import java.io.StringWriter -import java.text.SimpleDateFormat -import java.util.* - - -/** -[REDACTED_AUTHOR] - */ -class FeedbackManager( - val infoHolder: AdditionalEmailInfo, - private val logCollector: TangemLogCollector, -) { - - private lateinit var activity: Activity - - fun updateActivity(activity: Activity) { - this.activity = activity - } - - fun send(emailData: EmailData, onFail: ((Exception) -> Unit)? = null) { - if (!this::activity.isInitialized) return - - emailData.prepare(infoHolder) - val fileLog = if (emailData is ScanFailsEmail) createLogFile() else null - activity.sendEmail( - email = getSupportEmail(), - subject = activity.getString(emailData.subjectResId), - message = emailData.joinTogether(activity, infoHolder), - file = fileLog, - onFail = onFail - ) - } - - private fun getSupportEmail(): String { - return if (TapWorkarounds.isStart2CoinIssuer(infoHolder.cardIssuer)) { - S2C_SUPPORT_EMAIL - } else { - DEFAULT_SUPPORT_EMAIL - } - } - - private fun createLogFile(): File? { - return try { - val file = File(activity.filesDir, "logs.txt") - file.delete() - file.createNewFile() - - val stringWriter = StringWriter() - logCollector.getLogs().forEach { stringWriter.append(it) } - val fileWriter = FileWriter(file) - fileWriter.write(stringWriter.toString()) - fileWriter.close() - logCollector.clearLogs() - file - } catch (ex: Exception) { - Timber.e(ex, "Can't create a file for email attachment") - null - } - } - - companion object { - const val DEFAULT_SUPPORT_EMAIL = "support@tangem.com" - const val S2C_SUPPORT_EMAIL = "cardsupport@start2coin.com" - } -} - -class TangemLogCollector : TangemSdkLogger { - private val dateFormatter = SimpleDateFormat("HH:mm:ss.SSS") - private val logs = mutableListOf() - private val mutex = Object() - - override fun log(message: () -> String, level: Log.Level) { - val time = dateFormatter.format(Date()) - synchronized(mutex) { - logs.add("$time: ${message()}\n") - } - } - - fun getLogs(): List = synchronized(mutex) { logs.toList() } - - fun clearLogs() { - synchronized(mutex) { logs.clear() } - } -} - -class AdditionalEmailInfo { - class EmailWalletInfo( - var blockchain: Blockchain = Blockchain.Unknown, - var address: String = "", - var explorerLink: String = "", - var host: String = "", - var derivationPath: String = "", - ) - - var appVersion: String = "" - - // card - var cardId: String = "" - var cardFirmwareVersion: String = "" - var cardIssuer: String = "" - var cardBlockchain: String = "" - - // wallets - internal val walletsInfo = mutableListOf() - internal val tokens = mutableMapOf>() - internal var onSendErrorWalletInfo: EmailWalletInfo? = null - var signedHashesCount: String = "" - - // device - var phoneModel: String = Build.MODEL - var osVersion: String = Build.VERSION.SDK_INT.toString() - - // send error - var destinationAddress: String = "" - var amount: String = "" - var fee: String = "" - var token: String = "" - - fun setAppVersion(context: Context) { - try { - val pInfo = context.packageManager.getPackageInfo(context.packageName, 0) - appVersion = pInfo.versionName - } catch (e: PackageManager.NameNotFoundException) { - e.printStackTrace() - } - } - - fun setCardInfo(data: ScanResponse) { - cardId = data.card.cardId - cardBlockchain = data.walletData?.blockchain ?: "" - cardFirmwareVersion = data.card.firmwareVersion.stringValue - cardIssuer = data.card.issuer.name - signedHashesCount = data.card.wallets - .joinToString("; ") { "${it.curve.curve} - ${it.totalSignedHashes}" } - } - - fun setWalletsInfo(walletManagers: List) { - walletsInfo.clear() - tokens.clear() - walletManagers.forEach { manager -> - walletsInfo.add( - EmailWalletInfo( - blockchain = manager.wallet.blockchain, - address = getAddress(manager.wallet), - explorerLink = getExploreUri(manager.wallet), - host = manager.currentHost, - derivationPath = manager.wallet.publicKey.derivationPath?.rawPath ?: "" - ) - ) - if (manager.cardTokens.isNotEmpty()) { - tokens[manager.wallet.blockchain] = manager.cardTokens - } - } - } - - fun updateOnSendError(wallet: Wallet, host: String, amountToSend: Amount, feeAmount: Amount, destinationAddress: String) { - onSendErrorWalletInfo = EmailWalletInfo( - blockchain = wallet.blockchain, - address = getAddress(wallet), - explorerLink = getExploreUri(wallet), - host = host, - derivationPath = wallet.publicKey.derivationPath?.rawPath ?: "" - ) - - this.destinationAddress = destinationAddress - amount = amountToSend.value?.stripZeroPlainString() ?: "0" - fee = feeAmount.value?.stripZeroPlainString() ?: "0" - token = if (amountToSend.type is AmountType.Token) amountToSend.currencySymbol else "" - } - - private fun getAddress(wallet: Wallet): String { - return if (wallet.addresses.size == 1) { - wallet.address - } else { - val addresses = wallet.addresses.joinToString(", ") { - "${it.type.javaClass.simpleName} - ${it.value}" - } - "Multiple address: $addresses" - } - } - - private fun getExploreUri(wallet: Wallet): String { - return if (wallet.addresses.size == 1) { - wallet.getExploreUrl(wallet.address) - } else { - val links = wallet.addresses.joinToString(", ") { - "${it.type.javaClass.simpleName} - ${wallet.getExploreUrl(it.value)}" - } - "Multiple explorers links: $links" - } - } -} - -interface EmailData { - val subjectResId: Int - val mainMessageResId: Int - - fun getDataCollectionMessageResId(): Int = R.string.feedback_data_collection_message - - fun prepare(infoHolder: AdditionalEmailInfo) {} - - fun createOptionalMessage(infoHolder: AdditionalEmailInfo): String - - fun joinTogether(context: Context, infoHolder: AdditionalEmailInfo): String { - return StringBuilder().apply { - append(context.getString(mainMessageResId)) - appendLine(3) - append(context.getString(getDataCollectionMessageResId())) - appendLine() - append(createOptionalMessage(infoHolder)) - }.toString() - } -} - -class RateCanBeBetterEmail : EmailData { - override val subjectResId: Int = R.string.feedback_subject_rate_negative - override val mainMessageResId: Int = R.string.feedback_preface_rate_negative - - override fun createOptionalMessage(infoHolder: AdditionalEmailInfo): String = EmailDataBuilder(infoHolder) - .appendCardInfo() - .appendLine() - .appendPhoneInfo() - .build() -} - -class ScanFailsEmail : EmailData { - - override val subjectResId: Int = R.string.feedback_subject_scan_failed - override val mainMessageResId: Int = R.string.feedback_preface_scan_failed - - override fun joinTogether(context: Context, infoHolder: AdditionalEmailInfo): String = StringBuilder().apply { - append(context.getString(mainMessageResId)) - appendLine(4) - append(createOptionalMessage(infoHolder)) - }.toString() - - override fun createOptionalMessage(infoHolder: AdditionalEmailInfo): String = EmailDataBuilder(infoHolder) - .appendPhoneInfo() - .build() -} - -class SendTransactionFailedEmail( - val error: String -) : EmailData { - - override val subjectResId: Int = R.string.feedback_subject_tx_failed - override val mainMessageResId: Int = R.string.feedback_preface_tx_failed - - override fun createOptionalMessage(infoHolder: AdditionalEmailInfo): String = EmailDataBuilder(infoHolder) - .appendCardInfo() - .appendDelimiter() - .appendTxFailedBlockchainInfo(error) - .appendLine() - .appendPhoneInfo() - .build() -} - -class FeedbackEmail : EmailData { - override val subjectResId: Int - get() = if (isS2CCard) s2cSubject else tangemSubject - override val mainMessageResId: Int - get() = if (isS2CCard) s2cMainMessage else tangemMainMessage - - private val tangemSubject: Int = R.string.feedback_subject_support_tangem - private val tangemMainMessage: Int = R.string.feedback_preface_support - - private val s2cSubject: Int = R.string.feedback_subject_support - private val s2cMainMessage: Int = R.string.feedback_preface_support - - private var isS2CCard = false - - override fun prepare(infoHolder: AdditionalEmailInfo) { - isS2CCard = TapWorkarounds.isStart2CoinIssuer(infoHolder.cardIssuer) - } - - override fun createOptionalMessage(infoHolder: AdditionalEmailInfo): String = EmailDataBuilder(infoHolder) - .appendCardInfo() - .appendWalletsInfo() - .appendLine() - .appendPhoneInfo() - .build() -} - - -class EmailDataBuilder( - private val infoHolder: AdditionalEmailInfo -) { - val builder = StringBuilder() - - fun appendDelimiter(): EmailDataBuilder { - builder.appendDelimiter() - return this - } - - fun appendLine(count: Int = 1): EmailDataBuilder { - builder.appendLine(count) - return this - } - - fun appendCardInfo(): EmailDataBuilder { - builder.appendKeyValue("Card ID", infoHolder.cardId) - builder.appendKeyValue("Firmware version", infoHolder.cardFirmwareVersion) - builder.appendKeyValue("Card Blockchain", infoHolder.cardBlockchain) - builder.appendKeyValue("Signed hashes", infoHolder.signedHashesCount) - return this - } - - fun appendWalletsInfo(): EmailDataBuilder { - infoHolder.walletsInfo.forEach { - builder.appendDelimiter() - builder.appendKeyValue("Blockchain", it.blockchain.fullName) - builder.appendKeyValue("Host", it.host) - builder.appendKeyValue("Wallet address", it.address) - builder.appendKeyValue("Derivation path", it.derivationPath) - builder.appendKeyValue("Explorer link", it.explorerLink) - - infoHolder.tokens[it.blockchain]?.let { tokens -> - builder.append("Tokens:") - appendLine() - tokens.forEach { token -> - builder.appendKeyValue("Name", token.name) - builder.appendKeyValue("ID", token.id ?: "[custom token]") - builder.appendKeyValue("Contract address", token.contractAddress) - } - } - } - return this - } - - fun appendTxFailedBlockchainInfo(error: String): EmailDataBuilder { - val walletInfo = infoHolder.onSendErrorWalletInfo ?: AdditionalEmailInfo.EmailWalletInfo() - builder.appendKeyValue("Blockchain", walletInfo.blockchain.fullName) - builder.appendKeyValue("Derivation path", walletInfo.derivationPath) - builder.appendKeyValue("Host", walletInfo.host) - builder.appendKeyValue("Token", infoHolder.token) - builder.appendKeyValue("Error", error) - builder.appendDelimiter() - builder.appendKeyValue("Source address", walletInfo.address) - builder.appendKeyValue("Destination address", infoHolder.destinationAddress) - builder.appendKeyValue("Amount", infoHolder.amount) - builder.appendKeyValue("Fee", infoHolder.fee) - return this - } - - fun appendPhoneInfo(): EmailDataBuilder { - builder.appendKeyValue("Phone model", infoHolder.phoneModel) - builder.appendKeyValue("OS version", infoHolder.osVersion) - builder.appendKeyValue("App version", infoHolder.appVersion) - return this - } - - fun build(): String = builder.toString() -} - -private fun StringBuilder.appendKeyValue(key: String, value: String): StringBuilder { - return if (value.isNotBlank()) this.append("$key: $value\n") else this -} - -private fun StringBuilder.appendDelimiter(): StringBuilder = append("----------\n") - -private fun StringBuilder.appendLine(count: Int = 1): StringBuilder { - return append(List(count) { "\n" }.joinToString(separator = "")) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/SendTransactionFailsDialog.kt b/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/SendTransactionFailsDialog.kt index 8f4b106e97..2dfadd6362 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/SendTransactionFailsDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/SendTransactionFailsDialog.kt @@ -2,8 +2,8 @@ package com.tangem.tap.features.send.ui.dialogs import android.content.Context import androidx.appcompat.app.AlertDialog +import com.tangem.tap.common.feedback.SendTransactionFailedEmail import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.features.feedback.SendTransactionFailedEmail import com.tangem.tap.features.send.redux.SendAction import com.tangem.tap.store import com.tangem.wallet.R diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt index 9014d5d4ff..820fa4167a 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt @@ -16,10 +16,10 @@ import com.tangem.tap.common.extensions.getColor import com.tangem.tap.common.extensions.getString import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show +import com.tangem.tap.common.feedback.RateCanBeBetterEmail import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager -import com.tangem.tap.features.feedback.RateCanBeBetterEmail import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.store import com.tangem.wallet.R diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt index 496a8ff731..4122e0bd06 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt @@ -3,8 +3,8 @@ package com.tangem.tap.features.wallet.ui.dialogs import android.content.Context import androidx.appcompat.app.AlertDialog import com.tangem.tap.common.extensions.dispatchDialogHide +import com.tangem.tap.common.feedback.ScanFailsEmail import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.features.feedback.ScanFailsEmail import com.tangem.tap.store import com.tangem.wallet.R From c445651ca9824e2b992aa9f898f00639197cf6aa Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 13 Jul 2022 00:19:07 +0300 Subject: [PATCH 23/28] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/MainActivity.kt | 5 +- .../java/com/tangem/tap/TapApplication.kt | 11 ++- .../tap/common/feedback/FeedbackManager.kt | 95 +++++++++++++------ .../tap/common/zendesk/ZendeskConfig.kt | 16 ++++ .../configurable/config/ConfigManager.kt | 17 ++-- .../domain/configurable/config/JsonModels.kt | 4 +- 6 files changed, 108 insertions(+), 40 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/zendesk/ZendeskConfig.kt diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index a663e66e7a..7593f303e7 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -27,11 +27,11 @@ import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction import com.tangem.tap.features.shop.redux.ShopAction import com.tangem.wallet.R import com.tangem.wallet.databinding.ActivityMainBinding +import java.lang.ref.WeakReference +import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job -import java.lang.ref.WeakReference -import kotlin.coroutines.CoroutineContext lateinit var tangemSdk: TangemSdk lateinit var tangemSdkManager: TangemSdkManager @@ -57,7 +57,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) systemActions() - store.state.globalState.feedbackManager?.updateActivity(this) store.dispatch(NavigationAction.ActivityCreated(WeakReference(this))) tangemSdk = TangemSdk.init(this, TangemSdkManager.config) diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 61b576837d..518ad410c4 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -89,9 +89,16 @@ class TapApplication : Application(), ImageLoaderFactory { val localLoader = FeaturesLocalLoader(this, moshi) val remoteLoader = FeaturesRemoteLoader(moshi) val configManager = ConfigManager(localLoader, remoteLoader) - configManager.load { + configManager.load { config -> store.dispatch(GlobalAction.SetConfigManager(configManager)) - shopService = TangemShopService(this, configManager.config.shopify!!) + shopService = TangemShopService( + application = this, + shopifyShop = config.shopify!! + ) + store.state.globalState.feedbackManager?.initChat( + context = this, + zendeskConfig = config.zendesk!! + ) } val warningsManager = WarningMessagesManager(RemoteWarningLoader(moshi)) warningsManager.load { store.dispatch(GlobalAction.SetWarningManager(warningsManager)) } diff --git a/app/src/main/java/com/tangem/tap/common/feedback/FeedbackManager.kt b/app/src/main/java/com/tangem/tap/common/feedback/FeedbackManager.kt index 3a72ece232..dccb805723 100644 --- a/app/src/main/java/com/tangem/tap/common/feedback/FeedbackManager.kt +++ b/app/src/main/java/com/tangem/tap/common/feedback/FeedbackManager.kt @@ -1,14 +1,22 @@ package com.tangem.tap.common.feedback -import android.app.Activity +import android.content.Context 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.withForegroundActivity import java.io.File import java.io.FileWriter import java.io.StringWriter import timber.log.Timber - +import zendesk.configurations.Configuration +import zendesk.core.Zendesk +import zendesk.support.Support +import zendesk.support.request.RequestConfiguration +import zendesk.support.requestlist.RequestListActivity +import zendesk.support.requestlist.RequestListConfiguration /** [REDACTED_AUTHOR] @@ -17,38 +25,47 @@ class FeedbackManager( val infoHolder: AdditionalFeedbackInfo, private val logCollector: TangemLogCollector, ) { - - private lateinit var activity: Activity - - fun updateActivity(activity: Activity) { - this.activity = activity + fun initChat( + context: Context, + zendeskConfig: ZendeskConfig, + ) { + Zendesk.INSTANCE.init( + /* context = */ context, + /* zendeskUrl = */ zendeskConfig.url, + /* applicationId = */ zendeskConfig.appId, + /* oauthClientId = */ zendeskConfig.clientId, + ) + Support.INSTANCE.init(Zendesk.INSTANCE) } fun sendEmail(feedbackData: FeedbackData, onFail: ((Exception) -> Unit)? = null) { - if (!this::activity.isInitialized) return - feedbackData.prepare(infoHolder) - val fileLog = if (feedbackData is ScanFailsEmail) createLogFile() else null - activity.sendEmail( - email = getSupportEmail(), - subject = activity.getString(feedbackData.subjectResId), - message = feedbackData.joinTogether(activity, infoHolder), - file = fileLog, - onFail = onFail - ) - } - - private fun getSupportEmail(): String { - return if (TapWorkarounds.isStart2CoinIssuer(infoHolder.cardIssuer)) { - S2C_SUPPORT_EMAIL - } else { - DEFAULT_SUPPORT_EMAIL + foregroundActivityObserver.withForegroundActivity { activity -> + val fileLog = if (feedbackData is ScanFailsEmail) createLogFile(activity) else null + activity.sendEmail( + email = getSupportEmail(), + subject = activity.getString(feedbackData.subjectResId), + message = feedbackData.joinTogether(activity, infoHolder), + file = fileLog, + onFail = onFail + ) } } - private fun createLogFile(): File? { + fun openChat(feedbackData: FeedbackData) { + feedbackData.prepare(infoHolder) + foregroundActivityObserver.withForegroundActivity { activity -> + RequestListActivity.builder() + .show( + /* context = */ activity, + /* configurations = */ buildConfigs(activity, feedbackData) + ) + } + } + + private fun createLogFile(context: Context): File? { return try { - val file = File(activity.filesDir, "logs.txt") + val file = File(context.filesDir, "logs.txt") file.delete() file.createNewFile() @@ -60,11 +77,35 @@ class FeedbackManager( logCollector.clearLogs() file } catch (ex: Exception) { - Timber.e(ex, "Can't create a file for email attachment") + Timber.e(ex, "Can't create the logs file") null } } + private fun buildConfigs( + context: Context, + feedbackData: FeedbackData, + ): List { + return listOf( + // Request configuration + RequestConfiguration.Builder() + .withRequestSubject(context.getString(feedbackData.subjectResId)) + .config(), + // Request list configuration + RequestListConfiguration.Builder() + .withContactUsButtonVisible(true) + .config(), + ) + } + + private fun getSupportEmail(): String { + return if (TapWorkarounds.isStart2CoinIssuer(infoHolder.cardIssuer)) { + S2C_SUPPORT_EMAIL + } else { + DEFAULT_SUPPORT_EMAIL + } + } + companion object { const val DEFAULT_SUPPORT_EMAIL = "support@tangem.com" const val S2C_SUPPORT_EMAIL = "cardsupport@start2coin.com" diff --git a/app/src/main/java/com/tangem/tap/common/zendesk/ZendeskConfig.kt b/app/src/main/java/com/tangem/tap/common/zendesk/ZendeskConfig.kt new file mode 100644 index 0000000000..aad872b7df --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/zendesk/ZendeskConfig.kt @@ -0,0 +1,16 @@ +package com.tangem.tap.common.zendesk + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class ZendeskConfig( + @Json(name = "zendeskApiKey") + val apiKey: String, + @Json(name = "zendeskAppId") + val appId: String, + @Json(name = "zendeskClientId") + val clientId: String, + @Json(name = "zendeskUrl") + val url: String, +) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt index 52037f828c..795e76fe06 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt @@ -1,8 +1,8 @@ package com.tangem.tap.domain.configurable.config import com.tangem.blockchain.common.BlockchainSdkConfig -import com.tangem.common.extensions.VoidCallback import com.tangem.tap.common.shop.shopify.ShopifyShop +import com.tangem.tap.common.zendesk.ZendeskConfig import com.tangem.tap.domain.configurable.Loader /** @@ -20,7 +20,8 @@ data class Config( val isTopUpEnabled: Boolean = false, @Deprecated("Not relevant since version 3.23") val isCreatingTwinCardsAllowed: Boolean = false, - val shopify: ShopifyShop? = null + val shopify: ShopifyShop? = null, + val zendesk: ZendeskConfig? = null, ) class ConfigManager( @@ -33,11 +34,11 @@ class ConfigManager( private var defaultConfig = Config() - fun load(onComplete: VoidCallback? = null) { - localLoader.load { config -> - setupFeature(config.features) - setupKey(config.configValues) - onComplete?.invoke() + fun load(onComplete: ((config: Config) -> Unit)? = null) { + localLoader.load { configModel -> + setupFeature(configModel.features) + setupKey(configModel.configValues) + onComplete?.invoke(config) } // Uncomment to enable remote config // remoteLoader.load { config -> @@ -94,6 +95,7 @@ class ConfigManager( ), appsFlyerDevKey = values.appsFlyerDevKey, shopify = values.shopifyShop, + zendesk = values.zendesk, ) defaultConfig = defaultConfig.copy( coinMarketCapKey = values.coinMarketCapKey, @@ -109,6 +111,7 @@ class ConfigManager( ), appsFlyerDevKey = values.appsFlyerDevKey, shopify = values.shopifyShop, + zendesk = values.zendesk, ) } diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt b/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt index f9e053ee98..fc63ed5e9a 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt @@ -1,6 +1,7 @@ package com.tangem.tap.domain.configurable.config import com.tangem.tap.common.shop.shopify.ShopifyShop +import com.tangem.tap.common.zendesk.ZendeskConfig /** [REDACTED_AUTHOR] @@ -24,7 +25,8 @@ class ConfigValueModel( val blockcypherTokens: Set?, val infuraProjectId: String?, val appsFlyerDevKey: String, - val shopifyShop: ShopifyShop? + val shopifyShop: ShopifyShop?, + val zendesk: ZendeskConfig?, ) class ConfigModel(val features: FeatureModel?, val configValues: ConfigValueModel?) { From 1e6dc08081b4705a6733052c1a7c0c10cfaa8454 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 13 Jul 2022 00:23:04 +0300 Subject: [PATCH 24/28] Updated on 2026-08-14 --- .../tangem/tap/common/feedback/FeedbackData.kt | 13 +++++++++++++ .../tap/common/redux/global/GlobalAction.kt | 3 ++- .../tap/common/redux/global/GlobalMidlleware.kt | 5 ++++- .../tap/features/details/ui/DetailsFragment.kt | 7 ++++++- .../ui/dialogs/SendTransactionFailsDialog.kt | 2 +- .../wallet/ui/adapters/WarningMessagesAdapter.kt | 2 +- .../wallet/ui/dialogs/ScanFailsDialog.kt | 2 +- app/src/main/res/layout/fragment_details.xml | 16 +++++++++++++++- app/src/main/res/values-ru/strings_final.xml | 2 ++ app/src/main/res/values/strings_final.xml | 2 ++ 10 files changed, 47 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/feedback/FeedbackData.kt b/app/src/main/java/com/tangem/tap/common/feedback/FeedbackData.kt index 4750a444b3..dc2a7d07d5 100644 --- a/app/src/main/java/com/tangem/tap/common/feedback/FeedbackData.kt +++ b/app/src/main/java/com/tangem/tap/common/feedback/FeedbackData.kt @@ -92,4 +92,17 @@ class FeedbackEmail : FeedbackData { .appendLine() .appendPhoneInfo() .build() +} + +class SupportInfo : FeedbackData { + override val subjectResId: Int = R.string.details_ask_a_question + override val mainMessageResId: Int = R.string.details_ask_a_question + + override fun createOptionalMessage(infoHolder: AdditionalFeedbackInfo): String { + return FeedbackDataBuilder(infoHolder) + .appendCardInfo() + .appendDelimiter() + .appendPhoneInfo() + .build() + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt index a12a6d20bf..4927405ccd 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt @@ -77,7 +77,8 @@ sealed class GlobalAction : Action { data class SetFeedbackManager(val feedbackManager: FeedbackManager) : GlobalAction() data class SetAnanlyticHandlers(val analyticsHandlers: GlobalAnalyticsHandler) : GlobalAction() - data class SendFeedback(val feedbackData: FeedbackData) : GlobalAction() + data class SendEmail(val feedbackData: FeedbackData) : GlobalAction() + data class OpenChat(val feedbackData: FeedbackData) : GlobalAction() data class UpdateFeedbackInfo(val walletManagers: List) : GlobalAction() object ExchangeManager : GlobalAction() { diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt index 55c9b20908..17cb291621 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt @@ -79,9 +79,12 @@ private fun handleAction(action: Action, appState: () -> AppState?, dispatch: Di } } } - is GlobalAction.SendFeedback -> { + is GlobalAction.SendEmail -> { store.state.globalState.feedbackManager?.sendEmail(action.feedbackData) } + is GlobalAction.OpenChat -> { + store.state.globalState.feedbackManager?.openChat(action.feedbackData) + } is GlobalAction.UpdateWalletSignedHashes -> { store.dispatch(WalletAction.Warnings.CheckRemainingSignatures(action.remainingSignatures)) } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt index ce4eaf8018..71b6baa710 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/DetailsFragment.kt @@ -10,6 +10,7 @@ import by.kirich1409.viewbindingdelegate.viewBinding import com.tangem.domain.common.getTwinCardIdForUser import com.tangem.tap.common.extensions.show import com.tangem.tap.common.feedback.FeedbackEmail +import com.tangem.tap.common.feedback.SupportInfo import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction @@ -124,7 +125,7 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber - store.dispatch(GlobalAction.SendFeedback(SendTransactionFailedEmail(dialog.errorMessage))) + store.dispatch(GlobalAction.SendEmail(SendTransactionFailedEmail(dialog.errorMessage))) } setPositiveButton(R.string.common_no) { _, _ -> } setOnDismissListener { store.dispatch(SendAction.Dialog.Hide) } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt index 820fa4167a..f77b716bc7 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt @@ -135,7 +135,7 @@ class WarningMessageVH(val binding: LayoutWarningCardActionBinding) : RecyclerVi analyticsHandler?.triggerEvent(AnalyticsEvent.APP_RATING_NEGATIVE) store.dispatch(WalletAction.Warnings.AppRating.SetNeverToShow) store.dispatch(GlobalAction.HideWarningMessage(warning)) - store.dispatch(GlobalAction.SendFeedback(RateCanBeBetterEmail())) + store.dispatch(GlobalAction.SendEmail(RateCanBeBetterEmail())) } binding.btnReallyCool.setOnClickListener { val activity = binding.root.context.getActivity() ?: return@setOnClickListener diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt index 4122e0bd06..91afcc3819 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt @@ -19,7 +19,7 @@ class ScanFailsDialog { setTitle(context.getString(R.string.common_warning)) setMessage(R.string.alert_troubleshooting_scan_card_title) setPositiveButton(R.string.alert_button_request_support) { _, _ -> - store.dispatch(GlobalAction.SendFeedback(ScanFailsEmail())) + store.dispatch(GlobalAction.SendEmail(ScanFailsEmail())) } setNeutralButton(R.string.alert_troubleshooting_scan_card_ok) { _, _ -> } setOnDismissListener { store.dispatchDialogHide() } diff --git a/app/src/main/res/layout/fragment_details.xml b/app/src/main/res/layout/fragment_details.xml index 58ac038846..2171b91e61 100644 --- a/app/src/main/res/layout/fragment_details.xml +++ b/app/src/main/res/layout/fragment_details.xml @@ -302,8 +302,22 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tv_send_feedback" /> + + - \ No newline at end of file + diff --git a/app/src/main/res/values-ru/strings_final.xml b/app/src/main/res/values-ru/strings_final.xml index 0deae6285f..d2028c922e 100644 --- a/app/src/main/res/values-ru/strings_final.xml +++ b/app/src/main/res/values-ru/strings_final.xml @@ -72,4 +72,6 @@ Карты банков РФ в данный момент не принимаются У вас есть карта банка другой страны или платежной системы UnionPay? Да + + Задать вопрос diff --git a/app/src/main/res/values/strings_final.xml b/app/src/main/res/values/strings_final.xml index f5dfbc7abd..adbf1e7604 100644 --- a/app/src/main/res/values/strings_final.xml +++ b/app/src/main/res/values/strings_final.xml @@ -74,4 +74,6 @@ Do you have a bank card of another country or a UnionPay card? Yes No + + Ask a question From cfb002bee764d2996d4394e51c45b0ad21d09089 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 8 Jul 2022 12:05:54 +0300 Subject: [PATCH 25/28] Updated on 2026-08-14 --- app/src/main/AndroidManifest.xml | 17 ++++++++++++++++- app/src/main/res/values/zendesk_styles.xml | 7 +++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 app/src/main/res/values/zendesk_styles.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 22fda180cd..d180642a75 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -128,6 +128,21 @@ + + + + + + + + + + - \ No newline at end of file + diff --git a/app/src/main/res/values/zendesk_styles.xml b/app/src/main/res/values/zendesk_styles.xml new file mode 100644 index 0000000000..ebb4fcab6b --- /dev/null +++ b/app/src/main/res/values/zendesk_styles.xml @@ -0,0 +1,7 @@ + + + + From 33cd0b0fc619a5b108f6ea33911283f8a1b0d87f Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 14 Jul 2022 11:51:08 +0300 Subject: [PATCH 26/28] Updated on 2026-08-14 --- .../warningMessage/WarningMessagesManager.kt | 26 ------------------- .../tap/features/wallet/redux/WalletAction.kt | 10 ++++--- .../redux/middlewares/WarningsMiddleware.kt | 17 ++++++------ .../ui/adapters/WarningMessagesAdapter.kt | 22 ++-------------- .../tap/persistence/PreferencesStorage.kt | 11 +------- 5 files changed, 17 insertions(+), 69 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt b/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt index ac2d922c0d..10dbcb2cbb 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt @@ -6,7 +6,6 @@ import com.tangem.tap.common.extensions.containsAny import com.tangem.tap.common.extensions.removeBy import com.tangem.wallet.BuildConfig import com.tangem.wallet.R -import java.util.* /** [REDACTED_AUTHOR] @@ -188,31 +187,6 @@ class WarningMessagesManager( WarningMessage.Origin.Local ) - fun restoreFundsWarning(): WarningMessage = WarningMessage( - title = "", - message = "", - type = WarningMessage.Type.Temporary, - priority = WarningMessage.Priority.Warning, - listOf(WarningMessage.Location.MainScreen), - blockchains = null, - titleResId = R.string.alert_title, - messageResId = R.string.alert_funds_restoration_message, - origin = WarningMessage.Origin.Local, - buttonTextId = R.string.warning_button_learn_more - ) - const val REMAINING_SIGNATURES_WARNING = 10 - private const val RESTORE_FUNDS_GUIDE_URL_RU = - "https://tangem.com/ru/kak-vosstanovit-tokeny-otpravlennye-ne-na-tot-adres-v-tangem-wallet" - private const val RESTORE_FUNDS_GUIDE_URL_EN = - "https://tangem.com/en/how-to-recover-crypto-sent-to-the-wrong-address-in-tangem-wallet" - - fun getRestoreFundsGuideUrl(locale: String): String { - return if (locale == Locale("ru").language) { - RESTORE_FUNDS_GUIDE_URL_RU - } else { - RESTORE_FUNDS_GUIDE_URL_EN - } - } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt index ea1d5fbced..89d77cd33f 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt @@ -1,7 +1,11 @@ package com.tangem.tap.features.wallet.redux import android.content.Context -import com.tangem.blockchain.common.* +import com.tangem.blockchain.common.Amount +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.Token +import com.tangem.blockchain.common.Wallet +import com.tangem.blockchain.common.WalletManager import com.tangem.blockchain.common.address.AddressType import com.tangem.common.card.Card import com.tangem.tap.common.entities.FiatCurrency @@ -13,8 +17,8 @@ import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.features.wallet.models.Currency import com.tangem.wallet.R -import org.rekotlin.Action import java.math.BigDecimal +import org.rekotlin.Action sealed class WalletAction : Action { @@ -106,8 +110,6 @@ sealed class WalletAction : Action { } class CheckRemainingSignatures(val remainingSignatures: Int?) : Warnings() - - object RestoreFundsWarningClosed : Warnings() } data class LoadFiatRate( diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt index 5197928d6e..7aa45f997d 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt @@ -8,7 +8,6 @@ import com.tangem.common.card.Card import com.tangem.common.card.FirmwareVersion import com.tangem.domain.common.ScanResponse import com.tangem.domain.common.TapWorkarounds.isTestCard -import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation import com.tangem.tap.common.analytics.AnalyticsEvent import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.isGreaterThan @@ -26,15 +25,15 @@ import com.tangem.tap.preferencesStorage import com.tangem.tap.scope import com.tangem.tap.store import com.tangem.wallet.R +import java.math.BigDecimal import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import java.math.BigDecimal class WarningsMiddleware { fun handle(action: WalletAction.Warnings, globalState: GlobalState?) { when (action) { - WalletAction.Warnings.Update -> setWarningMessages() + is WalletAction.Warnings.Update -> setWarningMessages() is WalletAction.Warnings.CheckIfNeeded -> { showCardWarningsIfNeeded(globalState) val readyToShow = preferencesStorage.appRatingLaunchObserver.isReadyToShow() @@ -66,9 +65,11 @@ class WarningsMiddleware { ) } } - is WalletAction.Warnings.RestoreFundsWarningClosed -> { - preferencesStorage.saveRestoreFundsWarningClosed() - } + is WalletAction.Warnings.AppRating, + is WalletAction.Warnings.CheckHashesCount, + is WalletAction.Warnings.CheckHashesCount.ConfirmHashesCount, + is WalletAction.Warnings.CheckHashesCount.NeedToCheckHashesCountOnline, + is WalletAction.Warnings.Set -> Unit } } @@ -93,9 +94,6 @@ class WarningsMiddleware { addWarningMessage(WarningMessagesManager.testCardWarning(), autoUpdate = true) return@let } - if (card.useOldStyleDerivation && !preferencesStorage.wasRestoreFundsWarningClosed()) { - addWarningMessage(warning = WarningMessagesManager.restoreFundsWarning()) - } showWarningLowRemainingSignaturesIfNeeded(card) if (card.firmwareVersion.type != FirmwareVersion.FirmwareType.Release) { @@ -190,6 +188,7 @@ class WarningsMiddleware { true ) } + null -> Unit } } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt index f77b716bc7..dee8fef67c 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt @@ -1,16 +1,13 @@ package com.tangem.tap.features.wallet.ui.adapters -import android.content.res.Resources import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.core.os.ConfigurationCompat import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.google.android.play.core.review.ReviewManagerFactory import com.tangem.tap.common.analytics.AnalyticsEvent -import com.tangem.tap.common.extensions.dispatchOpenUrl import com.tangem.tap.common.extensions.getActivity import com.tangem.tap.common.extensions.getColor import com.tangem.tap.common.extensions.getString @@ -19,7 +16,6 @@ import com.tangem.tap.common.extensions.show import com.tangem.tap.common.feedback.RateCanBeBetterEmail import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.configurable.warningMessage.WarningMessage -import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.store import com.tangem.wallet.R @@ -89,26 +85,12 @@ class WarningMessageVH(val binding: LayoutWarningCardActionBinding) : RecyclerVi binding.btnClose.hide() val buttonAction = - when { - warning.titleResId == R.string.warning_important_security_info -> { + when (warning.titleResId) { + R.string.warning_important_security_info -> { View.OnClickListener { store.dispatch(WalletAction.DialogAction.SignedHashesMultiWalletDialog) } } - warning.messageResId == R.string.alert_funds_restoration_message -> { - binding.btnClose.show() - binding.btnClose.setOnClickListener { - store.dispatch(GlobalAction.HideWarningMessage(warning)) - store.dispatch(WalletAction.Warnings.RestoreFundsWarningClosed) - } - val locale = ConfigurationCompat - .getLocales(Resources.getSystem().configuration) - .get(0) - val url = WarningMessagesManager.getRestoreFundsGuideUrl(locale.language) - View.OnClickListener { - store.dispatchOpenUrl(url) - } - } else -> { View.OnClickListener { store.dispatch(GlobalAction.HideWarningMessage(warning)) diff --git a/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt b/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt index f676467852..15073fb931 100644 --- a/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt +++ b/app/src/main/java/com/tangem/tap/persistence/PreferencesStorage.kt @@ -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.* +import java.util.Calendar class PreferencesStorage(applicationContext: Application) { @@ -53,20 +53,11 @@ class PreferencesStorage(applicationContext: Application) { preferences.edit { putInt(APP_LAUNCH_COUNT_KEY, ++count) } } - fun wasRestoreFundsWarningClosed(): Boolean { - return preferences.getBoolean(RESTORE_FUNDS_CLOSED_KEY, false) - } - - fun saveRestoreFundsWarningClosed() { - preferences.edit { putBoolean(RESTORE_FUNDS_CLOSED_KEY, true) } - } - companion object { private const val PREFERENCES_NAME = "tapPrefs" 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 RESTORE_FUNDS_CLOSED_KEY = "restoreFundsClosed" } } From 6989986c9e7eba05b3880be79d7479e915053c0f Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 15 Jul 2022 12:38:49 +0300 Subject: [PATCH 27/28] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/common/feedback/FeedbackManager.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/tangem/tap/common/feedback/FeedbackManager.kt b/app/src/main/java/com/tangem/tap/common/feedback/FeedbackManager.kt index dccb805723..4e551608e5 100644 --- a/app/src/main/java/com/tangem/tap/common/feedback/FeedbackManager.kt +++ b/app/src/main/java/com/tangem/tap/common/feedback/FeedbackManager.kt @@ -12,6 +12,7 @@ import java.io.FileWriter import java.io.StringWriter import timber.log.Timber import zendesk.configurations.Configuration +import zendesk.core.AnonymousIdentity import zendesk.core.Zendesk import zendesk.support.Support import zendesk.support.request.RequestConfiguration @@ -36,6 +37,7 @@ class FeedbackManager( /* oauthClientId = */ zendeskConfig.clientId, ) Support.INSTANCE.init(Zendesk.INSTANCE) + Zendesk.INSTANCE.setIdentity(AnonymousIdentity()) } fun sendEmail(feedbackData: FeedbackData, onFail: ((Exception) -> Unit)? = null) { From 3aff4c7e9e00d802ec0f1d82b7380c3122a3a058 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 15 Jul 2022 13:28:19 +0300 Subject: [PATCH 28/28] Updated on 2026-08-14 --- app/src/main/res/layout/fragment_wallet.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/res/layout/fragment_wallet.xml b/app/src/main/res/layout/fragment_wallet.xml index 1545257fc9..e77908ee26 100644 --- a/app/src/main/res/layout/fragment_wallet.xml +++ b/app/src/main/res/layout/fragment_wallet.xml @@ -174,6 +174,8 @@ android:layout_height="wrap_content" android:visibility="gone" android:layout_marginTop="30dp" + android:layout_marginStart="16dp" + android:layout_marginEnd="16dp" app:layout_constraintTop_toBottomOf="@id/l_address" app:layout_constraintVertical_bias="1" app:layout_constraintBottom_toBottomOf="parent" /> @@ -185,6 +187,8 @@ android:layout_height="wrap_content" android:visibility="gone" android:layout_marginTop="30dp" + android:layout_marginStart="16dp" + android:layout_marginEnd="16dp" app:layout_constraintTop_toBottomOf="@id/l_address" app:layout_constraintVertical_bias="1" app:layout_constraintBottom_toBottomOf="parent" />