diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 794eed1b33..051cb803b8 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -32,6 +32,7 @@ import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository import com.tangem.domain.card.ScanCardProcessor import com.tangem.domain.card.repository.CardRepository import com.tangem.domain.common.LogConfig +import com.tangem.domain.feedback.FeedbackManagerFeatureToggles import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase import com.tangem.domain.tokens.repository.CurrenciesRepository @@ -174,6 +175,9 @@ internal class TapApplication : Application(), ImageLoaderFactory { @Inject lateinit var cardRepository: CardRepository + + @Inject + lateinit var feedbackManagerFeatureToggles: FeedbackManagerFeatureToggles // endregion Injected override fun onCreate() { @@ -261,6 +265,7 @@ internal class TapApplication : Application(), ImageLoaderFactory { wasTwinsOnboardingShownUseCase = wasTwinsOnboardingShownUseCase, saveTwinsOnboardingShownUseCase = saveTwinsOnboardingShownUseCase, cardRepository = cardRepository, + feedbackManagerFeatureToggles = feedbackManagerFeatureToggles, ), ), ) diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/Onboarding.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/Onboarding.kt index 9088608413..f35a636601 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/Onboarding.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/Onboarding.kt @@ -87,8 +87,6 @@ sealed class Onboarding( class SetupFinished : Twins("Twin Setup Finished") } - class ButtonChat : Onboarding("Onboarding", "Button - Chat") - class EnableBiometrics(state: AnalyticsParam.OnOffState) : Onboarding( category = "Onboarding / Biometric", event = "Enable Biometric", diff --git a/app/src/main/java/com/tangem/tap/common/feedback/DefaultFeedbackManagerFeatureToggles.kt b/app/src/main/java/com/tangem/tap/common/feedback/DefaultFeedbackManagerFeatureToggles.kt new file mode 100644 index 0000000000..85acfd9af6 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/feedback/DefaultFeedbackManagerFeatureToggles.kt @@ -0,0 +1,12 @@ +package com.tangem.tap.common.feedback + +import com.tangem.core.featuretoggle.manager.FeatureTogglesManager +import com.tangem.domain.feedback.FeedbackManagerFeatureToggles + +internal class DefaultFeedbackManagerFeatureToggles( + private val featureTogglesManager: FeatureTogglesManager, +) : FeedbackManagerFeatureToggles { + + override val isLocalLogsEnabled: Boolean + get() = featureTogglesManager.isFeatureEnabled(name = "LOCAL_USER_LOGS_ENABLED") +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/di/FeedbackManagerFeatureTogglesModule.kt b/app/src/main/java/com/tangem/tap/di/FeedbackManagerFeatureTogglesModule.kt new file mode 100644 index 0000000000..4202ba1af8 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/di/FeedbackManagerFeatureTogglesModule.kt @@ -0,0 +1,23 @@ +package com.tangem.tap.di + +import com.tangem.core.featuretoggle.manager.FeatureTogglesManager +import com.tangem.domain.feedback.FeedbackManagerFeatureToggles +import com.tangem.tap.common.feedback.DefaultFeedbackManagerFeatureToggles +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal object FeedbackManagerFeatureTogglesModule { + + @Provides + @Singleton + fun provideFeedbackManagerFeatureToggles( + featureTogglesManager: FeatureTogglesManager, + ): FeedbackManagerFeatureToggles { + return DefaultFeedbackManagerFeatureToggles(featureTogglesManager) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/di/data/CardSDKLoggerModule.kt b/app/src/main/java/com/tangem/tap/di/data/CardSDKLoggerModule.kt index 89e6a89d62..6da93efeb4 100644 --- a/app/src/main/java/com/tangem/tap/di/data/CardSDKLoggerModule.kt +++ b/app/src/main/java/com/tangem/tap/di/data/CardSDKLoggerModule.kt @@ -32,6 +32,7 @@ internal object CardSDKLoggerModule { Log.Level.View, Log.Level.Network, Log.Level.Error, + Log.Level.Biometric, ) return TangemCardSDKLogger( diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DelegatedKeystoreManager.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DelegatedKeystoreManager.kt index 6073f2de68..3c548bd255 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DelegatedKeystoreManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DelegatedKeystoreManager.kt @@ -8,11 +8,15 @@ internal class DelegatedKeystoreManager( private val keystoreManagerProvider: Provider, ) : KeystoreManager { - override suspend fun authenticateAndGetKey(keyAlias: String): SecretKey? { - return keystoreManagerProvider().authenticateAndGetKey(keyAlias) + override suspend fun get(keyAlias: String): SecretKey? { + return keystoreManagerProvider().get(keyAlias) } - override suspend fun storeKey(keyAlias: String, key: SecretKey) { - keystoreManagerProvider().storeKey(keyAlias, key) + override suspend fun get(keyAliases: Collection): Map { + return keystoreManagerProvider().get(keyAliases) + } + + override suspend fun store(keyAlias: String, key: SecretKey) { + return keystoreManagerProvider().store(keyAlias, key) } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/BiometricUserWalletsKeysRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/BiometricUserWalletsKeysRepository.kt index 17c733dd35..0b06c9565f 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/BiometricUserWalletsKeysRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/BiometricUserWalletsKeysRepository.kt @@ -81,41 +81,38 @@ internal class BiometricUserWalletsKeysRepository( } private suspend fun getAllInternal(): CompletionResult> { - return getUserWalletsIds() - .map { userWalletId -> - getEncryptionKey(userWalletId) - .doOnFailure { error -> - when (error) { - is TangemSdkError.UserCanceledAuthentication -> { - // If the user cancels biometric authentication, then cancel operation with error - return CompletionResult.Failure(error) - } - is TangemSdkError.KeystoreInvalidated -> { - // If the biometric cryptography key was invalidated, - // then delete all user wallets encryption keys and cancel operation with error - getUserWalletsIds().forEach { userWalletId -> - deleteEncryptionKey(userWalletId) - } + return catching { + val userWalletIds = getUserWalletsIds() - return CompletionResult.Failure(error) - } + getEncryptionKeys(userWalletIds) + } + .doOnFailure { error -> + when (error) { + is TangemSdkError.KeystoreInvalidated -> { + // If the biometric cryptography key was invalidated, then delete all encryption keys + getUserWalletsIds().forEach { userWalletId -> + deleteEncryptionKey(userWalletId) } } - } - .fold(listOf()) { acc, data -> - if (data != null) acc + data else acc + else -> Unit + } } } - private suspend fun getEncryptionKey(userWalletId: UserWalletId): CompletionResult { - return catching { authenticatedStorage.get(StorageKey.UserWalletEncryptionKey(userWalletId).name) } - .map { it.decodeToKey() } + private suspend fun getEncryptionKeys(userWalletsIds: List): List { + val keys = userWalletsIds.map { userWalletId -> + StorageKey.UserWalletEncryptionKey(userWalletId).name + } + + return authenticatedStorage.get(keys).mapNotNull { (_, encodedData) -> + encodedData.decodeToKey() + } } private suspend fun storeEncryptionKey(encryptionKey: UserWalletEncryptionKey): CompletionResult { return catching { authenticatedStorage.store( - key = StorageKey.UserWalletEncryptionKey(encryptionKey.walletId).name, + keyAlias = StorageKey.UserWalletEncryptionKey(encryptionKey.walletId).name, data = encryptionKey.encode(), ) } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingMenuProvider.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingMenuProvider.kt index b979fd2d8c..936a202358 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingMenuProvider.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingMenuProvider.kt @@ -5,7 +5,7 @@ import android.view.MenuInflater import android.view.MenuItem import androidx.core.view.MenuProvider import com.tangem.core.analytics.Analytics -import com.tangem.tap.common.analytics.events.Onboarding +import com.tangem.core.analytics.models.Basic import com.tangem.tap.common.feedback.SupportInfo import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.store @@ -21,7 +21,7 @@ class OnboardingMenuProvider : MenuProvider { override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) { R.id.menu_item_chat_support -> { - Analytics.send(Onboarding.ButtonChat()) + Analytics.send(Basic.ButtonSupport()) // changed on email support [REDACTED_TASK_KEY] store.dispatch(GlobalAction.SendEmail(SupportInfo())) true diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt index dd07c89355..0655d01aa9 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt @@ -22,6 +22,7 @@ import com.tangem.common.CardIdFormatter import com.tangem.common.CompletionResult import com.tangem.common.core.CardIdDisplayFormat import com.tangem.core.analytics.Analytics +import com.tangem.core.analytics.models.Basic import com.tangem.core.ui.extensions.setStatusBarColor import com.tangem.domain.common.util.cardTypesResolver import com.tangem.feature.onboarding.data.model.CreateWalletResponse @@ -501,6 +502,7 @@ class OnboardingWalletFragment : private fun makeSeedPhraseRouter(): SeedPhraseRouter = SeedPhraseRouter( onBack = ::legacyOnBackHandler, onOpenChat = { + Analytics.send(Basic.ButtonSupport()) // changed on email support [REDACTED_TASK_KEY] store.dispatch(GlobalAction.SendEmail(SupportInfo())) }, diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/WalletActivationErrorDialog.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/WalletActivationErrorDialog.kt index cfabd25957..ddec9cdfb3 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/WalletActivationErrorDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/WalletActivationErrorDialog.kt @@ -3,6 +3,8 @@ package com.tangem.tap.features.onboarding.products.wallet.ui.dialogs import android.app.Dialog import android.content.Context import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.tangem.core.analytics.Analytics +import com.tangem.core.analytics.models.Basic import com.tangem.tap.common.extensions.dispatchDialogHide import com.tangem.tap.common.feedback.SupportInfo import com.tangem.tap.common.redux.global.GlobalAction @@ -19,6 +21,7 @@ object WalletActivationErrorDialog { setPositiveButton(R.string.common_ok) { _, _ -> dialog.onConfirm() } setNegativeButton(R.string.common_support) { _, _ -> // changed on email support [REDACTED_TASK_KEY] + Analytics.send(Basic.ButtonSupport()) store.dispatch(GlobalAction.SendEmail(SupportInfo())) } setOnDismissListener { store.dispatchDialogHide() } diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/RequestFeeErrorDialog.kt b/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/RequestFeeErrorDialog.kt index cc67c24921..9cf4359f72 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/RequestFeeErrorDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/RequestFeeErrorDialog.kt @@ -2,6 +2,8 @@ package com.tangem.tap.features.send.ui.dialogs import android.content.Context import androidx.appcompat.app.AlertDialog +import com.tangem.core.analytics.Analytics +import com.tangem.core.analytics.models.Basic import com.tangem.tap.common.feedback.SendTransactionFailedEmail import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.features.send.redux.SendAction @@ -19,6 +21,7 @@ object RequestFeeErrorDialog { setTitle(R.string.common_fee_error) setMessage(context.getString(R.string.alert_failed_to_send_transaction_message, errorMessage)) setNegativeButton(R.string.details_row_title_contact_to_support) { _, _ -> + Analytics.send(Basic.ButtonSupport()) store.dispatch(GlobalAction.SendEmail(SendTransactionFailedEmail(errorMessage))) } setPositiveButton(R.string.common_retry) { _, _ -> dialog.onRetry() } 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 4c300f7aeb..f862ff5e94 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 @@ -5,6 +5,8 @@ import androidx.appcompat.app.AlertDialog import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.BlockchainSdkError import com.tangem.common.module.ModuleMessageConverter +import com.tangem.core.analytics.Analytics +import com.tangem.core.analytics.models.Basic import com.tangem.sdk.extensions.localizedDescription import com.tangem.tap.common.extensions.stripZeroPlainString import com.tangem.tap.common.feedback.SendTransactionFailedEmail @@ -31,6 +33,7 @@ object SendTransactionFailsDialog { setTitle(R.string.alert_failed_to_send_transaction_title) setMessage(context.getString(R.string.alert_failed_to_send_transaction_message, errorMessage)) setNeutralButton(R.string.details_row_title_contact_to_support) { _, _ -> + Analytics.send(Basic.ButtonSupport()) store.dispatch(GlobalAction.SendEmail(SendTransactionFailedEmail(errorMessage))) } setPositiveButton(R.string.common_cancel) { _, _ -> } diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/data/DefaultTokensListRepository.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/data/DefaultTokensListRepository.kt index 114990f05b..ca1cf6190f 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/data/DefaultTokensListRepository.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/data/DefaultTokensListRepository.kt @@ -29,7 +29,7 @@ internal class DefaultTokensListRepository( private val testnetTokensStorage: TestnetTokensStorage, ) : TokensListRepository { - override fun getAvailableTokens(searchText: String?): Flow> { + override fun getAvailableTokens(searchText: String?, needFilterExcluded: Boolean): Flow> { return Pager( config = PagingConfig( pageSize = 100, @@ -42,6 +42,7 @@ internal class DefaultTokensListRepository( dispatchers = dispatchers, getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, searchText = searchText, + needFilterExcluded = needFilterExcluded, ) getSelectedWalletSyncUseCase().fold( diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/data/TangemApiTokensPagingSource.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/data/TangemApiTokensPagingSource.kt index 4fc0e646b3..acf336dac3 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/data/TangemApiTokensPagingSource.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/data/TangemApiTokensPagingSource.kt @@ -27,8 +27,10 @@ internal class TangemApiTokensPagingSource( private val dispatchers: CoroutineDispatcherProvider, private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, private val searchText: String?, + needFilterExcluded: Boolean = false, ) : PagingSource() { + private val coinsResponseConverter = CoinsResponseConverter(needFilterExcluded) override fun getRefreshKey(state: PagingState): Int? { return state.anchorPosition?.let { anchorPosition -> state.closestPageToPosition(anchorPosition)?.prevKey?.plus(other = 1) @@ -41,7 +43,7 @@ internal class TangemApiTokensPagingSource( return runCatching(dispatchers.io) { val supportedBlockchains = getSelectedWalletSyncUseCase().fold( - ifLeft = { Blockchain.values().toList() }, + ifLeft = { Blockchain.entries }, ifRight = { it.scanResponse.card.supportedBlockchains(it.scanResponse.cardTypesResolver) }, ) @@ -55,7 +57,7 @@ internal class TangemApiTokensPagingSource( }.fold( onSuccess = { response -> LoadResult.Page( - data = CoinsResponseConverter.convert(response), + data = coinsResponseConverter.convert(response), prevKey = if (page == 0) null else page.minus(other = 1), nextKey = if (response.coins.isEmpty()) null else page.plus(other = 1), ) diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/data/converters/CoinsResponseConverter.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/data/converters/CoinsResponseConverter.kt index d8c3de965b..2bffd45d01 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/data/converters/CoinsResponseConverter.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/data/converters/CoinsResponseConverter.kt @@ -3,6 +3,7 @@ package com.tangem.tap.features.tokens.impl.data.converters import com.tangem.blockchain.common.Blockchain import com.tangem.datasource.api.tangemTech.models.CoinsResponse import com.tangem.domain.common.extensions.fromNetworkId +import com.tangem.domain.common.extensions.isSupportedInApp import com.tangem.tap.features.tokens.impl.domain.models.Token import com.tangem.utils.converter.Converter @@ -11,9 +12,7 @@ import com.tangem.utils.converter.Converter * [REDACTED_AUTHOR] */ -internal object CoinsResponseConverter : Converter> { - - private const val DEFAULT_IMAGE_HOST = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/" +internal class CoinsResponseConverter(val needFilterExcluded: Boolean) : Converter> { override fun convert(value: CoinsResponse): List { return value.coins.map { token -> @@ -25,6 +24,10 @@ internal object CoinsResponseConverter : Converter> { networks = token.networks.mapNotNull { network -> val blockchain = Blockchain.fromNetworkId(network.networkId) ?: return@mapNotNull null + if (needFilterExcluded && !blockchain.isSupportedInApp()) { + return@mapNotNull null + } + // filter tokens, if contractAddress != null, assume that it is a token if (network.contractAddress != null && !blockchain.canHandleTokens() @@ -47,4 +50,8 @@ internal object CoinsResponseConverter : Converter> { fun getIconUrl(id: String, imageHost: String? = null): String { return "${imageHost ?: DEFAULT_IMAGE_HOST}large/$id.png" } + + private companion object { + const val DEFAULT_IMAGE_HOST = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/" + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/data/converters/TestnetTokensConfigConverter.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/data/converters/TestnetTokensConfigConverter.kt index 5a532d7012..d1babd19b7 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/data/converters/TestnetTokensConfigConverter.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/data/converters/TestnetTokensConfigConverter.kt @@ -13,13 +13,14 @@ import com.tangem.utils.converter.Converter */ internal object TestnetTokensConfigConverter : Converter> { + private val coinsResponseConverter = CoinsResponseConverter(needFilterExcluded = false) override fun convert(value: TestnetTokensConfig): List { return value.tokens.map { token -> Token( id = token.id, name = token.name, symbol = token.symbol, - iconUrl = CoinsResponseConverter.getIconUrl(token.id), + iconUrl = coinsResponseConverter.getIconUrl(token.id), networks = token.networks?.mapNotNull { network -> val blockchain = Blockchain.fromNetworkId(network.id) ?: return@mapNotNull null @@ -27,7 +28,7 @@ internal object TestnetTokensConfigConverter : Converter> { - return repository.getAvailableTokens(searchText = searchText.ifBlank(defaultValue = { null })) + override fun getTokensList(searchText: String, needFilterExcluded: Boolean): Flow> { + return repository.getAvailableTokens( + searchText = searchText.ifBlank(defaultValue = { null }), + needFilterExcluded = needFilterExcluded, + ) } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/domain/TokensListInteractor.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/domain/TokensListInteractor.kt index c363dfc3c2..4b8edb914d 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/domain/TokensListInteractor.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/domain/TokensListInteractor.kt @@ -12,5 +12,5 @@ import kotlinx.coroutines.flow.Flow internal interface TokensListInteractor { /** Get tokens list using filter by text [searchText] */ - fun getTokensList(searchText: String): Flow> + fun getTokensList(searchText: String, needFilterExcluded: Boolean): Flow> } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/domain/TokensListRepository.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/domain/TokensListRepository.kt index 8b2f16a644..67a9626b6b 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/domain/TokensListRepository.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/domain/TokensListRepository.kt @@ -15,8 +15,9 @@ internal interface TokensListRepository { * Get available tokens list * * @param searchText search text + * @param needFilterExcluded flag that determines if excluded tokens should be filtered * * @throws com.tangem.datasource.api.common.response.ApiResponseError */ - fun getAvailableTokens(searchText: String?): Flow> + fun getAvailableTokens(searchText: String?, needFilterExcluded: Boolean): Flow> } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt index 337e8f24ec..0853b72b8f 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt @@ -250,7 +250,7 @@ internal class TokensListViewModel @Inject constructor( } private fun getTokensListBySearchText(text: String): Flow> { - return interactor.getTokensList(searchText = text) + return interactor.getTokensList(searchText = text, needFilterExcluded = !isManageAccess) .mapToTokenItemState() .cachedIn(viewModelScope) } diff --git a/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt b/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt index 48823fcd71..4c7a8f4461 100644 --- a/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt +++ b/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt @@ -3,10 +3,12 @@ package com.tangem.tap.proxy import androidx.core.text.isDigitsOnly import com.google.firebase.crashlytics.FirebaseCrashlytics import com.tangem.Message +import com.tangem.blockchain.blockchains.algorand.AlgorandTransactionExtras import com.tangem.blockchain.blockchains.binance.BinanceTransactionExtras import com.tangem.blockchain.blockchains.cosmos.CosmosTransactionExtras import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager +import com.tangem.blockchain.blockchains.hedera.HederaTransactionBuilder import com.tangem.blockchain.blockchains.optimism.OptimismWalletManager import com.tangem.blockchain.blockchains.stellar.StellarMemo import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras @@ -138,6 +140,8 @@ class TransactionManagerImpl( Blockchain.XRP -> memo.toLongOrNull()?.let { XrpTransactionBuilder.XrpTransactionExtras(it) } Blockchain.Cosmos -> CosmosTransactionExtras(memo) Blockchain.TON -> TonTransactionExtras(memo) + Blockchain.Hedera -> HederaTransactionBuilder.HederaTransactionExtras(memo) + Blockchain.Algorand -> AlgorandTransactionExtras(memo) else -> null } } diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt index c2b7b52edd..0afb10ab8d 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt @@ -10,6 +10,7 @@ import com.tangem.domain.card.ScanCardProcessor import com.tangem.domain.card.ScanCardUseCase import com.tangem.domain.card.repository.CardRepository import com.tangem.domain.card.repository.CardSdkConfigRepository +import com.tangem.domain.feedback.FeedbackManagerFeatureToggles import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase import com.tangem.domain.tokens.repository.CurrenciesRepository @@ -65,4 +66,5 @@ data class DaggerGraphState( val wasTwinsOnboardingShownUseCase: WasTwinsOnboardingShownUseCase? = null, val saveTwinsOnboardingShownUseCase: SaveTwinsOnboardingShownUseCase? = null, val cardRepository: CardRepository? = null, + val feedbackManagerFeatureToggles: FeedbackManagerFeatureToggles? = null, ) : StateType \ No newline at end of file diff --git a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/Basic.kt b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/Basic.kt index 9e1da068ec..8820f8ff12 100644 --- a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/Basic.kt +++ b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/Basic.kt @@ -75,4 +75,6 @@ sealed class Basic( ) class WalletOpened : Basic(event = "Wallet Opened") + + class ButtonSupport : Basic("Request Support") } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/AccountCreatorModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/AccountCreatorModule.kt index 7abd08cf54..3b97ba24d9 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/AccountCreatorModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/AccountCreatorModule.kt @@ -16,7 +16,7 @@ internal object AccountCreatorModule { @Provides @Singleton - fun provideAccountCreator(authProvider: AuthProvider, @DevTangemApi tangemTechApi: TangemTechApi): AccountCreator { + fun provideAccountCreator(authProvider: AuthProvider, tangemTechApi: TangemTechApi): AccountCreator { return DefaultAccountCreator(authProvider, tangemTechApi) } } \ No newline at end of file diff --git a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json index 0180204de2..1b7b643cea 100644 --- a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json +++ b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json @@ -18,5 +18,9 @@ { "name": "GENERAL_USER_WALLETS_LIST_MANAGER_ENABLED", "version": "5.7.0" + }, + { + "name": "LOCAL_USER_LOGS_ENABLED", + "version": "5.8.0" } ] diff --git a/domain/legacy/src/main/java/com/tangem/domain/feedback/FeedbackManagerFeatureToggles.kt b/domain/legacy/src/main/java/com/tangem/domain/feedback/FeedbackManagerFeatureToggles.kt new file mode 100644 index 0000000000..2b259e17b6 --- /dev/null +++ b/domain/legacy/src/main/java/com/tangem/domain/feedback/FeedbackManagerFeatureToggles.kt @@ -0,0 +1,6 @@ +package com.tangem.domain.feedback + +interface FeedbackManagerFeatureToggles { + + val isLocalLogsEnabled: Boolean +} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetFeePaidCryptoCurrencyStatusSyncUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetFeePaidCryptoCurrencyStatusSyncUseCase.kt index 7b43c6221f..cd41c82702 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetFeePaidCryptoCurrencyStatusSyncUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetFeePaidCryptoCurrencyStatusSyncUseCase.kt @@ -3,7 +3,6 @@ package com.tangem.domain.tokens import arrow.core.Either import arrow.core.raise.either import com.tangem.domain.tokens.error.TokenListError -import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.FeePaidCurrency import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations @@ -35,17 +34,13 @@ class GetFeePaidCryptoCurrencyStatusSyncUseCase( return either { when (feePaidCurrency) { - FeePaidCurrency.Coin -> getCryptoCurrencyStatus(operations, cryptoCurrency.id) + FeePaidCurrency.Coin -> + operations + .getNetworkCoinSync(cryptoCurrency.network.id, cryptoCurrency.network.derivationPath) + .getOrNull() FeePaidCurrency.SameCurrency -> cryptoCurrencyStatus - is FeePaidCurrency.Token -> getCryptoCurrencyStatus(operations, feePaidCurrency.tokenId) + is FeePaidCurrency.Token -> operations.getCurrencyStatusSync(feePaidCurrency.tokenId).getOrNull() } } } - - private suspend fun getCryptoCurrencyStatus( - operations: CurrenciesStatusesOperations, - cryptoCurrencyId: CryptoCurrency.ID, - ): CryptoCurrencyStatus? { - return operations.getCurrencyStatusSync(cryptoCurrencyId).getOrNull() - } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt index 92d6969de0..fac9f7fc95 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt @@ -109,6 +109,18 @@ internal class CurrenciesStatusesOperations( } } + suspend fun getNetworkCoinSync( + networkId: Network.ID, + derivationPath: Network.DerivationPath, + ): Either { + val currency = recover( + block = { getNetworkCoin(networkId, derivationPath) }, + recover = { return it.left() }, + ) + + return getCurrencyStatusSync(currency.id) + } + suspend fun getPrimaryCurrencyStatusSync(): Either = either { val currency = catch( block = { currenciesRepository.getSingleCurrencyWalletPrimaryCurrency(userWalletId) }, diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt index fd57ba8927..cea2fca9cc 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/StateBuilder.kt @@ -915,10 +915,10 @@ internal class StateBuilder( ) } - fun addAlert(uiState: SwapStateHolder, onClick: () -> Unit): SwapStateHolder { + fun addAlert(uiState: SwapStateHolder, message: TextReference? = null, onClick: () -> Unit): SwapStateHolder { return uiState.copy( alert = SwapWarning.GenericWarning( - message = null, + message = message, onClick = onClick, ), ) diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt index e67ec959e9..a559fb5054 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/viewmodels/SwapViewModel.kt @@ -9,6 +9,7 @@ import arrow.core.getOrElse import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.analytics.models.Basic +import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.utils.InputNumberFormatter import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency @@ -119,7 +120,7 @@ internal class SwapViewModel @Inject constructor( val cryptoCurrencyStatus = getCryptoCurrencyStatusUseCase(it.walletId, initialCryptoCurrency.id).getOrNull() if (cryptoCurrencyStatus == null) { - uiState = stateBuilder.addAlert(uiState, swapRouter::back) + uiState = stateBuilder.addAlert(uiState = uiState, onClick = swapRouter::back) } else { initialCryptoCurrencyStatus = cryptoCurrencyStatus initTokens() @@ -482,7 +483,12 @@ internal class SwapViewModel @Inject constructor( return } val fromCurrency = requireNotNull(dataState.fromCryptoCurrency) - val fee = requireNotNull(dataState.selectedFee) + val fee = dataState.selectedFee + // TODO: unexpected crash for some users, this workaround to prevent app crash and follow to support + if (fee == null) { + makeDefaultAlert(TextReference.Str("Fee estimation error. Please send feedback to support.")) + return + } viewModelScope.launch(dispatchers.main) { runCatching(dispatchers.io) { swapInteractor.onSwap( @@ -821,6 +827,12 @@ internal class SwapViewModel @Inject constructor( } } + private fun makeDefaultAlert(message: TextReference) { + uiState = stateBuilder.addAlert(uiState, message) { + uiState = stateBuilder.clearAlert(uiState) + } + } + @Suppress("LongMethod", "CyclomaticComplexMethod") private fun createUiActions(): UiActions { return UiActions( diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 30a5d4ce98..4b9302bf6b 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -85,9 +85,9 @@ web3j = "4.10.1" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-510" +tangemBlockchainSdk = "develop-514" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "develop-324" +tangemCardSdk = "develop-332" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ # endregion Tangem