diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2791ba88f4..85ed2fdfb3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -76,11 +76,10 @@ dependencies { implementation(projects.data.txhistory) implementation(projects.data.wallets) implementation(projects.data.analytics) + implementation(projects.data.transaction) /** Features */ implementation(projects.features.onboarding) - implementation(projects.features.learn2earn.api) - implementation(projects.features.learn2earn.impl) implementation(projects.features.referral.presentation) implementation(projects.features.referral.domain) implementation(projects.features.referral.data) @@ -99,6 +98,8 @@ dependencies { implementation(projects.features.manageTokens.api) implementation(projects.features.manageTokens.impl) implementation(projects.features.send.impl) + implementation(projects.features.qrScanning.api) + implementation(projects.features.qrScanning.impl) /** AndroidX libraries */ implementation(deps.androidx.core.ktx) @@ -146,6 +147,7 @@ dependencies { /** DI */ implementation(deps.hilt.android) + kapt(deps.hilt.kapt) /** Other libraries */ @@ -158,7 +160,6 @@ dependencies { implementation(deps.timber) implementation(deps.reKotlin) implementation(deps.zxing.qrCore) - implementation(deps.otaliastudiosCameraView) implementation(deps.coil) implementation(deps.appsflyer) implementation(deps.amplitude) @@ -186,7 +187,9 @@ dependencies { } /** Testing libraries */ + testImplementation(deps.test.coroutine) testImplementation(deps.test.junit) + testImplementation(deps.test.mockk) testImplementation(deps.test.truth) androidTestImplementation(deps.test.junit.android) androidTestImplementation(deps.test.espresso) @@ -208,7 +211,7 @@ dependencies { /** Excluded dependencies */ implementation("com.google.guava:guava:30.0-android") { // excludes version 9999.0-empty-to-avoid-conflict-with-guava - exclude(group="com.google.guava", module = "listenablefuture") + exclude(group = "com.google.guava", module = "listenablefuture") } } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3eb4bc059b..32e44932c0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -137,14 +137,6 @@ - - - - @@ -206,6 +210,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac manageTokensRouter = manageTokensRouter, cardSdkConfigRepository = cardSdkConfigRepository, sendRouter = sendRouter, + qrScanningRouter = qrScanningRouter, ), ) } @@ -247,20 +252,15 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac private fun createAppThemeModeFlow(): SharedFlow { val tapApplication = application as TapApplication - val featureToggle = tapApplication.darkThemeFeatureToggle - return if (featureToggle.isDarkThemeEnabled) { - tapApplication.getAppThemeModeUseCase() - .map { maybeMode -> - maybeMode.getOrElse { AppThemeMode.DEFAULT } - } - .shareIn( - scope = lifecycleScope + Dispatchers.IO, - started = SharingStarted.WhileSubscribed(stopTimeoutMillis = 5_000), - ) - } else { - MutableStateFlow(AppThemeMode.FORCE_LIGHT) - } + return tapApplication.getAppThemeModeUseCase() + .map { maybeMode -> + maybeMode.getOrElse { AppThemeMode.DEFAULT } + } + .shareIn( + scope = lifecycleScope + Dispatchers.IO, + started = SharingStarted.WhileSubscribed(stopTimeoutMillis = 5_000), + ) } override fun onStart() { @@ -454,7 +454,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac private fun checkForNotificationPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && - BuildConfig.DEBUG && + BuildConfig.LOG_ENABLED && ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED ) { diff --git a/app/src/main/java/com/tangem/tap/NavBarInsetsFragmentLifecycleCallback.kt b/app/src/main/java/com/tangem/tap/NavBarInsetsFragmentLifecycleCallback.kt index cef921c1d6..fefb785d4c 100644 --- a/app/src/main/java/com/tangem/tap/NavBarInsetsFragmentLifecycleCallback.kt +++ b/app/src/main/java/com/tangem/tap/NavBarInsetsFragmentLifecycleCallback.kt @@ -11,6 +11,7 @@ import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager.FragmentLifecycleCallbacks class NavBarInsetsFragmentLifecycleCallback : FragmentLifecycleCallbacks() { + override fun onFragmentViewCreated(fm: FragmentManager, f: Fragment, v: View, savedInstanceState: Bundle?) { if (v is ComposeView) return diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 0aecd2719a..f892dddee4 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -10,8 +10,6 @@ import com.orhanobut.logger.AndroidLogAdapter import com.orhanobut.logger.Logger import com.tangem.Log import com.tangem.LogFormat -import com.tangem.blockchain.common.BlockchainSdkConfig -import com.tangem.blockchain.common.WalletManagerFactory import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.filter.OneTimeEventFilter @@ -30,23 +28,22 @@ import com.tangem.domain.apptheme.GetAppThemeModeUseCase import com.tangem.domain.apptheme.repository.AppThemeModeRepository import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository import com.tangem.domain.card.ScanCardProcessor +import com.tangem.domain.card.repository.DerivationsRepository import com.tangem.domain.common.LogConfig import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.legacy.UserWalletsListManager -import com.tangem.domain.wallets.legacy.WalletManagersRepository import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.features.managetokens.featuretoggles.ManageTokensFeatureToggles import com.tangem.features.send.api.featuretoggles.SendFeatureToggles -import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles +import com.tangem.features.tester.api.TesterFeatureToggles import com.tangem.tap.common.analytics.AnalyticsFactory import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder import com.tangem.tap.common.analytics.handlers.BlockchainExceptionHandler import com.tangem.tap.common.analytics.handlers.amplitude.AmplitudeAnalyticsHandler import com.tangem.tap.common.analytics.handlers.appsFlyer.AppsFlyerAnalyticsHandler import com.tangem.tap.common.analytics.handlers.firebase.FirebaseAnalyticsHandler -import com.tangem.tap.common.analytics.topup.TopUpController import com.tangem.tap.common.chat.ChatManager import com.tangem.tap.common.feedback.AdditionalFeedbackInfo import com.tangem.tap.common.feedback.FeedbackManager @@ -59,24 +56,11 @@ import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.shop.TangemShopService import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager import com.tangem.tap.domain.tasks.product.DerivationsFinder -import com.tangem.tap.domain.tokens.UserTokensRepository -import com.tangem.tap.domain.tokens.UserTokensStorageService -import com.tangem.tap.domain.totalBalance.TotalFiatBalanceCalculator -import com.tangem.tap.domain.totalBalance.di.provideDefaultImplementation import com.tangem.tap.domain.userWalletList.di.provideBiometricImplementation import com.tangem.tap.domain.userWalletList.di.provideRuntimeImplementation -import com.tangem.tap.domain.walletCurrencies.WalletCurrenciesManager -import com.tangem.tap.domain.walletCurrencies.di.provideDefaultImplementation -import com.tangem.tap.domain.walletStores.WalletStoresManager -import com.tangem.tap.domain.walletStores.di.provideDefaultImplementation -import com.tangem.tap.domain.walletStores.repository.WalletAmountsRepository -import com.tangem.tap.domain.walletStores.repository.WalletStoresRepository -import com.tangem.tap.domain.walletStores.repository.di.provideDefaultImplementation import com.tangem.tap.domain.walletconnect.WalletConnectRepository import com.tangem.tap.domain.walletconnect2.domain.WalletConnectSessionsRepository import com.tangem.tap.features.customtoken.api.featuretoggles.CustomTokenFeatureToggles -import com.tangem.tap.features.details.DarkThemeFeatureToggle -import com.tangem.tap.features.details.featuretoggles.DetailsFeatureToggles import com.tangem.tap.proxy.AppStateHolder import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider @@ -95,47 +79,8 @@ lateinit var activityResultCaller: ActivityResultCaller lateinit var preferencesStorage: PreferencesDataSource lateinit var walletConnectRepository: WalletConnectRepository lateinit var shopService: TangemShopService -internal lateinit var userTokensRepository: UserTokensRepository internal lateinit var derivationsFinder: DerivationsFinder -private val walletStoresRepository by lazy { WalletStoresRepository.provideDefaultImplementation() } -private val walletManagersRepository by lazy { - WalletManagersRepository.provideDefaultImplementation( - walletManagerFactory = WalletManagerFactory( - config = store.state.globalState.configManager - ?.config - ?.blockchainSdkConfig - ?: BlockchainSdkConfig(), - ), - ) -} -private val walletAmountsRepository by lazy { - WalletAmountsRepository.provideDefaultImplementation( - tangemTechService = store.state.domainNetworks.tangemTechService, - ) -} -val walletStoresManager by lazy { - WalletStoresManager.provideDefaultImplementation( - userTokensRepository = userTokensRepository, - walletStoresRepository = walletStoresRepository, - walletManagersRepository = walletManagersRepository, - walletAmountsRepository = walletAmountsRepository, - appCurrencyProvider = { store.state.globalState.appCurrency }, - ) -} -val walletCurrenciesManager by lazy { - WalletCurrenciesManager.provideDefaultImplementation( - userTokensRepository = userTokensRepository, - walletStoresRepository = walletStoresRepository, - walletManagersRepository = walletManagersRepository, - walletAmountsRepository = walletAmountsRepository, - appCurrencyProvider = { store.state.globalState.appCurrency }, - ) -} -val totalFiatBalanceCalculator by lazy { - TotalFiatBalanceCalculator.provideDefaultImplementation() -} - @HiltAndroidApp internal class TapApplication : Application(), ImageLoaderFactory { @@ -161,18 +106,12 @@ internal class TapApplication : Application(), ImageLoaderFactory { @Inject lateinit var preferencesDataSource: PreferencesDataSource - @Inject - lateinit var walletFeatureToggles: WalletFeatureToggles - @Inject lateinit var walletConnect2Repository: WalletConnect2Repository @Inject lateinit var walletConnectSessionsRepository: WalletConnectSessionsRepository - // @Inject - // lateinit var learn2earnInteractor: Learn2earnInteractor - @Inject lateinit var manageTokensFeatureToggles: ManageTokensFeatureToggles @@ -200,12 +139,6 @@ internal class TapApplication : Application(), ImageLoaderFactory { @Inject lateinit var balanceHidingRepository: BalanceHidingRepository - @Inject - lateinit var detailsFeatureToggles: DetailsFeatureToggles - - @Inject - lateinit var darkThemeFeatureToggle: DarkThemeFeatureToggle - @Inject lateinit var userTokensStore: UserTokensStore @@ -220,6 +153,12 @@ internal class TapApplication : Application(), ImageLoaderFactory { @Inject lateinit var oneTimeEventFilter: OneTimeEventFilter + + @Inject + lateinit var derivationsRepository: DerivationsRepository + + @Inject + lateinit var testerFeatureToggles: TesterFeatureToggles // endregion Injected override fun onCreate() { @@ -227,7 +166,7 @@ internal class TapApplication : Application(), ImageLoaderFactory { store = createReduxStore() - if (BuildConfig.DEBUG) { + if (BuildConfig.LOG_ENABLED) { Logger.addLogAdapter(AndroidLogAdapter(TimberFormatStrategy())) Timber.plant( object : Timber.DebugTree() { @@ -250,7 +189,6 @@ internal class TapApplication : Application(), ImageLoaderFactory { runBlocking { initUserWalletsListManager() featureTogglesManager.init() - // learn2earnInteractor.init() } val configLoader = FeaturesLocalLoader(assetReader, MoshiConverter.sdkMoshi, BuildConfig.ENVIRONMENT) @@ -266,23 +204,12 @@ internal class TapApplication : Application(), ImageLoaderFactory { ) } - val userTokensStorageService = UserTokensStorageService.init(context = this) - userTokensRepository = UserTokensRepository.init( - tangemTechService = store.state.domainNetworks.tangemTechService, - networkConnectionManager = networkConnectionManager, - storageService = userTokensStorageService, - ) derivationsFinder = DerivationsFinder( - legacyTokensStore = userTokensStorageService, newTokensStore = userTokensStore, - walletFeatureToggles = walletFeatureToggles, dispatchers = AppCoroutineDispatcherProvider(), ) appStateHolder.mainStore = store - appStateHolder.userTokensRepository = userTokensRepository - appStateHolder.walletStoresManager = walletStoresManager - initTopUpController() walletConnect2Repository.init(projectId = configManager.config.walletConnectProjectId) } @@ -292,10 +219,8 @@ internal class TapApplication : Application(), ImageLoaderFactory { middleware = AppState.getMiddleware(), state = AppState( daggerGraphState = DaggerGraphState( - assetReader = assetReader, networkConnectionManager = networkConnectionManager, customTokenFeatureToggles = customTokenFeatureToggles, - walletFeatureToggles = walletFeatureToggles, walletConnectRepository = walletConnect2Repository, walletConnectSessionsRepository = walletConnectSessionsRepository, manageTokensFeatureToggles = manageTokensFeatureToggles, @@ -307,26 +232,15 @@ internal class TapApplication : Application(), ImageLoaderFactory { currenciesRepository = currenciesRepository, appThemeModeRepository = appThemeModeRepository, balanceHidingRepository = balanceHidingRepository, - detailsFeatureToggles = detailsFeatureToggles, walletsRepository = walletsRepository, sendFeatureToggles = sendFeatureToggles, + derivationsRepository = derivationsRepository, + testerFeatureToggles = testerFeatureToggles, ), ), ) } - private fun initTopUpController() { - val topUpController = TopUpController( - scanResponseProvider = { - store.state.globalState.scanResponse - ?: store.state.globalState.onboardingState.onboardingManager?.scanResponse - }, - walletStoresManagerProvider = { walletStoresManager }, - topupWalletStorage = preferencesStorage.toppedUpWalletStorage, - ) - store.dispatch(GlobalAction.SetTopUpController(topUpController)) - } - override fun newImageLoader(): ImageLoader { return createCoilImageLoader( context = this, 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 11af89cb89..a7da950412 100644 --- a/app/src/main/java/com/tangem/tap/common/DialogManager.kt +++ b/app/src/main/java/com/tangem/tap/common/DialogManager.kt @@ -5,18 +5,13 @@ import android.content.Context import com.tangem.core.navigation.StateDialog import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.global.GlobalState -import com.tangem.tap.common.ui.SimpleAlertDialog -import com.tangem.tap.common.ui.SimpleCancelableAlertDialog +import com.tangem.tap.common.ui.* import com.tangem.tap.features.details.redux.walletconnect.WalletConnectDialog import com.tangem.tap.features.details.ui.walletconnect.dialogs.* -import com.tangem.tap.features.onboarding.AddressInfoBottomSheetDialog import com.tangem.tap.features.onboarding.OnboardingDialog import com.tangem.tap.features.onboarding.products.twins.ui.dialog.TwinningProcessNotCompletedDialog import com.tangem.tap.features.onboarding.products.wallet.redux.BackupDialog import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.* -import com.tangem.tap.features.wallet.redux.models.WalletDialog -import com.tangem.tap.features.wallet.ui.dialogs.* -import com.tangem.tap.features.wallet.ui.wallet.CurrencySelectionDialog import com.tangem.tap.store import com.tangem.wallet.R import org.rekotlin.StoreSubscriber @@ -50,13 +45,14 @@ class DialogManager : StoreSubscriber { if (dialog != null) return dialog = when (state.dialog) { - is AppDialog.SimpleOkDialog -> SimpleOkDialog.create(state.dialog, context) is AppDialog.SimpleOkDialogRes -> SimpleOkDialog.create(state.dialog, context) - is AppDialog.SimpleOkErrorDialog -> SimpleOkDialog.create(state.dialog, context) - is AppDialog.SimpleOkWarningDialog -> SimpleOkDialog.create(state.dialog, context) is StateDialog.ScanFailsDialog -> ScanFailsDialog.create(context) is AppDialog.AddressInfoDialog -> AddressInfoBottomSheetDialog(state.dialog, context) is AppDialog.TestActionsDialog -> TestActionsBottomSheetDialog(state.dialog, context) + is AppDialog.RussianCardholdersWarningDialog -> RussianCardholdersWarningBottomSheetDialog( + context, + state.dialog.data, + ) is OnboardingDialog.TwinningProcessNotCompleted -> TwinningProcessNotCompletedDialog.create(context) is OnboardingDialog.InterruptOnboarding -> InterruptOnboardingDialog.create(context, state.dialog) is WalletConnectDialog.UnsupportedCard -> @@ -124,6 +120,7 @@ class DialogManager : StoreSubscriber { onReject = state.dialog.onReject, ) } + is BackupDialog.AttestationFailed -> AttestationFailedDialog.create(context) is BackupDialog.AddMoreBackupCards -> AddMoreBackupCardsDialog.create(context) is BackupDialog.BackupInProgress -> BackupInProgressDialog.create(context) is BackupDialog.UnfinishedBackupFound -> UnfinishedBackupFoundDialog.create(context) @@ -132,11 +129,7 @@ class DialogManager : StoreSubscriber { context = context, cardId = state.dialog.cardId, ) - is WalletDialog.CurrencySelectionDialog -> CurrencySelectionDialog.create(state.dialog, context) - is WalletDialog.ChooseTradeActionDialog -> ChooseTradeActionBottomSheetDialog(context, state.dialog) - is WalletDialog.SelectAmountToSendDialog -> AmountToSendBottomSheetDialog(context, state.dialog) - is WalletDialog.SignedHashesMultiWalletDialog -> SignedHashesWarningDialog.create(context) - is WalletDialog.TokensAreLinkedDialog -> SimpleAlertDialog.create( + is AppDialog.TokensAreLinkedDialog -> SimpleAlertDialog.create( title = context.getString(state.dialog.titleRes, state.dialog.currencySymbol), message = context.getString( state.dialog.messageRes, @@ -145,15 +138,13 @@ class DialogManager : StoreSubscriber { ), context = context, ) - is WalletDialog.RemoveWalletDialog -> SimpleCancelableAlertDialog.create( + is AppDialog.RemoveWalletDialog -> SimpleCancelableAlertDialog.create( title = context.getString(state.dialog.titleRes, state.dialog.currencyTitle), messageRes = state.dialog.messageRes, context = context, primaryButtonRes = state.dialog.primaryButtonRes, primaryButtonAction = state.dialog.onOk, ) - is WalletDialog.RussianCardholdersWarningDialog -> - RussianCardholdersWarningBottomSheetDialog(context, state.dialog.data) else -> null } dialog?.show() diff --git a/app/src/main/java/com/tangem/tap/common/GlobalLayoutStateHandler.kt b/app/src/main/java/com/tangem/tap/common/GlobalLayoutStateHandler.kt index 298bc14995..0a51404cf9 100644 --- a/app/src/main/java/com/tangem/tap/common/GlobalLayoutStateHandler.kt +++ b/app/src/main/java/com/tangem/tap/common/GlobalLayoutStateHandler.kt @@ -20,7 +20,7 @@ class GlobalLayoutStateHandler( if (attachImmediately) attach() } - fun attach() { + private fun attach() { if (isAttached) { Timber.d("Already attached") return diff --git a/app/src/main/java/com/tangem/tap/common/Handler.kt b/app/src/main/java/com/tangem/tap/common/Handler.kt index 37b39734cb..7842da9fbf 100644 --- a/app/src/main/java/com/tangem/tap/common/Handler.kt +++ b/app/src/main/java/com/tangem/tap/common/Handler.kt @@ -11,20 +11,6 @@ fun postUi(ms: Long = 0, func: Runnable) { if (ms == 0L) uiHandler.post { func.run() } else uiHandler.postDelayed(func, ms) } -fun postBackground(ms: Long = 0, func: Runnable) { - if (ms == 0L) backgroundHandler.post { func.run() } else backgroundHandler.postDelayed(func, ms) -} - fun postUiDelayBg(ms: Long, func: Runnable) { backgroundHandler.postDelayed({ uiHandler.post(func) }, ms) -} - -fun post(ms: Long = 0, func: Runnable) { - if (ms == 0L) { - func.run() - } else { - val currentLooper = Looper.myLooper() ?: return - - Handler(currentLooper).postDelayed(func, ms) - } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/ShimmerRecyclerAdapter.kt b/app/src/main/java/com/tangem/tap/common/ShimmerRecyclerAdapter.kt deleted file mode 100644 index ff09e08de7..0000000000 --- a/app/src/main/java/com/tangem/tap/common/ShimmerRecyclerAdapter.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.tangem.tap.common - -import android.view.ViewGroup -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView - -/** -[REDACTED_AUTHOR] - */ -open class ShimmerRecyclerAdapter( - private val viewHolderViewFactory: (ViewGroup) -> ViewGroup, -) : ListAdapter(DiffUtilCallback) { - - override fun getItemId(position: Int): Long { - return if (currentList.isEmpty()) 0 else currentList[position].hashCode().toLong() - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ShimmerVH { - return ShimmerVH(viewHolderViewFactory.invoke(parent)) - } - - override fun onBindViewHolder(holder: ShimmerVH, position: Int) {} - - object DiffUtilCallback : DiffUtil.ItemCallback() { - override fun areContentsTheSame(oldItem: ShimmerData, newItem: ShimmerData) = oldItem == newItem - override fun areItemsTheSame(oldItem: ShimmerData, newItem: ShimmerData) = oldItem == newItem - } -} - -class ShimmerVH(viewGroup: ViewGroup) : RecyclerView.ViewHolder(viewGroup) - -@Suppress("UnusedPrivateMember") -data class ShimmerData(private val any: String = "") \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/TestActions.kt b/app/src/main/java/com/tangem/tap/common/TestActions.kt index b787e7a1b3..955b89e867 100644 --- a/app/src/main/java/com/tangem/tap/common/TestActions.kt +++ b/app/src/main/java/com/tangem/tap/common/TestActions.kt @@ -8,11 +8,8 @@ import androidx.appcompat.widget.LinearLayoutCompat import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.tangem.tap.common.extensions.dispatchDialogHide -import com.tangem.tap.common.extensions.dispatchDialogShow -import com.tangem.tap.common.extensions.show import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.store -import com.tangem.wallet.BuildConfig /** [REDACTED_AUTHOR] @@ -22,18 +19,6 @@ object TestActions { // It used only for the test actions in debug or debug_beta builds var testAmountInjectionForWalletManagerEnabled = false - - /** - * @param isTestView - true must be used if you want to show or hide your view depends on BuildConfig - */ - fun initFor(view: View, actions: List, isTestView: Boolean = false) { - if (!BuildConfig.TEST_ACTION_ENABLED) return - if (isTestView) view.show(BuildConfig.TEST_ACTION_ENABLED) - - view.setOnClickListener { - store.dispatchDialogShow(AppDialog.TestActionsDialog(actions)) - } - } } typealias TestAction = Pair Unit> diff --git a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsFactory.kt b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsFactory.kt index ed2e3868b5..e6383007c0 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsFactory.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsFactory.kt @@ -22,10 +22,6 @@ class AnalyticsFactory { filters.add(filter) } - fun addParamsInterceptor(interceptor: ParamsInterceptor) { - interceptors.add(interceptor) - } - fun build(analytics: Analytics, data: AnalyticsHandlerBuilder.Data) { builders.mapNotNull { it.build(data) }.forEach { analytics.addHandler(it.id(), it) } filters.forEach { analytics.addFilter(it) } diff --git a/app/src/main/java/com/tangem/tap/common/analytics/converters/AnalyticsErrorConverter.kt b/app/src/main/java/com/tangem/tap/common/analytics/converters/AnalyticsErrorConverter.kt index 1e279cc84b..b8e9b2e458 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/converters/AnalyticsErrorConverter.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/converters/AnalyticsErrorConverter.kt @@ -1,10 +1,10 @@ package com.tangem.tap.common.analytics.converters import com.tangem.blockchain.common.BlockchainSdkError -import com.tangem.common.Converter import com.tangem.common.core.TangemSdkError import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.features.demo.DemoTransactionSender +import com.tangem.utils.converter.Converter /** [REDACTED_AUTHOR] diff --git a/app/src/main/java/com/tangem/tap/common/analytics/converters/ParamCardCurrencyConverter.kt b/app/src/main/java/com/tangem/tap/common/analytics/converters/ParamCardCurrencyConverter.kt index 0a7928efd5..303dcf3b25 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/converters/ParamCardCurrencyConverter.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/converters/ParamCardCurrencyConverter.kt @@ -1,17 +1,18 @@ package com.tangem.tap.common.analytics.converters import com.tangem.blockchain.common.Blockchain -import com.tangem.common.Converter import com.tangem.domain.common.CardTypesResolver import com.tangem.tap.common.analytics.events.AnalyticsParam +import com.tangem.utils.converter.Converter +import com.tangem.core.analytics.models.AnalyticsParam as CoreAnalyticsParam /** [REDACTED_AUTHOR] */ -class ParamCardCurrencyConverter : Converter { +class ParamCardCurrencyConverter : Converter { - override fun convert(value: CardTypesResolver): AnalyticsParam.CardCurrency? { - if (value.isMultiwalletAllowed()) return AnalyticsParam.CardCurrency.MultiCurrency + override fun convert(value: CardTypesResolver): CoreAnalyticsParam.WalletType? { + if (value.isMultiwalletAllowed()) return CoreAnalyticsParam.WalletType.MultiCurrency val type = when { value.isTangemNote() -> AnalyticsParam.CurrencyType.Blockchain(value.getBlockchain()) @@ -21,6 +22,6 @@ class ParamCardCurrencyConverter : Converter null } ?: return null - return AnalyticsParam.CardCurrency.SingleCurrency(type) + return CoreAnalyticsParam.WalletType.SingleCurrency(type.value) } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/converters/ShopOrderToEventConverter.kt b/app/src/main/java/com/tangem/tap/common/analytics/converters/ShopOrderToEventConverter.kt index 6b8f903fb9..fd658029b7 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/converters/ShopOrderToEventConverter.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/converters/ShopOrderToEventConverter.kt @@ -1,9 +1,9 @@ package com.tangem.tap.common.analytics.converters import com.shopify.buy3.Storefront -import com.tangem.common.Converter import com.tangem.tap.common.analytics.events.Shop import com.tangem.tap.features.shop.domain.models.ProductType +import com.tangem.utils.converter.Converter /** [REDACTED_AUTHOR] diff --git a/app/src/main/java/com/tangem/tap/common/analytics/converters/TopUpEventConverter.kt b/app/src/main/java/com/tangem/tap/common/analytics/converters/TopUpEventConverter.kt deleted file mode 100644 index ba62707b7f..0000000000 --- a/app/src/main/java/com/tangem/tap/common/analytics/converters/TopUpEventConverter.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.tangem.tap.common.analytics.converters - -import com.tangem.common.Converter -import com.tangem.domain.common.CardTypesResolver -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.common.analytics.events.Basic - -/** -[REDACTED_AUTHOR] - */ -class TopUpEventConverter : Converter, Basic.ToppedUp?> { - - override fun convert(value: Pair): Basic.ToppedUp? { - val (userWalletId, resolver) = value - val paramCardCurrency = ParamCardCurrencyConverter().convert(resolver) ?: return null - - return Basic.ToppedUp(userWalletId, paramCardCurrency) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/AnalyticsParam.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/AnalyticsParam.kt index e3195c7e34..489effa20d 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/AnalyticsParam.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/AnalyticsParam.kt @@ -6,42 +6,36 @@ import com.tangem.tap.features.details.redux.SecurityOption sealed class AnalyticsParam { sealed class CurrencyType(val value: String) { - class Currency(currency: com.tangem.tap.features.wallet.models.Currency) : CurrencyType(currency.currencySymbol) + class Currency(currency: com.tangem.tap.domain.model.Currency) : CurrencyType(currency.currencySymbol) class Blockchain(blockchain: com.tangem.blockchain.common.Blockchain) : CurrencyType(blockchain.currency) class Token(token: com.tangem.blockchain.common.Token) : CurrencyType(token.symbol) - class FiatCurrency(fiatCurrency: com.tangem.tap.common.entities.FiatCurrency) : CurrencyType(fiatCurrency.code) class Amount(amount: com.tangem.blockchain.common.Amount) : CurrencyType(amount.currencySymbol) } - // MultiCurrency or CurrencyType - sealed class CardCurrency(val value: String) { - object MultiCurrency : CardCurrency(value = "Multicurrency") - class SingleCurrency(type: CurrencyType) : CardCurrency(type.value) - } - sealed class CardBalanceState(val value: String) { object Empty : CardBalanceState("Empty") object Full : CardBalanceState("Full") - object CustomToken : CardBalanceState("Custom Token") - object BlockchainError : CardBalanceState("Blockchain Error") - object NoRate : CardBalanceState("No Rate") companion object } sealed class RateApp(val value: String) { object Liked : RateApp("Liked") - object Disliked : RateApp("Disliked") object Closed : RateApp("Close") } sealed class OnOffState(val value: String) { + object On : OnOffState("On") object Off : OnOffState("Off") + + companion object { + + operator fun invoke(value: Boolean): OnOffState = if (value) On else Off + } } sealed class UserCode(val value: String) { object AccessCode : UserCode("Access Code") - object Passcode : UserCode("Passcode") } sealed class SecurityMode(val value: String) { @@ -78,62 +72,6 @@ sealed class AnalyticsParam { object BlockchainSdk : Error("Blockchain Sdk Error") } - sealed class ScannedFrom(val value: String) { - object Introduction : ScannedFrom("Introduction") - object Main : ScannedFrom("Main") - object SignIn : ScannedFrom("Sign In") - object MyWallets : ScannedFrom("My Wallets") - } - - sealed class TxSentFrom(val value: String) { - data class Send( - override val blockchain: String, - override val token: String, - override val feeType: FeeType, - ) : TxSentFrom("Send"), TxData - - data class Swap( - override val blockchain: String, - override val token: String, - override val feeType: FeeType, - ) : TxSentFrom("Swap"), TxData - - data class Approve( - override val blockchain: String, - override val token: String, - override val feeType: FeeType, - val permissionType: String, - ) : TxSentFrom("Approve"), TxData - - object WalletConnect : TxSentFrom("WalletConnect") - object Sell : TxSentFrom("Sell") - } - - sealed interface TxData { - val blockchain: String - val token: String - val feeType: FeeType - } - - sealed class FeeType(val value: String) { - object Fixed : FeeType("Fixed") - object Min : FeeType("Min") - object Normal : FeeType("Normal") - object Max : FeeType("Max") - - companion object { - fun fromString(feeType: String): FeeType { - return when (feeType) { - Min.value -> Min - Normal.value -> Normal - Max.value -> Max - Fixed.value -> Fixed - else -> Fixed - } - } - } - } - sealed class WalletCreationType(val value: String) { object PrivateKey : WalletCreationType(value = "Private Key") object NewSeed : WalletCreationType(value = "New Seed") diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/MainScreen.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/MainScreen.kt index fbbc88c099..f97dca3a02 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/MainScreen.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/MainScreen.kt @@ -12,23 +12,14 @@ sealed class MainScreen( class ScreenOpened : MainScreen("Screen opened") class ButtonScanCard : MainScreen("Button - Scan Card") - class ButtonMyWallets : MainScreen("Button - My Wallets") class EnableBiometrics(state: AnalyticsParam.OnOffState) : MainScreen( event = "Enable Biometric", params = mapOf("State" to state.value), ) - class MainCurrencyChanged(currencyType: AnalyticsParam.CurrencyType) : MainScreen( - event = "Main Currency Changed", - params = mapOf("Currency Type" to currencyType.value), - ) - class NoticeRateAppButton(result: AnalyticsParam.RateApp) : MainScreen( event = "Notice - Rate The App Button Tapped", params = mapOf("Result" to result.value), ) - - class NoticeBackupYourWalletTapped : MainScreen("Notice - Backup Your Wallet Tapped") - class NoticeScanYourCardTapped : MainScreen("Notice - Scan Your Card Tapped") } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/MyWallets.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/MyWallets.kt deleted file mode 100644 index 87ba002047..0000000000 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/MyWallets.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.tangem.tap.common.analytics.events - -import com.tangem.core.analytics.models.AnalyticsEvent - -sealed class MyWallets( - event: String, - params: Map = mapOf(), -) : AnalyticsEvent("My Wallets", event, params) { - - class MyWalletsScreenOpened : MyWallets(event = "My Wallets Screen Opened") - - sealed class Button { - class ScanNewCard : MyWallets(event = "Button - Scan New Card") - class UnlockWithBiometrics : MyWallets(event = "Button - Unlock all with Face ID") - class EditWalletTapped : MyWallets(event = "Button - Edit Wallet Tapped") - class DeleteWalletTapped : MyWallets(event = "Button - Delete Wallet Tapped") - class WalletUnlockTapped : MyWallets(event = "Button - Wallet Unlock Tapped") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/Portfolio.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/Portfolio.kt deleted file mode 100644 index 8270df641c..0000000000 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/Portfolio.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.tangem.tap.common.analytics.events - -import com.tangem.core.analytics.models.AnalyticsEvent - -/** -[REDACTED_AUTHOR] - */ - -sealed class Portfolio( - event: String, - params: Map = mapOf(), -) : AnalyticsEvent("Portfolio", event, params) { - - class Refreshed : Portfolio("Refreshed") - - class ButtonManageTokens : Portfolio("Button - Manage Tokens") - - class TokenTapped : Portfolio("Token is Tapped") - - class OrganizeTokens : Portfolio("Button - Organize Tokens") -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/Settings.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/Settings.kt index b2123614eb..b9a831cb3d 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/Settings.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/Settings.kt @@ -89,5 +89,12 @@ sealed class Settings( event = "App Theme Switched", params = mapOf("State" to theme.value), ) + + object EnableBiometrics : AppSettings(event = "Notice - Enable Biometric") + + class HideBalanceChanged(state: AnalyticsParam.OnOffState) : AppSettings( + event = "Hide Balance Changed", + params = mapOf("State" to state.value), + ) } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/Token.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/Token.kt index 9b8cecc417..3b5ae220eb 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/Token.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/Token.kt @@ -1,7 +1,6 @@ package com.tangem.tap.common.analytics.events import com.tangem.core.analytics.models.AnalyticsEvent -import com.tangem.tap.common.analytics.events.AnalyticsParam.CurrencyType /** [REDACTED_AUTHOR] @@ -13,50 +12,6 @@ sealed class Token( error: Throwable? = null, ) : AnalyticsEvent(category, event, params, error) { - class Refreshed : Token("Token", "Refreshed") - class ButtonExplore : Token("Token", "Button - Explore") - - class ButtonRemoveToken(type: CurrencyType) : Token( - "Token", - "Button - Remove Token", - params = mapOf("Token" to type.value), - ) - - class ButtonBuy(type: CurrencyType) : Token( - category = "Token", - event = "Button - Buy", - params = mapOf("Token" to type.value), - ) - - class ButtonSell(type: CurrencyType) : Token( - category = "Token", - event = "Button - Sell", - params = mapOf("Token" to type.value), - ) - - class ButtonExchange(type: CurrencyType) : Token( - category = "Token", - event = "Button - Exchange", - params = mapOf("Token" to type.value), - ) - - class ButtonSend(type: CurrencyType) : Token( - category = "Token", - event = "Button - Send", - params = mapOf("Token" to type.value), - ) - - class Bought(type: CurrencyType) : Token( - category = "Token", - event = "Token Bought", - params = mapOf("Token" to type.value), - ) - - object ShowWalletAddress : Token( - category = "Token", - event = "Button - Show the Wallet Address", - ) - sealed class Receive( event: String, params: Map = mapOf(), diff --git a/app/src/main/java/com/tangem/tap/common/analytics/handlers/amplitude/AmplitudeClient.kt b/app/src/main/java/com/tangem/tap/common/analytics/handlers/amplitude/AmplitudeClient.kt index ddb27d18ff..74cec2eaf2 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/handlers/amplitude/AmplitudeClient.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/handlers/amplitude/AmplitudeClient.kt @@ -3,8 +3,8 @@ package com.tangem.tap.common.analytics.handlers.amplitude import android.app.Application import com.amplitude.api.Amplitude import com.amplitude.api.AmplitudeClient -import com.tangem.common.Converter import com.tangem.core.analytics.api.EventLogger +import com.tangem.utils.converter.Converter import org.json.JSONObject /** diff --git a/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseClient.kt b/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseClient.kt index 33751b5e0a..20597e0fb9 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseClient.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseClient.kt @@ -2,7 +2,6 @@ package com.tangem.tap.common.analytics.handlers.firebase import android.os.Bundle import androidx.core.os.bundleOf -import com.google.firebase.analytics.FirebaseAnalytics import com.google.firebase.analytics.ktx.analytics import com.google.firebase.crashlytics.ktx.crashlytics import com.google.firebase.ktx.Firebase @@ -29,8 +28,4 @@ internal class FirebaseClient : FirebaseAnalyticsClient { } private fun Map.toBundle(): Bundle = bundleOf(*this.toList().toTypedArray()) - - companion object { - const val ORDER_EVENT = FirebaseAnalytics.Event.PURCHASE - } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/paramsInterceptor/CardContextInterceptor.kt b/app/src/main/java/com/tangem/tap/common/analytics/paramsInterceptor/CardContextInterceptor.kt index cf97ded034..6a963e544f 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/paramsInterceptor/CardContextInterceptor.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/paramsInterceptor/CardContextInterceptor.kt @@ -48,6 +48,7 @@ class CardContextInterceptor( ProductType.Wallet2 -> "Wallet 2.0" ProductType.Ring -> "Ring" ProductType.Start2Coin -> "Start2Coin" + ProductType.Visa -> "VISA" else -> if (DemoHelper.isDemoCard(scanResponse)) { if (DemoHelper.isTestDemoCard(scanResponse)) { "Demo Test" diff --git a/app/src/main/java/com/tangem/tap/common/analytics/topup/TopUpController.kt b/app/src/main/java/com/tangem/tap/common/analytics/topup/TopUpController.kt deleted file mode 100644 index 376143ec6b..0000000000 --- a/app/src/main/java/com/tangem/tap/common/analytics/topup/TopUpController.kt +++ /dev/null @@ -1,207 +0,0 @@ -package com.tangem.tap.common.analytics.topup - -import com.tangem.common.extensions.guard -import com.tangem.common.extensions.isZero -import com.tangem.core.analytics.Analytics -import com.tangem.data.source.preferences.model.DataSourceTopupInfo -import com.tangem.data.source.preferences.storage.ToppedUpWalletStorage -import com.tangem.domain.common.BlockchainNetwork -import com.tangem.domain.common.CardTypesResolver -import com.tangem.domain.common.util.cardTypesResolver -import com.tangem.domain.models.scan.ScanResponse -import com.tangem.domain.userwallets.UserWalletIdBuilder -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.common.analytics.converters.TopUpEventConverter -import com.tangem.tap.common.analytics.events.AnalyticsParam -import com.tangem.tap.domain.model.TotalFiatBalance -import com.tangem.tap.domain.model.WalletDataModel -import com.tangem.tap.domain.model.WalletStoreModel -import com.tangem.tap.domain.walletCurrencies.WalletCurrenciesManager -import com.tangem.tap.domain.walletStores.WalletStoresManager -import com.tangem.tap.features.wallet.models.Currency -import com.tangem.tap.scope -import com.tangem.utils.extensions.copy -import kotlinx.coroutines.launch -import java.math.BigDecimal - -/** -[REDACTED_AUTHOR] - */ -class TopUpController( - var scanResponseProvider: (() -> ScanResponse?)? = null, - var walletStoresManagerProvider: (() -> WalletStoresManager)? = null, - private val topupWalletStorage: ToppedUpWalletStorage, -) : WalletCurrenciesManager.Listener { - - private var hadMissedDerivations: Boolean = false - private val addedCurrencies = mutableListOf() - - override fun didUpdate(userWallet: UserWallet, currency: Currency) { - tryToSend() - } - - override fun willCurrenciesAdd(userWallet: UserWallet, currenciesToAdd: List) { - addedCurrencies.addAll(currenciesToAdd.distinct()) - } - - fun walletStoresChanged(walletStores: List) { - val missedDerivations = walletStores - .flatMap { it.walletsData } - .map { it.status } - .filterIsInstance() - - hadMissedDerivations = missedDerivations.isNotEmpty() - } - - fun scanToGetDerivations() { - hadMissedDerivations = true - } - - fun addMissingDerivations(blockchains: List) { - hadMissedDerivations = blockchains.isNotEmpty() - } - - fun totalBalanceStateChanged(totalFiatBalance: TotalFiatBalance) { - if (totalFiatBalance is TotalFiatBalance.Loaded) tryToSend() - } - - fun loadDataSuccess() { - tryToSend() - } - - private fun tryToSend() { - if (hadMissedDerivations) return - val scanResponse = scanResponseProvider?.invoke() ?: return - val userWalletId = UserWalletIdBuilder.scanResponse(scanResponse).build() ?: return - val walletStoresManager = walletStoresManagerProvider?.invoke() ?: return - - scope.launch { - val walletDataModels = walletStoresManager.getSync(userWalletId).flatMap { it.walletsData } - if (walletDataModels.isEmpty()) return@launch - - val isCorrectStatus = walletDataModels.any { - it.status is WalletDataModel.Loading || - it.status is WalletDataModel.NoAccount || - it.status is WalletDataModel.Unreachable || - it.status is WalletDataModel.MissedDerivation || - it.status.isErrorStatus - } - if (isCorrectStatus) return@launch - - val isToppedUpInPast = findToppedUpCurrenciesInPast(walletDataModels).isNotEmpty() - if (isToppedUpInPast) { - val newWalletInfo = DataSourceTopupInfo( - walletId = userWalletId.stringValue, - cardBalanceState = DataSourceTopupInfo.CardBalanceState.Full, - ) - topupWalletStorage.save(newWalletInfo) - return@launch - } - - val cardBalanceState = BalanceCalculator(walletDataModels).calculate().toCardBalanceState() - send(userWalletId, cardBalanceState, scanResponse.cardTypesResolver) - } - } - - /** - * A UserWalletId registration should be after creating wallets - */ - fun registerEmptyWallet(scanResponse: ScanResponse) { - UserWalletIdBuilder.scanResponse(scanResponse).build()?.let { - topupWalletStorage.save( - DataSourceTopupInfo( - walletId = it.stringValue, - cardBalanceState = DataSourceTopupInfo.CardBalanceState.Empty, - ), - ) - } - } - - fun send(scanResponse: ScanResponse, cardBalanceState: AnalyticsParam.CardBalanceState) { - UserWalletIdBuilder.scanResponse(scanResponse).build()?.let { - send(it, cardBalanceState, scanResponse.cardTypesResolver) - } - } - - fun send( - userWalletId: UserWalletId, - cardBalanceState: AnalyticsParam.CardBalanceState, - cardTypesResolver: CardTypesResolver, - ) { - val topupInfo = topupWalletStorage.restore(userWalletId.stringValue).guard { - val topupInfo = DataSourceTopupInfo( - walletId = userWalletId.stringValue, - cardBalanceState = when (cardBalanceState) { - AnalyticsParam.CardBalanceState.BlockchainError -> - DataSourceTopupInfo.CardBalanceState.BlockchainError - AnalyticsParam.CardBalanceState.CustomToken -> - DataSourceTopupInfo.CardBalanceState.CustomToken - AnalyticsParam.CardBalanceState.Empty -> - DataSourceTopupInfo.CardBalanceState.Empty - AnalyticsParam.CardBalanceState.Full -> - DataSourceTopupInfo.CardBalanceState.Full - AnalyticsParam.CardBalanceState.NoRate -> - DataSourceTopupInfo.CardBalanceState.NoRate - }, - ) - topupWalletStorage.save(topupInfo) - return - } - - val isToppedUp = topupInfo.cardBalanceState == DataSourceTopupInfo.CardBalanceState.Full - if (isToppedUp) return - - if (cardBalanceState.isToppedUp()) { - topupWalletStorage.save(topupInfo.copy(cardBalanceState = DataSourceTopupInfo.CardBalanceState.Full)) - TopUpEventConverter().convert(value = userWalletId to cardTypesResolver)?.let { - Analytics.send(it) - } - } - } - - private fun findToppedUpCurrenciesInPast(walletDataModels: List): List { - val currenciesToppedUpInPast = addedCurrencies.copy() - .mapNotNull { currency -> - val foundCurrencyModel = walletDataModels - .find { it.currency == currency } - ?: return@mapNotNull null - - if (foundCurrencyModel.status.amount.isZero()) null else foundCurrencyModel - } - addedCurrencies.clear() - - return currenciesToppedUpInPast - } - - private fun BigDecimal.toCardBalanceState(): AnalyticsParam.CardBalanceState = when { - isZero() -> AnalyticsParam.CardBalanceState.Empty - else -> AnalyticsParam.CardBalanceState.Full - } - - private fun AnalyticsParam.CardBalanceState.isToppedUp(): Boolean = this == AnalyticsParam.CardBalanceState.Full -} - -private interface IBalanceCalculator { - fun calculate(): BigDecimal -} - -private class BalanceCalculator( - private val walletDataModels: List, -) : IBalanceCalculator { - - override fun calculate(): BigDecimal { - val singleToken = walletDataModels - .filter { it.currency.isToken() } - .firstOrNull { it.isCardSingleToken } - - val totalAmount = singleToken?.status?.amount - ?: walletDataModels.calculateTotalCryptoAmount() - - return totalAmount - } - - private fun List.calculateTotalCryptoAmount(): BigDecimal = this - .map { it.status.amount } - .reduce(BigDecimal::plus) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/compose/AutoSizeText.kt b/app/src/main/java/com/tangem/tap/common/compose/AutoSizeText.kt deleted file mode 100644 index 63d98b6269..0000000000 --- a/app/src/main/java/com/tangem/tap/common/compose/AutoSizeText.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.tangem.tap.common.compose - -import androidx.compose.material.LocalTextStyle -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.drawWithContent -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.unit.TextUnit -import androidx.compose.ui.unit.sp - -@Suppress("MagicNumber") -@Composable -fun TextAutoSize( - text: String, - fontSizeRange: FontSizeRange, - modifier: Modifier = Modifier, - textStyle: TextStyle = LocalTextStyle.current, -) { - val fontSizeValue = remember { mutableStateOf(fontSizeRange.max.value) } - val readyToDraw = remember { mutableStateOf(false) } - - val textState = remember { mutableStateOf(text) } - if (textState.value != text) { - readyToDraw.value = false - fontSizeValue.value = fontSizeRange.max.value - textState.value = text - } - - Text( - modifier = modifier.drawWithContent { if (readyToDraw.value) drawContent() }, - text = text, - softWrap = false, - style = textStyle, - fontSize = fontSizeValue.value.sp, - onTextLayout = { - if (it.hasVisualOverflow) { - val nextFontSizeValue = fontSizeValue.value - fontSizeRange.step.value - if (nextFontSizeValue <= fontSizeRange.min.value) { - fontSizeValue.value = fontSizeRange.min.value - readyToDraw.value = true - } else { - fontSizeValue.value = nextFontSizeValue * 0.8f - } - } else { - readyToDraw.value = true - } - }, - ) -} - -data class FontSizeRange( - val min: TextUnit, - val max: TextUnit, - val step: TextUnit = DEFAULT_TEXT_STEP, -) { - init { - require(min < max) { "min should be less than max, $this" } - require(step.value > 0) { "step should be greater than 0, $this" } - } - - companion object { - private val DEFAULT_TEXT_STEP = 1.sp - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/compose/Button.kt b/app/src/main/java/com/tangem/tap/common/compose/Button.kt deleted file mode 100644 index ee94c8b15b..0000000000 --- a/app/src/main/java/com/tangem/tap/common/compose/Button.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.tangem.tap.common.compose - -import androidx.compose.material.ripple.LocalRippleTheme -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider - -/** - * Used for disable ripple if button is enable = false - */ -@Composable -fun ToggledRippleTheme(isEnabled: Boolean, content: @Composable () -> Unit) { - val theme = LocalRippleTheme provides if (isEnabled) LocalRippleTheme.current else NoRippleTheme() - CompositionLocalProvider(theme) { content() } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/compose/ComposableValueDebouncer.kt b/app/src/main/java/com/tangem/tap/common/compose/ComposableValueDebouncer.kt deleted file mode 100644 index 44395f9279..0000000000 --- a/app/src/main/java/com/tangem/tap/common/compose/ComposableValueDebouncer.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.tangem.tap.common.compose - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import com.tangem.domain.common.util.ValueDebouncer - -/** -[REDACTED_AUTHOR] - * This is an empty compose view. It just remember the ValueDebouncer inside of itself. - */ -@Composable -fun valueDebouncerAsState( - initialValue: T, - onValueChange: (T) -> Unit, - debounce: Long = 600, - onEmitValueReceive: (T) -> Unit = {}, -): ValueDebouncer { - return remember { - ValueDebouncer( - initialValue = initialValue, - debounceDuration = debounce, - onEmitValueReceived = { emitValue -> - emitValue?.let { onEmitValueReceive(it) } - }, - onValueChanged = { changedValue -> - changedValue?.let { onValueChange(it) } - }, - ) - } -} - -@Composable -fun valueDebouncerNullableAsState( - initialValue: T?, - onValueChange: (T?) -> Unit, - debounce: Long = 400, - onEmitValueReceive: (T?) -> Unit = {}, -): ValueDebouncer { - return remember { - ValueDebouncer( - initialValue = initialValue, - debounceDuration = debounce, - onEmitValueReceived = onEmitValueReceive, - onValueChanged = onValueChange, - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/compose/ErrorViews.kt b/app/src/main/java/com/tangem/tap/common/compose/ErrorViews.kt deleted file mode 100644 index 90928c9d15..0000000000 --- a/app/src/main/java/com/tangem/tap/common/compose/ErrorViews.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.tangem.tap.common.compose - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.padding -import androidx.compose.material.LocalTextStyle -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp - -/** -[REDACTED_AUTHOR] - */ -@Composable -fun ErrorView(text: String, modifier: Modifier = Modifier, style: TextStyle = LocalTextStyle.current) { - Text( - text, - color = MaterialTheme.colors.error, - modifier = modifier, - style = style, - ) -} - -@Preview -@Composable -private fun ErrorViewTest() { - Box(Modifier.padding(16.dp)) { - ErrorView(text = "Some error description") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/compose/NoRippleTheme.kt b/app/src/main/java/com/tangem/tap/common/compose/NoRippleTheme.kt deleted file mode 100644 index f0afe076ba..0000000000 --- a/app/src/main/java/com/tangem/tap/common/compose/NoRippleTheme.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.tangem.tap.common.compose - -import androidx.compose.material.ripple.RippleAlpha -import androidx.compose.material.ripple.RippleTheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.Color - -/** -[REDACTED_AUTHOR] - */ -class NoRippleTheme : RippleTheme { - @Composable - override fun defaultColor() = Color.Unspecified - - @Composable - override fun rippleAlpha(): RippleAlpha = RippleAlpha(0.0f, 0.0f, 0.0f, 0.0f) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/compose/PinCodeWidget.kt b/app/src/main/java/com/tangem/tap/common/compose/PinCodeWidget.kt deleted file mode 100644 index 41e5637ba1..0000000000 --- a/app/src/main/java/com/tangem/tap/common/compose/PinCodeWidget.kt +++ /dev/null @@ -1,165 +0,0 @@ -package com.tangem.tap.common.compose - -import androidx.compose.foundation.background -import androidx.compose.foundation.gestures.detectTapGestures -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.layout.wrapContentSize -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.text.KeyboardActions -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material.LocalTextStyle -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Surface -import androidx.compose.material.Text -import androidx.compose.material.TextField -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.alpha -import androidx.compose.ui.draw.clip -import androidx.compose.ui.focus.FocusRequester -import androidx.compose.ui.focus.focusRequester -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.platform.LocalSoftwareKeyboardController -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.text.input.TextFieldValue -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.core.text.isDigitsOnly - -/** -[REDACTED_AUTHOR] - */ -@OptIn(ExperimentalComposeUiApi::class) -@Composable -fun PinCodeWidget( - config: PinViewConfig = tangemPinConfig, - onPinChange: (String, Boolean) -> Unit = { pin, isLastSymbolEntered -> }, -) { - val focusRequester = remember { FocusRequester() } - val keyboardController = LocalSoftwareKeyboardController.current - - val rTextFieldValue = remember { mutableStateOf(TextFieldValue("")) } - val indexedSymbols: List = createPinSymbolsList(config.pinsCount, rTextFieldValue.value.text) - - fun isLastSymbolEntered(): Boolean = rTextFieldValue.value.text.length == config.pinsCount - - fun handleOnTextFieldValueChanged(value: TextFieldValue) { - if (!value.text.isDigitsOnly()) return - - if (value.text.length <= config.pinsCount) { - rTextFieldValue.value = value - onPinChange(value.text, isLastSymbolEntered()) - } - } - - Box( - modifier = config.modifier - .pointerInput(Unit) { - detectTapGestures { - focusRequester.requestFocus() - keyboardController?.show() - } - }, - ) { - Row { - for (index in 0 until config.pinsCount) { - PinElement( - config = config, - pinSymbol = indexedSymbols[index] ?: "", - ) - } - } - TextField( - modifier = Modifier - .alpha(0f) - .size(1.dp) - .align(Alignment.Center) - .focusRequester(focusRequester), - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Decimal, - imeAction = if (isLastSymbolEntered()) ImeAction.Done else ImeAction.Next, - ), - keyboardActions = KeyboardActions( - onDone = { keyboardController?.hide() }, - ), - value = rTextFieldValue.value, - onValueChange = ::handleOnTextFieldValueChanged, - ) - } - - LaunchedEffect(Unit) { - focusRequester.requestFocus() - } -} - -@Composable -private fun PinElement(config: PinViewConfig, pinSymbol: String) { - Box(Modifier.padding(config.pinBoxPadding)) { - Box(config.pinBoxModifier) { - Text( - text = pinSymbol, - modifier = config.pinTextModifier.align(Alignment.Center), - style = config.pinsTextStyle ?: LocalTextStyle.current, - ) - } - } -} - -private fun createPinSymbolsList(size: Int, text: String): List = List(size) { - try { - text[it].toString() - } catch (ex: IndexOutOfBoundsException) { - null - } -} - -data class PinViewConfig( - val modifier: Modifier = Modifier, - val pinBoxModifier: Modifier = Modifier, - val pinBoxPadding: Dp = 0.dp, - val pinTextModifier: Modifier = Modifier, - val pinsCount: Int = 4, - val pinsTextStyle: TextStyle? = null, -) - -private val tangemPinConfig = PinViewConfig( - modifier = Modifier - .wrapContentSize(), - pinBoxModifier = Modifier - .width(42.dp) - .height(56.dp) - .clip(RoundedCornerShape(8.dp)) - .background(Color(0xFFF0F0F0)), - pinBoxPadding = 6.dp, - pinTextModifier = Modifier, - pinsCount = 4, - pinsTextStyle = TextStyle( - fontWeight = FontWeight(500), - fontSize = 24.sp, - ), -) - -@Preview -@Composable -private fun PinCodeWidgetPreview() { - Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background) { - PinCodeWidget(tangemPinConfig) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/compose/Warning.kt b/app/src/main/java/com/tangem/tap/common/compose/Warning.kt deleted file mode 100644 index 6d4a79d192..0000000000 --- a/app/src/main/java/com/tangem/tap/common/compose/Warning.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.tangem.tap.common.compose - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.padding -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Surface -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.colorResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.tangem.common.module.ModuleMessage -import com.tangem.core.ui.components.SpacerH8 -import com.tangem.tap.domain.moduleMessage.ModuleMessageConverter -import com.tangem.wallet.R - -/** -[REDACTED_AUTHOR] - */ -@Composable -fun AddCustomTokenWarning(warning: ModuleMessage, converter: ModuleMessageConverter, modifier: Modifier = Modifier) { - Surface( - modifier = modifier, - shape = MaterialTheme.shapes.small, - color = colorResource(id = R.color.warning_warning), - elevation = 4.dp, - ) { - Column( - modifier = Modifier.padding(16.dp), - ) { - Text( - text = stringResource(id = R.string.common_warning), - color = colorResource(id = R.color.white), - fontSize = 14.sp, - fontWeight = FontWeight.Bold, - ) - SpacerH8() - Text( - text = converter.convert(warning).message, - color = colorResource(id = R.color.white), - fontSize = 13.sp, - lineHeight = 18.sp, - ) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/compose/extensions/Context.kt b/app/src/main/java/com/tangem/tap/common/compose/extensions/Context.kt deleted file mode 100644 index 719c80d0d4..0000000000 --- a/app/src/main/java/com/tangem/tap/common/compose/extensions/Context.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.tangem.tap.common.compose.extensions - -import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalContext -import com.tangem.tap.common.extensions.copyToClipboard -import com.tangem.tap.common.extensions.getFromClipboard - -/** -[REDACTED_AUTHOR] - */ -@Suppress("ComposableFunctionName") -@Composable -fun copyToClipboard(value: Any, label: String = "") { - LocalContext.current.copyToClipboard(value, label) -} - -@Suppress("ComposableFunctionName") -@Composable -fun getFromClipboard(default: CharSequence? = null): CharSequence? { - return LocalContext.current.getFromClipboard(default) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/compose/extensions/Dp.kt b/app/src/main/java/com/tangem/tap/common/compose/extensions/Dp.kt index 42acdcf306..04240d7b55 100644 --- a/app/src/main/java/com/tangem/tap/common/compose/extensions/Dp.kt +++ b/app/src/main/java/com/tangem/tap/common/compose/extensions/Dp.kt @@ -14,6 +14,4 @@ fun Dp.toPx(): Float { return with(LocalDensity.current) { currentDp.toPx() } } -fun DpSize.halfWidth(): Dp = this.width / 2 - fun DpSize.halfHeight(): Dp = this.height / 2 \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/compose/extensions/LazyListState.kt b/app/src/main/java/com/tangem/tap/common/compose/extensions/LazyListState.kt deleted file mode 100644 index dab9b8f654..0000000000 --- a/app/src/main/java/com/tangem/tap/common/compose/extensions/LazyListState.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.tangem.tap.common.compose.extensions - -import androidx.compose.foundation.lazy.LazyListState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.ui.platform.LocalView -import com.tangem.tap.common.extensions.hideKeyboard - -@Composable -fun LazyListState.OnBottomReached(loadMoreThreshold: Int, onLoadMore: () -> Unit) { - require(loadMoreThreshold >= 0) - val shouldLoadMore by remember { - derivedStateOf { - val lastVisibleItem = layoutInfo.visibleItemsInfo.lastOrNull() - ?: return@derivedStateOf false - lastVisibleItem.index >= layoutInfo.totalItemsCount - 1 - loadMoreThreshold - } - } - - LaunchedEffect(shouldLoadMore) { - if (shouldLoadMore) onLoadMore() - } -} - -@Composable -fun LazyListState.HideKeyboardOnScroll() { - if (isScrollInProgress) { - LocalView.current.hideKeyboard() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/compose/extensions/MutableState.kt b/app/src/main/java/com/tangem/tap/common/compose/extensions/MutableState.kt deleted file mode 100644 index 459086b9c3..0000000000 --- a/app/src/main/java/com/tangem/tap/common/compose/extensions/MutableState.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.tangem.tap.common.compose.extensions - -import androidx.compose.runtime.MutableState - -/** -[REDACTED_AUTHOR] - */ -fun MutableState>.addAndNotify(value: T) { - this.value = this.value.toMutableList().apply { add(value) } -} - -fun MutableState>.removeAndNotify(value: T) { - this.value = this.value.toMutableList().apply { remove(value) } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/compose/extensions/Painter.kt b/app/src/main/java/com/tangem/tap/common/compose/extensions/Painter.kt index 32c3110ba5..dfabb2cddf 100644 --- a/app/src/main/java/com/tangem/tap/common/compose/extensions/Painter.kt +++ b/app/src/main/java/com/tangem/tap/common/compose/extensions/Painter.kt @@ -5,7 +5,6 @@ import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp -import com.tangem.sdk.extensions.dpToPx import com.tangem.sdk.extensions.pxToDp /** @@ -17,8 +16,5 @@ fun Painter.dpSize(): DpSize = DpSize( intrinsicSize.height.pxToDp().dp, ) -@Composable -private fun Float.dpToPx(): Float = LocalContext.current.dpToPx(this) - @Composable private fun Float.pxToDp(): Float = LocalContext.current.pxToDp(this) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/entities/Button.kt b/app/src/main/java/com/tangem/tap/common/entities/Button.kt index b873b7ef3e..b0aca7f994 100644 --- a/app/src/main/java/com/tangem/tap/common/entities/Button.kt +++ b/app/src/main/java/com/tangem/tap/common/entities/Button.kt @@ -1,7 +1,6 @@ package com.tangem.tap.common.entities import com.tangem.tap.features.send.redux.states.ButtonState -import com.tangem.tap.features.wallet.redux.ProgressState open class Button(val enabled: Boolean) diff --git a/app/src/main/java/com/tangem/tap/common/entities/FiatCurrency.kt b/app/src/main/java/com/tangem/tap/common/entities/FiatCurrency.kt deleted file mode 100644 index c0df80eb2a..0000000000 --- a/app/src/main/java/com/tangem/tap/common/entities/FiatCurrency.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.tangem.tap.common.entities - -data class FiatCurrency( - val code: String, - val name: String, - val symbol: String, -) { - val displayName: String - get() = "${this.name} (${this.code}) - ${this.symbol}" - - companion object { - val Default = FiatCurrency( - symbol = "$", - code = "USD", - name = "US Dollar", - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/entities/ProgressState.kt b/app/src/main/java/com/tangem/tap/common/entities/ProgressState.kt new file mode 100644 index 0000000000..61adc5cd45 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/entities/ProgressState.kt @@ -0,0 +1,5 @@ +package com.tangem.tap.common.entities + +import com.tangem.tap.common.toggleWidget.WidgetState + +enum class ProgressState : WidgetState { Loading, Done, Error } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/AssetManager.kt b/app/src/main/java/com/tangem/tap/common/extensions/AssetManager.kt deleted file mode 100644 index 9130b6c91d..0000000000 --- a/app/src/main/java/com/tangem/tap/common/extensions/AssetManager.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.tangem.tap.common.extensions - -import android.content.res.AssetManager - -fun AssetManager.readJsonFileToString(fileName: String): String = - this.open("$fileName.json").bufferedReader().readText() \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Bitmap.kt b/app/src/main/java/com/tangem/tap/common/extensions/Bitmap.kt index 80e756c21c..76eea0c892 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Bitmap.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Bitmap.kt @@ -1,7 +1,6 @@ package com.tangem.tap.common.extensions import android.graphics.Bitmap -import android.graphics.BitmapFactory import java.io.ByteArrayOutputStream @Suppress("MagicNumber") @@ -9,9 +8,4 @@ fun Bitmap.toByteArray(): ByteArray { val stream = ByteArrayOutputStream() this.compress(Bitmap.CompressFormat.JPEG, 20, stream) return stream.toByteArray() -} - -@Suppress("MagicNumber") -fun ByteArray.toBitmap(): Bitmap { - return BitmapFactory.decodeByteArray(this, 0, this.size) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Context.kt b/app/src/main/java/com/tangem/tap/common/extensions/Context.kt index 8cdf3302b7..14f4cd0dbf 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Context.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Context.kt @@ -7,10 +7,6 @@ import android.net.* import androidx.annotation.* import androidx.core.content.* -fun Context.isPermissionGranted(permission: String): Boolean { - return ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED -} - /** * Get uri to any resource type via given Resource Instance * @param resId - resource id diff --git a/app/src/main/java/com/tangem/tap/common/extensions/ImageRequest.kt b/app/src/main/java/com/tangem/tap/common/extensions/ImageRequest.kt deleted file mode 100644 index 2eb87310f9..0000000000 --- a/app/src/main/java/com/tangem/tap/common/extensions/ImageRequest.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.tangem.tap.common.extensions - -import coil.request.ImageRequest - -/** -[REDACTED_AUTHOR] - */ -fun ImageRequest.Builder.cardImageData(any: Any?): ImageRequest.Builder = apply { - data(any) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/ImageView.kt b/app/src/main/java/com/tangem/tap/common/extensions/ImageView.kt deleted file mode 100644 index 0c3a471d54..0000000000 --- a/app/src/main/java/com/tangem/tap/common/extensions/ImageView.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.tangem.tap.common.extensions - -import android.widget.ImageView -import androidx.annotation.DrawableRes - -/** -[REDACTED_AUTHOR] - */ -fun ImageView.setDrawable(@DrawableRes resId: Int) { - setImageDrawable(context.getDrawableCompat(resId)) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt index 51ac9818c4..df5762ec97 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Navigation.kt @@ -14,7 +14,6 @@ import com.tangem.tap.features.details.ui.cardsettings.coderecovery.AccessCodeRe import com.tangem.tap.features.details.ui.details.DetailsFragment import com.tangem.tap.features.details.ui.resetcard.ResetCardFragment import com.tangem.tap.features.details.ui.securitymode.SecurityModeFragment -import com.tangem.tap.features.details.ui.walletconnect.QrScanFragment import com.tangem.tap.features.details.ui.walletconnect.WalletConnectFragment import com.tangem.tap.features.disclaimer.ui.DisclaimerFragment import com.tangem.tap.features.home.HomeFragment @@ -27,9 +26,6 @@ import com.tangem.tap.features.saveWallet.ui.SaveWalletBottomSheetFragment import com.tangem.tap.features.send.ui.SendFragment import com.tangem.tap.features.shop.ui.ShopFragment import com.tangem.tap.features.tokens.impl.presentation.TokensListFragment -import com.tangem.tap.features.wallet.ui.WalletDetailsFragment -import com.tangem.tap.features.wallet.ui.WalletFragment -import com.tangem.tap.features.walletSelector.ui.WalletSelectorBottomSheetFragment import com.tangem.tap.features.welcome.ui.WelcomeFragment import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.store @@ -132,16 +128,9 @@ private fun fragmentFactory(screen: AppScreen): Fragment { AppScreen.OnboardingTwins -> TwinsCardsFragment() AppScreen.OnboardingOther -> OnboardingOtherCardsFragment() AppScreen.Wallet -> { - val featureToggles = store.state.daggerGraphState.get( - getDependency = DaggerGraphState::walletFeatureToggles, - ) - if (featureToggles.isRedesignedScreenEnabled) { - store.state.daggerGraphState - .get(getDependency = DaggerGraphState::walletRouter) - .getEntryFragment() - } else { - WalletFragment() - } + store.state.daggerGraphState + .get(getDependency = DaggerGraphState::walletRouter) + .getEntryFragment() } AppScreen.Send -> { val featureToggles = store.state.daggerGraphState.get( @@ -176,24 +165,20 @@ private fun fragmentFactory(screen: AppScreen): Fragment { } AppScreen.AddCustomToken -> AddCustomTokenFragment() AppScreen.WalletDetails -> { - val featureToggles = store.state.daggerGraphState.get( - getDependency = DaggerGraphState::walletFeatureToggles, - ) - if (featureToggles.isRedesignedScreenEnabled) { - store.state.daggerGraphState - .get(getDependency = DaggerGraphState::tokenDetailsRouter) - .getEntryFragment() - } else { - WalletDetailsFragment() - } + store.state.daggerGraphState + .get(getDependency = DaggerGraphState::tokenDetailsRouter) + .getEntryFragment() } AppScreen.WalletConnectSessions -> WalletConnectFragment() - AppScreen.QrScan -> QrScanFragment() + AppScreen.QrScanning -> { + store.state.daggerGraphState + .get(getDependency = DaggerGraphState::qrScanningRouter) + .getEntryFragment() + } AppScreen.ReferralProgram -> ReferralFragment() AppScreen.Swap -> SwapFragment() AppScreen.Welcome -> WelcomeFragment() AppScreen.SaveWallet -> SaveWalletBottomSheetFragment() - AppScreen.WalletSelector -> WalletSelectorBottomSheetFragment() AppScreen.AppCurrencySelector -> AppCurrencySelectorFragment() AppScreen.ModalNotification -> ModalNotificationBottomSheetFragment() } diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt b/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt index 5f259977e0..a2206cb3bc 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt @@ -1,17 +1,11 @@ package com.tangem.tap.common.extensions -import android.text.Spanned -import android.text.SpannedString -import android.text.style.RelativeSizeSpan -import androidx.core.text.buildSpannedString import com.tangem.common.extensions.isZero -import timber.log.Timber import java.math.BigDecimal import java.math.RoundingMode import java.text.DecimalFormat import java.text.DecimalFormatSymbols -import java.text.NumberFormat -import java.util.* +import java.util.Locale // TODO: move extensions to utils fun BigDecimal.toFormattedString( @@ -29,105 +23,6 @@ fun BigDecimal.toFormattedString( return df.format(this) } -/** - * To formatted crypto currency string - * Specific method because there is no crypto currency codes in Locale - */ -@Suppress("MagicNumber") -fun BigDecimal.toFormattedCryptoCurrencyString( - decimals: Int, - currency: String, - roundingMode: RoundingMode = RoundingMode.DOWN, - limitNumberOfDecimals: Boolean = true, -): String { - val decimalsForRounding = if (limitNumberOfDecimals) { - if (decimals > 8) 8 else decimals - } else { - decimals - } - try { - val locale = Locale.getDefault() - val formatter = NumberFormat.getCurrencyInstance(locale) - // first create currency instance for "USD" with Locale default to replace currency to crypto later - Currency.getInstance("USD")?.let { currencyTmp -> - formatter.currency = currencyTmp - formatter.maximumFractionDigits = decimalsForRounding - formatter.minimumFractionDigits = 0 - formatter.isGroupingUsed = true - formatter.roundingMode = roundingMode - // cause formatter created for USD, replace Currency with Crypto symbol on right by Locale place - return formatter.format(this).replace(currencyTmp.getSymbol(locale), "$currency ") - } - } catch (e: IllegalArgumentException) { - Timber.e(e, "can't parse currency") - } - // if something went wrong - use old way to format - val formattedAmount = this.toFormattedString( - decimals = decimalsForRounding, - roundingMode = roundingMode, - locale = Locale.getDefault(), - ) - return "$formattedAmount $currency " -} - -fun BigDecimal.toFiatRateString(fiatCurrencyName: String, fiatCode: String): String { - try { - val formatter = NumberFormat.getCurrencyInstance(Locale.getDefault()) - Currency.getInstance(fiatCode)?.let { currency -> - formatter.currency = currency - formatter.maximumFractionDigits = 2 - formatter.roundingMode = RoundingMode.HALF_UP - return formatter.format(this).replace(currency.symbol, "$fiatCurrencyName ") - } - } catch (e: IllegalArgumentException) { - Timber.e(e, "can't parse currency") - } - val value = this - .setScale(2, RoundingMode.HALF_UP) - .formatWithSpaces() - return "$value $fiatCurrencyName" -} - -fun BigDecimal.toFiatString( - rateValue: BigDecimal, - fiatCurrencyName: String, - fiatCode: String, - formatWithSpaces: Boolean = false, -): String { - val fiatValue = rateValue.multiply(this) - return fiatValue.toFormattedFiatValue( - fiatCurrencyName = fiatCurrencyName, - fiatCode = fiatCode, - formatWithSpaces = formatWithSpaces, - ) -} - -fun BigDecimal.toFiatValue(rateValue: BigDecimal): BigDecimal { - val fiatValue = rateValue.multiply(this) - return fiatValue.setScale(2, RoundingMode.HALF_UP) -} - -fun BigDecimal.toFormattedFiatValue( - fiatCurrencyName: String, - fiatCode: String, - formatWithSpaces: Boolean = false, -): String { - try { - val formatter = NumberFormat.getCurrencyInstance(Locale.getDefault()) - Currency.getInstance(fiatCode)?.let { currency -> - formatter.currency = currency - formatter.maximumFractionDigits = 2 - formatter.roundingMode = RoundingMode.HALF_UP - return formatter.format(this).replace(currency.symbol, "$fiatCurrencyName ") - } - } catch (e: IllegalArgumentException) { - Timber.e(e, "can't parse currency") - } - val fiatValue = this.setScale(2, RoundingMode.HALF_UP) - .let { if (formatWithSpaces) it.formatWithSpaces() else it } - return " $fiatValue $fiatCurrencyName" -} - fun BigDecimal.stripZeroPlainString(): String = this.stripTrailingZeros().toPlainString() // 0.00 -> 0.00 @@ -146,70 +41,4 @@ fun BigDecimal.setPrecision(precision: Int, roundingMode: RoundingMode = Roundin return this.setScale(scale() - precision() + precision, roundingMode) } -fun BigDecimal.isPositive(): Boolean = this.compareTo(BigDecimal.ZERO) == 1 -fun BigDecimal.isNegative(): Boolean = this.compareTo(BigDecimal.ZERO) == -1 -fun BigDecimal.isGreaterThan(value: BigDecimal): Boolean = this.compareTo(value) == 1 -fun BigDecimal.isLessThan(value: BigDecimal): Boolean = this.compareTo(value) == -1 - -fun BigDecimal.isGreaterThanOrEqual(value: BigDecimal): Boolean { - val compareResult = this.compareTo(value) - return compareResult == 1 || compareResult == 0 -} - -fun BigDecimal.isLessThanOrEqual(value: BigDecimal): Boolean { - val compareResult = this.compareTo(value) - return compareResult == -1 || compareResult == 0 -} - -fun BigDecimal.formatAmountAsSpannedString( - currencySymbol: String, - reminderPartSizeProportion: Float = 0.7f, -): SpannedString { - val amount = this - .setScale(2, RoundingMode.HALF_UP) - .formatWithSpaces() - val integer = amount.substringBefore('.') - val reminder = amount.substringAfter('.') - - // test formatter log Log.e("TEST ", BigDecimal("1234567890987654321.1234567890987654321").formatWithSpaces()) - - return buildSpannedString { - append(integer) - append('.') - append( - "$reminder $currencySymbol", - RelativeSizeSpan(reminderPartSizeProportion), - Spanned.SPAN_EXCLUSIVE_EXCLUSIVE, - ) - } -} - -@Suppress("MagicNumber") -fun BigDecimal.formatWithSpaces(): String { - val str = this.toString() - var integerStr = str.substringBefore('.') - val reminderStr = str.substringAfter('.') - val packets = arrayListOf() - - var index: Int = integerStr.length - while (0 < index) { - if (index <= 3) { - packets.add(integerStr) - break - } - index -= 3 - packets.add(integerStr.substring(startIndex = index)) - integerStr = integerStr.substring(startIndex = 0, endIndex = index) - } - - return buildString { - packets.reversed().forEachIndexed { index, packet -> - append(packet) - if (index != packets.lastIndex) append(' ') - } - if (reminderStr.isNotBlank()) { - append('.') - append(reminderStr) - } - } -} \ No newline at end of file +fun BigDecimal.isPositive(): Boolean = this.signum() == 1 \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Store.kt b/app/src/main/java/com/tangem/tap/common/extensions/Store.kt index bcd2445a5f..79c87a7f28 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Store.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Store.kt @@ -42,16 +42,8 @@ fun Store<*>.dispatchNotification(resId: Int) { dispatchOnMain(GlobalAction.ShowNotification(resId)) } -suspend fun Store.onUserWalletSelected( - userWallet: UserWallet, - refresh: Boolean = false, - sendAnalyticsEvent: Boolean = false, -) { - state.globalState.tapWalletManager.onWalletSelected(userWallet, refresh, sendAnalyticsEvent) -} - -fun Store<*>.dispatchToastNotification(resId: Int) { - dispatchOnMain(GlobalAction.ShowToastNotification(resId)) +suspend fun Store.onUserWalletSelected(userWallet: UserWallet, sendAnalyticsEvent: Boolean = false) { + state.globalState.tapWalletManager.onWalletSelected(userWallet, sendAnalyticsEvent) } fun Store<*>.dispatchErrorNotification(error: TapError) { diff --git a/app/src/main/java/com/tangem/tap/common/extensions/String.kt b/app/src/main/java/com/tangem/tap/common/extensions/String.kt index 911bd470a1..448ae3e36a 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/String.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/String.kt @@ -1,41 +1,12 @@ package com.tangem.tap.common.extensions -import android.content.Context import android.graphics.Bitmap import android.graphics.Color -import android.net.Uri -import android.text.Spannable -import android.text.style.ForegroundColorSpan -import androidx.core.content.ContextCompat -import androidx.core.text.toSpannable import com.google.zxing.BarcodeFormat import com.google.zxing.EncodeHintType import com.google.zxing.qrcode.QRCodeWriter import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel -import java.util.* - -fun String?.ellipsizeBeforeSpace(allowedSize: Int): String { - if (this.isNullOrBlank()) return "" - val size = this.length - val sizeDifference = size - allowedSize - val endIndex = this.indexOf(" ") - val startIndex = endIndex - sizeDifference - val newString = this.removeRange(startIndex, endIndex) - return newString.substring(0 until startIndex) + "..." + - newString.substring(startIndex until newString.length) -} - -fun String.colorSegment(context: Context, color: Int, startIndex: Int = 0, endIndex: Int = this.length): Spannable { - return this.toSpannable() - .also { spannable -> - spannable.setSpan( - ForegroundColorSpan(ContextCompat.getColor(context, color)), - startIndex, - endIndex, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE, - ) - } -} +import java.util.Hashtable @Suppress("MagicNumber") fun String.toQrCode(): Bitmap { @@ -58,8 +29,6 @@ fun String.toQrCode(): Bitmap { return bmp } -fun String.urlEncode(): String = Uri.encode(this) - fun String.removePrefixOrNull(prefix: String): String? = when { startsWith(prefix) -> substring(prefix.length) else -> null diff --git a/app/src/main/java/com/tangem/tap/common/extensions/StringBuilder.kt b/app/src/main/java/com/tangem/tap/common/extensions/StringBuilder.kt index 961e77e8f7..b6101bd4d8 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/StringBuilder.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/StringBuilder.kt @@ -1,24 +1,3 @@ package com.tangem.tap.common.extensions -/** -[REDACTED_AUTHOR] - */ -fun StringBuilder.appendIf(value: String, predicate: (String) -> Boolean): StringBuilder { - if (predicate(value)) this.append(value) - return this -} - -fun StringBuilder.appendIfNotNull(value: String?, prefix: String? = null, postfix: String? = null): StringBuilder { - if (value == null) return this - - prefix?.let { append(it) } - append(value) - postfix?.let { append(it) } - return this -} - -fun String.appendIfNotNull(value: String?, prefix: String? = null, postfix: String? = null): String { - return StringBuilder(this).apply { appendIfNotNull(value, prefix, postfix) }.toString() -} - fun StringBuilder.breakLine(count: Int = 1): StringBuilder = append("\n".repeat(count)) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Toast.kt b/app/src/main/java/com/tangem/tap/common/extensions/Toast.kt deleted file mode 100644 index 23d969af40..0000000000 --- a/app/src/main/java/com/tangem/tap/common/extensions/Toast.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.tangem.tap.common.extensions - -import android.content.Context -import android.view.View -import android.widget.Toast -import androidx.annotation.StringRes -import androidx.fragment.app.Fragment - -fun Context.toast(message: String, length: Int = Toast.LENGTH_LONG) { - Toast.makeText(this, message, length).show() -} - -fun Context.toast(@StringRes messageRes: Int, length: Int = Toast.LENGTH_LONG) { - Toast.makeText(this, this.getString(messageRes), length).show() -} - -fun View.toast(message: String, length: Int = Toast.LENGTH_LONG) { - context.toast(message, length) -} - -fun View.toast(@StringRes messageRes: Int, length: Int = Toast.LENGTH_LONG) { - context.toast(context.getString(messageRes), length) -} - -fun Fragment.toast(message: String, length: Int = Toast.LENGTH_LONG) { - context?.toast(message, length) -} - -fun Fragment.toast(@StringRes messageRes: Int, length: Int = Toast.LENGTH_LONG) { - context?.let { it.toast(it.getString(messageRes), length) } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Token.kt b/app/src/main/java/com/tangem/tap/common/extensions/Token.kt deleted file mode 100644 index 7acbdfd9fb..0000000000 --- a/app/src/main/java/com/tangem/tap/common/extensions/Token.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.tangem.tap.common.extensions - -import android.graphics.Color -import androidx.annotation.ColorInt -import androidx.core.graphics.luminance -import androidx.core.graphics.toColorInt -import com.tangem.blockchain.common.Token - -@Suppress("MagicNumber") -@ColorInt -fun Token.getColor(isTestnet: Boolean = false): Int { - val defaultColor = "#C7C7CC".toColorInt() // equivalent to R.color.lightGray4 - - return if (isTestnet) { - defaultColor - } else { - try { - ("#" + this.contractAddress.subSequence(2..7).toString()).toColorInt() - } catch (exception: Exception) { - defaultColor - } - } -} - -@Suppress("MagicNumber") -@ColorInt -fun Token.getTextColor(isTestnet: Boolean = false): Int = when { - isTestnet -> Color.WHITE - this.getColor().luminance > 0.5 -> Color.BLACK - else -> Color.WHITE -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Transition.kt b/app/src/main/java/com/tangem/tap/common/extensions/Transition.kt deleted file mode 100644 index d1e6b742f5..0000000000 --- a/app/src/main/java/com/tangem/tap/common/extensions/Transition.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.tangem.tap.common.extensions - -import androidx.transition.Transition - -/** -[REDACTED_AUTHOR] - */ -inline fun Transition.addListener( - crossinline onStart: (animator: Transition) -> Unit = {}, - crossinline onEnd: (animator: Transition) -> Unit = {}, - crossinline onCancel: (animator: Transition) -> Unit = {}, - crossinline onPause: (animator: Transition) -> Unit = {}, - crossinline onRepeat: (animator: Transition) -> Unit = {}, -): Transition.TransitionListener { - val listener = object : Transition.TransitionListener { - override fun onTransitionStart(transition: Transition) = onStart(transition) - override fun onTransitionEnd(transition: Transition) = onEnd(transition) - override fun onTransitionCancel(transition: Transition) = onCancel(transition) - override fun onTransitionPause(transition: Transition) = onPause(transition) - override fun onTransitionResume(transition: Transition) = onRepeat(transition) - } - addListener(listener) - return listener -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/UI.kt b/app/src/main/java/com/tangem/tap/common/extensions/UI.kt index 980f206701..524edaded8 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/UI.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/UI.kt @@ -3,25 +3,12 @@ package com.tangem.tap.common.extensions import android.app.Activity -import android.content.ClipData -import android.content.ClipboardManager -import android.content.Context -import android.content.ContextWrapper -import android.content.Intent +import android.content.* import android.graphics.drawable.Drawable -import android.util.TypedValue import android.view.View -import android.view.ViewGroup -import android.view.inputmethod.InputMethodManager -import androidx.annotation.ColorInt -import androidx.annotation.ColorRes -import androidx.annotation.DrawableRes -import androidx.annotation.PluralsRes -import androidx.annotation.StringRes +import androidx.annotation.* import androidx.core.content.ContextCompat -import androidx.core.view.isVisible import androidx.fragment.app.Fragment -import com.google.android.material.card.MaterialCardView fun Context.getDrawableCompat(@DrawableRes drawableResId: Int): Drawable? { return ContextCompat.getDrawable(this, drawableResId) @@ -72,48 +59,9 @@ fun View.hide(invokeBeforeStateChanged: (() -> Unit)? = null) { this.visibility = View.GONE } -fun View.invisible(invisible: Boolean = true, invokeBeforeStateChanged: (() -> Unit)? = null) { - if (invisible) { - if (this.visibility == View.INVISIBLE) return - - invokeBeforeStateChanged?.invoke() - this.visibility = View.INVISIBLE - } else { - this.show(invokeBeforeStateChanged) - } -} - -fun Context.dpToPixels(dp: Int): Int = TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, - dp.toFloat(), - this.resources.displayMetrics, -).toInt() - tailrec fun Context?.getActivity(): Activity? = this as? Activity ?: (this as? ContextWrapper)?.baseContext?.getActivity() -fun MaterialCardView.setMargins( - marginLeftDp: Int = 16, - marginTopDp: Int = 8, - marginRightDp: Int = 16, - marginBottomDp: Int = 8, -) { - val params = this.layoutParams - (params as ViewGroup.MarginLayoutParams).setMargins( - context.dpToPixels(marginLeftDp), - context.dpToPixels(marginTopDp), - context.dpToPixels(marginRightDp), - context.dpToPixels(marginBottomDp), - ) - this.layoutParams = params -} - -fun View.hideKeyboard() { - val inputMethodManager = - context.getSystemService(Context.INPUT_METHOD_SERVICE) as? InputMethodManager - inputMethodManager?.hideSoftInputFromWindow(this.windowToken, 0) -} - fun Context.copyToClipboard(value: Any, label: String = "") { val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager ?: return @@ -140,37 +88,6 @@ fun Context.shareText(text: String) { startActivity(shareIntent) } -fun Fragment.shareText(text: String) { - requireContext().shareText(text) -} - fun View.getString(resId: Int, vararg formatArgs: Any?): String { return context.getString(resId, *formatArgs) -} - -fun View.animateVisibility( - show: Boolean, - durationMillis: Long = SHORT_ANIMATION_DURATION, - hiddenVisibility: Int = View.GONE, -) { - if (show) { - if (this.visibility == View.VISIBLE) return - this.animate() - .alpha(1f) - .setDuration(durationMillis) - .withStartAction { - this.alpha = 0f - this.isVisible = true - } - } else { - if (this.visibility == hiddenVisibility) return - this.animate() - .alpha(0f) - .setDuration(durationMillis) - .withStartAction { - this.visibility = hiddenVisibility - } - } -} - -private const val SHORT_ANIMATION_DURATION = 80L \ No newline at end of file +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/ViewGroup.kt b/app/src/main/java/com/tangem/tap/common/extensions/ViewGroup.kt index 50c90e1133..214138e5dc 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/ViewGroup.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/ViewGroup.kt @@ -3,15 +3,9 @@ package com.tangem.tap.common.extensions import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.ViewParent -import androidx.core.view.forEach import androidx.transition.AutoTransition import androidx.transition.Transition import androidx.transition.TransitionManager -import com.google.android.material.chip.Chip -import com.google.android.material.chip.ChipGroup -import com.tangem.tap.common.GlobalLayoutStateHandler -import timber.log.Timber /** [REDACTED_AUTHOR] @@ -20,30 +14,6 @@ fun ViewGroup.inflate(viewToInflate: Int, attachToRoot: Boolean = false): View { return LayoutInflater.from(context).inflate(viewToInflate, this, attachToRoot) } -fun ViewParent?.beginDelayedTransition(transition: Transition = AutoTransition()) { - if (this == null) Timber.e("Can't invoke beginDelayedTransition, because parent is NULL") - (this as? ViewGroup)?.beginDelayedTransition(transition) -} - fun ViewGroup.beginDelayedTransition(transition: Transition = AutoTransition()) { TransitionManager.beginDelayedTransition(this, transition) -} - -fun View.beginDelayedTransition(transition: Transition = AutoTransition()) { - (this as? ViewGroup)?.beginDelayedTransition(transition) -} - -fun ChipGroup.fitChipsByGroupWidth() { - val layoutStateHandler = GlobalLayoutStateHandler(this) - layoutStateHandler.onStateChanged = stateHandler@{ - if (it.childCount < 2) { - layoutStateHandler.detach() - return@stateHandler - } - - val spacingBetweenViews = it.chipSpacingHorizontal * (it.childCount - 1) - val width = (it.width - spacingBetweenViews) / it.childCount - it.forEach { chip -> (chip as? Chip)?.width = width } - layoutStateHandler.detach() - } } \ No newline at end of file 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 0427435795..9f6ba786f0 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 @@ -3,15 +3,14 @@ package com.tangem.tap.common.extensions import com.tangem.blockchain.common.BlockchainSdkError import com.tangem.blockchain.common.Wallet import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.common.address.AddressType import com.tangem.common.services.Result import com.tangem.domain.common.extensions.amountToCreateAccount import com.tangem.tap.common.TestActions import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder import com.tangem.tap.domain.TapError import com.tangem.tap.domain.getFirstToken -import com.tangem.tap.domain.model.WalletDataModel -import com.tangem.tap.features.demo.isDemoCard -import com.tangem.tap.features.wallet.redux.reducers.createAddressesData +import com.tangem.tap.domain.model.WalletAddressData import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.store @@ -59,7 +58,7 @@ suspend fun WalletManager.safeUpdate(isDemoCard: Boolean): Result = try } } -fun WalletManager.getTopUpUrl(): String? { +internal fun WalletManager.getTopUpUrl(): String? { val globalState = store.state.globalState val defaultAddress = wallet.address @@ -73,9 +72,28 @@ fun WalletManager.getTopUpUrl(): String? { ) } -fun WalletManager?.getAddressData(): WalletDataModel.AddressData? { +internal fun WalletManager?.getAddressData(): WalletAddressData? { val wallet = this?.wallet ?: return null val addressDataList = wallet.createAddressesData() return if (addressDataList.isEmpty()) null else addressDataList[0] +} + +private fun Wallet.createAddressesData(): List { + val listOfAddressData = mutableListOf() + // put a defaultAddress at the first place + addresses.forEach { + val addressData = WalletAddressData( + it.value, + it.type, + getShareUri(it.value), + getExploreUrl(it.value), + ) + if (it.type == AddressType.Default) { + listOfAddressData.add(0, addressData) + } else { + listOfAddressData.add(addressData) + } + } + return listOfAddressData } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/WebView.kt b/app/src/main/java/com/tangem/tap/common/extensions/WebView.kt index 7aa93b2bb1..1afe53b3ba 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/WebView.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/WebView.kt @@ -2,17 +2,6 @@ package com.tangem.tap.common.extensions import android.webkit.WebView -/** -[REDACTED_AUTHOR] - */ -fun WebView.configureSettings() { - resumeTimers() - settings.apply { - javaScriptEnabled = true - domStorageEnabled = true - } -} - fun WebView.stop() { stopLoading() pauseTimers() 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 index 860f1e3374..b588d0d641 100644 --- a/app/src/main/java/com/tangem/tap/common/feedback/AdditionalFeedbackInfo.kt +++ b/app/src/main/java/com/tangem/tap/common/feedback/AdditionalFeedbackInfo.kt @@ -3,6 +3,7 @@ package com.tangem.tap.common.feedback import android.os.Build import com.tangem.blockchain.common.* import com.tangem.blockchain.common.address.Address +import com.tangem.crypto.NetworkType import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.userwallets.UserWalletIdBuilder @@ -35,6 +36,7 @@ class AdditionalFeedbackInfo { var cardIssuer: String = "" var cardBlockchain: String = "" var userWalletId: String = "" + var extendedPublicKey: String = "" // wallets val walletsInfo = CopyOnWriteArrayList() @@ -70,6 +72,9 @@ class AdditionalFeedbackInfo { cardIssuer = data.card.issuer.name signedHashesCount = formatSignedHashes(data.card.wallets) userWalletId = UserWalletIdBuilder.scanResponse(data).build()?.stringValue ?: "" + extendedPublicKey = data.card.wallets.firstOrNull { it.extendedPublicKey != null } + ?.extendedPublicKey + ?.serialize(networkType = NetworkType.Mainnet).orEmpty() } @Deprecated("Don't use it directly") 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 index 7ad4477154..33c6bba4f2 100644 --- a/app/src/main/java/com/tangem/tap/common/feedback/FeedbackDataBuilder.kt +++ b/app/src/main/java/com/tangem/tap/common/feedback/FeedbackDataBuilder.kt @@ -1,5 +1,6 @@ package com.tangem.tap.common.feedback +import com.tangem.blockchain.common.Blockchain import com.tangem.tap.common.extensions.breakLine class FeedbackDataBuilder( @@ -27,26 +28,32 @@ class FeedbackDataBuilder( } fun appendWalletsInfo(): FeedbackDataBuilder { - infoHolder.walletsInfo.forEach { + infoHolder.walletsInfo.forEach { walletInfo -> builder.appendDelimiter() - builder.appendKeyValue("Blockchain", it.blockchain.fullName) - builder.appendKeyValue("Derivation path", it.derivationPath) - builder.appendKeyValue("Outputs count", it.outputsCount) + builder.appendKeyValue("Blockchain", walletInfo.blockchain.fullName) + builder.appendKeyValue("Derivation path", walletInfo.derivationPath) - if (it.tokens.isNotEmpty()) { + if (walletInfo.blockchain == Blockchain.Bitcoin) { + builder.appendKeyValue("XPUB", infoHolder.extendedPublicKey) + } + + builder.appendKeyValue("Outputs count", walletInfo.outputsCount) + + if (walletInfo.tokens.isNotEmpty()) { builder.append("Tokens:") breakLine() - it.tokens.forEach { token -> + walletInfo.tokens.forEach { token -> builder.appendKeyValue("ID", token.id ?: "[custom token]") builder.appendKeyValue("Name", token.name) builder.appendKeyValue("Contract address", token.contractAddress) } } - builder.appendKeyValue("Host", it.host) - builder.appendKeyValue("Wallet address", it.addresses) - builder.appendKeyValue("Explorer link", it.explorerLink) + builder.appendKeyValue("Host", walletInfo.host) + builder.appendKeyValue("Wallet address", walletInfo.addresses) + builder.appendKeyValue("Explorer link", walletInfo.explorerLink) } + return this } diff --git a/app/src/main/java/com/tangem/tap/common/qrCodeScan/ScanQrCodeActivity.kt b/app/src/main/java/com/tangem/tap/common/qrCodeScan/ScanQrCodeActivity.kt deleted file mode 100644 index f49613149a..0000000000 --- a/app/src/main/java/com/tangem/tap/common/qrCodeScan/ScanQrCodeActivity.kt +++ /dev/null @@ -1,86 +0,0 @@ -package com.tangem.tap.common.qrCodeScan - -import android.Manifest -import android.content.Intent -import android.content.pm.PackageManager -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity -import androidx.camera.lifecycle.ProcessCameraProvider -import androidx.core.app.ActivityCompat -import androidx.core.content.ContextCompat -import by.kirich1409.viewbindingdelegate.viewBinding -import com.google.common.util.concurrent.ListenableFuture -import com.otaliastudios.cameraview.CameraView.PERMISSION_REQUEST_CODE -import com.tangem.tap.features.details.ui.walletconnect.dialogs.PreviewBinder -import com.tangem.wallet.R -import com.tangem.wallet.databinding.LayoutQrScanningBinding -import java.util.concurrent.ExecutorService -import java.util.concurrent.Executors - -/** -[REDACTED_AUTHOR] - */ -class ScanQrCodeActivity : AppCompatActivity() { - - private val binding: LayoutQrScanningBinding by viewBinding(LayoutQrScanningBinding::bind) - - private var cameraProviderFuture: ListenableFuture? = null - private var cameraExecutor: ExecutorService? = null - - private val binder = PreviewBinder() - - override fun onCreate(state: Bundle?) { - super.onCreate(state) - - if (!permissionIsGranted()) requestPermission() - - setContentView(R.layout.layout_qr_scanning) - - cameraProviderFuture = ProcessCameraProvider.getInstance(this) - cameraExecutor = Executors.newSingleThreadExecutor() - - cameraProviderFuture?.addListener( - { - val cameraProvider = cameraProviderFuture?.get() - binder.bindPreview( - context = this, - binding = binding, - lifecycleOwner = this, - cameraProvider = requireNotNull(cameraProvider), - cameraExecutor = requireNotNull(cameraExecutor), - onScanned = { result -> - setResult(SCAN_QR_REQUEST_CODE, Intent().apply { putExtra(SCAN_RESULT, result) }) - finish() - }, - ) - }, - ContextCompat.getMainExecutor(this), - ) - - binding.overlay.post { - binding.overlay.setViewFinder() - } - } - - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { - if (requestCode != PERMISSION_REQUEST_CODE) return - - if (grantResults.isEmpty() || grantResults[0] != PackageManager.PERMISSION_GRANTED) { - finish() - } - } - - private fun permissionIsGranted(): Boolean { - val cameraPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) - return cameraPermission == PackageManager.PERMISSION_GRANTED - } - - private fun requestPermission() { - ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), PERMISSION_REQUEST_CODE) - } - - companion object { - const val SCAN_QR_REQUEST_CODE = 1001 - const val SCAN_RESULT = "scanResult" - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/qrCodeScan/ViewFinderOverlay.kt b/app/src/main/java/com/tangem/tap/common/qrCodeScan/ViewFinderOverlay.kt deleted file mode 100644 index f4939b828f..0000000000 --- a/app/src/main/java/com/tangem/tap/common/qrCodeScan/ViewFinderOverlay.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.tangem.tap.common.qrCodeScan - -import android.content.Context -import android.graphics.* -import android.util.AttributeSet -import android.view.View -import androidx.core.content.ContextCompat -import com.tangem.wallet.R - -class ViewFinderOverlay(context: Context, attrs: AttributeSet) : View(context, attrs) { - - private val boxPaint: Paint = Paint().apply { - color = ContextCompat.getColor(context, R.color.white) - style = Paint.Style.STROKE - strokeWidth = context.resources.getDimensionPixelOffset(R.dimen.qr_border_stroke_width).toFloat() - } - - private val boxWidthRatio = 0.8F - private val boxCornerRadius: Float = - context.resources.getDimensionPixelOffset(R.dimen.qr_border_corner_radius).toFloat() - - private var boxRect: RectF? = null - - @Suppress("MagicNumber") - fun setViewFinder() { - val overlayWidth = width.toFloat() - val overlayHeight = height.toFloat() - val boxSize = overlayWidth * boxWidthRatio - val cx = overlayWidth / 2 - val cy = overlayHeight / 2 - boxRect = RectF(cx - boxSize / 2, cy - boxSize / 2, cx + boxSize / 2, cy + boxSize / 2) - - invalidate() - } - - override fun draw(canvas: Canvas) { - super.draw(canvas) - boxRect?.let { - canvas.drawRoundRect(it, boxCornerRadius, boxCornerRadius, boxPaint) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppDialog.kt b/app/src/main/java/com/tangem/tap/common/redux/AppDialog.kt index 38479e6a1f..fc362509c3 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppDialog.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppDialog.kt @@ -3,16 +3,14 @@ package com.tangem.tap.common.redux import com.tangem.common.extensions.VoidCallback import com.tangem.core.navigation.StateDialog import com.tangem.tap.common.TestAction -import com.tangem.tap.domain.model.WalletDataModel -import com.tangem.tap.features.wallet.models.Currency +import com.tangem.tap.domain.model.Currency +import com.tangem.tap.domain.model.WalletAddressData +import com.tangem.wallet.R /** [REDACTED_AUTHOR] */ sealed class AppDialog : StateDialog { - data class SimpleOkDialog(val header: String, val message: String, val onOk: VoidCallback? = null) : AppDialog() - data class SimpleOkErrorDialog(val message: String, val onOk: VoidCallback? = null) : AppDialog() - data class SimpleOkWarningDialog(val message: String, val onOk: VoidCallback? = null) : AppDialog() data class SimpleOkDialogRes( val headerId: Int, val messageId: Int, @@ -20,24 +18,33 @@ sealed class AppDialog : StateDialog { val onOk: VoidCallback? = null, ) : AppDialog() - data class OkCancelDialogRes( - val headerId: Int, - val messageId: Int, - val okButton: DialogButton, - val cancelButton: DialogButton, - ) : AppDialog() - - data class DialogButton( - val title: Int, - val action: VoidCallback? = null, - ) - - data class AddressInfoDialog( + internal data class AddressInfoDialog( val currency: Currency, - val addressData: WalletDataModel.AddressData, + val addressData: WalletAddressData, ) : AppDialog() data class TestActionsDialog( val actionsList: List, ) : AppDialog() + + data class RussianCardholdersWarningDialog(val data: Data?) : AppDialog() { + data class Data(val topUpUrl: String) + } + + data class RemoveWalletDialog( + val currencyTitle: String, + val onOk: () -> Unit, + ) : AppDialog() { + val messageRes: Int = R.string.token_details_hide_alert_message + val titleRes: Int = R.string.token_details_hide_alert_title + val primaryButtonRes: Int = R.string.token_details_hide_alert_hide + } + + data class TokensAreLinkedDialog( + val currencyTitle: String, + val currencySymbol: String, + ) : AppDialog() { + val messageRes: Int = R.string.token_details_unable_hide_alert_message + val titleRes: Int = R.string.token_details_unable_hide_alert_title + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt index 8175888c08..a1232dd031 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt @@ -15,8 +15,6 @@ import com.tangem.tap.features.send.redux.reducers.SendScreenReducer import com.tangem.tap.features.shop.redux.ShopReducer import com.tangem.tap.features.signin.redux.SignInReducer import com.tangem.tap.features.tokens.legacy.redux.TokensReducer -import com.tangem.tap.features.wallet.redux.reducers.WalletReducer -import com.tangem.tap.features.walletSelector.redux.WalletSelectorReducer import com.tangem.tap.features.welcome.redux.WelcomeReducer import com.tangem.tap.proxy.AppStateHolder import com.tangem.tap.proxy.redux.DaggerGraphReducer @@ -33,7 +31,6 @@ fun appReducer(action: Action, state: AppState?, appStateHolder: AppStateHolder) onboardingNoteState = OnboardingNoteReducer.reduce(action, state), onboardingWalletState = OnboardingWalletReducer.reduce(action, state), onboardingOtherCardsState = OnboardingOtherCardsReducer.reduce(action, state), - walletState = WalletReducer.reduce(action, state, appStateHolder), twinCardsState = TwinCardsReducer.reduce(action, state), sendState = SendScreenReducer.reduce(action, state.sendState), detailsState = DetailsReducer.reduce(action, state), @@ -43,7 +40,6 @@ fun appReducer(action: Action, state: AppState?, appStateHolder: AppStateHolder) shopState = ShopReducer.reduce(action, state.shopState), welcomeState = WelcomeReducer.reduce(action, state), saveWalletState = SaveWalletReducer.reduce(action, state), - walletSelectorState = WalletSelectorReducer.reduce(action, state), signInState = SignInReducer.reduce(action, state), daggerGraphState = DaggerGraphReducer.reduce(action, state), ) diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt index 7b9b9f2bd4..e312758a52 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt @@ -35,10 +35,7 @@ import com.tangem.tap.features.signin.redux.SignInMiddleware import com.tangem.tap.features.signin.redux.SignInState import com.tangem.tap.features.tokens.legacy.redux.TokensMiddleware import com.tangem.tap.features.tokens.legacy.redux.TokensState -import com.tangem.tap.features.wallet.redux.WalletState -import com.tangem.tap.features.wallet.redux.middlewares.WalletMiddleware -import com.tangem.tap.features.walletSelector.redux.WalletSelectorMiddleware -import com.tangem.tap.features.walletSelector.redux.WalletSelectorState +import com.tangem.tap.features.wallet.redux.middlewares.TradeCryptoMiddleware import com.tangem.tap.features.welcome.redux.WelcomeMiddleware import com.tangem.tap.features.welcome.redux.WelcomeState import com.tangem.tap.proxy.redux.DaggerGraphMiddleware @@ -54,7 +51,6 @@ data class AppState( val onboardingNoteState: OnboardingNoteState = OnboardingNoteState(), val onboardingWalletState: OnboardingWalletState = OnboardingWalletState(), val onboardingOtherCardsState: OnboardingOtherCardsState = OnboardingOtherCardsState(), - val walletState: WalletState = WalletState(), val twinCardsState: TwinCardsState = TwinCardsState(), val sendState: SendState = SendState(), val detailsState: DetailsState = DetailsState(), @@ -64,7 +60,6 @@ data class AppState( val shopState: ShopState = ShopState(), val welcomeState: WelcomeState = WelcomeState(), val saveWalletState: SaveWalletState = SaveWalletState(), - val walletSelectorState: WalletSelectorState = WalletSelectorState(), val signInState: SignInState = SignInState(), val daggerGraphState: DaggerGraphState = DaggerGraphState(), ) : StateType { @@ -72,7 +67,7 @@ data class AppState( private val domainState: DomainState get() = domainStore.state - val domainNetworks: NetworkServices + private val domainNetworks: NetworkServices get() = domainState.globalState.networkServices val featureRepositoryProvider: FeatureRepositoryProvider @@ -92,7 +87,6 @@ data class AppState( OnboardingNoteMiddleware.handler, OnboardingWalletMiddleware.handler, OnboardingOtherCardsMiddleware.handler, - WalletMiddleware().walletMiddleware, TwinCardsMiddleware.handler, SendMiddleware().sendMiddleware, DetailsMiddleware().detailsMiddleware, @@ -103,12 +97,12 @@ data class AppState( ShopMiddleware().shopMiddleware, WelcomeMiddleware().middleware, SaveWalletMiddleware().middleware, - WalletSelectorMiddleware().middleware, LockUserWalletsTimerMiddleware().middleware, AccessCodeRequestPolicyMiddleware().middleware, SignInMiddleware.middleware, DaggerGraphMiddleware.daggerGraphMiddleware, LegacyMiddleware.legacyMiddleware, + TradeCryptoMiddleware.middleware, ) } } diff --git a/app/src/main/java/com/tangem/tap/common/redux/FeatureRepositoryProvider.kt b/app/src/main/java/com/tangem/tap/common/redux/FeatureRepositoryProvider.kt index 5794edffb9..55e4c0b653 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/FeatureRepositoryProvider.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/FeatureRepositoryProvider.kt @@ -3,13 +3,9 @@ package com.tangem.tap.common.redux import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.tap.features.home.data.HomeRepositoryImpl import com.tangem.tap.features.home.domain.HomeRepository -import com.tangem.tap.features.wallet.data.WalletRepositoryImpl -import com.tangem.tap.features.wallet.domain.WalletRepository import com.tangem.utils.coroutines.CoroutineDispatcherProvider class FeatureRepositoryProvider(tangemTechApi: TangemTechApi, dispatchers: CoroutineDispatcherProvider) { - val walletRepository: WalletRepository = WalletRepositoryImpl(tangemTechApi, dispatchers) - val homeRepository: HomeRepository = HomeRepositoryImpl(tangemTechApi, dispatchers) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt index 4a7cb7b8f8..1b862e7ed4 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt @@ -20,22 +20,14 @@ class NotificationsHandler(coordinatorLayout: CoordinatorLayout) { private val basicCoordinatorLayout = WeakReference(coordinatorLayout) private var baseLayout = basicCoordinatorLayout - fun replaceBaseLayout(coordinatorLayout: CoordinatorLayout) { - baseLayout = WeakReference(coordinatorLayout) - } - - fun returnBaseLayout() { - baseLayout = basicCoordinatorLayout - } - - fun showNotification(message: String) { + private fun showNotification(message: String) { baseLayout.get()?.let { layout -> Snackbar.make(layout, message, Snackbar.LENGTH_LONG) .also { snackbar -> snackbar.show() } } } - fun showDebugNotification(message: String) { + private fun showDebugNotification(message: String) { baseLayout.get()?.let { layout -> Snackbar.make(layout, message, Snackbar.LENGTH_LONG) .also { snackbar -> diff --git a/app/src/main/java/com/tangem/tap/common/redux/Request.kt b/app/src/main/java/com/tangem/tap/common/redux/Request.kt deleted file mode 100644 index 4faae37c93..0000000000 --- a/app/src/main/java/com/tangem/tap/common/redux/Request.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.tangem.tap.common.redux - -import org.rekotlin.Action - -abstract class Request : Action { - abstract suspend fun execute() -} \ 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 13a9ee6ac8..c2bd7ec6d8 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 @@ -5,17 +5,15 @@ import com.tangem.common.CompletionResult import com.tangem.core.navigation.StateDialog import com.tangem.datasource.config.ConfigManager import com.tangem.datasource.config.models.ChatConfig +import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.apptheme.model.AppThemeMode import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.wallets.legacy.UserWalletsListManager -import com.tangem.tap.common.analytics.topup.TopUpController -import com.tangem.tap.common.entities.FiatCurrency 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.ToastNotificationAction import com.tangem.tap.domain.TapError import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager @@ -26,7 +24,6 @@ sealed class GlobalAction : Action { // notifications data class ShowNotification(override val messageResource: Int) : GlobalAction(), NotificationAction - data class ShowToastNotification(override val messageResource: Int) : GlobalAction(), ToastNotificationAction data class ShowErrorNotification(override val error: TapError) : GlobalAction(), ErrorAction data class DebugShowErrorNotification(override val error: TapError) : GlobalAction(), DebugErrorAction @@ -61,9 +58,9 @@ sealed class GlobalAction : Action { data class SetIfCardVerifiedOnline(val verified: Boolean) : GlobalAction() - data class ChangeAppCurrency(val appCurrency: FiatCurrency) : GlobalAction() + data class ChangeAppCurrency(val appCurrency: AppCurrency) : GlobalAction() object RestoreAppCurrency : GlobalAction() { - data class Success(val appCurrency: FiatCurrency) : GlobalAction() + data class Success(val appCurrency: AppCurrency) : GlobalAction() } data class UpdateWalletSignedHashes( @@ -78,7 +75,6 @@ sealed class GlobalAction : Action { data class SetConfigManager(val configManager: ConfigManager) : GlobalAction() data class SetWarningManager(val warningManager: WarningMessagesManager) : GlobalAction() data class SetFeedbackManager(val feedbackManager: FeedbackManager) : GlobalAction() - data class SetTopUpController(val topUpController: TopUpController) : GlobalAction() data class SendEmail(val feedbackData: FeedbackData) : GlobalAction() data class OpenChat(val feedbackData: FeedbackData, val chatConfig: ChatConfig? = null) : GlobalAction() diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt index d20875298c..841c3ff5a4 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt @@ -6,19 +6,18 @@ import com.tangem.common.extensions.guard import com.tangem.core.analytics.Analytics import com.tangem.core.navigation.StateDialog import com.tangem.datasource.config.models.Config +import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.common.LogConfig import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ScanResponse -import com.tangem.tap.* -import com.tangem.tap.common.analytics.events.Basic -import com.tangem.tap.common.entities.FiatCurrency +import com.tangem.core.analytics.models.Basic 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.extensions.dispatchWithMain import com.tangem.tap.common.redux.AppState import com.tangem.tap.features.send.redux.SendAction -import com.tangem.tap.features.wallet.redux.WalletAction +import com.tangem.tap.mainScope import com.tangem.tap.network.exchangeServices.BuyExchangeService import com.tangem.tap.network.exchangeServices.CardExchangeRules import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager @@ -27,6 +26,9 @@ import com.tangem.tap.network.exchangeServices.mercuryo.MercuryoEnvironment import com.tangem.tap.network.exchangeServices.mercuryo.MercuryoService import com.tangem.tap.network.exchangeServices.moonpay.MoonPayService import com.tangem.tap.proxy.redux.DaggerGraphState +import com.tangem.tap.scope +import com.tangem.tap.store +import com.tangem.tap.userWalletsListManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch @@ -67,17 +69,7 @@ private fun handleAction(action: Action, appState: () -> AppState?) { } } is GlobalAction.RestoreAppCurrency -> { - val daggerGraphState = store.state.daggerGraphState - val walletFeatureToggles = daggerGraphState.get(DaggerGraphState::walletFeatureToggles) - val detailsFeatureToggles = daggerGraphState.get(DaggerGraphState::detailsFeatureToggles) - - if (walletFeatureToggles.isRedesignedScreenEnabled || - detailsFeatureToggles.isRedesignedAppCurrencySelectorEnabled - ) { - restoreAppCurrencyNew() - } else { - restoreAppCurrencyLegacy() - } + restoreAppCurrency() } is GlobalAction.HideWarningMessage -> { store.state.globalState.warningManager?.let { @@ -87,7 +79,6 @@ private fun handleAction(action: Action, appState: () -> AppState?) { // store.dispatch(WalletAction.Warnings.CheckHashesCount.SaveCardId) // } - store.dispatch(WalletAction.Warnings.Update) store.dispatch(SendAction.Warnings.Update) } } @@ -115,9 +106,6 @@ private fun handleAction(action: Action, appState: () -> AppState?) { } feedbackManager.openChat(chatConfig, action.feedbackData) } - is GlobalAction.UpdateWalletSignedHashes -> { - store.dispatch(WalletAction.Warnings.CheckRemainingSignatures(action.remainingSignatures)) - } is GlobalAction.UpdateFeedbackInfo -> { store.state.globalState.feedbackManager?.infoHolder ?.setWalletsInfo(action.walletManagers) @@ -169,9 +157,6 @@ private fun handleAction(action: Action, appState: () -> AppState?) { } } } - is GlobalAction.SetTopUpController -> { - walletCurrenciesManager.addListener(action.topUpController) - } is GlobalAction.UpdateUserWalletsListManager -> { val walletManagersFacade = store.state.daggerGraphState.get(DaggerGraphState::walletManagersFacade) @@ -201,23 +186,12 @@ private fun handleAction(action: Action, appState: () -> AppState?) { } } -private fun restoreAppCurrencyLegacy() { - store.dispatch( - GlobalAction.RestoreAppCurrency.Success( - preferencesStorage.fiatCurrenciesPrefStorage.getAppCurrency() - ?.run { FiatCurrency(code, name, symbol) } - ?: FiatCurrency.Default, - ), - ) -} - -private fun restoreAppCurrencyNew() { +private fun restoreAppCurrency() { scope.launch { val currency = store.state.daggerGraphState.get(DaggerGraphState::appCurrencyRepository) .getSelectedAppCurrency() .firstOrNull() - ?.run { FiatCurrency(code, name, symbol) } - ?: FiatCurrency.Default + ?: AppCurrency.Default store.dispatchWithMain(GlobalAction.RestoreAppCurrency.Success(currency)) } 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 2b282844d5..f6fe0f4b00 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 @@ -52,7 +52,6 @@ fun globalReducer(action: Action, state: AppState, appStateHolder: AppStateHolde globalState.copy(configManager = action.configManager) } is GlobalAction.SetWarningManager -> globalState.copy(warningManager = action.warningManager) - is GlobalAction.SetTopUpController -> globalState.copy(topUpController = action.topUpController) is GlobalAction.UpdateWalletSignedHashes -> { val card = globalState.scanResponse?.card ?: return globalState val wallet = card.wallets 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 c20fbea0f9..5cdc02d930 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 @@ -2,11 +2,10 @@ package com.tangem.tap.common.redux.global import com.tangem.core.navigation.StateDialog import com.tangem.datasource.config.ConfigManager +import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.apptheme.model.AppThemeMode import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.wallets.legacy.UserWalletsListManager -import com.tangem.tap.common.analytics.topup.TopUpController -import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.feedback.FeedbackManager import com.tangem.tap.domain.TapWalletManager import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager @@ -23,13 +22,12 @@ data class GlobalState( val configManager: ConfigManager? = null, val warningManager: WarningMessagesManager? = null, val feedbackManager: FeedbackManager? = null, - val appCurrency: FiatCurrency = FiatCurrency.Default, + val appCurrency: AppCurrency = AppCurrency.Default, val scanCardFailsCounter: Int = 0, val dialog: StateDialog? = null, val exchangeManager: CurrencyExchangeManager = CurrencyExchangeManager.dummy(), val userCountryCode: String? = null, val userWalletsListManager: UserWalletsListManager? = null, - val topUpController: TopUpController? = null, val appThemeMode: AppThemeMode = AppThemeMode.DEFAULT, ) : StateType diff --git a/app/src/main/java/com/tangem/tap/common/shop/TangemShopService.kt b/app/src/main/java/com/tangem/tap/common/shop/TangemShopService.kt index 97d9bd093b..1a1b5827f0 100644 --- a/app/src/main/java/com/tangem/tap/common/shop/TangemShopService.kt +++ b/app/src/main/java/com/tangem/tap/common/shop/TangemShopService.kt @@ -169,7 +169,7 @@ class TangemShopService(application: Application, shopifyShop: ShopifyShop) { return Result.success(products) } - suspend fun applyPromoCode(promoCode: String, productType: ProductType): Result { + private suspend fun applyPromoCode(promoCode: String, productType: ProductType): Result { val checkout = checkouts[productType] ?: return Result.failure(Exception("No checkout")) val result = if (promoCode.isBlank()) { diff --git a/app/src/main/java/com/tangem/tap/common/shop/shopify/ShopifyService.kt b/app/src/main/java/com/tangem/tap/common/shop/shopify/ShopifyService.kt index 0f7cfdf42b..705bd067a0 100644 --- a/app/src/main/java/com/tangem/tap/common/shop/shopify/ShopifyService.kt +++ b/app/src/main/java/com/tangem/tap/common/shop/shopify/ShopifyService.kt @@ -19,26 +19,7 @@ import kotlin.coroutines.suspendCoroutine @Suppress("LargeClass") class ShopifyService(private val application: Application, val shop: ShopifyShop) { - val client: GraphClient by lazy { initClient() } - - suspend fun getShopName(): Result { - val query = query { rootQuery: QueryRootQuery -> - rootQuery - .shop { shopQuery: ShopQuery -> - shopQuery - .name() - } - } - return when (val result = queryAsync(query)) { - is GraphCallResult.Success -> { - val name = result.response.data!!.shop.name - Result.success(name) - } - is GraphCallResult.Failure -> { - Result.failure(result.error) - } - } - } + private val client: GraphClient by lazy { initClient() } @Suppress("MagicNumber") suspend fun getProducts(collectionTitleFilter: String? = null): Result> { diff --git a/app/src/main/java/com/tangem/tap/common/snackBar/MaxAmountSnackbar.kt b/app/src/main/java/com/tangem/tap/common/snackBar/MaxAmountSnackbar.kt index b540a9d7c2..939fc17c61 100644 --- a/app/src/main/java/com/tangem/tap/common/snackBar/MaxAmountSnackbar.kt +++ b/app/src/main/java/com/tangem/tap/common/snackBar/MaxAmountSnackbar.kt @@ -8,9 +8,13 @@ import android.view.ViewGroup import android.widget.FrameLayout import androidx.constraintlayout.widget.ConstraintLayout import androidx.coordinatorlayout.widget.CoordinatorLayout +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updateLayoutParams import com.google.android.material.snackbar.BaseTransientBottomBar import com.google.android.material.snackbar.ContentViewCallback import com.google.android.material.snackbar.Snackbar +import com.tangem.sdk.extensions.dpToPx import com.tangem.wallet.R /** @@ -27,15 +31,29 @@ class MaxAmountSnackbar( val parent = view.findSuitableParent() ?: throw IllegalArgumentException( "No suitable parent found from the given view. Please provide a valid view.", ) - val inflater = LayoutInflater.from(view.context) + val inflater = LayoutInflater.from(parent.context) val customView = inflater.inflate(R.layout.view_snackbar_max_amount, parent, false) as MaxAmountSnackbarView customView.setOnClickListener { onClick() } return MaxAmountSnackbar(parent, customView).apply { + updateBottomMargin() duration = Snackbar.LENGTH_INDEFINITE } } + private fun MaxAmountSnackbar.updateBottomMargin() { + ViewCompat.setOnApplyWindowInsetsListener(this.view) { _, insets -> + val imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom + val bottomInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom + + this.view.updateLayoutParams { + bottomMargin = imeInsets - bottomInsets + context.dpToPx(dp = 8f).toInt() + } + + insets + } + } + private fun View?.findSuitableParent(): ViewGroup? { var view = this var fallback: ViewGroup? = null diff --git a/app/src/main/java/com/tangem/tap/common/text/Truncate.kt b/app/src/main/java/com/tangem/tap/common/text/Truncate.kt index 0aeb17d6d2..64aeea2d59 100644 --- a/app/src/main/java/com/tangem/tap/common/text/Truncate.kt +++ b/app/src/main/java/com/tangem/tap/common/text/Truncate.kt @@ -28,12 +28,12 @@ abstract class BaseTruncate : Truncate { protected var hasBeenTruncated = false override fun apply(tv: TextView, text: String, with: String): String { - val roughLength = getRoughFitLength(tv, text, with) + val roughLength = getRoughFitLength(tv, text) val fittedText = preciseFitting(tv, roughTruncate(text, roughLength), with) return if (hasBeenTruncated) attachWith(fittedText, with) else fittedText } - protected fun getRoughFitLength(tv: TextView, text: String, with: String): Int { + private fun getRoughFitLength(tv: TextView, text: String): Int { val existingSpace = tv.measuredWidth - (tv.paddingStart + tv.paddingEnd) val textWillTakeSpace = tv.paint.measureText(text) val overSizeRatio: Float = textWillTakeSpace / existingSpace @@ -44,7 +44,7 @@ abstract class BaseTruncate : Truncate { return maxLengthOfText.toInt() } - protected fun preciseFitting(tv: TextView, text: String, with: String): String { + private fun preciseFitting(tv: TextView, text: String, with: String): String { if (!hasBeenTruncated) return text val spaceForText = tv.measuredWidth - (tv.paddingStart + tv.paddingEnd) @@ -126,11 +126,5 @@ fun TextView.truncateWith(text: String, type: TruncateType, with: String = "..." return truncate.apply(this, text, with) } -fun TextView.truncateStartWith(text: String, with: String = "..."): String = - this.truncateWith(text, TruncateType.START, with) - fun TextView.truncateMiddleWith(text: String, with: String = "..."): String = - this.truncateWith(text, TruncateType.MIDDLE, with) - -fun TextView.truncateEndWith(text: String, with: String = "..."): String = - this.truncateWith(text, TruncateType.END, with) \ No newline at end of file + this.truncateWith(text, TruncateType.MIDDLE, with) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt b/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt index 56f80d5d78..22817c3f93 100644 --- a/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt +++ b/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt @@ -3,9 +3,9 @@ package com.tangem.tap.common.toggleWidget import android.graphics.drawable.Drawable import android.view.View import com.google.android.material.button.MaterialButton +import com.tangem.tap.common.entities.ProgressState import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show -import com.tangem.tap.features.wallet.redux.ProgressState /** [REDACTED_AUTHOR] diff --git a/app/src/main/java/com/tangem/tap/common/toggleWidget/RefreshBalanceWidget.kt b/app/src/main/java/com/tangem/tap/common/toggleWidget/RefreshBalanceWidget.kt index 6a5bd17f60..3996e28d5c 100644 --- a/app/src/main/java/com/tangem/tap/common/toggleWidget/RefreshBalanceWidget.kt +++ b/app/src/main/java/com/tangem/tap/common/toggleWidget/RefreshBalanceWidget.kt @@ -2,25 +2,16 @@ package com.tangem.tap.common.toggleWidget import android.view.View import android.view.ViewGroup -import android.view.animation.AccelerateInterpolator -import android.view.animation.AlphaAnimation -import android.view.animation.Animation -import android.view.animation.AnimationSet -import android.view.animation.AnticipateOvershootInterpolator -import android.view.animation.LinearInterpolator -import android.view.animation.RotateAnimation -import android.view.animation.ScaleAnimation +import android.view.animation.* import android.widget.ViewSwitcher -import com.tangem.tap.features.wallet.redux.ProgressState +import com.tangem.tap.common.entities.ProgressState import com.tangem.wallet.R /** [REDACTED_AUTHOR] */ @Suppress("MagicNumber") -class RefreshBalanceWidget( - private val root: ViewGroup, -) : ViewStateWidget { +class RefreshBalanceWidget(root: ViewGroup) : ViewStateWidget { var isShowing: Boolean? = null private set diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/AddressInfoBottomSheetDialog.kt b/app/src/main/java/com/tangem/tap/common/ui/AddressInfoBottomSheetDialog.kt similarity index 77% rename from app/src/main/java/com/tangem/tap/features/onboarding/AddressInfoBottomSheetDialog.kt rename to app/src/main/java/com/tangem/tap/common/ui/AddressInfoBottomSheetDialog.kt index 280ca66499..6a2a14d073 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/AddressInfoBottomSheetDialog.kt +++ b/app/src/main/java/com/tangem/tap/common/ui/AddressInfoBottomSheetDialog.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.features.onboarding +package com.tangem.tap.common.ui import android.content.Context import android.os.Bundle @@ -7,14 +7,9 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.tangem.core.analytics.Analytics import com.tangem.tap.common.analytics.events.Token -import com.tangem.tap.common.extensions.copyToClipboard -import com.tangem.tap.common.extensions.dispatchDialogHide -import com.tangem.tap.common.extensions.dispatchShare -import com.tangem.tap.common.extensions.dispatchToastNotification -import com.tangem.tap.common.extensions.getString -import com.tangem.tap.common.extensions.toQrCode +import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.AppDialog -import com.tangem.tap.domain.model.WalletDataModel +import com.tangem.tap.domain.model.WalletAddressData import com.tangem.tap.store import com.tangem.wallet.R import com.tangem.wallet.databinding.DialogOnboardingAddressInfoBinding @@ -22,7 +17,7 @@ import com.tangem.wallet.databinding.DialogOnboardingAddressInfoBinding /** [REDACTED_AUTHOR] */ -class AddressInfoBottomSheetDialog( +internal class AddressInfoBottomSheetDialog( private val stateDialog: AppDialog.AddressInfoDialog, context: Context, ) : BottomSheetDialog(context) { @@ -47,7 +42,7 @@ class AddressInfoBottomSheetDialog( showData(data = stateDialog.addressData) } - private fun showData(data: WalletDataModel.AddressData) = with(binding!!) { + private fun showData(data: WalletAddressData) = with(binding!!) { pseudoToolbar.imvClose.setOnClickListener { dismissWithAnimation = true cancel() @@ -57,7 +52,6 @@ class AddressInfoBottomSheetDialog( btnFlCopyAddress.setOnClickListener { Analytics.send(Token.Receive.ButtonCopyAddress()) context.copyToClipboard(data.address) - store.dispatchToastNotification(R.string.copy_toast_msg) } btnFlShare.setOnClickListener { Analytics.send(Token.Receive.ButtonShareAddress()) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/RussianCardholdersWarningBottomSheetDialog.kt b/app/src/main/java/com/tangem/tap/common/ui/RussianCardholdersWarningBottomSheetDialog.kt similarity index 82% rename from app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/RussianCardholdersWarningBottomSheetDialog.kt rename to app/src/main/java/com/tangem/tap/common/ui/RussianCardholdersWarningBottomSheetDialog.kt index f4f2a4c143..64763a6216 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/RussianCardholdersWarningBottomSheetDialog.kt +++ b/app/src/main/java/com/tangem/tap/common/ui/RussianCardholdersWarningBottomSheetDialog.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.features.wallet.ui.dialogs +package com.tangem.tap.common.ui import android.content.Context import android.os.Bundle @@ -6,17 +6,16 @@ import android.view.LayoutInflater import com.google.android.material.bottomsheet.BottomSheetDialog import com.tangem.core.analytics.Analytics import com.tangem.core.navigation.NavigationAction -import com.tangem.domain.tokens.legacy.TradeCryptoAction import com.tangem.tap.common.analytics.events.Token import com.tangem.tap.common.extensions.dispatchDialogHide import com.tangem.tap.common.extensions.dispatchOpenUrl -import com.tangem.tap.features.wallet.redux.models.WalletDialog +import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.store import com.tangem.wallet.databinding.DialogRussiansCardholdersWarningBinding class RussianCardholdersWarningBottomSheetDialog( context: Context, - private val dialogData: WalletDialog.RussianCardholdersWarningDialog.Data?, + private val dialogData: AppDialog.RussianCardholdersWarningDialog.Data?, ) : BottomSheetDialog(context) { private var binding: DialogRussiansCardholdersWarningBinding? = null @@ -40,9 +39,8 @@ class RussianCardholdersWarningBottomSheetDialog( if (dialogData != null) { store.dispatchOpenUrl(dialogData.topUpUrl) Analytics.send(Token.Topup.ScreenOpened()) - } else { - store.dispatch(TradeCryptoAction.Buy(checkUserLocation = false)) } + dismiss() } binding?.btnNo?.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/common/ui/ScanFailsDialog.kt similarity index 95% rename from app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt rename to app/src/main/java/com/tangem/tap/common/ui/ScanFailsDialog.kt index 30d7d0b8e4..0034c0a08a 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt +++ b/app/src/main/java/com/tangem/tap/common/ui/ScanFailsDialog.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.features.wallet.ui.dialogs +package com.tangem.tap.common.ui import android.content.Context import androidx.appcompat.app.AlertDialog diff --git a/app/src/main/java/com/tangem/tap/common/ui/SimpleOkDialog.kt b/app/src/main/java/com/tangem/tap/common/ui/SimpleOkDialog.kt new file mode 100644 index 0000000000..e0eb7acf6f --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/ui/SimpleOkDialog.kt @@ -0,0 +1,31 @@ +package com.tangem.tap.common.ui + +import android.content.Context +import androidx.appcompat.app.AlertDialog +import com.tangem.tap.common.extensions.dispatchDialogHide +import com.tangem.tap.common.redux.AppDialog +import com.tangem.tap.store +import com.tangem.wallet.R + +/** +[REDACTED_AUTHOR] + */ +object SimpleOkDialog { + + fun create(dialog: AppDialog.SimpleOkDialogRes, context: Context): AlertDialog { + val message = if (dialog.args.isEmpty()) { + context.getString(dialog.messageId) + } else { + context.getString(dialog.messageId, *dialog.args.toTypedArray()) + } + return AlertDialog.Builder(context).apply { + setTitle(context.getString(dialog.headerId)) + setMessage(message) + setPositiveButton(R.string.common_ok) { _, _ -> } + setOnDismissListener { + store.dispatchDialogHide() + dialog.onOk?.invoke() + } + }.create() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/utils/SafeStoreSubscriber.kt b/app/src/main/java/com/tangem/tap/common/utils/SafeStoreSubscriber.kt deleted file mode 100644 index ac430a6ea5..0000000000 --- a/app/src/main/java/com/tangem/tap/common/utils/SafeStoreSubscriber.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.tangem.tap.common.utils - -import android.os.Handler -import android.os.Looper -import org.rekotlin.StoreSubscriber - -private val mainLooper by lazy { Looper.getMainLooper() } -private val mainHandler by lazy { Handler(mainLooper) } - -/** - * A subscriber interface for safely subscribing to state changes in a Store. - * - * @param State the type of the state in the Store - */ -interface SafeStoreSubscriber : StoreSubscriber { - - /** - * A function that will be called when the state in the Store changes. - * - * @param state the new state in the Store - */ - override fun newState(state: State) { - if (Thread.currentThread() != mainLooper.thread) { - mainHandler.post { newStateOnMain(state) } - } else { - newStateOnMain(state) - } - } - - /** - * A function that will be called on the main thread when the state in the Store changes. - * - * @param state the new state in the Store - */ - fun newStateOnMain(state: State) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/data/RuntimeUserWalletsStore.kt b/app/src/main/java/com/tangem/tap/data/RuntimeUserWalletsStore.kt index a84ddc77c6..884fa65323 100644 --- a/app/src/main/java/com/tangem/tap/data/RuntimeUserWalletsStore.kt +++ b/app/src/main/java/com/tangem/tap/data/RuntimeUserWalletsStore.kt @@ -21,4 +21,8 @@ internal class RuntimeUserWalletsStore( ?.firstOrNull() ?.singleOrNull { it.walletId == key } } + + override suspend fun update(userWalletId: UserWalletId, update: suspend (UserWallet) -> UserWallet) { + walletsStateHolder.userWalletsListManager?.update(userWalletId, update) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/di/data/CardDataModule.kt b/app/src/main/java/com/tangem/tap/di/data/CardDataModule.kt new file mode 100644 index 0000000000..0f7457c20f --- /dev/null +++ b/app/src/main/java/com/tangem/tap/di/data/CardDataModule.kt @@ -0,0 +1,27 @@ +package com.tangem.tap.di.data + +import com.tangem.datasource.local.userwallet.UserWalletsStore +import com.tangem.domain.card.repository.DerivationsRepository +import com.tangem.tap.domain.TangemSdkManager +import com.tangem.tap.domain.card.DefaultDerivationsRepository +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +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 CardDataModule { + + @Singleton + @Provides + fun providesDerivationsRepository( + tangemSdkManager: TangemSdkManager, + userWalletsStore: UserWalletsStore, + dispatchers: CoroutineDispatcherProvider, + ): DerivationsRepository { + return DefaultDerivationsRepository(tangemSdkManager, userWalletsStore, dispatchers) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/di/domain/CardDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/CardDomainModule.kt index 918b41ff04..0d32faca9a 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/CardDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/CardDomainModule.kt @@ -3,6 +3,7 @@ package com.tangem.tap.di.domain import com.tangem.domain.card.* import com.tangem.domain.card.repository.CardRepository import com.tangem.domain.card.repository.CardSdkConfigRepository +import com.tangem.domain.card.repository.DerivationsRepository import com.tangem.domain.demo.DemoConfig import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.wallets.legacy.WalletsStateHolder @@ -61,8 +62,14 @@ internal object CardDomainModule { @Provides @ViewModelScoped - fun provideDerivePublicKeysUseCase(tangemSdkManager: TangemSdkManager): DerivePublicKeysUseCase { - return DefaultDerivePublicKeysUseCase(tangemSdkManager = tangemSdkManager) + fun provideDerivePublicKeysUseCase( + tangemSdkManager: TangemSdkManager, + derivationsRepository: DerivationsRepository, + ): DerivePublicKeysUseCase { + return DefaultDerivePublicKeysUseCase( + tangemSdkManager = tangemSdkManager, + derivationsRepository = derivationsRepository, + ) } @Provides diff --git a/app/src/main/java/com/tangem/tap/di/domain/QrScanningDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/QrScanningDomainModule.kt new file mode 100644 index 0000000000..ba2c69965b --- /dev/null +++ b/app/src/main/java/com/tangem/tap/di/domain/QrScanningDomainModule.kt @@ -0,0 +1,20 @@ +package com.tangem.tap.di.domain + +import com.tangem.feature.qrscanning.repo.QrScanningEventsRepository +import com.tangem.feature.qrscanning.usecase.ListenToQrScanningUseCase +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 QrScanningDomainModule { + + @Provides + @Singleton + fun provideListenToQrScanUseCase(repository: QrScanningEventsRepository): ListenToQrScanningUseCase { + return ListenToQrScanningUseCase(repository) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt index d62f5001d9..b07f25f84d 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt @@ -18,6 +18,18 @@ import dagger.hilt.android.scopes.ViewModelScoped @Suppress("TooManyFunctions") internal object TokensDomainModule { + @Provides + @ViewModelScoped + fun provideAddCryptoCurrenciesUseCase( + currenciesRepository: CurrenciesRepository, + networksRepository: NetworksRepository, + ): AddCryptoCurrenciesUseCase { + return AddCryptoCurrenciesUseCase( + currenciesRepository = currenciesRepository, + networksRepository = networksRepository, + ) + } + @Provides @ViewModelScoped fun provideFetchTokenListUseCase( @@ -242,4 +254,44 @@ internal object TokensDomainModule { fun provideGetGlobalTokenListUseCase(tokensListRepository: TokensListRepository): GetGlobalTokenListUseCase { return GetGlobalTokenListUseCase(repository = tokensListRepository) } + + @Provides + @ViewModelScoped + fun provideCheckTokenCompatibilityUseCase( + repository: NetworksCompatibilityRepository, + ): CheckCurrencyCompatibilityUseCase { + return CheckCurrencyCompatibilityUseCase(repository) + } + + @Provides + @ViewModelScoped + fun provideFindTokenByContractAddressUseCase( + tokensListRepository: TokensListRepository, + ): FindTokenByContractAddressUseCase { + return FindTokenByContractAddressUseCase(repository = tokensListRepository) + } + + @Provides + @ViewModelScoped + fun provideValidateContractAddressUseCase( + tokensListRepository: TokensListRepository, + ): ValidateContractAddressUseCase { + return ValidateContractAddressUseCase(tokensListRepository = tokensListRepository) + } + + @Provides + @ViewModelScoped + fun provideAreTokensSupportedByNetworkUseCase( + repository: NetworksCompatibilityRepository, + ): AreTokensSupportedByNetworkUseCase { + return AreTokensSupportedByNetworkUseCase(repository = repository) + } + + @Provides + @ViewModelScoped + fun provideGetNetworksSupportedByWallet( + repository: NetworksCompatibilityRepository, + ): GetNetworksSupportedByWallet { + return GetNetworksSupportedByWallet(repository = repository) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt index ead5fca293..5429ab3030 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TransactionDomainModule.kt @@ -2,6 +2,8 @@ package com.tangem.tap.di.domain import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.demo.IsDemoCardUseCase +import com.tangem.domain.transaction.TransactionRepository +import com.tangem.domain.transaction.usecase.CreateTransactionUseCase import com.tangem.domain.transaction.usecase.GetFeeUseCase import com.tangem.domain.transaction.usecase.SendTransactionUseCase import com.tangem.domain.walletmanager.WalletManagersFacade @@ -38,4 +40,10 @@ internal object TransactionDomainModule { walletManagersFacade = walletManagersFacade, ) } + + @Provides + @ViewModelScoped + fun provideCreateTransactionUseCase(transactionRepository: TransactionRepository): CreateTransactionUseCase { + return CreateTransactionUseCase(transactionRepository) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt index 236f6e9b8f..c7f58a8226 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt @@ -3,8 +3,10 @@ package com.tangem.tap.di.domain import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.legacy.WalletsStateHolder +import com.tangem.domain.wallets.repository.WalletAddressServiceRepository import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.domain.wallets.usecase.* +import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -91,4 +93,36 @@ internal object WalletsDomainModule { fun providesShouldSaveUserWalletsUseCase(walletsRepository: WalletsRepository): ShouldSaveUserWalletsUseCase { return ShouldSaveUserWalletsUseCase(walletsRepository = walletsRepository) } + + @Provides + @ViewModelScoped + fun providesValidateWalletAddressUseCase( + walletAddressServiceRepository: WalletAddressServiceRepository, + dispatchers: CoroutineDispatcherProvider, + ): ValidateWalletAddressUseCase { + return ValidateWalletAddressUseCase( + walletAddressServiceRepository = walletAddressServiceRepository, + dispatchers = dispatchers, + ) + } + + @Provides + @ViewModelScoped + fun providesValidateWalletMemoUseCase( + walletAddressServiceRepository: WalletAddressServiceRepository, + ): ValidateWalletMemoUseCase { + return ValidateWalletMemoUseCase(walletAddressServiceRepository = walletAddressServiceRepository) + } + + @Provides + @ViewModelScoped + fun providesParseSharedAddressUseCase( + walletAddressServiceRepository: WalletAddressServiceRepository, + dispatchers: CoroutineDispatcherProvider, + ): ParseSharedAddressUseCase { + return ParseSharedAddressUseCase( + walletAddressServiceRepository = walletAddressServiceRepository, + dispatchers = dispatchers, + ) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt index ea5bd2e3aa..33ea89804c 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -25,9 +25,8 @@ import com.tangem.operations.derivation.DerivationTaskResponse import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask import com.tangem.operations.pins.SetUserCodeCommand import com.tangem.operations.usersetttings.SetUserCodeRecoveryAllowedTask -import com.tangem.tap.common.analytics.events.Basic +import com.tangem.core.analytics.models.Basic import com.tangem.tap.derivationsFinder -import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask import com.tangem.tap.domain.tasks.product.CreateProductWalletTask import com.tangem.tap.domain.tasks.product.CreateProductWalletTaskResponse import com.tangem.tap.domain.tasks.product.ResetToFactorySettingsTask @@ -126,15 +125,6 @@ class TangemSdkManager( } } - suspend fun createWallet(cardId: String?): CompletionResult { - return runTaskAsyncReturnOnMain( - CreateWalletAndRescanTask(), - cardId, - initialMessage = Message(resources.getString(R.string.initial_message_create_wallet_body)), - ) - .map { CardDTO(it) } - } - suspend fun derivePublicKeys( cardId: String?, derivations: Map>, @@ -267,6 +257,9 @@ class TangemSdkManager( filter = CardFilter( allowedCardTypes = FirmwareVersion.FirmwareType.values().toList(), maxFirmwareVersion = FirmwareVersion(major = 6, minor = 33), + batchIdFilter = CardFilter.Companion.ItemFilter.Deny( + items = setOf("0027", "0030", "0031", "0035", "DA88"), + ), ), ) } diff --git a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt index 8ac459bf2b..70b2540317 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt @@ -26,7 +26,6 @@ sealed class TapError( val stateError: String, ) : TapError(R.string.common_custom_string, listOf("Unsupported state: $stateError")) - object UnknownBlockchain : TapError(R.string.wallet_error_unsupported_blockchain_subtitle) object NoInternetConnection : TapError(R.string.wallet_notification_no_internet) object AmountExceedsBalance : TapError(R.string.send_validation_amount_exceeds_balance) data class AmountLowerExistentialDeposit( @@ -41,7 +40,6 @@ sealed class TapError( object DustChange : TapError(R.string.send_error_dust_change) sealed class WalletManager { - object CreationError : CustomError(customMessage = "Can't create wallet manager") class NoAccountError(amountToCreateAccount: String) : CustomError(amountToCreateAccount) class InternalError(message: String) : CustomError(message) object BlockchainIsUnreachableTryLater : TapError(R.string.wallet_balance_blockchain_unreachable_try_later) diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt index 9edc4ee417..8d9ba42e6a 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -4,39 +4,27 @@ import com.tangem.blockchain.common.BlockchainSdkConfig import com.tangem.blockchain.common.Token import com.tangem.blockchain.common.Wallet import com.tangem.blockchain.common.WalletManagerFactory -import com.tangem.common.doOnFailure -import com.tangem.common.doOnSuccess import com.tangem.core.analytics.Analytics import com.tangem.datasource.config.ConfigManager -import com.tangem.domain.common.extensions.toNetworkId import com.tangem.domain.common.extensions.withMainContext import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.wallets.models.UserWallet import com.tangem.operations.attestation.Attestation -import com.tangem.tap.* -import com.tangem.tap.common.analytics.events.Basic -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.common.extensions.dispatchWithMain +import com.tangem.core.analytics.models.Basic import com.tangem.tap.common.extensions.setContext -import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.domain.walletStores.WalletStoresError -import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor -import com.tangem.tap.domain.walletconnect2.domain.models.Account import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction import com.tangem.tap.features.disclaimer.createDisclaimer import com.tangem.tap.features.disclaimer.redux.DisclaimerAction import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.proxy.redux.DaggerGraphState +import com.tangem.tap.store +import com.tangem.tap.tangemSdkManager import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.launch -import org.rekotlin.Store -import timber.log.Timber class TapWalletManager( private val dispatchers: CoroutineDispatcherProvider = AppCoroutineDispatcherProvider(), @@ -55,15 +43,15 @@ class TapWalletManager( val walletManagerFactory: WalletManagerFactory by lazy { WalletManagerFactory(blockchainSdkConfig) } - suspend fun onWalletSelected(userWallet: UserWallet, refresh: Boolean, sendAnalyticsEvent: Boolean) { + suspend fun onWalletSelected(userWallet: UserWallet, sendAnalyticsEvent: Boolean) { // If a previous job was running, it gets cancelled before the new one starts, // ensuring that only one job is active at any given time. loadUserWalletDataJob = CoroutineScope(dispatchers.io) - .launch { loadUserWalletData(userWallet, refresh, sendAnalyticsEvent) } + .launch { loadUserWalletData(userWallet, sendAnalyticsEvent) } .also { it.join() } } - private suspend fun loadUserWalletData(userWallet: UserWallet, refresh: Boolean, sendAnalyticsEvent: Boolean) { + private suspend fun loadUserWalletData(userWallet: UserWallet, sendAnalyticsEvent: Boolean) { Analytics.setContext(userWallet.scanResponse) if (sendAnalyticsEvent) { Analytics.send(Basic.WalletOpened()) @@ -78,114 +66,11 @@ class TapWalletManager( withMainContext { // Order is important store.dispatch(DisclaimerAction.SetDisclaimer(card.createDisclaimer())) - store.dispatchWalletAction(action = WalletAction.UserWalletChanged(userWallet)) - store.dispatchWalletAction( - action = WalletAction.UpdateCanSaveUserWallets( - canSaveUserWallets = store.state.daggerGraphState.get(DaggerGraphState::walletsRepository) - .shouldSaveUserWalletsSync(), - ), - ) store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse)) store.dispatch(WalletConnectAction.ResetState) store.dispatch(GlobalAction.SaveScanResponse(scanResponse)) store.dispatch(WalletConnectAction.RestoreSessions(scanResponse)) store.dispatch(GlobalAction.SetIfCardVerifiedOnline(!attestationFailed)) - store.dispatchWalletAction(action = WalletAction.Warnings.CheckIfNeeded) - } - - val walletFeatureToggles = store.state.daggerGraphState.get(DaggerGraphState::walletFeatureToggles) - if (!walletFeatureToggles.isRedesignedScreenEnabled) { - setupWalletConnectV2(userWallet) - loadData(userWallet = userWallet, refresh = refresh) - } - } - - private fun Store.dispatchWalletAction(action: WalletAction) { - val walletFeatureToggles = state.daggerGraphState.get(DaggerGraphState::walletFeatureToggles) - - if (!walletFeatureToggles.isRedesignedScreenEnabled) { - dispatch(action = action) - } - } - - private fun setupWalletConnectV2(userWallet: UserWallet) { - val cardId = if (userWallet.scanResponse.card.backupStatus?.isActive != true) { - userWallet.cardId - } else { // if wallet has backup, any card from wallet can be used to sign - null - } - scope.launch { - val wcInteractor = store.state.daggerGraphState.walletConnectInteractor ?: return@launch - wcInteractor.startListening( - userWalletId = userWallet.walletId.stringValue, - cardId = cardId, - ) - } - } - - suspend fun loadData(userWallet: UserWallet, refresh: Boolean = false) { - walletStoresManager.fetch(userWallet, refresh) - .doOnSuccess { - Timber.d("Wallet stores fetched for ${userWallet.walletId}") - store.dispatchOnMain(WalletAction.LoadData.Success) - store.state.globalState.topUpController?.loadDataSuccess() - store.dispatchWithMain(WalletAction.Warnings.CheckHashesCount.VerifyOnlineIfNeeded) - - val wcInteractor = store.state.daggerGraphState.walletConnectInteractor - wcInteractor?.setUserChains(getAccountsForWc(wcInteractor)) - } - .doOnFailure { error -> - val errorAction = when (error) { - is WalletStoresError -> when (error) { - is WalletStoresError.FetchFiatRatesError -> WalletAction.LoadData.Failure(error = null) - is WalletStoresError.UpdateWalletManagerTokensError -> WalletAction.LoadData.Failure( - error = TapError.WalletManager.InternalError( - message = error.cause.localizedMessage ?: error.customMessage, - ), - ) - is WalletStoresError.WalletManagerNotCreated -> WalletAction.LoadData.Failure( - error = TapError.WalletManager.CreationError, - ) - is WalletStoresError.UnknownBlockchain -> WalletAction.LoadData.Failure( - error = TapError.UnknownBlockchain, - ) - is WalletStoresError.NoInternetConnection -> WalletAction.LoadData.Failure( - error = TapError.NoInternetConnection, - ) - } - else -> WalletAction.LoadData.Failure(error = null) - } - - Timber.e(error, "Wallet stores fetching failed for ${userWallet.walletId}") - - store.dispatchOnMain(errorAction) - } - } - - private suspend fun getAccountsForWc(wcInteractor: WalletConnectInteractor): List { - val walletManagerToggles = store.state.daggerGraphState - .get(DaggerGraphState::walletFeatureToggles) - val walletManagers = if (walletManagerToggles.isRedesignedScreenEnabled) { - val walletManagerFacade = store.state.daggerGraphState - .get(DaggerGraphState::walletManagersFacade) - val userWallet = userWalletsListManager.selectedUserWalletSync ?: return emptyList() - walletManagerFacade.getStoredWalletManagers(userWallet.walletId) - } else { - store.state.walletState.walletManagers - } - - return walletManagers.mapNotNull { - val wallet = it.wallet - val chainId = wcInteractor.blockchainHelper.networkIdToChainIdOrNull( - wallet.blockchain.toNetworkId(), - ) - chainId?.let { - Account( - chainId, - wallet.address, - wallet.publicKey.derivationPath?.rawPath, - ) - } } } diff --git a/app/src/main/java/com/tangem/tap/domain/card/DefaultDerivationsRepository.kt b/app/src/main/java/com/tangem/tap/domain/card/DefaultDerivationsRepository.kt new file mode 100644 index 0000000000..4b2f3852b6 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/card/DefaultDerivationsRepository.kt @@ -0,0 +1,80 @@ +package com.tangem.tap.domain.card + +import com.tangem.common.doOnFailure +import com.tangem.common.doOnSuccess +import com.tangem.common.extensions.ByteArrayKey +import com.tangem.crypto.hdWallet.DerivationPath +import com.tangem.datasource.local.userwallet.UserWalletsStore +import com.tangem.domain.card.repository.DerivationsRepository +import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.wallets.models.UserWallet +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.operations.derivation.ExtendedPublicKeysMap +import com.tangem.tap.domain.TangemSdkManager +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import com.tangem.utils.coroutines.runCatching +import timber.log.Timber + +internal typealias Derivations = Map> +private typealias DerivedKeys = Map + +internal class DefaultDerivationsRepository( + private val tangemSdkManager: TangemSdkManager, + private val userWalletsStore: UserWalletsStore, + private val dispatchers: CoroutineDispatcherProvider, +) : DerivationsRepository { + + override suspend fun derivePublicKeys(userWalletId: UserWalletId, currencies: List) { + val userWallet = userWalletsStore.getSyncOrNull(userWalletId) ?: error("User wallet not found") + + if (!userWallet.scanResponse.card.settings.isHDWalletAllowed) { + Timber.d("Nothing to derive") + return + } + + val derivations = MissedDerivationsFinder(scanResponse = userWallet.scanResponse) + .find(currencies) + .ifEmpty { + Timber.d("Nothing to derive") + return + } + + tangemSdkManager.derivePublicKeys(cardId = null, derivations = derivations) + .doOnSuccess { response -> + updatePublicKeys(userWalletId = userWalletId, keys = response.entries).fold( + onSuccess = { return }, + onFailure = { throw it }, + ) + } + .doOnFailure { throw it } + + error("This code should never be reached") + } + + private suspend fun updatePublicKeys(userWalletId: UserWalletId, keys: DerivedKeys): Result { + return runCatching(dispatchers.io) { + userWalletsStore.update( + userWalletId = userWalletId, + update = { userWallet -> userWallet.updateDerivedKeys(keys) }, + ) + } + } + + private fun UserWallet.updateDerivedKeys(keys: DerivedKeys): UserWallet { + return copy( + scanResponse = scanResponse.copy( + derivedKeys = getUpdatedDerivedKeys(oldKeys = scanResponse.derivedKeys, newKeys = keys), + ), + ) + } + + private fun getUpdatedDerivedKeys(oldKeys: DerivedKeys, newKeys: DerivedKeys): DerivedKeys { + return (oldKeys.keys + newKeys.keys).toSet() + .associateWith { walletKey -> + val oldDerivations = ExtendedPublicKeysMap(oldKeys[walletKey] ?: emptyMap()) + val newDerivations = newKeys[walletKey] ?: ExtendedPublicKeysMap(emptyMap()) + + ExtendedPublicKeysMap(oldDerivations + newDerivations) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/card/DefaultDerivePublicKeysUseCase.kt b/app/src/main/java/com/tangem/tap/domain/card/DefaultDerivePublicKeysUseCase.kt index d402c97a7c..a09df2969a 100644 --- a/app/src/main/java/com/tangem/tap/domain/card/DefaultDerivePublicKeysUseCase.kt +++ b/app/src/main/java/com/tangem/tap/domain/card/DefaultDerivePublicKeysUseCase.kt @@ -8,12 +8,15 @@ import com.tangem.common.doOnSuccess import com.tangem.common.extensions.ByteArrayKey import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.domain.card.DerivePublicKeysUseCase +import com.tangem.domain.card.repository.DerivationsRepository +import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.wallets.models.UserWalletId import com.tangem.operations.derivation.DerivationTaskResponse import com.tangem.tap.domain.TangemSdkManager -// TODO: [REDACTED_JIRA] internal class DefaultDerivePublicKeysUseCase( private val tangemSdkManager: TangemSdkManager, + private val derivationsRepository: DerivationsRepository, ) : DerivePublicKeysUseCase { override suspend fun invoke( @@ -26,4 +29,13 @@ internal class DefaultDerivePublicKeysUseCase( return Unit.left() } + + override suspend fun invoke( + userWalletId: UserWalletId, + currencies: List, + ): Either { + return Either.catch { + derivationsRepository.derivePublicKeys(userWalletId, currencies) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/card/MissedDerivationsFinder.kt b/app/src/main/java/com/tangem/tap/domain/card/MissedDerivationsFinder.kt new file mode 100644 index 0000000000..97c0a7bf8f --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/card/MissedDerivationsFinder.kt @@ -0,0 +1,118 @@ +package com.tangem.tap.domain.card + +import com.tangem.blockchain.blockchains.cardano.CardanoUtils +import com.tangem.blockchain.common.Blockchain +import com.tangem.common.card.EllipticCurve +import com.tangem.common.extensions.ByteArrayKey +import com.tangem.common.extensions.toMapKey +import com.tangem.crypto.hdWallet.DerivationPath +import com.tangem.domain.common.configs.CardConfig +import com.tangem.domain.common.util.derivationStyleProvider +import com.tangem.domain.models.scan.KeyWalletPublicKey +import com.tangem.domain.models.scan.ScanResponse +import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.operations.derivation.ExtendedPublicKeysMap + +private typealias DerivationData = Pair> + +/** + * Finder of missed derivations + * + * @property scanResponse scanning response + * +[REDACTED_AUTHOR] + */ +internal class MissedDerivationsFinder(private val scanResponse: ScanResponse) { + + /** Find missed derivations for given currencies [currencies] */ + fun find(currencies: List): Derivations { + return buildMap> { + currencies + .mapToNewDerivations() + .forEach { data -> + val current = this[data.first] + if (current != null) { + current.addAll(data.second) + current.distinct() + } else { + this[data.first] = data.second.toMutableList() + } + } + } + } + + private fun List.mapToNewDerivations(): List { + val config = CardConfig.createConfig(scanResponse.card) + return mapNotNull { currency -> + val blockchain = Blockchain.fromId(id = currency.network.id.value) + val curve = config.primaryCurve(blockchain) ?: return@mapNotNull null + + findNewDerivations(curve = curve, scanResponse = scanResponse, currency = currency) + } + } + + private fun findNewDerivations( + curve: EllipticCurve, + scanResponse: ScanResponse, + currency: CryptoCurrency, + ): DerivationData? { + val wallet = scanResponse.card.wallets.firstOrNull { it.curve == curve } ?: return null + val publicKey = wallet.publicKey.toMapKey() + + val derivationCandidates = currency + .getDerivationCandidates(curve) + .ifEmpty { return null } + .filterAlreadyDerivedKeys(publicKey) + .ifEmpty { return null } + + return publicKey to derivationCandidates + } + + private fun CryptoCurrency.getDerivationCandidates(curve: EllipticCurve): List { + val blockchain = Blockchain.fromId(id = network.id.value) + + return buildList { + add(blockchain.getDerivationPath(curve = curve)) + add(blockchain.getCustomDerivationPath(curve = curve, currency = this@getDerivationCandidates)) + add(blockchain.getCardanoDerivationPathIfNeeded(currency = this@getDerivationCandidates)) + } + .filterNotNull() + .distinct() + } + + private fun Blockchain.getDerivationPath(curve: EllipticCurve): DerivationPath? { + return if (getSupportedCurves().contains(curve)) { + derivationPath(style = scanResponse.derivationStyleProvider.getDerivationStyle()) + } else { + null + } + } + + private fun Blockchain.getCustomDerivationPath(curve: EllipticCurve, currency: CryptoCurrency): DerivationPath? { + return if (getSupportedCurves().contains(curve)) { + currency.network.derivationPath.value?.let(::DerivationPath) + } else { + null + } + } + + private fun Blockchain.getCardanoDerivationPathIfNeeded(currency: CryptoCurrency): DerivationPath? { + return if (currency is CryptoCurrency.Coin && this == Blockchain.Cardano) { + currency.network.derivationPath.value?.let { + CardanoUtils.extendedDerivationPath(derivationPath = DerivationPath(it)) + } + } else { + null + } + } + + private fun List.filterAlreadyDerivedKeys(publicKey: KeyWalletPublicKey): List { + val alreadyDerivedPaths = getAlreadyDerivedKeys(publicKey) + return filterNot(alreadyDerivedPaths::contains) + } + + private fun getAlreadyDerivedKeys(publicKey: KeyWalletPublicKey): List { + val extendedPublicKeysMap = scanResponse.derivedKeys[publicKey] ?: ExtendedPublicKeysMap(emptyMap()) + return extendedPublicKeysMap.keys.toList() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessage.kt b/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessage.kt index a36e254ee7..2f8f892e51 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessage.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessage.kt @@ -51,14 +51,12 @@ data class WarningMessage( } enum class Location { - @Json(name = "main") - MainScreen, @Json(name = "send") SendScreen, } enum class Origin { - Local, Remote + Remote, } } \ No newline at end of file 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 f1345d6989..caa4125ff7 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 @@ -1,26 +1,17 @@ package com.tangem.tap.domain.configurable.warningMessage import com.tangem.blockchain.common.Blockchain -import com.tangem.utils.extensions.removeBy -import com.tangem.wallet.R import java.util.concurrent.CopyOnWriteArrayList /** [REDACTED_AUTHOR] */ -// TODO: Delete with WalletFeatureToggles -@Deprecated(message = "Used only in old wallet screen") +// TODO: Delete with SendFeatureToggles +@Deprecated(message = "Used only in old send screen") class WarningMessagesManager { private val warningsList = CopyOnWriteArrayList() - fun addWarning(warning: WarningMessage) { - if (findWarning(warning) == null) { - warningsList.add(warning) - sortByPriority() - } - } - fun getWarnings(location: WarningMessage.Location, blockchains: List): List { return warningsList.filter { message -> val messageBlockchains = message.blockchainList @@ -45,131 +36,7 @@ class WarningMessagesManager { } } - fun removeWarnings(origin: WarningMessage.Origin) { - warningsList.removeBy { it.origin == origin } - sortByPriority() - } - - fun removeWarnings(messageRes: Int) { - warningsList.removeBy { it.messageResId == messageRes } - } - - fun containsWarning(warning: WarningMessage) = warning in warningsList - - private fun sortByPriority() { - warningsList.sortBy { it.priority.ordinal } - } - private fun findWarning(warning: WarningMessage): WarningMessage? { return warningsList.firstOrNull { it == warning } } - - companion object { - const val REMAINING_SIGNATURES_WARNING = 10 - - val devCardWarning = WarningMessage( - title = "", - message = "", - type = WarningMessage.Type.Permanent, - priority = WarningMessage.Priority.Critical, - location = listOf(WarningMessage.Location.MainScreen), - blockchains = null, - titleResId = R.string.common_warning, - // messageResId = R.string.alert_developer_card, - origin = WarningMessage.Origin.Local, - ) - - val alreadySignedHashesWarning = WarningMessage( - title = "", - message = "", - type = WarningMessage.Type.Temporary, - priority = WarningMessage.Priority.Info, - location = listOf(WarningMessage.Location.MainScreen), - blockchains = null, - titleResId = R.string.common_warning, - // messageResId = R.string.alert_card_signed_transactions, - origin = WarningMessage.Origin.Local, - ) - - val signedHashesMultiWalletWarning = WarningMessage( - title = "", - message = "", - type = WarningMessage.Type.Temporary, - priority = WarningMessage.Priority.Info, - location = listOf(WarningMessage.Location.MainScreen), - blockchains = null, - // titleResId = R.string.warning_important_security_info, - // messageResId = R.string.warning_signed_tx_previously, - origin = WarningMessage.Origin.Local, - buttonTextId = R.string.warning_button_learn_more, - titleFormatArg = "\u26A0", - ) - - val appRatingWarning = WarningMessage( - title = "", - message = "", - type = WarningMessage.Type.AppRating, - priority = WarningMessage.Priority.Info, - location = listOf(WarningMessage.Location.MainScreen), - blockchains = null, - titleResId = R.string.warning_rate_app_title, - messageResId = R.string.warning_rate_app_message, - origin = WarningMessage.Origin.Local, - ) - - val onlineVerificationFailed = WarningMessage( - title = "", - message = "", - type = WarningMessage.Type.Permanent, - priority = WarningMessage.Priority.Critical, - location = listOf(WarningMessage.Location.MainScreen), - blockchains = null, - titleResId = R.string.warning_failed_to_verify_card_title, - messageResId = R.string.warning_failed_to_verify_card_message, - origin = WarningMessage.Origin.Local, - ) - - val testCardWarning = WarningMessage( - title = "", - message = "", - type = WarningMessage.Type.TestCard, - priority = WarningMessage.Priority.Critical, - location = listOf(WarningMessage.Location.MainScreen, WarningMessage.Location.SendScreen), - blockchains = null, - titleResId = R.string.common_warning, - messageResId = R.string.warning_testnet_card_message, - origin = WarningMessage.Origin.Local, - ) - - val demoCardWarning = WarningMessage( - title = "", - message = "", - type = WarningMessage.Type.Permanent, - priority = WarningMessage.Priority.Critical, - location = listOf(WarningMessage.Location.MainScreen), - blockchains = null, - titleResId = R.string.common_warning, - // messageResId = R.string.alert_demo_message, - origin = WarningMessage.Origin.Local, - ) - - fun remainingSignaturesNotEnough(remainingSignatures: Int): WarningMessage { - return WarningMessage( - title = "", - message = "", - type = WarningMessage.Type.Permanent, - priority = WarningMessage.Priority.Critical, - location = listOf(WarningMessage.Location.MainScreen), - blockchains = null, - titleResId = R.string.common_warning, - // messageResId = R.string.warning_low_signatures_format, - origin = WarningMessage.Origin.Local, - messageFormatArg = remainingSignatures.toString(), - ) - } - - // fun isAlreadySignedHashesWarning(warning: WarningMessage): Boolean { - // return warning.messageResId == R.string.alert_card_signed_transactions - // } - } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt index 363565bf3e..c71548c0bd 100644 --- a/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt +++ b/app/src/main/java/com/tangem/tap/domain/extensions/Card.kt @@ -9,17 +9,6 @@ import com.tangem.domain.userwallets.Artwork import com.tangem.operations.attestation.CardVerifyAndGetInfo import com.tangem.operations.attestation.OnlineCardVerifier -val CardDTO.remainingSignatures: Int? - get() = this.wallets.firstOrNull()?.remainingSignatures - -val CardDTO.isHdWalletAllowedByApp: Boolean - get() = settings.isHDWalletAllowed - -@Suppress("UnnecessaryParentheses") -fun CardDTO.hasSignedHashes(): Boolean { - return wallets.any { (it.totalSignedHashes ?: 0) > 0 } -} - fun CardDTO.signedHashesCount(): Int { return wallets.sumOf { it.totalSignedHashes ?: 0 } } diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/Token.kt b/app/src/main/java/com/tangem/tap/domain/extensions/Token.kt deleted file mode 100644 index 6963ed3682..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/extensions/Token.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.tangem.tap.domain.extensions - -import com.tangem.blockchain.common.Token - -/** -[REDACTED_AUTHOR] - */ - -private val tokenCustomIconUrls = mutableMapOf() - -fun Token.getCustomIconUrl(): String? { - return tokenCustomIconUrls[this.contractAddress] -} - -fun Token.setCustomIconUrl(url: String) { - tokenCustomIconUrls[this.contractAddress] = url -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/model/Currency.kt b/app/src/main/java/com/tangem/tap/domain/model/Currency.kt new file mode 100644 index 0000000000..5992c61d6a --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/model/Currency.kt @@ -0,0 +1,64 @@ +package com.tangem.tap.domain.model + +import com.tangem.domain.common.BlockchainNetwork +import com.tangem.domain.features.addCustomToken.CustomCurrency +import com.tangem.tap.common.redux.global.CryptoCurrencyName +import com.tangem.blockchain.common.Blockchain as SdkBlockchain +import com.tangem.blockchain.common.Token as SdkToken + +sealed interface Currency { + val blockchain: SdkBlockchain + val currencySymbol: CryptoCurrencyName + val derivationPath: String? + val decimals + get() = when (this) { + is Blockchain -> blockchain.decimals() + is Token -> token.decimals + } + + data class Token( + val token: SdkToken, + override val blockchain: SdkBlockchain, + override val derivationPath: String?, + ) : Currency { + override val currencySymbol = token.symbol + } + + data class Blockchain( + override val blockchain: SdkBlockchain, + override val derivationPath: String?, + ) : Currency { + override val currencySymbol: CryptoCurrencyName = blockchain.currency + } + + companion object { + fun fromBlockchainNetwork(blockchainNetwork: BlockchainNetwork, token: SdkToken? = null): Currency { + return if (token != null) { + Token( + token = token, + blockchain = blockchainNetwork.blockchain, + derivationPath = blockchainNetwork.derivationPath, + ) + } else { + Blockchain( + blockchain = blockchainNetwork.blockchain, + derivationPath = blockchainNetwork.derivationPath, + ) + } + } + + fun fromCustomCurrency(customCurrency: CustomCurrency): Currency { + return when (customCurrency) { + is CustomCurrency.CustomBlockchain -> Blockchain( + blockchain = customCurrency.network, + derivationPath = customCurrency.derivationPath?.rawPath, + ) + is CustomCurrency.CustomToken -> Token( + token = customCurrency.token, + blockchain = customCurrency.network, + derivationPath = customCurrency.derivationPath?.rawPath, + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt b/app/src/main/java/com/tangem/tap/domain/model/PendingTransaction.kt similarity index 55% rename from app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt rename to app/src/main/java/com/tangem/tap/domain/model/PendingTransaction.kt index e2b74e5ebd..c4a810e12e 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt +++ b/app/src/main/java/com/tangem/tap/domain/model/PendingTransaction.kt @@ -1,14 +1,8 @@ -package com.tangem.tap.features.wallet.models +package com.tangem.tap.domain.model -import com.tangem.blockchain.common.Amount -import com.tangem.blockchain.common.AmountType -import com.tangem.blockchain.common.Token import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.TransactionStatus import com.tangem.blockchain.common.Wallet -import com.tangem.blockchain.extensions.isAboveZero -import com.tangem.tap.common.extensions.toFormattedString -import java.math.BigDecimal data class PendingTransaction( val transactionData: TransactionData, @@ -20,10 +14,6 @@ data class PendingTransaction( PendingTransactionType.Unknown -> null } - val amountValue: BigDecimal? = transactionData.amount.value - - val amountValueUi: String? = amountValue?.toFormattedString(transactionData.amount.decimals) - val currency: String = transactionData.amount.currencySymbol private fun nullIfUnknown(address: String): String? = if (address == "unknown") null else address @@ -46,15 +36,6 @@ fun List.toPendingTransactions(walletAddress: String): List.filterByCoin(): List { - return this.filter { it.transactionData.amount.type == AmountType.Coin } -} - -fun TransactionData.toPendingTransactionForToken(token: Token, walletAddress: String): PendingTransaction? { - if (this.amount.currencySymbol != token.symbol) return null - return this.toPendingTransaction(walletAddress) -} - fun Wallet.getPendingTransactions(type: PendingTransactionType? = null): List { val txs = recentTransactions.toPendingTransactions(address) return when (type) { @@ -63,24 +44,6 @@ fun Wallet.getPendingTransactions(type: PendingTransactionType? = null): List { - return recentTransactions.mapNotNull { it.toPendingTransactionForToken(token, address) } -} - fun Wallet.hasPendingTransactions(): Boolean { return getPendingTransactions().isNotEmpty() -} - -fun Wallet.getSendableAmounts(): List { - return amounts.values - .filter { it.type != AmountType.Reserve } - .filter { it.isAboveZero() } -} - -fun Wallet.hasSendableAmounts(): Boolean { - return getSendableAmounts().isNotEmpty() -} - -fun Wallet.isSendableAmount(type: AmountType): Boolean { - return amounts[type]?.isAboveZero() == true } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/model/TotalFiatBalance.kt b/app/src/main/java/com/tangem/tap/domain/model/TotalFiatBalance.kt deleted file mode 100644 index 0180d85435..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/model/TotalFiatBalance.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.tangem.tap.domain.model - -import java.math.BigDecimal - -/** - * Represents fiat balance of [WalletStoreModel] list - * @property amount Amount of the total balance - * */ -sealed interface TotalFiatBalance { - val amount: BigDecimal? - - object Loading : TotalFiatBalance { - override val amount: BigDecimal? = null - } - - object Failed : TotalFiatBalance { - override val amount: BigDecimal? = null - } - - data class Loaded( - override val amount: BigDecimal, - val isWarning: Boolean, - ) : TotalFiatBalance -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/model/WalletAddressData.kt b/app/src/main/java/com/tangem/tap/domain/model/WalletAddressData.kt new file mode 100644 index 0000000000..a24fe98346 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/model/WalletAddressData.kt @@ -0,0 +1,10 @@ +package com.tangem.tap.domain.model + +import com.tangem.blockchain.common.address.AddressType + +internal data class WalletAddressData( + val address: String, + val type: AddressType, + val shareUrl: String, + val exploreUrl: String, +) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/model/WalletDataModel.kt b/app/src/main/java/com/tangem/tap/domain/model/WalletDataModel.kt deleted file mode 100644 index fc0acca07c..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/model/WalletDataModel.kt +++ /dev/null @@ -1,88 +0,0 @@ -package com.tangem.tap.domain.model - -import com.tangem.blockchain.common.address.AddressType -import com.tangem.tap.domain.model.WalletDataModel.AddressData -import com.tangem.tap.domain.model.WalletDataModel.Status -import com.tangem.tap.features.wallet.models.Currency -import com.tangem.tap.features.wallet.models.PendingTransaction -import java.math.BigDecimal - -/** - * Contains info about wallet store currency - * @param currency Wallet's [Currency] - * @param status Wallet's [Status], represents current status of that currency - * @param walletAddresses List of wallet data [AddressData] - * @param existentialDeposit Amount that must be held on currency's balance, if balance is below that amount all - * founds will be destroyed. Null if currency don't have existential deposit - * @param fiatRate Wallet's fiat rate, used to calculate fiat balance. Null if not provided - * @param isCardSingleToken shows that [Currency] is a card token - * @param isCustom shows that currency is a custom - * */ -data class WalletDataModel( - val currency: Currency, - val status: Status, - // FIXME: Left only selected wallet address here and move list of wallet addresses to WalletStoreModel - val walletAddresses: WalletAddresses?, - val existentialDeposit: BigDecimal?, - val fiatRate: BigDecimal?, - val isCardSingleToken: Boolean, - val isCustom: Boolean, -) { - - data class WalletAddresses( - val selectedAddress: AddressData, - val list: List, - ) - - data class AddressData( - val address: String, - val type: AddressType, - val shareUrl: String, - val exploreUrl: String, - ) - - /** - * Represent current status of currency - * @property amount Currency amount - * @property pendingTransactions List of currency [PendingTransaction] sent in currency's blockchain - * @property errorMessage Status error message, null if not provided - * @property isErrorStatus true if current status is error status, false otherwise - * */ - sealed class Status { - open val amount: BigDecimal = BigDecimal.ZERO - open val pendingTransactions: List = emptyList() - open val errorMessage: String? = null - open val isErrorStatus: Boolean = false - } - - object Loading : Status() - - data class VerifiedOnline( - override val amount: BigDecimal, - ) : Status() - - data class TransactionInProgress( - override val amount: BigDecimal, - override val pendingTransactions: List, - ) : Status() - - data class SameCurrencyTransactionInProgress( - override val amount: BigDecimal, - override val pendingTransactions: List, - ) : Status() - - data class NoAccount( - val amountToCreateAccount: BigDecimal?, - ) : Status() - - data class Unreachable( - override val errorMessage: String?, - override val amount: BigDecimal = BigDecimal.ZERO, - ) : Status() { - override val isErrorStatus: Boolean = true - } - - object MissedDerivation : Status() { - override val isErrorStatus: Boolean = true - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/model/WalletStoreModel.kt b/app/src/main/java/com/tangem/tap/domain/model/WalletStoreModel.kt deleted file mode 100644 index 99282954dc..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/model/WalletStoreModel.kt +++ /dev/null @@ -1,86 +0,0 @@ -package com.tangem.tap.domain.model - -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.WalletManager -import com.tangem.crypto.hdWallet.DerivationPath -import com.tangem.domain.common.BlockchainNetwork -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.domain.model.WalletStoreModel.WalletRent -import com.tangem.tap.features.wallet.models.Currency -import java.math.BigDecimal - -// FIXME: Move list of wallet addresses from WalletDataModel to this class -/** - * Contains info about the blockchain and its currencies - * - * @param userWalletId ID of the associated [UserWallet] - * @param blockchain [Blockchain] of this WalletStore - * @param derivationPath [DerivationPath] of this store, null if the card does not support the - * [HD Wallet](https://coinsutra.com/hd-wallets-deterministic-wallet/) - * @param walletsData List of [WalletDataModel] which represents store's blockchain currency and tokens currencies - * @param walletRent [WalletRent], null if store has no rent or currency balance is greater then - * [WalletRent.exemptionAmount] - * @param blockchainNetwork [BlockchainNetwork]. - * TODO: Remove after WalletMiddleware refactoring - * @param walletManager [WalletManager], may be null if it fails to create this manager. - * TODO: Remove after WalletMiddleware refactoring - * - * @property blockchainWalletData Returns the [WalletDataModel] of the blockchain of this wallet store - * or throw [NoSuchElementException] if this wallet store not contains [WalletDataModel] of the blockchain - * */ -data class WalletStoreModel( - val userWalletId: UserWalletId, - val blockchain: Blockchain, - val derivationPath: DerivationPath?, - val walletsData: List, - val walletRent: WalletRent?, - @Deprecated("Don't use it, will be removed") - val blockchainNetwork: BlockchainNetwork, - @Deprecated("Don't use it, will be removed") - val walletManager: WalletManager?, -) { - - val blockchainWalletData: WalletDataModel - get() = walletsData.first { it.currency is Currency.Blockchain } - - /** - * Represents wallet blockchain rent - * @param rent Amount that will be charged in overtime if the blockchain does not have an amount greater than - * the [WalletRent.exemptionAmount] - * @param exemptionAmount Amount that should be on the blockchain balance not to pay rent - * */ - data class WalletRent( - val rent: BigDecimal, - val exemptionAmount: BigDecimal, - ) - - // TODO: Remove the generated methods after blockchainNetwork and walletManager are removed from the model - // region Generated - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is WalletStoreModel) return false - - if (userWalletId != other.userWalletId) return false - if (blockchain != other.blockchain) return false - if (derivationPath != other.derivationPath) return false - if (walletsData != other.walletsData) return false - if (walletRent != other.walletRent) return false - - return true - } - - override fun hashCode(): Int { - var result = userWalletId.hashCode() - result = 31 * result + blockchain.hashCode() - result = 31 * result + (derivationPath?.hashCode() ?: 0) - result = 31 * result + walletsData.hashCode() - result = 31 * result + (walletRent?.hashCode() ?: 0) - return result - } - - override fun toString(): String { - return "WalletStoreModel(userWalletId=$userWalletId, blockchain=$blockchain, derivationPath=$derivationPath, " + - "walletsData=$walletsData, walletRent=$walletRent)" - } - // endregion Generated -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/model/builders/WalletStoreBuilder.kt b/app/src/main/java/com/tangem/tap/domain/model/builders/WalletStoreBuilder.kt deleted file mode 100644 index 7bd2f77fe1..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/model/builders/WalletStoreBuilder.kt +++ /dev/null @@ -1,188 +0,0 @@ -package com.tangem.tap.domain.model.builders - -import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider -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.derivation.DerivationStyle -import com.tangem.crypto.hdWallet.DerivationPath -import com.tangem.domain.common.BlockchainNetwork -import com.tangem.domain.common.util.cardTypesResolver -import com.tangem.domain.common.util.derivationStyleProvider -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.tap.domain.model.WalletDataModel -import com.tangem.tap.domain.model.WalletStoreModel -import com.tangem.tap.features.wallet.models.Currency -import com.tangem.tap.features.wallet.redux.reducers.createAddressesData -import java.math.BigDecimal - -interface WalletStoreBuilder { - fun build(): WalletStoreModel - - interface BlockchainNetworkWalletStoreBuilder : WalletStoreBuilder { - fun walletManager(walletManager: WalletManager?): WalletStoreBuilder - } - - interface WalletMangerWalletStoreBuilder : WalletStoreBuilder - - companion object { - operator fun invoke( - userWallet: UserWallet, - blockchainNetwork: BlockchainNetwork, - ): BlockchainNetworkWalletStoreBuilder { - return BlockchainNetworkWalletStoreBuilderImpl(userWallet, blockchainNetwork) - } - - operator fun invoke(userWallet: UserWallet, walletManager: WalletManager): WalletMangerWalletStoreBuilder { - return WalletMangerWalletStoreBuilderImpl(userWallet, walletManager) - } - } -} - -private class BlockchainNetworkWalletStoreBuilderImpl( - private val userWallet: UserWallet, - private val blockchainNetwork: BlockchainNetwork, -) : WalletStoreBuilder.BlockchainNetworkWalletStoreBuilder { - private var walletManager: WalletManager? = null - - override fun walletManager(walletManager: WalletManager?) = this.apply { - this.walletManager = walletManager - } - - override fun build(): WalletStoreModel { - val cardDerivationStyle = userWallet.scanResponse.derivationStyleProvider.getDerivationStyle() - val blockchainWalletData = blockchainNetwork.getBlockchainWalletData(walletManager, cardDerivationStyle) - val tokensWalletsData = blockchainNetwork.getTokensWalletsData( - walletManager = walletManager, - cardDerivationStyle = cardDerivationStyle, - primaryToken = userWallet.scanResponse.cardTypesResolver.getPrimaryToken(), - ) - - return WalletStoreModel( - userWalletId = userWallet.walletId, - blockchain = blockchainNetwork.blockchain, - derivationPath = blockchainNetwork.derivationPath?.let { DerivationPath(it) }, - walletsData = listOf(blockchainWalletData) + tokensWalletsData, - walletRent = null, - walletManager = walletManager, - blockchainNetwork = blockchainNetwork, - ) - } -} - -private class WalletMangerWalletStoreBuilderImpl( - private val userWallet: UserWallet, - private val walletManager: WalletManager, -) : WalletStoreBuilder.WalletMangerWalletStoreBuilder { - - override fun build(): WalletStoreModel { - val wallet = walletManager.wallet - val blockchainWalletData = wallet.blockchain.toBlockchainWalletData(walletManager) - val tokenWalletsData = wallet.getTokens().firstOrNull()?.toTokenWalletData( - walletManager = walletManager, - primaryToken = userWallet.scanResponse.cardTypesResolver.getPrimaryToken(), - ) - - return WalletStoreModel( - userWalletId = userWallet.walletId, - blockchain = wallet.blockchain, - derivationPath = wallet.publicKey.derivationPath, - walletsData = listOf(blockchainWalletData) + listOfNotNull(tokenWalletsData), - walletRent = null, - walletManager = walletManager, - blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager), - ) - } -} - -private fun BlockchainNetwork.getBlockchainWalletData( - walletManager: WalletManager?, - cardDerivationStyle: DerivationStyle?, -): WalletDataModel { - val currency = Currency.Blockchain( - blockchain = blockchain, - derivationPath = derivationPath, - ) - return WalletDataModel( - currency = currency, - status = WalletDataModel.Loading, - walletAddresses = walletManager?.wallet?.getWalletAddresses(), - existentialDeposit = getExistentialDeposit(walletManager), - fiatRate = null, - isCardSingleToken = false, - isCustom = currency.isCustomCurrency(cardDerivationStyle), - ) -} - -private fun BlockchainNetwork.getTokensWalletsData( - walletManager: WalletManager?, - cardDerivationStyle: DerivationStyle?, - primaryToken: Token?, -): List { - return this.tokens - .map { token -> - val currency = Currency.Token( - token = token, - blockchain = blockchain, - derivationPath = derivationPath, - ) - WalletDataModel( - currency = currency, - status = WalletDataModel.Loading, - walletAddresses = walletManager?.wallet?.getWalletAddresses(), - existentialDeposit = getExistentialDeposit(walletManager), - fiatRate = null, - isCardSingleToken = token == primaryToken, - isCustom = currency.isCustomCurrency(cardDerivationStyle), - ) - } -} - -private fun Blockchain.toBlockchainWalletData(walletManager: WalletManager): WalletDataModel { - val wallet = walletManager.wallet - return WalletDataModel( - currency = Currency.Blockchain( - blockchain = this, - derivationPath = wallet.publicKey.derivationPath?.rawPath, - ), - status = WalletDataModel.Loading, - walletAddresses = wallet.getWalletAddresses(), - existentialDeposit = getExistentialDeposit(walletManager), - fiatRate = null, - isCardSingleToken = false, - isCustom = false, - ) -} - -private fun Token.toTokenWalletData(walletManager: WalletManager, primaryToken: Token?): WalletDataModel { - val wallet = walletManager.wallet - return WalletDataModel( - currency = Currency.Token( - token = this, - blockchain = wallet.blockchain, - derivationPath = wallet.publicKey.derivationPath?.rawPath, - ), - status = WalletDataModel.Loading, - walletAddresses = wallet.getWalletAddresses(), - existentialDeposit = getExistentialDeposit(walletManager), - fiatRate = null, - isCardSingleToken = this == primaryToken, - isCustom = false, - ) -} - -private fun getExistentialDeposit(walletManager: WalletManager?): BigDecimal? { - return (walletManager as? ExistentialDepositProvider)?.getExistentialDeposit() -} - -private fun Wallet.getWalletAddresses(): WalletDataModel.WalletAddresses? { - return this.createAddressesData() - .takeIf { it.isNotEmpty() } - ?.let { addresses -> - WalletDataModel.WalletAddresses( - list = addresses, - selectedAddress = addresses.first(), - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/moduleMessage/ConvertedMessage.kt b/app/src/main/java/com/tangem/tap/domain/moduleMessage/ConvertedMessage.kt deleted file mode 100644 index a8add4f2a5..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/moduleMessage/ConvertedMessage.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.tangem.tap.domain.moduleMessage - -/** -[REDACTED_AUTHOR] - * Base interface for conversion outputs from ModuleMessageConverter - */ -interface ConvertedMessage { - val message: String -} - -/** - * Dialog message used to construct a android.Dialog - */ -interface DialogMessage : ConvertedMessage { - val title: String - override val message: String - val onPositive: String? - val onNegative: String? - val onNeutral: String? -} - -data class ConvertedStringMessage( - override val message: String, -) : ConvertedMessage - -data class ConvertedDialogMessage( - override val title: String, - override val message: String, - override val onPositive: String? = null, - override val onNegative: String? = null, - override val onNeutral: String? = null, -) : DialogMessage \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/moduleMessage/ModuleMessageConverter.kt b/app/src/main/java/com/tangem/tap/domain/moduleMessage/ModuleMessageConverter.kt deleted file mode 100644 index efca92a6e8..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/moduleMessage/ModuleMessageConverter.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.tangem.tap.domain.moduleMessage - -import android.content.Context -import com.tangem.common.module.ModuleMessage -import com.tangem.common.module.ModuleMessageConverter -import com.tangem.domain.DomainModuleMessage -import com.tangem.tap.domain.moduleMessage.domain.DomainMessageConverter - -class ModuleMessageConverter( - private val context: Context, -) : ModuleMessageConverter { - - override fun convert(message: ModuleMessage): ConvertedMessage { - val convertedMessage = when (message) { - is DomainModuleMessage -> DomainMessageConverter(context).convert(message) - else -> null - } - return convertedMessage ?: convertUnknownMessage(message) - } - - private fun convertUnknownMessage(message: ModuleMessage): ConvertedMessage { - return ConvertedStringMessage("Unknown message: ${message::class.java.simpleName}") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/moduleMessage/domain/DomainMessageConverter.kt b/app/src/main/java/com/tangem/tap/domain/moduleMessage/domain/DomainMessageConverter.kt deleted file mode 100644 index 67c58be34a..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/moduleMessage/domain/DomainMessageConverter.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.tangem.tap.domain.moduleMessage.domain - -import android.content.Context -import com.tangem.common.module.ModuleMessageConverter -import com.tangem.domain.AddCustomTokenError -import com.tangem.domain.DomainModuleError -import com.tangem.domain.DomainModuleMessage -import com.tangem.tap.domain.moduleMessage.ConvertedMessage -import com.tangem.tap.domain.moduleMessage.domain.converter.AddCustomTokenErrorConverter - -/** -[REDACTED_AUTHOR] - */ -class DomainMessageConverter( - private val context: Context, -) : ModuleMessageConverter { - - override fun convert(message: DomainModuleMessage): ConvertedMessage? { - return when (message) { - is DomainModuleError -> DomainErrorConverter(context).convert(message) - else -> null - } - } -} - -class DomainErrorConverter( - private val context: Context, -) : ModuleMessageConverter { - - override fun convert(message: DomainModuleError): ConvertedMessage? = when (message) { - is AddCustomTokenError -> AddCustomTokenErrorConverter(context).convert(message) - else -> null - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/moduleMessage/domain/converter/AddCustomTokenErrorConverter.kt b/app/src/main/java/com/tangem/tap/domain/moduleMessage/domain/converter/AddCustomTokenErrorConverter.kt deleted file mode 100644 index daa0901b14..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/moduleMessage/domain/converter/AddCustomTokenErrorConverter.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.tangem.tap.domain.moduleMessage.domain.converter - -import android.content.Context -import com.tangem.common.module.ModuleMessageConverter -import com.tangem.domain.AddCustomTokenError -import com.tangem.domain.DomainModuleError -import com.tangem.tap.domain.moduleMessage.ConvertedMessage -import com.tangem.tap.domain.moduleMessage.ConvertedStringMessage -import com.tangem.wallet.R - -/** -[REDACTED_AUTHOR] - */ -internal class AddCustomTokenErrorConverter( - private val context: Context, -) : ModuleMessageConverter { - - @Suppress("MagicNumber") - override fun convert(message: DomainModuleError): ConvertedMessage? { - val customTokenError = message as? AddCustomTokenError ?: throw UnsupportedOperationException() - - val rawMessage = when (customTokenError) { - AddCustomTokenError.Warning.PotentialScamToken -> R.string.custom_token_validation_error_not_found - AddCustomTokenError.Warning.TokenAlreadyAdded -> R.string.custom_token_validation_error_already_added - AddCustomTokenError.Warning.UnsupportedSolanaToken -> R.string.alert_manage_tokens_unsupported_message - AddCustomTokenError.InvalidContractAddress -> R.string.custom_token_creation_error_invalid_contract_address - AddCustomTokenError.NetworkIsNotSelected -> R.string.custom_token_creation_error_network_not_selected - AddCustomTokenError.InvalidDerivationPath -> R.string.custom_token_creation_error_invalid_derivation_path - AddCustomTokenError.InvalidDecimalsCount -> { - context.getString(R.string.custom_token_creation_error_wrong_decimals, 30) - } - AddCustomTokenError.FieldIsEmpty -> R.string.custom_token_creation_error_required_field - else -> null - } - - return when (rawMessage) { - is Int -> ConvertedStringMessage(context.getString(rawMessage)) - is String -> ConvertedStringMessage(rawMessage) - else -> null - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/statePrinter/OnboardingWalletStateConverter.kt b/app/src/main/java/com/tangem/tap/domain/statePrinter/OnboardingWalletStateConverter.kt deleted file mode 100644 index 3e98a50996..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/statePrinter/OnboardingWalletStateConverter.kt +++ /dev/null @@ -1,89 +0,0 @@ -package com.tangem.tap.domain.statePrinter - -import com.squareup.moshi.FromJson -import com.squareup.moshi.ToJson -import com.tangem.common.json.MoshiJsonConverter -import com.tangem.domain.redux.state.StringStateConverter -import com.tangem.tap.common.redux.AppState -import com.tangem.tap.features.onboarding.products.wallet.redux.BackupStep -import com.tangem.tap.store -import timber.log.Timber -import java.math.BigInteger - -/** -[REDACTED_AUTHOR] - */ -class OnboardingWalletStateConverter : StringStateConverter { - private val converter = MoshiJsonConverter( - MoshiJsonConverter.getTangemSdkAdapters() + internalAdapters(), - MoshiJsonConverter.getTangemSdkTypedAdapters(), - ) - - private fun internalAdapters(): List { - return listOf( - BackupStepAdapter(), - BigIntegerAdapter(), - - ) - } - - override fun convert(stateHolder: AppState): String { - val model = stateHolder.onboardingWalletState - val json = converter.prettyPrint(model) - - return json - } -} - -fun printOnboardingWalletState() { - val stringState = OnboardingWalletStateConverter().convert(store.state) - Timber.d(stringState) -} - -class BackupStepAdapter { - @ToJson - fun toJson(src: BackupStep): String { - return when (src) { - BackupStep.AddBackupCards -> "AddBackupCards" - BackupStep.EnterAccessCode -> "EnterAccessCode" - BackupStep.Finished -> "Finished" - BackupStep.InitBackup -> "InitBackup" - BackupStep.ReenterAccessCode -> "ReenterAccessCode" - BackupStep.ScanOriginCard -> "ScanOriginCard" - BackupStep.SetAccessCode -> "SetAccessCode" - is BackupStep.WriteBackupCard -> "WriteBackupCard[${src.cardNumber}]" - BackupStep.WritePrimaryCard -> "WritePrimaryCard" - } - } - - @Suppress("MagicNumber") - @FromJson - fun fromJson(json: String): BackupStep { - return when (json) { - "AddBackupCards" -> BackupStep.AddBackupCards - "EnterAccessCode" -> BackupStep.EnterAccessCode - "Finished" -> BackupStep.Finished - "InitBackup" -> BackupStep.InitBackup - "ReenterAccessCode" -> BackupStep.ReenterAccessCode - "ScanOriginCard" -> BackupStep.ScanOriginCard - "SetAccessCode" -> BackupStep.SetAccessCode - "WriteBackupCard[1]" -> BackupStep.WriteBackupCard(1) - "WriteBackupCard[2]" -> BackupStep.WriteBackupCard(2) - "WriteBackupCard[3]" -> BackupStep.WriteBackupCard(3) - "WritePrimaryCard" -> BackupStep.WritePrimaryCard - else -> throw UnsupportedOperationException() - } - } -} - -class BigIntegerAdapter { - @ToJson - fun toJson(src: BigInteger): String { - return src.toString() - } - - @FromJson - fun fromJson(json: String): BigInteger { - return BigInteger(json) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/statePrinter/ScanResponseConverter.kt b/app/src/main/java/com/tangem/tap/domain/statePrinter/ScanResponseConverter.kt deleted file mode 100644 index e59536ab1e..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/statePrinter/ScanResponseConverter.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.tangem.tap.domain.statePrinter - -import com.tangem.common.extensions.toHexString -import com.tangem.common.json.MoshiJsonConverter -import com.tangem.domain.redux.state.StringStateConverter -import com.tangem.tap.common.redux.AppState -import com.tangem.tap.store -import timber.log.Timber - -/** -[REDACTED_AUTHOR] - */ -class ScanResponseConverter : StringStateConverter { - private val converter = MoshiJsonConverter.INSTANCE - - override fun convert(stateHolder: AppState): String { - val scanResponse = stateHolder.globalState.scanResponse ?: return "NULL" - - val scanResponseMap = mutableMapOf() - val derivedKeysMap = mutableMapOf() - scanResponse.derivedKeys.forEach { (keyWalletPubKey, mapExPubKeys) -> - val exPubKeysMap = mapExPubKeys.entries.map { (derivationPath, exPubKey) -> - mapOf( - "derivationPath" to derivationPath.rawPath, - "extendedPublicKey" to mapOf( - "publicKey" to exPubKey.publicKey.toHexString(), - "chainCode" to exPubKey.chainCode.toHexString(), - ), - ) - } - derivedKeysMap[keyWalletPubKey.bytes.toHexString()] = exPubKeysMap - } - scanResponseMap["derivedKeys"] = derivedKeysMap - - val json = converter.prettyPrint(scanResponseMap) - - return json - } -} - -fun printScanResponseState() { - val stringState = ScanResponseConverter().convert(store.state) - Timber.d(stringState) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/statePrinter/WalletStateConverter.kt b/app/src/main/java/com/tangem/tap/domain/statePrinter/WalletStateConverter.kt deleted file mode 100644 index 57762aaf51..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/statePrinter/WalletStateConverter.kt +++ /dev/null @@ -1,68 +0,0 @@ -package com.tangem.tap.domain.statePrinter - -import com.tangem.blockchain.common.Wallet -import com.tangem.common.json.MoshiJsonConverter -import com.tangem.domain.redux.state.StringStateConverter -import com.tangem.tap.common.redux.AppState -import com.tangem.tap.store -import timber.log.Timber - -/** -[REDACTED_AUTHOR] - */ -class WalletStateConverter : StringStateConverter { - private val converter = MoshiJsonConverter.INSTANCE - - override fun convert(stateHolder: AppState): String { - val walletState = stateHolder.walletState - - val walletManagers = mutableListOf>() - walletState.walletManagers.forEach { - val wallet = it.wallet - - val walletManagerMap = mutableMapOf() - walletManagerMap["walletManager"] = mapOf( - "wallet" to convertWallet(it.wallet), - ) - - walletManagers.add(walletManagerMap) - } - val json = converter.prettyPrint(walletManagers) - return json - } - - private fun convertWallet(wallet: Wallet): MutableMap { - val walletMap = mutableMapOf() - val amounts = mutableMapOf() - wallet.amounts.forEach { (type, amount) -> - val amountMap = mapOf( - "value" to amount.value?.toPlainString(), - "currencySymbol" to amount.currencySymbol, - "decimals" to amount.decimals, - "type" to amount.type::class.java.simpleName, - ) - amounts[type::class.java.simpleName] = amountMap - } - - val publicKeyMap = mapOf( - "seedKey" to wallet.publicKey.seedKey, - "derivedKey" to wallet.publicKey.derivedKey, - "derivationPath" to wallet.publicKey.derivationPath?.rawPath, - "blockchainKey" to wallet.publicKey.blockchainKey, - ) - - walletMap["address"] = wallet.address - walletMap["blockchain"] = wallet.blockchain.name - walletMap["curve"] = wallet.blockchain.getSupportedCurves()[0].curve - walletMap["publicKey"] = publicKeyMap - walletMap["amounts"] = amounts - walletMap["addresses"] = wallet.addresses.toString() - - return walletMap - } -} - -fun printWalletState() { - val stringState = WalletStateConverter().convert(store.state) - Timber.d(stringState) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/SignHashTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/SignHashTask.kt deleted file mode 100644 index d3b749658c..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/tasks/SignHashTask.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.tangem.tap.domain.tasks - -import com.tangem.blockchain.common.Wallet -import com.tangem.common.CompletionResult -import com.tangem.common.core.CardSession -import com.tangem.common.core.CardSessionRunnable -import com.tangem.common.core.CompletionCallback -import com.tangem.operations.CommandResponse -import com.tangem.operations.sign.SignHashCommand - -class TangemSignHashResponse( - val signature: ByteArray, - val totalSignedHashes: Int?, - val remainingSignatures: Int?, -) : CommandResponse - -class SignHashTask( - private val hash: ByteArray, - private val publicKey: Wallet.PublicKey, -) : CardSessionRunnable { - override fun run(session: CardSession, callback: CompletionCallback) { - SignHashCommand(hash, publicKey.seedKey, publicKey.derivationPath).run(session) { response -> - - when (response) { - is CompletionResult.Success -> { - callback( - CompletionResult.Success( - TangemSignHashResponse( - response.data.signature, - response.data.totalSignedHashes, - session.environment.card?.wallet(publicKey.seedKey)?.remainingSignatures, - ), - ), - ) - } - is CompletionResult.Failure -> - callback(CompletionResult.Failure(response.error)) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt index b6536cfd4b..48e5ed9a7a 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt @@ -21,7 +21,7 @@ import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.KeyWalletPublicKey import com.tangem.operations.CommandResponse import com.tangem.operations.backup.PrimaryCard -import com.tangem.operations.backup.StartPrimaryCardLinkingTask +import com.tangem.operations.backup.StartPrimaryCardLinkingCommand import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask import com.tangem.operations.derivation.ExtendedPublicKeysMap import com.tangem.operations.wallet.CreateWalletTask @@ -210,7 +210,7 @@ private class CreateWalletTangemWallet( session: CardSession, callback: (result: CompletionResult) -> Unit, ) { - StartPrimaryCardLinkingTask().run(session) { result -> + StartPrimaryCardLinkingCommand().run(session) { result -> when (result) { is CompletionResult.Success -> { primaryCard = result.data @@ -309,7 +309,5 @@ private class CreateWalletTangemWallet( } } - private companion object { - val CURVES_FOR_WALLETS = listOf(EllipticCurve.Secp256k1, EllipticCurve.Ed25519) - } + private companion object } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/DerivationsFinder.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/DerivationsFinder.kt index 38a23139b6..d229d8f05d 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/DerivationsFinder.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/DerivationsFinder.kt @@ -11,10 +11,7 @@ import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.userwallets.UserWalletIdBuilder import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles -import com.tangem.tap.domain.tokens.UserTokensStorageService import com.tangem.tap.features.demo.DemoHelper -import com.tangem.tap.features.wallet.models.Currency import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.withContext @@ -25,9 +22,7 @@ internal data class BlockchainToDerive( // FIXME: May be move to DI, currently unnecessary internal class DerivationsFinder( - private val legacyTokensStore: UserTokensStorageService, private val newTokensStore: UserTokensStore, - private val walletFeatureToggles: WalletFeatureToggles, private val dispatchers: CoroutineDispatcherProvider, ) { @@ -40,11 +35,7 @@ internal class DerivationsFinder( val derivationStyle = derivationStyleProvider.getDerivationStyle() var blockchains = withContext(dispatchers.io) { - if (walletFeatureToggles.isRedesignedScreenEnabled) { - getBlockchainsNew(userWalletId) - } else { - getBlockchainsLegacy(userWalletId) - } + getBlockchains(userWalletId) } if (blockchains.isEmpty()) { @@ -72,7 +63,7 @@ internal class DerivationsFinder( return blockchains } - private suspend fun getBlockchainsNew(userWalletId: UserWalletId): MutableSet { + private suspend fun getBlockchains(userWalletId: UserWalletId): MutableSet { val responseTokens = newTokensStore.getSyncOrNull(userWalletId) ?.tokens ?: return hashSetOf() @@ -88,22 +79,6 @@ internal class DerivationsFinder( .toMutableSet() } - private fun getBlockchainsLegacy(userWalletId: UserWalletId): MutableSet { - val currencies = legacyTokensStore.getUserTokens(userWalletId.stringValue) - ?.takeIf { it.isNotEmpty() } - ?: return hashSetOf() - - return currencies.asSequence() - .filterIsInstance() - .map { coin -> - val blockchain = coin.blockchain - val derivationPath = coin.derivationPath?.let(::DerivationPath) - - BlockchainToDerive(blockchain, derivationPath) - } - .toMutableSet() - } - // TODO: Move to user wallet config private fun getDemoBlockchains(derivationStyle: DerivationStyle?): MutableSet { return DemoHelper.config.demoBlockchains.mapToBlockchainsWithDerivations(derivationStyle) diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt index a3fac64bc1..8d811541cf 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt @@ -18,6 +18,7 @@ import com.tangem.domain.common.TapWorkarounds.isExcluded import com.tangem.domain.common.TapWorkarounds.isNotSupportedInThatRelease import com.tangem.domain.common.TapWorkarounds.isStart2Coin import com.tangem.domain.common.TapWorkarounds.isTangemTwins +import com.tangem.domain.common.TapWorkarounds.isVisa import com.tangem.domain.common.TwinsHelper import com.tangem.domain.common.configs.CardConfig import com.tangem.domain.common.util.derivationStyleProvider @@ -172,10 +173,10 @@ private class ScanWalletProcessor( } private fun determineProductTypeForSingleCurrencyWallet(card: CardDTO): ProductType { - return if (card.isStart2Coin) { - ProductType.Start2Coin - } else { - ProductType.Note + return when { + card.isStart2Coin -> ProductType.Start2Coin + card.isVisa -> ProductType.Visa + else -> ProductType.Note } } diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/Currency.kt b/app/src/main/java/com/tangem/tap/domain/tokens/Currency.kt deleted file mode 100644 index 2cb1488c24..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/tokens/Currency.kt +++ /dev/null @@ -1,102 +0,0 @@ -package com.tangem.tap.domain.tokens - -import com.squareup.moshi.JsonClass -import com.tangem.blockchain.common.Blockchain -import com.tangem.datasource.api.tangemTech.models.CoinsResponse -import com.tangem.domain.common.extensions.fromNetworkId - -@JsonClass(generateAdapter = true) -data class CurrencyFromJson( - val id: String, - val name: String, - val symbol: String, - val networks: List? = null, -) - -@JsonClass(generateAdapter = true) -data class ContractFromJson( - val networkId: String, - val contractAddress: String?, - val decimalCount: Int?, -) - -@JsonClass(generateAdapter = true) -data class CurrenciesFromJson( - val imageHost: String?, - val coins: List, -) - -fun List.toContracts(): List { - return mapNotNull { Contract.fromJsonObject(it) } -} - -data class Currency( - val id: String, - val name: String, - val symbol: String, - val iconUrl: String, - val contracts: List, -) { - - companion object { - fun fromJsonObject(currency: CurrencyFromJson): Currency { - return Currency( - id = currency.id, - name = currency.name, - symbol = currency.symbol, - iconUrl = getIconUrl(currency.id, null), - contracts = currency.networks?.toContracts() ?: emptyList(), - ) - } - - fun fromCoinResponse(currency: CoinsResponse.Coin, imageHost: String?): Currency { - return Currency( - id = currency.id, - name = currency.name, - symbol = currency.symbol, - iconUrl = getIconUrl(currency.id, imageHost), - contracts = currency.networks.mapNotNull { Contract.fromNetwork(it, imageHost) }, - ) - } - } -} - -data class Contract( - val networkId: String, - val blockchain: Blockchain, - val address: String?, - val decimalCount: Int?, - val iconUrl: String, -) { - - companion object { - fun fromJsonObject(contract: ContractFromJson): Contract? { - val blockchain = Blockchain.fromNetworkId(contract.networkId) ?: return null - return Contract( - networkId = contract.networkId, - blockchain = blockchain, - address = contract.contractAddress, - decimalCount = contract.decimalCount, - iconUrl = getIconUrl(contract.networkId, null), - ) - } - - fun fromNetwork(contract: CoinsResponse.Coin.Network, imageHost: String?): Contract? { - val blockchain = Blockchain.fromNetworkId(contract.networkId) ?: return null - return Contract( - networkId = contract.networkId, - blockchain = blockchain, - address = contract.contractAddress, - decimalCount = contract.decimalCount?.toInt(), - iconUrl = getIconUrl(contract.networkId, imageHost), - ) - } - } -} - -fun getIconUrl(id: String, imageHost: String? = null): String { - return "${imageHost ?: DEFAULT_IMAGE_HOST}large/$id.png" -} - -private 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/domain/tokens/UserTokensRepository.kt b/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt deleted file mode 100644 index 514b75eb10..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt +++ /dev/null @@ -1,146 +0,0 @@ -package com.tangem.tap.domain.tokens - -import com.tangem.blockchain.common.derivation.DerivationStyle -import com.tangem.common.core.TangemSdkError -import com.tangem.datasource.api.common.response.getOrThrow -import com.tangem.datasource.api.tangemTech.TangemTechApi -import com.tangem.datasource.api.tangemTech.TangemTechService -import com.tangem.datasource.api.tangemTech.models.UserTokensResponse -import com.tangem.datasource.connection.NetworkConnectionManager -import com.tangem.domain.common.BlockchainNetwork -import com.tangem.domain.models.scan.CardDTO -import com.tangem.domain.userwallets.UserWalletIdBuilder -import com.tangem.tap.domain.tokens.converters.CurrencyConverter -import com.tangem.tap.features.demo.DemoHelper -import com.tangem.tap.features.wallet.models.Currency -import com.tangem.tap.features.wallet.models.toBlockchainNetworks -import com.tangem.tap.features.wallet.models.toCurrencies -import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.withContext -import timber.log.Timber - -class UserTokensRepository( - private val storageService: UserTokensStorageService, - private val tangemTechApi: TangemTechApi, - private val dispatchers: CoroutineDispatcherProvider, - private val networkConnectionManager: NetworkConnectionManager, -) { - - suspend fun getUserTokens(card: CardDTO, derivationStyle: DerivationStyle?): List = - withContext(dispatchers.io) { - val userWalletId = getUserWalletId(card) ?: return@withContext emptyList() - - if (DemoHelper.isDemoCardId(card.cardId)) { - return@withContext loadTokensOffline(userWalletId = userWalletId).ifEmpty { - loadDemoCurrencies( - derivationStyle, - ) - } - } - - if (!networkConnectionManager.isOnline) return@withContext loadTokensOffline(userWalletId = userWalletId) - - return@withContext remoteGetUserTokens(userWalletId = userWalletId) - } - - suspend fun saveUserTokens(card: CardDTO, tokens: List) = withContext(dispatchers.io) { - val userWalletId = getUserWalletId(card) ?: return@withContext - val userTokens = tokens.toUserTokensResponse() - remoteSaveUserTokens(userWalletId = userWalletId, userTokens = userTokens) - storageService.saveUserTokens(userWalletId = userWalletId, tokens = userTokens) - } - - // FIXME: Move to data layer - suspend fun loadBlockchainsToDerive(card: CardDTO, derivationStyle: DerivationStyle?): List = - withContext(dispatchers.io) { - val userWalletId = getUserWalletId(card) ?: return@withContext emptyList() - val blockchainNetworks = loadTokensOffline(userWalletId = userWalletId).toBlockchainNetworks() - - if (DemoHelper.isDemoCardId(card.cardId)) { - return@withContext blockchainNetworks.ifEmpty(loadDemoCurrencies(derivationStyle)::toBlockchainNetworks) - } - - return@withContext blockchainNetworks - } - - private fun loadTokensOffline(userWalletId: String): List { - return storageService.getUserTokens(userWalletId = userWalletId) ?: emptyList() - } - - // FIXME: Move to user wallet config - private fun loadDemoCurrencies(derivationStyle: DerivationStyle?): List { - return DemoHelper.config.demoBlockchains - .map { blockchain -> - BlockchainNetwork( - blockchain = blockchain, - derivationPath = blockchain.derivationPath(derivationStyle)?.rawPath, - tokens = emptyList(), - ) - } - .flatMap(BlockchainNetwork::toCurrencies) - } - - private fun List.toUserTokensResponse(): UserTokensResponse { - return UserTokensResponse( - tokens = CurrencyConverter.convertList(input = this), - group = UserTokensResponse.GroupType.NONE, - sort = UserTokensResponse.SortType.MANUAL, - ) - } - - private suspend fun handleGetUserTokensFailure(userWalletId: String, error: Throwable): List { - return when { - error is TangemSdkError.NetworkError && error.customMessage.contains(NOT_FOUND_HTTP_CODE) -> { - storageService - .getUserTokens(userWalletId) - ?.also { remoteSaveUserTokens(userWalletId = userWalletId, userTokens = it.toUserTokensResponse()) } - ?: emptyList() - } - else -> { - storageService.getUserTokens(userWalletId)?.distinct() ?: emptyList() - } - } - } - - private suspend fun remoteGetUserTokens(userWalletId: String): List { - return runCatching { tangemTechApi.getUserTokens(userWalletId) } - .fold( - onSuccess = { response -> - response.getOrThrow() - .also { storageService.saveUserTokens(userWalletId, it) } - .tokens - .mapNotNull(Currency.Companion::fromTokenResponse) - .distinct() - }, - onFailure = { handleGetUserTokensFailure(userWalletId = userWalletId, error = it) }, - ) - } - - private suspend fun remoteSaveUserTokens(userWalletId: String, userTokens: UserTokensResponse) { - // it can throw okhttp3.internal.http2.StreamResetException: stream was reset: INTERNAL_ERROR - // if the /user-tokens endpoint disabled - runCatching { tangemTechApi.saveUserTokens(userWalletId, userTokens) } - .onFailure { Timber.e(it) } - } - - private fun getUserWalletId(card: CardDTO): String? = UserWalletIdBuilder.card(card).build()?.stringValue - - companion object { - private const val NOT_FOUND_HTTP_CODE = "404" - - // TODO("After adding DI") get dependencies by DI - fun init( - tangemTechService: TangemTechService, - networkConnectionManager: NetworkConnectionManager, - storageService: UserTokensStorageService, - ): UserTokensRepository { - return UserTokensRepository( - storageService = storageService, - tangemTechApi = tangemTechService.api, - dispatchers = AppCoroutineDispatcherProvider(), - networkConnectionManager = networkConnectionManager, - ) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensStorageService.kt b/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensStorageService.kt deleted file mode 100644 index 08fb2c4ce4..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensStorageService.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.tangem.tap.domain.tokens - -import android.content.Context -import com.squareup.moshi.JsonAdapter -import com.tangem.Log -import com.tangem.datasource.api.common.MoshiConverter -import com.tangem.datasource.api.tangemTech.models.UserTokensResponse -import com.tangem.datasource.files.AndroidFileReader -import com.tangem.datasource.files.FileReader -import com.tangem.tap.features.wallet.models.Currency - -@Deprecated("Use [com.tangem.datasource.local.token.UserTokensStore] instead.") -class UserTokensStorageService(private val fileReader: FileReader) { - private val userTokensAdapter: JsonAdapter = - MoshiConverter.networkMoshi.adapter(UserTokensResponse::class.java) - - fun getUserTokens(userWalletId: String): List? { - return try { - val json = fileReader.readFile(getFileNameForUserTokens(userWalletId)) - userTokensAdapter.fromJson(json)?.tokens?.mapNotNull { Currency.fromTokenResponse(it) } - } catch (exception: Exception) { - Log.error { exception.stackTraceToString() } - null - } - } - - fun saveUserTokens(userWalletId: String, tokens: UserTokensResponse) { - val json = userTokensAdapter.toJson(tokens) - fileReader.rewriteFile(json, getFileNameForUserTokens(userWalletId)) - } - - companion object { - private const val FILE_NAME_PREFIX_USER_TOKENS = "user_tokens" - private fun getFileNameForUserTokens(userId: String): String = "${FILE_NAME_PREFIX_USER_TOKENS}_$userId" - - fun init(context: Context) = UserTokensStorageService(AndroidFileReader(context)) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/converters/CurrencyConverter.kt b/app/src/main/java/com/tangem/tap/domain/tokens/converters/CurrencyConverter.kt deleted file mode 100644 index 6c60dfbc89..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/tokens/converters/CurrencyConverter.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.tangem.tap.domain.tokens.converters - -import com.tangem.datasource.api.tangemTech.models.UserTokensResponse -import com.tangem.domain.common.extensions.toNetworkId -import com.tangem.tap.features.wallet.models.Currency -import com.tangem.utils.converter.Converter - -/** Converter from domain model [Currency] to data model [UserTokensResponse.Token] */ -object CurrencyConverter : Converter { - - override fun convert(value: Currency) = UserTokensResponse.Token( - id = value.coinId, - networkId = value.blockchain.toNetworkId(), - derivationPath = value.derivationPath, - name = value.currencyName, - symbol = value.currencySymbol, - decimals = value.decimals, - contractAddress = if (value is Currency.Token) value.token.contractAddress else null, - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/models/BlockchainDao.kt b/app/src/main/java/com/tangem/tap/domain/tokens/models/BlockchainDao.kt deleted file mode 100644 index 4417004531..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/tokens/models/BlockchainDao.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.tangem.tap.domain.tokens.models - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import com.tangem.blockchain.common.Blockchain - -@Deprecated("The class is used only for migration from older versions of the app") -@JsonClass(generateAdapter = true) -data class BlockchainDao( - @Json(name = "key") - val name: String, - @Json(name = "testnet") - val isTestNet: Boolean, -) { - @Deprecated("The method is used only for migration from older versions of the app") - fun toBlockchain(): Blockchain { - val blockchain = Blockchain.values().find { it.name.lowercase() == name.lowercase() } - ?: error("Blockchain is null") - return if (!isTestNet) blockchain else blockchain.getTestnetVersion() ?: error("Invalid BlockchainDao") - } - - companion object { - @Deprecated("The method is used only for migration from older versions of the app") - fun fromBlockchain(blockchain: Blockchain): BlockchainDao { - val name = blockchain.name.removeSuffix("Testnet").lowercase() - return BlockchainDao(name, blockchain.isTestnet()) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/models/ObsoleteTokenDao.kt b/app/src/main/java/com/tangem/tap/domain/tokens/models/ObsoleteTokenDao.kt deleted file mode 100644 index 5ff4d8c8ca..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/tokens/models/ObsoleteTokenDao.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.tangem.tap.domain.tokens.models - -import com.squareup.moshi.JsonClass -import com.tangem.blockchain.common.Blockchain - -@JsonClass(generateAdapter = true) -data class ObsoleteTokenDao( - val name: String, - val symbol: String, - val contractAddress: String, - val decimalCount: Int, - val customIconUrl: String?, -) { - fun toTokenDao(blockchain: Blockchain): TokenDao { - return TokenDao( - name = name, - symbol = symbol, - contractAddress = contractAddress, - decimalCount = decimalCount, - blockchainDao = BlockchainDao.fromBlockchain(blockchain), - customIconUrl = customIconUrl, - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/models/TokenDao.kt b/app/src/main/java/com/tangem/tap/domain/tokens/models/TokenDao.kt deleted file mode 100644 index a70e0606b3..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/tokens/models/TokenDao.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.tangem.tap.domain.tokens.models - -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass -import com.tangem.blockchain.common.Token -import com.tangem.tap.domain.extensions.setCustomIconUrl - -@JsonClass(generateAdapter = true) -data class TokenDao( - val name: String, - val symbol: String, - val contractAddress: String, - val decimalCount: Int, - @Json(name = "blockchain") - val blockchainDao: BlockchainDao, - val customIconUrl: String? = null, - val type: String? = null, -) { - fun toToken(): Token { - return Token( - name = name, - symbol = symbol, - contractAddress = contractAddress, - decimals = decimalCount, - ).apply { - customIconUrl?.let { this.setCustomIconUrl(it) } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/totalBalance/TotalFiatBalanceCalculator.kt b/app/src/main/java/com/tangem/tap/domain/totalBalance/TotalFiatBalanceCalculator.kt deleted file mode 100644 index a011491b0e..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/totalBalance/TotalFiatBalanceCalculator.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.tangem.tap.domain.totalBalance - -import com.tangem.tap.domain.model.TotalFiatBalance -import com.tangem.tap.domain.model.WalletStoreModel - -interface TotalFiatBalanceCalculator { - - /** - * Calculate total fiat balance for list of [WalletStoreModel] - * @param walletStores List of [WalletStoreModel] to calculate fiat amount - * @param initial Initial [TotalFiatBalance] state, used when list of [WalletStoreModel] is empty - * @return [TotalFiatBalance] with state found with the [WalletStoreModel] list - * */ - suspend fun calculate(walletStores: List, initial: TotalFiatBalance): TotalFiatBalance - - /** - * Same as [TotalFiatBalanceCalculator.calculate] but returns null if list of [WalletStoreModel] is empty - * @param walletStores List of [WalletStoreModel] to calculate fiat amount - * @return [TotalFiatBalance] with state found with the [WalletStoreModel] list - * */ - suspend fun calculateOrNull(walletStores: List): TotalFiatBalance? - - companion object -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/totalBalance/di/TotalFiatBalanceCalculatorProvider.kt b/app/src/main/java/com/tangem/tap/domain/totalBalance/di/TotalFiatBalanceCalculatorProvider.kt deleted file mode 100644 index ea285cc404..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/totalBalance/di/TotalFiatBalanceCalculatorProvider.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.tangem.tap.domain.totalBalance.di - -import com.tangem.tap.domain.totalBalance.TotalFiatBalanceCalculator -import com.tangem.tap.domain.totalBalance.implementation.DefaultTotalFiatBalanceCalculator - -fun TotalFiatBalanceCalculator.Companion.provideDefaultImplementation(): TotalFiatBalanceCalculator { - return DefaultTotalFiatBalanceCalculator() -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/totalBalance/implementation/DefaultTotalFiatBalanceCalculator.kt b/app/src/main/java/com/tangem/tap/domain/totalBalance/implementation/DefaultTotalFiatBalanceCalculator.kt deleted file mode 100644 index 330561f5af..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/totalBalance/implementation/DefaultTotalFiatBalanceCalculator.kt +++ /dev/null @@ -1,104 +0,0 @@ -package com.tangem.tap.domain.totalBalance.implementation - -import com.tangem.tap.common.extensions.toFiatValue -import com.tangem.tap.domain.model.TotalFiatBalance -import com.tangem.tap.domain.model.WalletDataModel -import com.tangem.tap.domain.model.WalletStoreModel -import com.tangem.tap.domain.totalBalance.TotalFiatBalanceCalculator -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import java.math.BigDecimal - -internal class DefaultTotalFiatBalanceCalculator : TotalFiatBalanceCalculator { - override suspend fun calculate(walletStores: List, initial: TotalFiatBalance): TotalFiatBalance { - return calculateOrNull(walletStores) ?: initial - } - - override suspend fun calculateOrNull(walletStores: List): TotalFiatBalance? { - return if (walletStores.isEmpty()) { - null - } else { - withContext(Dispatchers.Default) { - val walletsData = walletStores - .asSequence() - .flatMap { it.walletsData } - - when (val status = walletsData.findStatus()) { - TotalFiatBalanceStatus.Loading -> TotalFiatBalance.Loading - TotalFiatBalanceStatus.Failed -> TotalFiatBalance.Failed - TotalFiatBalanceStatus.Warning, - TotalFiatBalanceStatus.Loaded, - -> TotalFiatBalance.Loaded( - amount = walletsData.calculateTotalFiatAmount(), - isWarning = status == TotalFiatBalanceStatus.Warning, - ) - } - } - } - } - - private fun Sequence.findStatus(): TotalFiatBalanceStatus { - return this - .mapToStatus() - .reduce { prevStatus, newStatus -> - getCurrentStatus(prevStatus, newStatus) - } - } - - private fun Sequence.mapToStatus(): Sequence { - return this.map { walletData -> - when (walletData.status) { - is WalletDataModel.VerifiedOnline, - is WalletDataModel.SameCurrencyTransactionInProgress, - is WalletDataModel.TransactionInProgress, - is WalletDataModel.NoAccount, - -> if (walletData.isWarningCase()) { - TotalFiatBalanceStatus.Warning - } else { - TotalFiatBalanceStatus.Loaded - } - is WalletDataModel.Unreachable, - is WalletDataModel.MissedDerivation, - -> TotalFiatBalanceStatus.Failed - is WalletDataModel.Loading -> TotalFiatBalanceStatus.Loading - } - } - } - - private fun Sequence.calculateTotalFiatAmount(): BigDecimal { - return this - .filterNot { it.isWarningCase() } - .map { walletData -> - walletData.fiatRate - ?.takeUnless { walletData.status.isErrorStatus } - ?.let { walletData.status.amount.toFiatValue(it) } - ?: BigDecimal.ZERO - } - .reduce { acc, value -> - acc + value - } - } - - private fun getCurrentStatus( - prevStatus: TotalFiatBalanceStatus, - newStatus: TotalFiatBalanceStatus, - ): TotalFiatBalanceStatus { - return TotalFiatBalanceStatus[minOf(prevStatus.ordinal, newStatus.ordinal)] - } - - private fun WalletDataModel.isWarningCase(): Boolean = isCustom && fiatRate == null - - private enum class TotalFiatBalanceStatus { - Loading, - Failed, - Warning, - Loaded, - ; - - companion object { - private val allValues = values() - - operator fun get(index: Int) = allValues[index] - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerProvider.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerProvider.kt index 719491a036..025cb4df17 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerProvider.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerProvider.kt @@ -3,7 +3,6 @@ package com.tangem.tap.domain.userWalletList.di import android.content.Context import com.squareup.moshi.Moshi import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory -import com.tangem.common.Provider import com.tangem.common.authentication.AuthenticatedStorage import com.tangem.common.json.TangemSdkAdapter import com.tangem.common.services.secure.SecureStorage @@ -20,6 +19,7 @@ import com.tangem.tap.domain.userWalletList.repository.implementation.DefaultUse import com.tangem.tap.domain.userWalletList.repository.implementation.DefaultUserWalletsSensitiveInformationRepository import com.tangem.tap.domain.userWalletList.utils.json.* import com.tangem.tap.tangemSdkManager +import com.tangem.utils.Provider private const val USER_WALLETS_STORAGE_NAME = "user_wallets_storage" diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt index c285a15c80..a27e52d22e 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt @@ -75,9 +75,9 @@ internal class BiometricUserWalletsListManager( } val selectedUserWallet = selectedUserWalletSync - if (selectedUserWallet == null || selectedUserWallet.isLocked) { - findAndSetUnlockedUserWallet(userWallets) - ?: throw UserWalletsListError.NoUserWalletSelected + if (selectedUserWallet == null) { + Timber.e("Unable to find selected user wallet") + throw UserWalletsListError.NoUserWalletSelected } else { selectedUserWallet } @@ -257,10 +257,7 @@ internal class BiometricUserWalletsListManager( prevState.copy( userWallets = wallets, - selectedUserWalletId = findOrSetSelectedUserWalletId( - prevSelectedWalletId = prevState.selectedUserWalletId, - userWallets = wallets, - ), + selectedUserWalletId = findOrSetSelectedWalletId(prevState.selectedUserWalletId, wallets), ) } } @@ -278,18 +275,22 @@ internal class BiometricUserWalletsListManager( } } - private fun findOrSetSelectedUserWalletId( + private fun findOrSetSelectedWalletId( prevSelectedWalletId: UserWalletId?, userWallets: List, ): UserWalletId? { - return prevSelectedWalletId - ?: (selectedUserWalletRepository.get() ?: findAndSetUnlockedUserWallet(userWallets)?.walletId) - } + val selectedWalletId = prevSelectedWalletId ?: selectedUserWalletRepository.get() + var possibleSelectedUserWallet = findSelectedUserWallet(userWallets, selectedWalletId) - private fun findAndSetUnlockedUserWallet(userWallets: List): UserWallet? { - return userWallets - .firstOrNull { !it.isLocked } - ?.also { selectedUserWalletRepository.set(it.walletId) } + if (possibleSelectedUserWallet == null || possibleSelectedUserWallet.isLocked) { + possibleSelectedUserWallet = userWallets.firstOrNull { !it.isLocked } ?: userWallets.firstOrNull() + + if (possibleSelectedUserWallet != null) { + selectedUserWalletRepository.set(possibleSelectedUserWallet.walletId) + } + } + + return possibleSelectedUserWallet?.walletId } private fun changeSelectedUserWalletIdIfNeeded(walletsIdsToRemove: List) { @@ -318,10 +319,11 @@ internal class BiometricUserWalletsListManager( } } - private fun findSelectedUserWallet(userWallets: List = state.value.userWallets): UserWallet? { - return userWallets.firstOrNull { - it.walletId == state.value.selectedUserWalletId - } + private fun findSelectedUserWallet( + userWallets: List = state.value.userWallets, + selectedUserWalletId: UserWalletId? = state.value.selectedUserWalletId, + ): UserWallet? { + return userWallets.firstOrNull { it.walletId == selectedUserWalletId } } private data class State( diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/model/UserWalletEncryptionKey.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/model/UserWalletEncryptionKey.kt index d17ee7c4a3..0a1b637e5b 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/model/UserWalletEncryptionKey.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/model/UserWalletEncryptionKey.kt @@ -13,9 +13,7 @@ internal data class UserWalletEncryptionKey( if (other !is UserWalletEncryptionKey) return false if (walletId != other.walletId) return false - if (!encryptionKey.contentEquals(other.encryptionKey)) return false - - return true + return encryptionKey.contentEquals(other.encryptionKey) } override fun hashCode(): Int { 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 64e0f9a1ce..6073f2de68 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 @@ -1,7 +1,7 @@ package com.tangem.tap.domain.userWalletList.repository -import com.tangem.common.Provider import com.tangem.common.authentication.KeystoreManager +import com.tangem.utils.Provider import javax.crypto.SecretKey internal class DelegatedKeystoreManager( diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsKeysStoreDecorator.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsKeysStoreDecorator.kt index 49e87a395a..b9be28a34f 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsKeysStoreDecorator.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsKeysStoreDecorator.kt @@ -1,7 +1,7 @@ package com.tangem.tap.domain.userWalletList.repository -import com.tangem.common.Provider import com.tangem.common.services.secure.SecureStorage +import com.tangem.utils.Provider /** * A decorator for [SecureStorage] that facilitates data migration between two storages. diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsSensitiveInformationRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsSensitiveInformationRepository.kt index 8c67c90889..13c56f4f22 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsSensitiveInformationRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsSensitiveInformationRepository.kt @@ -1,12 +1,12 @@ package com.tangem.tap.domain.userWalletList.repository.implementation -import android.security.keystore.KeyProperties import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi import com.squareup.moshi.Types import com.tangem.common.CompletionResult import com.tangem.common.catching import com.tangem.common.services.secure.SecureStorage +import com.tangem.crypto.operations.AESCipherOperations import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import com.tangem.tap.common.extensions.filterNotNull @@ -16,14 +16,14 @@ import com.tangem.tap.domain.userWalletList.repository.UserWalletsSensitiveInfor import com.tangem.tap.domain.userWalletList.utils.sensitiveInformation import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import javax.crypto.Cipher -import javax.crypto.spec.IvParameterSpec +import timber.log.Timber import javax.crypto.spec.SecretKeySpec internal class DefaultUserWalletsSensitiveInformationRepository( moshi: Moshi, private val secureStorage: SecureStorage, ) : UserWalletsSensitiveInformationRepository { + private val sensitiveInformationAdapter: JsonAdapter = moshi.adapter( UserWalletSensitiveInformation::class.java, ) @@ -31,12 +31,11 @@ internal class DefaultUserWalletsSensitiveInformationRepository( Types.newParameterizedType(Map::class.java, String::class.java, ByteArray::class.java), ) - private val cipher: Cipher by lazy { - Cipher.getInstance("$algorithm/$blockMode/$encryptionPadding") - } - override suspend fun save(userWallet: UserWallet, encryptionKey: ByteArray?): CompletionResult { - if (encryptionKey == null) return CompletionResult.Success(Unit) // Encryption key is null, do nothing + if (encryptionKey == null) { + return CompletionResult.Success(Unit) // Encryption key is null, do nothing + } + return catching { val encryptedSensitiveInformation = userWallet.sensitiveInformation .encode() @@ -63,7 +62,7 @@ internal class DefaultUserWalletsSensitiveInformationRepository( .mapValues { (userWalletId, encryptionKey) -> encryptedSensitiveInformation[userWalletId.stringValue] ?.getIvAndDecrypt(userWalletId.stringValue, encryptionKey.encryptionKey) - .decodeToSensitiveInformation() + ?.decodeToSensitiveInformation() } .filterNotNull() } @@ -80,7 +79,8 @@ internal class DefaultUserWalletsSensitiveInformationRepository( private suspend fun getAllEncrypted(): Map { return withContext(Dispatchers.IO) { secureStorage.get(StorageKey.UserWalletsSensitiveInformation.name) - .decodeToEncryptedSensitiveInformation() + ?.decodeToEncryptedSensitiveInformation() + .orEmpty() } } @@ -105,20 +105,25 @@ internal class DefaultUserWalletsSensitiveInformationRepository( } } - private suspend fun ByteArray?.decodeToEncryptedSensitiveInformation(): Map { + private suspend fun ByteArray.decodeToEncryptedSensitiveInformation(): Map? { return withContext(Dispatchers.Default) { this@decodeToEncryptedSensitiveInformation - ?.decodeToString(throwOnInvalidSequence = true) - ?.let(encryptedSensitiveInformationMapAdapter::fromJson) - .orEmpty() + .decodeToString(throwOnInvalidSequence = true) + .let(encryptedSensitiveInformationMapAdapter::fromJson) } } - private suspend fun ByteArray?.decodeToSensitiveInformation(): UserWalletSensitiveInformation? { + private suspend fun ByteArray.decodeToSensitiveInformation(): UserWalletSensitiveInformation? { return withContext(Dispatchers.Default) { - this@decodeToSensitiveInformation - ?.decodeToString(throwOnInvalidSequence = true) - ?.let(sensitiveInformationAdapter::fromJson) + try { + this@decodeToSensitiveInformation + .decodeToString(throwOnInvalidSequence = true) + .let(sensitiveInformationAdapter::fromJson) + } catch (e: CharacterCodingException) { + Timber.e(e, "Unable to decode sensitive information") + + null + } } } @@ -140,23 +145,24 @@ internal class DefaultUserWalletsSensitiveInformationRepository( private suspend fun ByteArray.encryptAndStoreIv(userWalletId: String, encryptionKey: ByteArray): ByteArray { return withContext(Dispatchers.Default) { - val secretKey = SecretKeySpec(encryptionKey, algorithm) - cipher.init(Cipher.ENCRYPT_MODE, secretKey) - val encryptedData = cipher.doFinal(this@encryptAndStoreIv) - secureStorage.store(data = cipher.iv, account = StorageKey.SensitiveInformationIv(userWalletId).name) + val secretKey = SecretKeySpec(encryptionKey, AESCipherOperations.KEY_ALGORITHM) + val cipher = AESCipherOperations.initEncryptionCipher(secretKey) + val encryptedData = AESCipherOperations.encrypt(cipher, decryptedData = this@encryptAndStoreIv) + + secureStorage.store(cipher.iv, StorageKey.SensitiveInformationIv(userWalletId).name) + encryptedData } } - private suspend fun ByteArray.getIvAndDecrypt(userWalletId: String, encryptionKey: ByteArray): ByteArray? { + private suspend fun ByteArray.getIvAndDecrypt(userWalletId: String, encryptionKey: ByteArray): ByteArray { return withContext(Dispatchers.Default) { + val secretKeySpec = SecretKeySpec(encryptionKey, AESCipherOperations.KEY_ALGORITHM) val iv = secureStorage.get(StorageKey.SensitiveInformationIv(userWalletId).name) - ?: error("IV not found") - val ivParam = IvParameterSpec(iv) - val secretKeySpec = SecretKeySpec(encryptionKey, algorithm) - cipher - .also { it.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParam) } - .doFinal(this@getIvAndDecrypt) + ?: error("IV for user wallet $userWalletId not found") + val cipher = AESCipherOperations.initDecryptionCipher(secretKeySpec, iv) + + AESCipherOperations.decrypt(cipher, encryptedData = this@getIvAndDecrypt) } } @@ -171,10 +177,4 @@ internal class DefaultUserWalletsSensitiveInformationRepository( override val name: String = "user_wallet_sensitive_information_iv_$userWalletId" } } - - companion object { - private const val algorithm = KeyProperties.KEY_ALGORITHM_AES - private const val blockMode = KeyProperties.BLOCK_MODE_CBC - private const val encryptionPadding = KeyProperties.ENCRYPTION_PADDING_PKCS7 - } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/utils/ByteArrayListExtensions.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/utils/ByteArrayListExtensions.kt index 8a150ac79e..fa3d2aefba 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/utils/ByteArrayListExtensions.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/utils/ByteArrayListExtensions.kt @@ -1,9 +1 @@ -package com.tangem.tap.domain.userWalletList.utils - -internal fun List.containsBA(element: ByteArray?): Boolean { - this.forEach { - if (it.contentEquals(element)) return true - } - - return false -} \ No newline at end of file +package com.tangem.tap.domain.userWalletList.utils \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletCurrencies/WalletCurrenciesManager.kt b/app/src/main/java/com/tangem/tap/domain/walletCurrencies/WalletCurrenciesManager.kt deleted file mode 100644 index 413550b22e..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletCurrencies/WalletCurrenciesManager.kt +++ /dev/null @@ -1,87 +0,0 @@ -package com.tangem.tap.domain.walletCurrencies - -import com.tangem.common.CompletionResult -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.tap.features.wallet.models.Currency - -interface WalletCurrenciesManager { - /** - * Update [UserWallet] currencies with same blockchain as provided [Currency] blockchain - * [UserWallet] currencies updates can be observed - * with [com.tangem.tap.domain.walletStores.WalletStoresManager.getAll] - * or [com.tangem.tap.domain.walletStores.WalletStoresManager.get] - * - * @param userWallet [UserWallet] which currencies will be updated - * @param currency [Currency] to find other currencies to update - * - * @return [CompletionResult] of operation - * */ - suspend fun update(userWallet: UserWallet, currency: Currency): CompletionResult - - /** - * Add list of [Currency] to [UserWallet]. - * [UserWallet] currencies updates can be observed - * with [com.tangem.tap.domain.walletStores.WalletStoresManager.getAll] - * or [com.tangem.tap.domain.walletStores.WalletStoresManager.get] - * - * @param userWallet [UserWallet] to add currencies - * @param currenciesToAdd list of [Currency] to add [UserWallet] - * - * @return [CompletionResult] of operation - * */ - suspend fun addCurrencies(userWallet: UserWallet, currenciesToAdd: List): CompletionResult - - /** - * Remove [Currency] from [UserWallet] - * [UserWallet] currencies updates can be observed - * with [com.tangem.tap.domain.walletStores.WalletStoresManager.getAll] - * or [com.tangem.tap.domain.walletStores.WalletStoresManager.get] - * - * @param userWallet [UserWallet] which currency will be removed - * @param currencyToRemove [Currency] to remove from [UserWallet] - * - * @return [CompletionResult] of operation - * */ - suspend fun removeCurrency(userWallet: UserWallet, currencyToRemove: Currency): CompletionResult - - /** - * Remove list of [Currency] from [UserWallet] - * [UserWallet] currencies updates can be observed - * with [com.tangem.tap.domain.walletStores.WalletStoresManager.getAll] - * or [com.tangem.tap.domain.walletStores.WalletStoresManager.get] - * - * @param userWallet [UserWallet] which currency will be removed - * @param currenciesToRemove list of [Currency] to remove from [UserWallet] - * - * @return [CompletionResult] of operation - * */ - suspend fun removeCurrencies(userWallet: UserWallet, currenciesToRemove: List): CompletionResult - - /** - * Add a callback [Listener] - * - * @param listener The callback that will add - */ - fun addListener(listener: Listener) - - /** - * Remove a callback [Listener] - * - * @param listener The callback that will removed - */ - fun removeListener(listener: Listener) - - /** - * Interface definition for a callbacks - */ - interface Listener { - fun willUpdate(userWallet: UserWallet, currency: Currency) {} - fun didUpdate(userWallet: UserWallet, currency: Currency) {} - fun willCurrenciesAdd(userWallet: UserWallet, currenciesToAdd: List) {} - fun willCurrenciesRemove(userWallet: UserWallet, currenciesToRemove: List) {} - fun willCurrencyRemove(userWallet: UserWallet, currencyToRemove: Currency) {} - } - - // For provider - companion object -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletCurrencies/di/WalletCurrenciesManagerProvider.kt b/app/src/main/java/com/tangem/tap/domain/walletCurrencies/di/WalletCurrenciesManagerProvider.kt deleted file mode 100644 index bf2d47611e..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletCurrencies/di/WalletCurrenciesManagerProvider.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.tangem.tap.domain.walletCurrencies.di - -import com.tangem.domain.wallets.legacy.WalletManagersRepository -import com.tangem.tap.common.entities.FiatCurrency -import com.tangem.tap.domain.tokens.UserTokensRepository -import com.tangem.tap.domain.walletCurrencies.WalletCurrenciesManager -import com.tangem.tap.domain.walletCurrencies.implementation.DefaultWalletCurrenciesManager -import com.tangem.tap.domain.walletStores.repository.WalletAmountsRepository -import com.tangem.tap.domain.walletStores.repository.WalletStoresRepository - -fun WalletCurrenciesManager.Companion.provideDefaultImplementation( - userTokensRepository: UserTokensRepository, - walletStoresRepository: WalletStoresRepository, - walletAmountsRepository: WalletAmountsRepository, - walletManagersRepository: WalletManagersRepository, - appCurrencyProvider: () -> FiatCurrency, -): WalletCurrenciesManager { - return DefaultWalletCurrenciesManager( - userTokensRepository = userTokensRepository, - walletStoresRepository = walletStoresRepository, - walletAmountsRepository = walletAmountsRepository, - walletManagersRepository = walletManagersRepository, - appCurrencyProvider = appCurrencyProvider, - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletCurrencies/implementation/DefaultWalletCurrenciesManager.kt b/app/src/main/java/com/tangem/tap/domain/walletCurrencies/implementation/DefaultWalletCurrenciesManager.kt deleted file mode 100644 index db1f9347dc..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletCurrencies/implementation/DefaultWalletCurrenciesManager.kt +++ /dev/null @@ -1,261 +0,0 @@ -package com.tangem.tap.domain.walletCurrencies.implementation - -import com.tangem.blockchain.common.derivation.DerivationStyle -import com.tangem.common.* -import com.tangem.domain.common.BlockchainNetwork -import com.tangem.domain.common.DerivationStyleProvider -import com.tangem.domain.common.util.derivationStyleProvider -import com.tangem.domain.models.scan.CardDTO -import com.tangem.domain.wallets.legacy.WalletManagersRepository -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.common.entities.FiatCurrency -import com.tangem.tap.domain.model.builders.WalletStoreBuilder -import com.tangem.tap.domain.tokens.UserTokensRepository -import com.tangem.tap.domain.walletCurrencies.WalletCurrenciesManager -import com.tangem.tap.domain.walletStores.repository.WalletAmountsRepository -import com.tangem.tap.domain.walletStores.repository.WalletStoresRepository -import com.tangem.tap.features.wallet.models.Currency -import com.tangem.tap.features.wallet.models.toBlockchainNetworks -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.withContext - -internal class DefaultWalletCurrenciesManager( - private val userTokensRepository: UserTokensRepository, - private val walletStoresRepository: WalletStoresRepository, - private val walletAmountsRepository: WalletAmountsRepository, - private val walletManagersRepository: WalletManagersRepository, - private val appCurrencyProvider: () -> FiatCurrency, -) : WalletCurrenciesManager { - - private val listeners = mutableListOf() - - override suspend fun update(userWallet: UserWallet, currency: Currency): CompletionResult = - withContext(Dispatchers.Default) { - listeners.forEach { it.willUpdate(userWallet, currency) } - val walletStore = walletStoresRepository.getSync(userWallet.walletId) - .find { - it.blockchain == currency.blockchain && - it.derivationPath?.rawPath == currency.derivationPath - } - - val updateResult = if (walletStore == null) { - CompletionResult.Success(Unit) - } else { - walletAmountsRepository.updateAmountsForWalletStore( - walletStore = walletStore, - userWallet = userWallet, - fiatCurrency = appCurrencyProvider(), - ) - } - listeners.forEach { it.didUpdate(userWallet, currency) } - updateResult - } - - override suspend fun addCurrencies( - userWallet: UserWallet, - currenciesToAdd: List, - ): CompletionResult = withContext(Dispatchers.Default) { - if (currenciesToAdd.isEmpty()) { - return@withContext CompletionResult.Success(Unit) - } - - val card = userWallet.scanResponse.card - val currenciesToAddWithMissingBlockchains = currenciesToAdd.addMissingBlockchainsIfNeeded( - userWallet.scanResponse.derivationStyleProvider, - ) - listeners.forEach { it.willCurrenciesAdd(userWallet, currenciesToAddWithMissingBlockchains) } - - updateWalletStores( - userWallet = userWallet, - blockchainNetworks = currenciesToAddWithMissingBlockchains - .toBlockchainNetworks() - .addSameBlockchainTokens(userWallet.walletId), - ) - .map { - saveUserCurrencies(card, getSavedCurrencies(userWallet.walletId)) - } - .flatMap { - updateWalletStoresAmounts( - userWallet = userWallet, - updatedCurrencies = currenciesToAddWithMissingBlockchains, - ) - } - } - - override suspend fun removeCurrencies( - userWallet: UserWallet, - currenciesToRemove: List, - ): CompletionResult = withContext(Dispatchers.Default) { - if (currenciesToRemove.isEmpty()) { - return@withContext CompletionResult.Success(Unit) - } - - listeners.forEach { it.willCurrenciesRemove(userWallet, currenciesToRemove) } - val card = userWallet.scanResponse.card - val remainingCurrencies = getSavedCurrencies(userWallet.walletId) - .filter { it !in currenciesToRemove } - val remainingBlockchains = remainingCurrencies - .filterIsInstance() - - walletStoresRepository.deleteDifference(userWallet.walletId, remainingBlockchains) - .flatMap { - updateWalletStores(userWallet, remainingCurrencies.toBlockchainNetworks()) - } - .doOnResult { - saveUserCurrencies(card, remainingCurrencies) - } - } - - override suspend fun removeCurrency(userWallet: UserWallet, currencyToRemove: Currency): CompletionResult { - listeners.forEach { it.willCurrencyRemove(userWallet, currencyToRemove) } - return removeCurrencies(userWallet, listOf(currencyToRemove)) - } - - override fun addListener(listener: WalletCurrenciesManager.Listener) { - listeners.add(listener) - } - - override fun removeListener(listener: WalletCurrenciesManager.Listener) { - listeners.remove(listener) - } - - private suspend fun getSavedCurrencies(userWalletId: UserWalletId): List { - return withContext(Dispatchers.Default) { - walletStoresRepository.getSync(userWalletId) - .flatMap { walletStore -> - walletStore.walletsData.map { it.currency } - } - } - } - - private suspend fun saveUserCurrencies(card: CardDTO, currencies: List) { - withContext(Dispatchers.IO) { - userTokensRepository.saveUserTokens( - card = card, - tokens = currencies, - ) - } - } - - // TODO: Need refactoring - private suspend fun List.addSameBlockchainTokens( - userWalletId: UserWalletId, - ): List { - val networks = arrayListOf() - val savedWalletStores = withContext(Dispatchers.Default) { - walletStoresRepository.getSync(userWalletId) - } - - this.forEach { network -> - val walletStore = savedWalletStores.firstOrNull { - it.blockchain == network.blockchain && it.derivationPath?.rawPath == network.derivationPath - } - - if (walletStore != null) { - val tokens = walletStore.walletsData - .asSequence() - .map { it.currency } - .filterIsInstance() - .map { it.token } - .toList() - - networks.add(network.copy(tokens = tokens + network.tokens.toSet())) - } else { - networks.add(network) - } - } - - return networks - } - - private fun List.addMissingBlockchainsIfNeeded( - derivationStyleProvider: DerivationStyleProvider, - ): List { - if (this.isEmpty()) return this - val currencies = this.asSequence() - - return currencies - .groupBy { currency -> - findBlockchainCurrency(currency, currencies, derivationStyleProvider.getDerivationStyle()) - } - .mapValues { (blockchainCurrency, blockchainCurrencies) -> - findBlockchainTokens(blockchainCurrency, blockchainCurrencies) - } - .flatMap { (blockchainCurrency, blockchainTokens) -> - arrayListOf(blockchainCurrency) + blockchainTokens - } - } - - private fun findBlockchainCurrency( - currency: Currency, - currencies: Sequence, - cardDerivationStyle: DerivationStyle?, - ): Currency.Blockchain { - return currencies - .filterIsInstance() - .firstOrNull { - it.blockchain == currency.blockchain && - it.derivationPath == currency.derivationPath - } - ?: Currency.Blockchain( - blockchain = currency.blockchain, - derivationPath = currency.derivationPath - ?: currency.blockchain.derivationPath(cardDerivationStyle)?.rawPath, - ) - } - - private fun findBlockchainTokens( - blockchainCurrency: Currency.Blockchain, - blockchainCurrencies: List, - ): List { - return blockchainCurrencies - .filterIsInstance() - .map { token -> - token.copy( - derivationPath = token.derivationPath ?: blockchainCurrency.derivationPath, - ) - } - } - - private suspend fun updateWalletStores( - userWallet: UserWallet, - blockchainNetworks: List, - ): CompletionResult { - return blockchainNetworks - .map { blockchainNetwork -> - walletManagersRepository.findOrMakeMultiCurrencyWalletManager( - userWallet = userWallet, - blockchainNetwork = blockchainNetwork, - ) - .flatMap { walletManager -> - walletStoresRepository.storeOrUpdate( - userWalletId = userWallet.walletId, - walletStore = WalletStoreBuilder(userWallet, blockchainNetwork) - .walletManager(walletManager) - .build(), - ) - } - } - .fold() - } - - private suspend fun updateWalletStoresAmounts( - userWallet: UserWallet, - updatedCurrencies: List, - ): CompletionResult { - val updatedBlockchains = updatedCurrencies - .filterIsInstance() - val updatedWalletStores = walletStoresRepository.get(userWallet.walletId) - .firstOrNull() - ?.filter { it.blockchainWalletData.currency in updatedBlockchains } - ?: return CompletionResult.Success(Unit) - - return walletAmountsRepository.updateAmountsForWalletStores( - walletStores = updatedWalletStores, - userWallet = userWallet, - fiatCurrency = appCurrencyProvider(), - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/WalletStoresError.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/WalletStoresError.kt deleted file mode 100644 index 32097cfcdf..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/WalletStoresError.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.tangem.tap.domain.walletStores - -import com.tangem.blockchain.common.Blockchain -import com.tangem.common.core.TangemError - -sealed class WalletStoresError(code: Int) : TangemError(code) { - override val silent: Boolean - get() = (cause as? TangemError)?.silent == true - - override val messageResId: Int? = null - override val message: String? - get() = customMessage - - class FetchFiatRatesError( - currencies: List, - override val cause: Throwable?, - ) : WalletStoresError(code = 60011) { - override var customMessage: String = "Failed to fetch fiat rates for currencies $currencies" - } - - class UnknownBlockchain : WalletStoresError(code = 60012) { - override var customMessage: String = "Unknown blockchain" - } - - object NoInternetConnection : WalletStoresError(code = 60013) { - override var customMessage: String = "No internet connection" - } - - class WalletManagerNotCreated(blockchain: Blockchain) : WalletStoresError(code = 60014) { - override var customMessage: String = "Wallet manager can not be created for $blockchain" - } - - class UpdateWalletManagerTokensError( - blockchain: Blockchain, - override val cause: Throwable, - ) : WalletStoresError(code = 600015) { - override var customMessage: String = "Unable to update wallet manager tokens for currency $blockchain: $cause" - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/WalletStoresManager.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/WalletStoresManager.kt deleted file mode 100644 index 10d89c17eb..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/WalletStoresManager.kt +++ /dev/null @@ -1,85 +0,0 @@ -package com.tangem.tap.domain.walletStores - -import com.tangem.blockchain.common.address.AddressType -import com.tangem.common.CompletionResult -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.domain.model.WalletStoreModel -import com.tangem.tap.features.wallet.models.Currency -import kotlinx.coroutines.flow.Flow - -interface WalletStoresManager { - /** - * Get all [WalletStoreModel]s updates - * - * @return [Flow] with map of [WalletStoreModel] list assigned by [UserWalletId] - * */ - fun getAll(): Flow>> - - /** - * Get [WalletStoreModel]s updates which associated with provided [UserWalletId] - * - * @param userWalletId [UserWalletId] of user wallet - * - * @return [Flow] with [WalletStoreModel] list - * */ - fun get(userWalletId: UserWalletId): Flow> - - suspend fun getSync(userWalletId: UserWalletId): List - - /** - * Delete [WalletStoreModel]s associated with provided [UserWalletId]s - * - * @param userWalletsIds [UserWalletId] list - * - * @return [CompletionResult] of operation - * */ - suspend fun delete(userWalletsIds: List): CompletionResult - - /** - * Clear all [WalletStoreModel]s - * - * @return [CompletionResult] of operation - * */ - suspend fun clear(): CompletionResult - - /** - * Fetch wallet stores associated with provided [UserWallet]. Fetched [WalletStoreModel]s updates can be observed - * with [get] and [getAll] methods - * - * @param userWallet [UserWallet] to fetch [WalletStoreModel]s - - * - * @return [CompletionResult] of operation - * */ - suspend fun fetch(userWallet: UserWallet, refresh: Boolean = false): CompletionResult - - /** - * Fetch wallet stores associated with provided [UserWallet]s. Fetched [WalletStoreModel]s updates can be observed - * with [get] and [getAll] methods - * - * @param userWallets [UserWallet]s list to fetch [WalletStoreModel]s - - * - * @return [CompletionResult] of operation - * */ - suspend fun fetch(userWallets: List, refresh: Boolean = false): CompletionResult - - /** - * Update [WalletStoreModel]s amounts associated with provided [UserWallet]s - * - * @param userWallets [UserWallet]s list to update [WalletStoreModel]s amounts - * - * @return [CompletionResult] of operation - * */ - suspend fun updateAmounts(userWallets: List): CompletionResult - - suspend fun updateSelectedAddress( - userWalletId: UserWalletId, - currency: Currency, - addressType: AddressType, - ): CompletionResult - - // For provider - companion object -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/di/WalletsStoresManagerProvider.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/di/WalletsStoresManagerProvider.kt deleted file mode 100644 index d913969cac..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/di/WalletsStoresManagerProvider.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.tangem.tap.domain.walletStores.di - -import com.tangem.domain.wallets.legacy.WalletManagersRepository -import com.tangem.tap.common.entities.FiatCurrency -import com.tangem.tap.domain.tokens.UserTokensRepository -import com.tangem.tap.domain.walletStores.WalletStoresManager -import com.tangem.tap.domain.walletStores.implementation.DefaultWalletStoresManager -import com.tangem.tap.domain.walletStores.implementation.DummyWalletStoresManager -import com.tangem.tap.domain.walletStores.repository.WalletAmountsRepository -import com.tangem.tap.domain.walletStores.repository.WalletStoresRepository - -fun WalletStoresManager.Companion.provideDummyImplementation(): WalletStoresManager { - return DummyWalletStoresManager() -} - -fun WalletStoresManager.Companion.provideDefaultImplementation( - userTokensRepository: UserTokensRepository, - walletStoresRepository: WalletStoresRepository, - walletAmountsRepository: WalletAmountsRepository, - walletManagersRepository: WalletManagersRepository, - appCurrencyProvider: () -> FiatCurrency, -): WalletStoresManager { - return DefaultWalletStoresManager( - userTokensRepository = userTokensRepository, - walletStoresRepository = walletStoresRepository, - walletAmountsRepository = walletAmountsRepository, - walletManagersRepository = walletManagersRepository, - appCurrencyProvider = appCurrencyProvider, - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/implementation/DefaultWalletStoresManager.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/implementation/DefaultWalletStoresManager.kt deleted file mode 100644 index 4e60254106..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/implementation/DefaultWalletStoresManager.kt +++ /dev/null @@ -1,202 +0,0 @@ -package com.tangem.tap.domain.walletStores.implementation - -import com.tangem.blockchain.common.WalletManager -import com.tangem.blockchain.common.address.AddressType -import com.tangem.common.* -import com.tangem.domain.common.util.derivationStyleProvider -import com.tangem.domain.wallets.legacy.WalletManagersRepository -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.common.entities.FiatCurrency -import com.tangem.tap.domain.model.WalletStoreModel -import com.tangem.tap.domain.model.builders.WalletStoreBuilder -import com.tangem.tap.domain.tokens.UserTokensRepository -import com.tangem.tap.domain.walletStores.WalletStoresError -import com.tangem.tap.domain.walletStores.WalletStoresManager -import com.tangem.tap.domain.walletStores.repository.WalletAmountsRepository -import com.tangem.tap.domain.walletStores.repository.WalletStoresRepository -import com.tangem.tap.domain.walletStores.repository.implementation.utils.updateSelectedAddress -import com.tangem.tap.features.wallet.models.Currency -import com.tangem.tap.features.wallet.models.toBlockchainNetworks -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.withContext - -internal class DefaultWalletStoresManager( - private val userTokensRepository: UserTokensRepository, - private val walletStoresRepository: WalletStoresRepository, - private val walletAmountsRepository: WalletAmountsRepository, - private val walletManagersRepository: WalletManagersRepository, - private val appCurrencyProvider: () -> FiatCurrency, -) : WalletStoresManager { - private val state = MutableStateFlow(State()) - - override fun getAll(): Flow>> { - return walletStoresRepository.getAll() - } - - override fun get(userWalletId: UserWalletId): Flow> { - return walletStoresRepository.get(userWalletId) - .distinctUntilChanged() - } - - override suspend fun getSync(userWalletId: UserWalletId): List { - return walletStoresRepository.getSync(userWalletId) - } - - override suspend fun delete(userWalletsIds: List): CompletionResult { - return walletStoresRepository.delete(userWalletsIds) - .flatMap { walletManagersRepository.delete(userWalletsIds) } - } - - override suspend fun clear(): CompletionResult { - return walletStoresRepository.clear() - } - - override suspend fun fetch(userWallets: List, refresh: Boolean): CompletionResult = - withContext(Dispatchers.Default) { - val fiatCurrency = appCurrencyProvider.invoke() - val isFiatCurrencyChanged = state.value.fiatCurrency != fiatCurrency - - state.update { prevState -> - prevState.copy( - fiatCurrency = fiatCurrency, - ) - } - - userWallets - .mapNotNull { userWallet -> - val hasNotWalletStoresForUserWallet = !walletStoresRepository.contains(userWallet.walletId) - if (refresh || hasNotWalletStoresForUserWallet || isFiatCurrencyChanged) { - fetchWalletsIfNeeded(userWallet) - } else { - null - } - } - .fold(arrayListOf()) { acc, data -> - acc.apply { add(data) } - } - .flatMap { - walletAmountsRepository.updateAmountsForUserWallets(it, fiatCurrency) - } - } - - override suspend fun fetch(userWallet: UserWallet, refresh: Boolean): CompletionResult { - return fetch(listOf(userWallet), refresh) - } - - override suspend fun updateAmounts(userWallets: List): CompletionResult { - val fiatCurrency = appCurrencyProvider.invoke() - - return walletAmountsRepository.updateAmountsForUserWallets(userWallets, fiatCurrency) - .doOnSuccess { - state.update { prevState -> - prevState.copy( - fiatCurrency = fiatCurrency, - ) - } - } - } - - override suspend fun updateSelectedAddress( - userWalletId: UserWalletId, - currency: Currency, - addressType: AddressType, - ): CompletionResult { - return walletStoresRepository.update(userWalletId) { walletStores -> - walletStores - .firstOrNull { - it.blockchain == currency.blockchain && - it.derivationPath?.rawPath == currency.derivationPath - } - ?.updateSelectedAddress(currency, addressType) - } - } - - private suspend fun fetchWalletsIfNeeded(userWallet: UserWallet): CompletionResult { - return if (userWallet.isMultiCurrency) { - fetchMultiWallets(userWallet) - } else { - fetchSingleWallet(userWallet) - } - .map { userWallet } - } - - private suspend fun fetchMultiWallets(userWallet: UserWallet): CompletionResult { - val scanResponse = userWallet.scanResponse - val derivationStyle = scanResponse.derivationStyleProvider.getDerivationStyle() - val userTokens = withContext(Dispatchers.IO) { - userTokensRepository.getUserTokens(scanResponse.card, derivationStyle) - } - val userWalletId = userWallet.walletId - - return withContext(Dispatchers.Default) { - userTokens.toBlockchainNetworks() - .also { blockchainNetworks -> - walletStoresRepository.deleteDifference( - userWalletId = userWalletId, - currentBlockchains = blockchainNetworks.map { - Currency.Blockchain(it.blockchain, it.derivationPath) - }, - ) - } - .map { blockchainNetwork -> - val storeWalletStore: suspend (WalletManager?) -> CompletionResult = - { walletManager -> - walletStoresRepository.storeOrUpdate( - userWalletId = userWalletId, - walletStore = WalletStoreBuilder(userWallet, blockchainNetwork) - .walletManager(walletManager) - .build(), - ) - } - - walletManagersRepository.findOrMakeMultiCurrencyWalletManager( - userWallet = userWallet, - blockchainNetwork = blockchainNetwork, - ) - .flatMap { walletManager -> - storeWalletStore(walletManager) - } - .flatMapOnFailure { error -> - when (error) { - is WalletStoresError.WalletManagerNotCreated, - is WalletStoresError.UpdateWalletManagerTokensError, - -> storeWalletStore(null) - else -> CompletionResult.Failure(error) - } - } - } - .fold() - } - } - - private suspend fun fetchSingleWallet(userWallet: UserWallet): CompletionResult { - return walletManagersRepository.findOrMakeSingleCurrencyWalletManager( - userWallet = userWallet, - ) - .flatMap { walletManager -> - val userWalletId = userWallet.walletId - walletStoresRepository.storeOrUpdate( - userWalletId = userWalletId, - walletStore = WalletStoreBuilder(userWallet, walletManager) - .build(), - ) - } - .flatMapOnFailure { error -> - when (error) { - is WalletStoresError.WalletManagerNotCreated, - is WalletStoresError.UpdateWalletManagerTokensError, - -> CompletionResult.Success(Unit) - else -> CompletionResult.Failure(error) - } - } - } - - internal data class State( - val fiatCurrency: FiatCurrency? = null, - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/implementation/DummyWalletStoresManager.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/implementation/DummyWalletStoresManager.kt deleted file mode 100644 index 9b6ab8abbb..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/implementation/DummyWalletStoresManager.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.tangem.tap.domain.walletStores.implementation - -import com.tangem.blockchain.common.address.AddressType -import com.tangem.common.CompletionResult -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.domain.model.WalletStoreModel -import com.tangem.tap.domain.walletStores.WalletStoresManager -import com.tangem.tap.features.wallet.models.Currency -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.emptyFlow - -internal class DummyWalletStoresManager : WalletStoresManager { - override fun getAll(): Flow>> { - return emptyFlow() - } - - override fun get(userWalletId: UserWalletId): Flow> { - return emptyFlow() - } - - override suspend fun getSync(userWalletId: UserWalletId): List { - return emptyList() - } - - override suspend fun delete(userWalletsIds: List): CompletionResult { - return CompletionResult.Success(Unit) - } - - override suspend fun clear(): CompletionResult { - return CompletionResult.Success(Unit) - } - - override suspend fun fetch(userWallet: UserWallet, refresh: Boolean): CompletionResult { - return CompletionResult.Success(Unit) - } - - override suspend fun fetch(userWallets: List, refresh: Boolean): CompletionResult { - return CompletionResult.Success(Unit) - } - - override suspend fun updateAmounts(userWallets: List): CompletionResult { - return CompletionResult.Success(Unit) - } - - override suspend fun updateSelectedAddress( - userWalletId: UserWalletId, - currency: Currency, - addressType: AddressType, - ): CompletionResult { - return CompletionResult.Success(Unit) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/WalletAmountsRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/WalletAmountsRepository.kt deleted file mode 100644 index c25635dd50..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/WalletAmountsRepository.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.tangem.tap.domain.walletStores.repository - -import com.tangem.common.CompletionResult -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.tap.common.entities.FiatCurrency -import com.tangem.tap.domain.model.WalletStoreModel - -interface WalletAmountsRepository { - - /** - * Fetch wallet amounts and fiat rates then update [com.tangem.tap.domain.walletStores.storage.WalletStoresStorage] - * and [com.tangem.tap.domain.walletStores.storage.WalletManagerStorage] with new data - * @param userWallets list of [UserWallet] which will be used to get the list of associated [WalletStoreModel] - * @param fiatCurrency current app [FiatCurrency] - * */ - suspend fun updateAmountsForUserWallets( - userWallets: List, - fiatCurrency: FiatCurrency, - ): CompletionResult - - /** - * Fetch wallet amounts and fiat rates then update [com.tangem.tap.domain.walletStores.storage.WalletStoresStorage] - * and [com.tangem.tap.domain.walletStores.storage.WalletManagerStorage] with new data - * @param userWallet [UserWallet] which will be used to get the list of associated [WalletStoreModel] - * @param fiatCurrency current app [FiatCurrency] - * */ - suspend fun updateAmountsForUserWallet(userWallet: UserWallet, fiatCurrency: FiatCurrency): CompletionResult - - suspend fun updateAmountsForWalletStores( - walletStores: List, - userWallet: UserWallet, - fiatCurrency: FiatCurrency, - ): CompletionResult - - /** - * Fetch wallet amounts and fiat rates then update [com.tangem.tap.domain.walletStores.storage.WalletStoresStorage] - * and [com.tangem.tap.domain.walletStores.storage.WalletManagerStorage] with new data - * @param walletStore [WalletStoreModel] to update - * @param userWallet [UserWallet] associated with provided [walletStore] - * @param fiatCurrency current app [FiatCurrency] - * */ - suspend fun updateAmountsForWalletStore( - walletStore: WalletStoreModel, - userWallet: UserWallet, - fiatCurrency: FiatCurrency, - ): CompletionResult - - companion object -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/WalletStoresRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/WalletStoresRepository.kt deleted file mode 100644 index 6007fcb2c3..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/WalletStoresRepository.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.tangem.tap.domain.walletStores.repository - -import com.tangem.common.CompletionResult -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.domain.model.WalletStoreModel -import com.tangem.tap.features.wallet.models.Currency -import kotlinx.coroutines.flow.Flow - -interface WalletStoresRepository { - fun getAll(): Flow>> - fun get(userWalletId: UserWalletId): Flow> - suspend fun getSync(userWalletId: UserWalletId): List - - suspend fun contains(userWalletId: UserWalletId): Boolean - - suspend fun delete(userWalletsIds: List): CompletionResult - - suspend fun deleteDifference( - userWalletId: UserWalletId, - currentBlockchains: List, - ): CompletionResult - - suspend fun clear(): CompletionResult - - suspend fun storeOrUpdate(userWalletId: UserWalletId, walletStore: WalletStoreModel): CompletionResult - - /** - * Updates [WalletStoreModel] in storage for user wallet with provided [UserWalletId] - * - * @param userWalletId [UserWalletId] of user wallet - * @param operation Lambda which receives list of [WalletStoreModel] assigned to user wallet with [userWalletId] - * and returns updated [WalletStoreModel]. If null returned then do nothing - * - * @return [CompletionResult] of operation - * */ - suspend fun update( - userWalletId: UserWalletId, - operation: (List) -> WalletStoreModel?, - ): CompletionResult - - companion object -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/di/RepositoryProvider.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/di/RepositoryProvider.kt deleted file mode 100644 index 769a1e0219..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/di/RepositoryProvider.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.tangem.tap.domain.walletStores.repository.di - -import com.tangem.blockchain.common.WalletManagerFactory -import com.tangem.datasource.api.tangemTech.TangemTechService -import com.tangem.domain.wallets.legacy.WalletManagersRepository -import com.tangem.tap.domain.walletStores.repository.WalletAmountsRepository -import com.tangem.tap.domain.walletStores.repository.WalletStoresRepository -import com.tangem.tap.domain.walletStores.repository.implementation.DefaultWalletAmountsRepository -import com.tangem.tap.domain.walletStores.repository.implementation.DefaultWalletManagersRepository -import com.tangem.tap.domain.walletStores.repository.implementation.DefaultWalletStoresRepository -import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider - -fun WalletStoresRepository.Companion.provideDefaultImplementation(): WalletStoresRepository { - return DefaultWalletStoresRepository() -} - -fun WalletManagersRepository.Companion.provideDefaultImplementation( - walletManagerFactory: WalletManagerFactory, -): WalletManagersRepository { - return DefaultWalletManagersRepository(walletManagerFactory) -} - -fun WalletAmountsRepository.Companion.provideDefaultImplementation( - tangemTechService: TangemTechService, -): WalletAmountsRepository { - // TODO("After adding DI") get dependencies by DI - return DefaultWalletAmountsRepository( - tangemTechApi = tangemTechService.api, - dispatchers = AppCoroutineDispatcherProvider(), - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletAmountsRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletAmountsRepository.kt deleted file mode 100644 index 0203abef4b..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletAmountsRepository.kt +++ /dev/null @@ -1,447 +0,0 @@ -package com.tangem.tap.domain.walletStores.repository.implementation - -import com.tangem.blockchain.blockchains.solana.RentProvider -import com.tangem.blockchain.common.AmountType -import com.tangem.blockchain.common.BlockchainSdkError -import com.tangem.blockchain.common.Wallet -import com.tangem.blockchain.common.WalletManager -import com.tangem.blockchain.extensions.Result.Failure -import com.tangem.blockchain.extensions.Result.Success -import com.tangem.common.* -import com.tangem.common.core.TangemError -import com.tangem.datasource.api.tangemTech.TangemTechApi -import com.tangem.domain.common.util.hasDerivation -import com.tangem.domain.models.scan.ScanResponse -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.common.TestActions -import com.tangem.tap.common.entities.FiatCurrency -import com.tangem.tap.domain.model.WalletStoreModel -import com.tangem.tap.domain.walletStores.WalletStoresError -import com.tangem.tap.domain.walletStores.repository.WalletAmountsRepository -import com.tangem.tap.domain.walletStores.repository.implementation.utils.* -import com.tangem.tap.domain.walletStores.storage.WalletManagerStorage -import com.tangem.tap.domain.walletStores.storage.WalletStoresStorage -import com.tangem.tap.features.demo.DemoHelper -import com.tangem.tap.features.demo.isDemoCard -import com.tangem.tap.features.wallet.models.Currency -import com.tangem.tap.features.wallet.models.PendingTransactionType -import com.tangem.tap.features.wallet.models.filterByCoin -import com.tangem.tap.features.wallet.models.getPendingTransactions -import com.tangem.tap.proxy.redux.DaggerGraphState -import com.tangem.tap.store -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import com.tangem.utils.extensions.addOrReplace -import kotlinx.coroutines.* -import kotlinx.coroutines.flow.firstOrNull -import timber.log.Timber -import java.math.BigDecimal -import kotlin.time.Duration - -@Suppress("LargeClass") -internal class DefaultWalletAmountsRepository( - private val tangemTechApi: TangemTechApi, - private val dispatchers: CoroutineDispatcherProvider, -) : WalletAmountsRepository { - - override suspend fun updateAmountsForUserWallets( - userWallets: List, - fiatCurrency: FiatCurrency, - ): CompletionResult { - return if (userWallets.isEmpty()) { - CompletionResult.Success(Unit) - } else { - withContext(Dispatchers.Default) { - awaitAll( - async { fetchAmountsForUserWallets(userWallets) }, - async { fetchFiatRates(userWallets, walletStores = null, fiatCurrency) }, - ).fold() - } - } - } - - override suspend fun updateAmountsForUserWallet( - userWallet: UserWallet, - fiatCurrency: FiatCurrency, - ): CompletionResult { - return updateAmountsForUserWallets(listOf(userWallet), fiatCurrency) - } - - override suspend fun updateAmountsForWalletStores( - walletStores: List, - userWallet: UserWallet, - fiatCurrency: FiatCurrency, - ): CompletionResult { - return if (walletStores.isEmpty()) { - CompletionResult.Success(Unit) - } else { - withContext(Dispatchers.Default) { - val userWalletId = userWallet.walletId - val scanResponse = userWallet.scanResponse - - awaitAll( - async { fetchAmountForWalletStores(userWalletId, scanResponse, walletStores) }, - async { fetchFiatRates(listOf(userWallet), walletStores, fiatCurrency) }, - ).fold() - } - } - } - - override suspend fun updateAmountsForWalletStore( - walletStore: WalletStoreModel, - userWallet: UserWallet, - fiatCurrency: FiatCurrency, - ): CompletionResult { - return updateAmountsForWalletStores(listOf(walletStore), userWallet, fiatCurrency) - } - - private suspend fun fetchFiatRates( - userWallets: List, - walletStores: List?, - fiatCurrency: FiatCurrency, - ): CompletionResult { - val networkConnectionManager = store.state.daggerGraphState.get(DaggerGraphState::networkConnectionManager) - if (!networkConnectionManager.isOnline) { - return CompletionResult.Failure(WalletStoresError.NoInternetConnection) - } - - val walletStoresInternal = walletStores ?: getWalletStores(userWallets) - val currencies = walletStoresInternal.asSequence().flatMap { it.walletsData }.map { it.currency } - - val coinsIds = currencies.mapNotNull { it.coinId }.distinct().toList() - - return withContext(dispatchers.io) { - runCatching { - tangemTechApi.getRates( - fiatCurrency.code.lowercase(), - coinsIds.joinToString(","), - ) - }.onSuccess { - updateWalletStoresWithFiatRates(walletStores = walletStoresInternal, fiatRates = it.rates) - return@withContext CompletionResult.Success(Unit) - }.onFailure { - val error = WalletStoresError.FetchFiatRatesError( - currencies = currencies.map(Currency::currencySymbol).toList(), - cause = it, - ) - - Timber.e( - error, - """ - Unable to fetch fiat rates - |- Coins ids: $coinsIds - """.trimIndent(), - ) - - return@withContext CompletionResult.Failure(error) - } - - error("Unreachable code because runCatching must return result") - } - } - - private suspend fun fetchAmountsForUserWallets(userWallets: List): CompletionResult = - withContext(Dispatchers.Default) { - userWallets.map { async { fetchAmountsForUserWallet(it) } }.awaitAll().fold() - } - - private suspend fun fetchAmountsForUserWallet(userWallet: UserWallet): CompletionResult = - withContext(Dispatchers.Default) { - val userWalletId = userWallet.walletId - val scanResponse = userWallet.scanResponse - val walletStores = getWalletStores(listOf(userWallet)) - - fetchAmountForWalletStores(userWalletId, scanResponse, walletStores) - } - - private suspend fun fetchAmountForWalletStores( - userWalletId: UserWalletId, - scanResponse: ScanResponse, - walletStores: List, - ): CompletionResult = coroutineScope { - val networkConnectionManager = store.state.daggerGraphState.get(DaggerGraphState::networkConnectionManager) - if (!networkConnectionManager.isOnline) { - walletStores.forEach { - updateWalletStoreWithUnreachable(it) - } - return@coroutineScope CompletionResult.Failure(WalletStoresError.NoInternetConnection) - } - - walletStores.map { walletStore -> - async { - // TODO: Find wallet manager via [com.tangem.domain.wallets.legacy.WalletManagersRepository] - val walletManager = walletStore.walletManager - fetchAmountsForWalletStore(userWalletId, scanResponse, walletStore, walletManager) - } - }.awaitAll().fold() - } - - private suspend fun fetchAmountsForWalletStore( - userWalletId: UserWalletId, - scanResponse: ScanResponse, - walletStore: WalletStoreModel, - walletManager: WalletManager?, - ): CompletionResult { - val isDerivationMissed = with(walletStore) { - derivationPath != null && !scanResponse.hasDerivation(blockchain, derivationPath.rawPath) - } - - return when { - isDerivationMissed -> { - updateWalletStoreWithMissedDerivation(walletStore) - } - walletManager == null -> { - updateWalletStoreWithUnreachable(walletStore) - } - else -> { - updateWalletManager(scanResponse, walletManager) - .map { - updateWalletManagerInStorage(userWalletId, walletManager) - } - .flatMap { - updateWalletStoreWithAmounts( - walletStore = walletStore, - updatedWallet = walletManager.wallet, - // FIXME: move DemoHelper to Demo core module maybe - isDemo = DemoHelper.isDemoCardId(scanResponse.card.cardId), - ) - } - .flatMap { - fetchWalletStoreRentIfNeeded(walletStore, walletManager) - } - .flatMapOnFailure { error -> - updateWalletStoreWithError( - walletStore = walletStore, - wallet = walletManager.wallet, - error = error, - ) - } - } - } - } - - private suspend fun updateWalletManager( - scanResponse: ScanResponse, - walletManager: WalletManager, - demoCardsDelay: Duration = with(Duration) { 500.milliseconds }, - ): CompletionResult = catching { - if (scanResponse.isDemoCard() || TestActions.testAmountInjectionForWalletManagerEnabled) { - delay(demoCardsDelay) - TestActions.testAmountInjectionForWalletManagerEnabled = false - } else { - walletManager.update() - } - } - - private suspend fun fetchWalletStoreRentIfNeeded( - walletStore: WalletStoreModel, - walletManager: WalletManager, - ): CompletionResult { - val rentProvider = walletManager as? RentProvider ?: return CompletionResult.Success(Unit) - - when (val result = rentProvider.minimalBalanceForRentExemption()) { - is Success -> { - val balance = walletManager.wallet.fundsAvailable(AmountType.Coin) - val outgoingTxs = walletManager.wallet.getPendingTransactions( - PendingTransactionType.Outgoing, - ).filterByCoin() - - val rentExempt = result.data - val setRent = if (outgoingTxs.isEmpty()) { - balance < rentExempt - } else { - val outgoingAmount = outgoingTxs.sumOf { it.amountValue ?: BigDecimal.ZERO } - val rest = balance.minus(outgoingAmount) - balance < rest - } - - updateWalletStoreWithRent( - walletStore = walletStore, - rent = if (setRent) { - WalletStoreModel.WalletRent( - rent = rentProvider.rentAmount(), - exemptionAmount = rentExempt, - ) - } else { - null - }, - ) - } - is Failure -> Unit - } - - return CompletionResult.Success(Unit) - } - - private suspend fun updateWalletStoreWithError(walletStore: WalletStoreModel, wallet: Wallet, error: TangemError) = - withContext(Dispatchers.Default) { - Timber.e( - error, - """ - Unable to fetch amounts - |- User wallet id: ${walletStore.userWalletId} - |- Blockchain: ${walletStore.blockchain} - |- Derivation path: ${walletStore.derivationPath?.rawPath} - """.trimIndent(), - ) - - if (error is BlockchainSdkError) { - WalletStoresStorage.update { prevState -> - prevState.replaceWalletStore( - walletStoreToUpdate = walletStore, - update = { - it.updateWithError( - wallet = wallet, - error = error, - ) - }, - ) - } - - CompletionResult.Success(Unit) - } else { - CompletionResult.Failure(error) - } - } - - private suspend fun updateWalletStoreWithAmounts( - walletStore: WalletStoreModel, - updatedWallet: Wallet, - isDemo: Boolean, - ) = withContext(Dispatchers.Default) { - Timber.d( - """ - Fetched amounts - |- User wallet id: ${walletStore.userWalletId} - |- Blockchain: ${walletStore.blockchain} - |- Derivation path: ${walletStore.derivationPath?.rawPath} - """.trimIndent(), - ) - - WalletStoresStorage.update { prevState -> - prevState.replaceWalletStore( - walletStoreToUpdate = walletStore, - update = { - if (isDemo) { - it.updateWithDemoAmounts(wallet = updatedWallet) - } else { - it.updateWithAmounts(wallet = updatedWallet) - } - }, - ) - } - - CompletionResult.Success(Unit) - } - - private suspend fun updateWalletStoreWithMissedDerivation(walletStore: WalletStoreModel) = - withContext(Dispatchers.Default) { - Timber.e( - """ - Missed derivation - |- User wallet id: ${walletStore.userWalletId} - |- Blockchain: ${walletStore.blockchain} - |- Derivation path: ${walletStore.derivationPath?.rawPath} - """.trimIndent(), - ) - - WalletStoresStorage.update { prevState -> - prevState.replaceWalletStore( - walletStoreToUpdate = walletStore, - update = { - it.updateWithMissedDerivation() - }, - ) - } - - CompletionResult.Success(Unit) - } - - private suspend fun updateWalletStoreWithUnreachable(walletStore: WalletStoreModel) = - withContext(Dispatchers.Default) { - Timber.e( - """ - Wallet manager is null - |- User wallet id: ${walletStore.userWalletId} - |- Blockchain: ${walletStore.blockchain} - |- Derivation path: ${walletStore.derivationPath?.rawPath} - """.trimIndent(), - ) - - WalletStoresStorage.update { prevState -> - prevState.replaceWalletStore( - walletStoreToUpdate = walletStore, - update = { - it.updateWithUnreachable() - }, - ) - } - - CompletionResult.Success(Unit) - } - - private suspend fun updateWalletStoresWithFiatRates( - walletStores: List, - fiatRates: Map, - ) = withContext(Dispatchers.Default) { - Timber.d( - """ - Fetched fiat rates - |- User wallets ids: ${walletStores.map { it.userWalletId }.distinct()} - """.trimIndent(), - ) - - WalletStoresStorage.update { prevState -> - prevState.replaceWalletStores( - walletStoresToUpdate = walletStores, - update = { - it.updateWithFiatRates(rates = fiatRates) - }, - ) - } - } - - private suspend fun updateWalletStoreWithRent(walletStore: WalletStoreModel, rent: WalletStoreModel.WalletRent?) = - withContext(Dispatchers.Default) { - Timber.d( - """ - Fetched wallet rent - |- User wallet id: ${walletStore.userWalletId} - |- Blockchain: ${walletStore.blockchain} - |- Derivation path: ${walletStore.derivationPath?.rawPath} - |- Rent: $rent - """.trimIndent(), - ) - - if (rent != walletStore.walletRent) { - WalletStoresStorage.update { prevState -> - prevState.replaceWalletStore( - walletStoreToUpdate = walletStore, - update = { - it.updateWithRent(rent) - }, - ) - } - } - } - - private suspend fun updateWalletManagerInStorage(userWalletId: UserWalletId, walletManager: WalletManager) = - withContext(Dispatchers.Default) { - WalletManagerStorage.update { prevManagers -> - val newManagersForUserWallet = prevManagers[userWalletId].orEmpty() - .addOrReplace(walletManager) { - it.wallet.blockchain == walletManager.wallet.blockchain - } - - prevManagers.apply { - set(userWalletId, newManagersForUserWallet) - } - } - } - - private suspend fun getWalletStores(userWallets: List): List { - return userWallets.map { it.walletId }.flatMap { userWalletId -> - WalletStoresStorage.getAll().firstOrNull()?.get(userWalletId).orEmpty() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletManagersRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletManagersRepository.kt deleted file mode 100644 index 883b3a821d..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletManagersRepository.kt +++ /dev/null @@ -1,223 +0,0 @@ -package com.tangem.tap.domain.walletStores.repository.implementation - -import com.tangem.blockchain.common.* -import com.tangem.common.CompletionResult -import com.tangem.common.catching -import com.tangem.common.doOnSuccess -import com.tangem.common.mapFailure -import com.tangem.crypto.hdWallet.DerivationPath -import com.tangem.domain.common.BlockchainNetwork -import com.tangem.domain.common.DerivationStyleProvider -import com.tangem.domain.common.TapWorkarounds.isTestCard -import com.tangem.domain.common.extensions.makeWalletManagerForApp -import com.tangem.domain.common.util.cardTypesResolver -import com.tangem.domain.common.util.derivationStyleProvider -import com.tangem.domain.models.scan.ScanResponse -import com.tangem.domain.wallets.legacy.WalletManagersRepository -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.domain.walletStores.WalletStoresError -import com.tangem.tap.domain.walletStores.storage.WalletManagerStorage -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.withContext -import timber.log.Timber - -internal class DefaultWalletManagersRepository( - private val walletManagerFactory: WalletManagerFactory, -) : WalletManagersRepository { - private val walletManagersStorage = WalletManagerStorage - - override suspend fun findOrMakeMultiCurrencyWalletManager( - userWallet: UserWallet, - blockchainNetwork: BlockchainNetwork, - ): CompletionResult { - return findOrMakeInternal(userWallet, blockchainNetwork) - } - - override suspend fun findOrMakeSingleCurrencyWalletManager( - userWallet: UserWallet, - ): CompletionResult { - return findOrMakeInternal(userWallet, blockchainNetwork = null) - } - - private suspend fun findOrMakeInternal( - userWallet: UserWallet, - blockchainNetwork: BlockchainNetwork?, - ): CompletionResult = withContext(Dispatchers.Default) { - val foundWalletManager = findWalletManager( - userWalletId = userWallet.walletId, - blockchain = blockchainNetwork?.blockchain, - derivationPath = blockchainNetwork?.derivationPath, - ) - - foundWalletManager?.updateTokens( - scanResponse = userWallet.scanResponse, - blockchainNetwork = blockchainNetwork, - ) - ?: makeAndStore(userWallet, blockchainNetwork) - } - - private suspend fun makeAndStore( - userWallet: UserWallet, - blockchainNetwork: BlockchainNetwork?, - ): CompletionResult { - val scanResponse = userWallet.scanResponse - val blockchain = blockchainNetwork?.blockchain - ?: scanResponse.cardTypesResolver.getBlockchain().let { blockchain -> - if (scanResponse.card.isTestCard) blockchain.getTestnetVersion() else blockchain - } - val derivationParams = getDerivationParams( - derivationPath = blockchainNetwork?.derivationPath, - derivationStyleProvider = scanResponse.derivationStyleProvider, - ) - - val walletManager = blockchain?.let { - walletManagerFactory.makeWalletManagerForApp( - scanResponse = userWallet.scanResponse, - blockchain = blockchain, - derivationParams = derivationParams, - ) - } - - return when { - blockchain == Blockchain.Unknown || blockchain == null -> { - val error = WalletStoresError.UnknownBlockchain() - Timber.e( - error, - """ - Unknown blockchain while creating wallet manager - |- User wallet ID: ${userWallet.walletId} - """.trimIndent(), - ) - CompletionResult.Failure(error) - } - walletManager != null -> { - walletManager.updateTokens( - scanResponse = scanResponse, - blockchainNetwork = blockchainNetwork, - ) - .doOnSuccess { store(userWallet.walletId, it) } - } - else -> { - val error = WalletStoresError.WalletManagerNotCreated(blockchain) - Timber.e( - error, - """ - Unable to create wallet manager - |- User wallet ID: ${userWallet.walletId} - |- Blockchain: $blockchain - |- Derivation path: ${blockchainNetwork?.derivationPath} - """.trimIndent(), - ) - CompletionResult.Failure(error) - } - } - } - - override suspend fun delete(userWalletIds: List): CompletionResult = catching { - walletManagersStorage.update { prevManagers -> - prevManagers.filterKeys { it !in userWalletIds } as HashMap> - } - } - - override suspend fun delete(userWalletId: UserWalletId, blockchain: Blockchain): CompletionResult = catching { - deleteInternal(userWalletId, blockchain) - } - - private suspend fun store(userWalletId: UserWalletId, walletManager: WalletManager) { - walletManagersStorage.update { prevManagers -> - prevManagers.apply { - set( - key = userWalletId, - value = this[userWalletId].orEmpty() + walletManager, - ) - } - } - } - - private suspend fun deleteInternal(userWalletId: UserWalletId, blockchain: Blockchain?) { - walletManagersStorage.update { prevManagers -> - prevManagers.apply { - if (blockchain == null) { - set( - key = userWalletId, - value = emptyList(), - ) - } else { - set( - key = userWalletId, - value = this[userWalletId] - ?.filter { it.wallet.blockchain == blockchain } - .orEmpty(), - ) - } - } - } - } - - private fun WalletManager.updateTokens( - scanResponse: ScanResponse, - blockchainNetwork: BlockchainNetwork?, - ): CompletionResult { - val walletManager = this - return catching { - val tokens = blockchainNetwork?.tokens ?: listOfNotNull(scanResponse.cardTypesResolver.getPrimaryToken()) - - if (tokens != walletManager.cardTokens) { - // TODO: remove ability to manipulate with walletManager.cardTokens - walletManager.cardTokens.clear() - walletManager.wallet.removeAllTokens() - if (tokens.isNotEmpty()) { - walletManager.cardTokens.addAll(tokens) - // add empty amounts to prepare templates of tokens WalletDataModel - // see: WalletMangerWalletStoreBuilderImpl.build() - tokens.forEach { walletManager.wallet.setAmount(Amount(it)) } - } - } - - walletManager - } - .mapFailure { - val error = WalletStoresError.UpdateWalletManagerTokensError( - blockchain = walletManager.wallet.blockchain, - cause = it, - ) - Timber.e(error) - error - } - } - - private suspend fun findWalletManager( - userWalletId: UserWalletId, - blockchain: Blockchain?, - derivationPath: String?, - ): WalletManager? { - return walletManagersStorage.getAll() - .firstOrNull() - ?.get(userWalletId) - ?.let { userWalletManagers -> - if (blockchain == null) { - userWalletManagers.firstOrNull() - } else { - userWalletManagers.firstOrNull { - it.wallet.blockchain == blockchain && - it.wallet.publicKey.derivationPath?.rawPath == derivationPath - } - } - } - } - - private fun getDerivationParams( - derivationPath: String?, - derivationStyleProvider: DerivationStyleProvider, - ): DerivationParams? { - val derivationStyle = derivationStyleProvider.getDerivationStyle() ?: return null - - return if (derivationPath == null) { - DerivationParams.Default(derivationStyle) - } else { - DerivationParams.Custom(DerivationPath(derivationPath)) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletStoresRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletStoresRepository.kt deleted file mode 100644 index fb78960bb1..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletStoresRepository.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.tangem.tap.domain.walletStores.repository.implementation - -import com.tangem.common.CompletionResult -import com.tangem.common.catching -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.domain.model.WalletStoreModel -import com.tangem.tap.domain.walletStores.repository.WalletStoresRepository -import com.tangem.tap.domain.walletStores.repository.implementation.utils.isSameWalletStore -import com.tangem.tap.domain.walletStores.repository.implementation.utils.replaceWalletStore -import com.tangem.tap.domain.walletStores.repository.implementation.utils.updateWithSelf -import com.tangem.tap.domain.walletStores.storage.WalletStoresStorage -import com.tangem.tap.features.wallet.models.Currency -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.withContext - -internal class DefaultWalletStoresRepository : WalletStoresRepository { - private val walletStoresStorage = WalletStoresStorage - - override fun getAll(): Flow>> { - return walletStoresStorage.getAll() - } - - override fun get(userWalletId: UserWalletId): Flow> { - return getAll().map { it[userWalletId].orEmpty() } - } - - override suspend fun getSync(userWalletId: UserWalletId): List { - return get(userWalletId).firstOrNull() ?: emptyList() - } - - override suspend fun contains(userWalletId: UserWalletId): Boolean { - return getSync(userWalletId).isNotEmpty() - } - - override suspend fun delete(userWalletsIds: List): CompletionResult = catching { - walletStoresStorage.update { prevStores -> - prevStores.filterKeys { it !in userWalletsIds } as HashMap> - } - } - - override suspend fun deleteDifference( - userWalletId: UserWalletId, - currentBlockchains: List, - ): CompletionResult = catching { - if (currentBlockchains != getSync(userWalletId)) { - walletStoresStorage.update { prevStores -> - prevStores.apply { - this[userWalletId] = this[userWalletId] - ?.filter { it.blockchainWalletData.currency in currentBlockchains } - .orEmpty() - } - } - } - } - - override suspend fun clear(): CompletionResult = catching { - walletStoresStorage.update { hashMapOf() } - } - - override suspend fun storeOrUpdate( - userWalletId: UserWalletId, - walletStore: WalletStoreModel, - ): CompletionResult = catching { - walletStoresStorage.update { prevStores -> - prevStores.addOrUpdate(userWalletId, walletStore) - } - } - - override suspend fun update( - userWalletId: UserWalletId, - operation: (List) -> WalletStoreModel?, - ): CompletionResult = catching { - val walletStores = getSync(userWalletId).toMutableList() - val updatedWalletStore = operation(walletStores) ?: return CompletionResult.Success(Unit) - - val index = walletStores.indexOfFirst { it.isSameWalletStore(updatedWalletStore) } - if (index == -1 || updatedWalletStore == walletStores[index]) return CompletionResult.Success(Unit) - - walletStores[index] = updatedWalletStore - - walletStoresStorage.update { prevStores -> - prevStores.apply { - this[userWalletId] = walletStores - } - } - } - - private suspend fun HashMap>.addOrUpdate( - userWalletId: UserWalletId, - walletStore: WalletStoreModel, - ): HashMap> = withContext(Dispatchers.Default) { - val currentWalletStores = this@addOrUpdate - val userWalletStores = currentWalletStores[userWalletId] - - if (userWalletStores.isNullOrEmpty()) { - currentWalletStores.apply { - set(userWalletId, listOf(walletStore)) - } - } else { - val currentWalletStore = userWalletStores.find(walletStore::isSameWalletStore) - if (currentWalletStore == null) { - currentWalletStores.apply { - set(userWalletId, userWalletStores + walletStore) - } - } else { - currentWalletStores.replaceWalletStore( - walletStoreToUpdate = currentWalletStore, - update = { it.updateWithSelf(walletStore) }, - ) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletDataOperations.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletDataOperations.kt deleted file mode 100644 index c7eef5a974..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletDataOperations.kt +++ /dev/null @@ -1,211 +0,0 @@ -package com.tangem.tap.domain.walletStores.repository.implementation.utils - -import com.tangem.blockchain.common.AmountType -import com.tangem.blockchain.common.BlockchainSdkError -import com.tangem.blockchain.common.Wallet -import com.tangem.blockchain.common.address.AddressType -import com.tangem.common.core.TangemError -import com.tangem.domain.common.extensions.amountToCreateAccount -import com.tangem.tap.domain.getFirstToken -import com.tangem.tap.domain.model.WalletDataModel -import com.tangem.tap.features.demo.DemoHelper -import com.tangem.tap.features.wallet.models.Currency -import com.tangem.tap.features.wallet.models.getPendingTransactions -import java.math.BigDecimal - -internal fun WalletDataModel.updateWithFiatRate(fiatRate: BigDecimal?): WalletDataModel { - return this.copy( - fiatRate = fiatRate, - ) -} - -internal fun List.updateWithFiatRates(fiatRates: Map): List { - return this.map { walletData -> - val rate = fiatRates[walletData.currency.coinId]?.toBigDecimal() - walletData.updateWithFiatRate(rate) - } -} - -internal fun List.updateWithAmounts(wallet: Wallet): List { - return this.map { walletData -> - walletData.updateWithAmount(wallet) - } -} - -internal fun WalletDataModel.updateWithAmount(wallet: Wallet): WalletDataModel { - val pendingTransactions = wallet.getPendingTransactions() - return this.copy( - status = when (currency) { - is Currency.Blockchain -> { - val amount = wallet.fundsAvailable(AmountType.Coin) - if (pendingTransactions.isEmpty()) { - WalletDataModel.VerifiedOnline(amount) - } else { - WalletDataModel.TransactionInProgress(amount, pendingTransactions) - } - } - - is Currency.Token -> { - val token = currency.token - val amount = wallet.fundsAvailable(AmountType.Token(token)) - val hasTokenPendingTransactions = pendingTransactions.any { - it.transactionData.amount.currencySymbol == token.symbol - } - - when { - hasTokenPendingTransactions -> { - WalletDataModel.TransactionInProgress(amount, pendingTransactions) - } - - pendingTransactions.isNotEmpty() -> { - // FIXME: Necessary to avoid passing pending transactions - // because SameCurrencyTransactionInProgress didn't use it. It used only to define main button - // availability. UI layer turned off pending transaction visibility for this state. - WalletDataModel.SameCurrencyTransactionInProgress(amount, pendingTransactions) - } - else -> { - WalletDataModel.VerifiedOnline(amount) - } - } - } - }, - ) -} - -internal fun WalletDataModel.updateWithDemoAmount(wallet: Wallet): WalletDataModel { - val amount = DemoHelper.config.getBalance(wallet.blockchain) - wallet.setAmount(amount) - return this.copy( - status = WalletDataModel.VerifiedOnline(amount = amount.value ?: BigDecimal.ZERO), - ) -} - -internal fun List.updateWithDemoAmounts(wallet: Wallet): List { - return this.map { walletData -> - walletData.updateWithDemoAmount(wallet) - } -} - -internal fun WalletDataModel.updateWithError(wallet: Wallet, error: TangemError): WalletDataModel { - return this.copy( - status = when (error) { - is BlockchainSdkError.AccountNotFound -> { - val amountToCreateAccount = wallet.blockchain - .amountToCreateAccount(wallet.getFirstToken()) - - if (amountToCreateAccount != null) { - WalletDataModel.NoAccount( - amountToCreateAccount = amountToCreateAccount, - ) - } else { - WalletDataModel.Unreachable( - errorMessage = error.customMessage, - ) - } - } - else -> WalletDataModel.Unreachable( - errorMessage = error.customMessage, - ) - }, - ) -} - -internal fun List.updateWithError(wallet: Wallet, error: TangemError): List { - return this.map { walletData -> - walletData.updateWithError(wallet, error) - } -} - -internal fun WalletDataModel.updateWithSelf(newWalletData: WalletDataModel): WalletDataModel { - val oldWalletData = this - val oldStatus = oldWalletData.status - return oldWalletData.copy( - status = when (val newStatus = newWalletData.status) { - is WalletDataModel.Loading -> when (oldStatus) { - is WalletDataModel.MissedDerivation -> WalletDataModel.Loading - else -> oldStatus - } - is WalletDataModel.MissedDerivation, - is WalletDataModel.NoAccount, - is WalletDataModel.Unreachable, - is WalletDataModel.SameCurrencyTransactionInProgress, - is WalletDataModel.TransactionInProgress, - is WalletDataModel.VerifiedOnline, - -> newStatus - }, - existentialDeposit = newWalletData.existentialDeposit, - walletAddresses = newWalletData.walletAddresses?.copy( - selectedAddress = oldWalletData.walletAddresses?.selectedAddress - ?: newWalletData.walletAddresses.selectedAddress, - ), - fiatRate = newWalletData.fiatRate ?: oldWalletData.fiatRate, - ) -} - -internal fun List.updateWithMissedDerivation(): List { - return this.map { walletData -> - walletData.copy( - status = WalletDataModel.MissedDerivation, - ) - } -} - -internal fun List.updateWithUnreachable(): List { - return this.map { walletData -> - walletData.copy( - status = WalletDataModel.Unreachable( - errorMessage = null, - amount = walletData.status.amount, - ), - ) - } -} - -internal fun List.updateWithSelf(newWalletsData: List): List { - val oldWalletsData = this - val updatedWalletsData = arrayListOf() - - newWalletsData.forEach { newWalletData -> - val walletDataToUpdate = oldWalletsData.firstOrNull(newWalletData::isSameWalletData) - if (walletDataToUpdate != null) { - updatedWalletsData.add(walletDataToUpdate.updateWithSelf(newWalletData)) - } else { - updatedWalletsData.add(newWalletData) - } - } - - return updatedWalletsData -} - -internal fun List.updateSelectedAddress( - currency: Currency, - addressType: AddressType, -): List { - val index = this.indexOfFirst { it.currency == currency } - if (index == -1) return this - - val oldWalletData = this[index] - val updatedWalletData = oldWalletData.updateSelectedAddress(addressType) - if (oldWalletData == updatedWalletData) return this - - return this.toMutableList().apply { - this[index] = updatedWalletData - } -} - -internal fun WalletDataModel.updateSelectedAddress(addressType: AddressType): WalletDataModel { - val addresses = walletAddresses ?: return this - val selectedAddress = addresses.list - .firstOrNull { it.type == addressType } - ?: addresses.selectedAddress - - return this.copy( - walletAddresses = addresses.copy( - selectedAddress = selectedAddress, - ), - ) -} - -internal fun WalletDataModel.isSameWalletData(other: WalletDataModel): Boolean { - return this.currency == other.currency -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletStoreOperations.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletStoreOperations.kt deleted file mode 100644 index bc6c07563d..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletStoreOperations.kt +++ /dev/null @@ -1,131 +0,0 @@ -package com.tangem.tap.domain.walletStores.repository.implementation.utils - -import com.tangem.blockchain.common.Wallet -import com.tangem.blockchain.common.address.AddressType -import com.tangem.common.core.TangemError -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.domain.model.WalletStoreModel -import com.tangem.tap.features.wallet.models.Currency -import timber.log.Timber - -internal inline fun HashMap>.replaceWalletStore( - walletStoreToUpdate: WalletStoreModel, - update: (walletStore: WalletStoreModel) -> WalletStoreModel, -): HashMap> { - return replaceWalletStores(listOf(walletStoreToUpdate), update) -} - -internal inline fun HashMap>.replaceWalletStores( - walletStoresToUpdate: List, - update: (walletStore: WalletStoreModel) -> WalletStoreModel, -): HashMap> { - return this.apply { - val currentWalletStores = this - walletStoresToUpdate - .groupBy { it.userWalletId } - .forEach { (userWalletId, walletStoresToUpdate) -> - currentWalletStores[userWalletId] = currentWalletStores[userWalletId] - ?.replaceWalletStores(walletStoresToUpdate, update) - .orEmpty() - } - } -} - -internal fun WalletStoreModel.updateWithError(wallet: Wallet, error: TangemError): WalletStoreModel { - return this.copy( - walletsData = walletsData.updateWithError( - wallet = wallet, - error = error, - ), - ) -} - -internal fun WalletStoreModel.updateWithAmounts(wallet: Wallet): WalletStoreModel { - return this.copy( - walletsData = walletsData.updateWithAmounts(wallet = wallet), - ) -} - -internal fun WalletStoreModel.updateWithDemoAmounts(wallet: Wallet): WalletStoreModel { - return this.copy( - walletsData = walletsData.updateWithDemoAmounts(wallet = wallet), - ) -} - -internal fun WalletStoreModel.updateWithFiatRates(rates: Map): WalletStoreModel { - return this.copy( - walletsData = walletsData.updateWithFiatRates(rates), - ) -} - -internal fun WalletStoreModel.updateWithSelf(newWalletStore: WalletStoreModel): WalletStoreModel { - val oldStore = this - return oldStore.copy( - derivationPath = newWalletStore.derivationPath, - walletsData = oldStore.walletsData.updateWithSelf(newWalletStore.walletsData), - walletRent = newWalletStore.walletRent, - blockchainNetwork = newWalletStore.blockchainNetwork, - walletManager = newWalletStore.walletManager, - ) -} - -internal fun WalletStoreModel.updateWithMissedDerivation(): WalletStoreModel { - return this.copy( - walletsData = walletsData.updateWithMissedDerivation(), - ) -} - -internal fun WalletStoreModel.updateWithUnreachable(): WalletStoreModel { - return this.copy( - walletsData = walletsData.updateWithUnreachable(), - ) -} - -internal fun WalletStoreModel.updateWithRent(rent: WalletStoreModel.WalletRent?): WalletStoreModel { - return this.copy( - walletRent = rent, - ) -} - -internal fun WalletStoreModel.updateSelectedAddress(currency: Currency, addressType: AddressType): WalletStoreModel { - return this.copy( - walletsData = walletsData.updateSelectedAddress(currency, addressType), - ) -} - -private inline fun List.replaceWalletStores( - walletStoresToUpdate: List, - update: (walletStore: WalletStoreModel) -> WalletStoreModel, -): List { - val mutableStores = ArrayList(this) - - walletStoresToUpdate.forEach { walletStoreToUpdate -> - val index = mutableStores.indexOfFirst(walletStoreToUpdate::isSameWalletStore) - // Can be possible if user hides wallet store when it's tokens is loading - if (index == -1) return@forEach - - val currentWalletStore = mutableStores[index] - val updatedWalletStore = update(currentWalletStore) - - if (currentWalletStore != updatedWalletStore) { - Timber.d( - """ - Update wallet store in storage - |- User wallet ID: ${updatedWalletStore.userWalletId} - |- Blockchain: ${updatedWalletStore.blockchain} - |- Derivation path: ${updatedWalletStore.derivationPath?.rawPath} - """.trimIndent(), - ) - - mutableStores[index] = updatedWalletStore - } - } - - return mutableStores -} - -internal fun WalletStoreModel.isSameWalletStore(other: WalletStoreModel): Boolean { - return this.userWalletId == other.userWalletId && - this.blockchain == other.blockchain && - this.derivationPath == other.derivationPath -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/storage/WalletManagerStorage.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/storage/WalletManagerStorage.kt deleted file mode 100644 index 3d492d01da..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/storage/WalletManagerStorage.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.tangem.tap.domain.walletStores.storage - -import com.tangem.blockchain.common.WalletManager -import com.tangem.domain.wallets.models.UserWalletId -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock - -internal object WalletManagerStorage { - private val managers = MutableSharedFlow>>(replay = 1) - private val mutex = Mutex() - - init { - managers.tryEmit(hashMapOf()) - } - - fun getAll(): SharedFlow>> { - return managers.asSharedFlow() - } - - suspend fun update( - f: suspend (HashMap>) -> HashMap>, - ) { - while (mutex.isLocked) { - delay(timeMillis = 60) - } - - mutex.withLock { - val prevState = managers.first() - managers.emit(f(prevState)) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/storage/WalletStoresStorage.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/storage/WalletStoresStorage.kt deleted file mode 100644 index 883783db86..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/storage/WalletStoresStorage.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.tangem.tap.domain.walletStores.storage - -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.domain.model.WalletStoreModel -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock - -internal object WalletStoresStorage { - private val stores = MutableSharedFlow>>(replay = 1) - private val mutex = Mutex() - - init { - stores.tryEmit(hashMapOf()) - } - - fun getAll(): SharedFlow>> { - return stores.asSharedFlow() - } - - suspend fun update( - f: suspend (HashMap>) -> HashMap>, - ) { - while (mutex.isLocked) { - delay(timeMillis = 60) - } - - mutex.withLock { - val prevState = stores.first() - stores.emit(f(prevState)) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/EthSignHelper.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/EthSignHelper.kt index a302d44747..80d7b046e9 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/EthSignHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/EthSignHelper.kt @@ -1,14 +1,9 @@ package com.tangem.tap.domain.walletconnect import com.github.salomonbrys.kotson.fromJson -import com.github.salomonbrys.kotson.toMap import com.google.gson.Gson import com.google.gson.GsonBuilder -import com.google.gson.JsonArray import com.google.gson.JsonParser -import com.google.gson.annotations.SerializedName -import com.tangem.blockchain.common.Blockchain -import com.trustwallet.walletconnect.JSONRPC_VERSION object EthSignHelper { private val gson: Gson by lazy { @@ -33,68 +28,4 @@ object EthSignHelper { null } } -} - -data class CustomJsonRpcRequest( - val id: Long, - val jsonrpc: String = JSONRPC_VERSION, - val method: WCMethodExtended?, - val params: JsonArray, -) { - - fun blockchainFromChainId(): Blockchain? { - return try { - val hex = params[0].asJsonObject.toMap()[CHAIN_ID_KEY]?.asString ?: "" - Blockchain.fromChainId(Integer.decode(hex)) - } catch (exception: Exception) { - null - } - } - - companion object { - const val CHAIN_ID_KEY = "chainId" - } -} - -enum class WCMethodExtended { - @SerializedName("wc_sessionRequest") - SESSION_REQUEST, - - @SerializedName("wc_sessionUpdate") - SESSION_UPDATE, - - @SerializedName("eth_sign") - ETH_SIGN, - - @SerializedName("personal_sign") - ETH_PERSONAL_SIGN, - - @SerializedName("eth_signTypedData") - ETH_SIGN_TYPE_DATA, - - @SerializedName("eth_signTypedData_v4") - ETH_SIGN_TYPE_DATA_V4, - - @SerializedName("eth_signTransaction") - ETH_SIGN_TRANSACTION, - - @SerializedName("eth_sendTransaction") - ETH_SEND_TRANSACTION, - - @SerializedName("bnb_sign") - BNB_SIGN, - - @SerializedName("bnb_tx_confirmation") - BNB_TRANSACTION_CONFIRM, - - @SerializedName("get_accounts") - GET_ACCOUNTS, - - @SerializedName("trust_signTransaction") - SIGN_TRANSACTION, - - @SerializedName("wallet_switchEthereumChain") - SWITCH_CHAIN, - - ; } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt index a4bda602c0..b99b599dfd 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt @@ -36,7 +36,7 @@ import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response import timber.log.Timber -import java.util.* +import java.util.UUID import java.util.concurrent.TimeUnit import kotlin.collections.set @@ -191,7 +191,7 @@ class WalletConnectManager { } } - fun removeSimilarSessions(activeData: WalletConnectActiveData) { + private fun removeSimilarSessions(activeData: WalletConnectActiveData) { val sessionsToRemove = sessions.filter { it.value.wallet.walletPublicKey?.equals(activeData.wallet.walletPublicKey) == true && it.value.peerMeta?.url == activeData.peerMeta?.url && @@ -206,7 +206,7 @@ class WalletConnectManager { activeData.client.rejectRequest(id) } - fun acceptRequest(topic: String, id: Long, data: String) { + private fun acceptRequest(topic: String, id: Long, data: String) { val activeData = sessions[topic] ?: return activeData.client.approveRequest(id, data) } @@ -368,7 +368,7 @@ class WalletConnectManager { } @Suppress("LongMethod", "ComplexMethod") - fun setListeners(client: WCClient) { + private fun setListeners(client: WCClient) { client.onSessionRequest = { id: Long, peer: WCPeerMeta -> Timber.d("OnSessionRequest: $peer") val session = client.session diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectRepository.kt index 8a5490c31a..e38e7fcaf1 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectRepository.kt @@ -25,11 +25,6 @@ class WalletConnectRepository(val context: Application) { saveSessions(sessions) } - fun removeSession(session: WalletConnectSession) { - val sessions = loadSavedSessions().filterNot { it == session } - saveSessions(sessions) - } - fun removeSession(session: WCSession) { val sessions = loadSavedSessions().filterNot { it.session == session } saveSessions(sessions) diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt index 3344347b20..cbc281470e 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt @@ -4,7 +4,7 @@ import com.tangem.Message import com.tangem.blockchain.blockchains.ethereum.EthereumGasLoader import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionExtras import com.tangem.blockchain.blockchains.ethereum.EthereumUtils -import com.tangem.blockchain.blockchains.ethereum.EthereumUtils.Companion.toKeccak +import com.tangem.blockchain.blockchains.ethereum.EthereumUtils.toKeccak import com.tangem.blockchain.common.* import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.extensions.Result @@ -16,11 +16,10 @@ import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.toDecompressedPublicKey import com.tangem.common.extensions.toHexString import com.tangem.core.analytics.Analytics +import com.tangem.core.analytics.models.Basic +import com.tangem.core.analytics.models.Basic.TransactionSent.MemoType import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.operations.sign.SignHashCommand -import com.tangem.tap.common.analytics.events.AnalyticsParam -import com.tangem.tap.common.analytics.events.Basic -import com.tangem.tap.common.analytics.events.Basic.TransactionSent.MemoType import com.tangem.tap.common.extensions.safeUpdate import com.tangem.tap.common.extensions.toFormattedString import com.tangem.tap.domain.walletconnect.BnbHelper.toWCBinanceTradeOrder @@ -31,16 +30,19 @@ import com.tangem.tap.domain.walletconnect2.domain.models.EthTransactionData import com.tangem.tap.domain.walletconnect2.domain.models.binance.WcBinanceTradeOrder import com.tangem.tap.domain.walletconnect2.domain.models.binance.WcBinanceTransferOrder import com.tangem.tap.features.demo.isDemoCard -import com.tangem.tap.features.details.redux.walletconnect.* +import com.tangem.tap.features.details.redux.walletconnect.BinanceMessageData +import com.tangem.tap.features.details.redux.walletconnect.WcEthTransactionType +import com.tangem.tap.features.details.redux.walletconnect.WcPersonalSignData +import com.tangem.tap.features.details.redux.walletconnect.WcTransactionData import com.tangem.tap.features.details.ui.walletconnect.dialogs.PersonalSignDialogData import com.tangem.tap.features.details.ui.walletconnect.dialogs.TransactionRequestDialogData import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.store import com.tangem.tap.tangemSdkManager import com.tangem.tap.userWalletsListManager -import com.trustwallet.walletconnect.models.ethereum.WCEthereumSignMessage.WCSignType.* import timber.log.Timber import java.math.BigDecimal +import com.tangem.core.analytics.models.AnalyticsParam as CoreAnalyticsParam class WalletConnectSdkHelper { @@ -176,7 +178,7 @@ class WalletConnectSdkHelper { ) return when (result) { SimpleResult.Success -> { - val sentFrom = AnalyticsParam.TxSentFrom.WalletConnect + val sentFrom = CoreAnalyticsParam.TxSentFrom.WalletConnect Analytics.send(Basic.TransactionSent(sentFrom = sentFrom, memoType = MemoType.Null)) val hash = data.walletManager.wallet.recentTransactions.last().hash if (hash?.startsWith(HEX_PREFIX) == true) { diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/ChainWithDerivation.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/ChainWithDerivation.kt deleted file mode 100644 index 8f43892142..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/ChainWithDerivation.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.domain.models - -data class ChainWithDerivation( - val chain: String, - val derivationPath: String?, -) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceCancelOrder.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceCancelOrder.kt index 5d5cc625d2..aebac1b973 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceCancelOrder.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceCancelOrder.kt @@ -1,6 +1,7 @@ package com.tangem.tap.domain.walletconnect2.domain.models.binance -import com.squareup.moshi.* +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass @Suppress("LongParameterList") @JsonClass(generateAdapter = true) @@ -21,12 +22,6 @@ class WcBinanceCancelOrder( msgs: List, ) : WcBinanceOrder(accountNumber, chainId, data, memo, sequence, source, msgs) { - enum class MessageKey(val key: String) { - REFID("refid"), - SENDER("sender"), - SYMBOL("symbol"), - } - data class Message( val refid: String, val sender: String, diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceTradeOrder.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceTradeOrder.kt index 35456d2eb5..d00997295d 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceTradeOrder.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceTradeOrder.kt @@ -1,6 +1,6 @@ package com.tangem.tap.domain.walletconnect2.domain.models.binance -import com.github.salomonbrys.kotson.* +import com.github.salomonbrys.kotson.jsonSerializer import com.google.gson.JsonObject import com.squareup.moshi.Json import com.squareup.moshi.JsonClass @@ -42,19 +42,6 @@ class WcBinanceTradeOrder( ) } -val tradeOrderDeserializer = jsonDeserializer { - WcBinanceTradeOrder.Message( - id = it.json[WcBinanceTradeOrder.MessageKey.ID.key].string, - orderType = it.json[WcBinanceTradeOrder.MessageKey.ORDER_TYPE.key].int, - price = it.json[WcBinanceTradeOrder.MessageKey.PRICE.key].long, - quantity = it.json[WcBinanceTradeOrder.MessageKey.QUANTITY.key].long, - sender = it.json[WcBinanceTradeOrder.MessageKey.SENDER.key].string, - side = it.json[WcBinanceTradeOrder.MessageKey.SIDE.key].int, - symbol = it.json[WcBinanceTradeOrder.MessageKey.SYMBOL.key].string, - timeInforce = it.json[WcBinanceTradeOrder.MessageKey.TIME_INFORCE.key].int, - ) -} - val tradeOrderSerializer = jsonSerializer { val jsonObject = JsonObject() jsonObject.addProperty(WcBinanceTradeOrder.MessageKey.ID.key, it.src.id) diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceTradePair.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceTradePair.kt deleted file mode 100644 index 54bf23dcaf..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceTradePair.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.tangem.tap.domain.walletconnect2.domain.models.binance - -data class WcBinanceTradePair(val from: String, val to: String) { - companion object { - fun from(symbol: String): WcBinanceTradePair? { - val pair = symbol.split("_") - - return if (pair.size > 1) { - val firstParts = pair[0].split("-") - val secondParts = pair[1].split("-") - WcBinanceTradePair(firstParts[0], secondParts[0]) - } else { - null - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceTransferOrder.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceTransferOrder.kt index 381069071e..6d46e17319 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceTransferOrder.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/models/binance/WcBinanceTransferOrder.kt @@ -17,11 +17,6 @@ class WcBinanceTransferOrder( msgs: List, ) : WcBinanceOrder(accountNumber, chainId, data, memo, sequence, source, msgs) { - enum class MessageKey(val key: String) { - INPUTS("inputs"), - OUTPUTS("outputs"), - } - data class Message( val inputs: List, val outputs: List, diff --git a/app/src/main/java/com/tangem/tap/features/BaseFragment.kt b/app/src/main/java/com/tangem/tap/features/BaseFragment.kt index 2c5ddc63f2..d9e668bf04 100644 --- a/app/src/main/java/com/tangem/tap/features/BaseFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/BaseFragment.kt @@ -23,7 +23,7 @@ import com.tangem.wallet.R */ abstract class BaseFragment(layoutId: Int) : Fragment(layoutId), FragmentOnBackPressedHandler { - protected lateinit var mainView: View + private lateinit var mainView: View override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/data/DefaultCustomTokenRepository.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/data/DefaultCustomTokenRepository.kt index b949793a7d..96f2eea824 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/data/DefaultCustomTokenRepository.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/data/DefaultCustomTokenRepository.kt @@ -1,6 +1,7 @@ package com.tangem.tap.features.customtoken.impl.data import com.tangem.blockchain.common.Blockchain +import com.tangem.datasource.api.common.response.getOrThrow import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.domain.common.extensions.supportedBlockchains import com.tangem.domain.common.extensions.toNetworkId @@ -39,6 +40,7 @@ class DefaultCustomTokenRepository( contractAddress = address, networkIds = networkId ?: supportedTokenNetworkIds.joinToString(separator = ","), ) + .getOrThrow() .coins.firstNotNullOfOrNull { coin -> val networksWithTheSameAddress = coin.networks.filter { network -> (network.contractAddress != null || network.decimalCount != null) && diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/di/CustomTokenInteractorModule.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/di/CustomTokenInteractorModule.kt index 193a65643f..44d4a98284 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/di/CustomTokenInteractorModule.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/di/CustomTokenInteractorModule.kt @@ -1,7 +1,9 @@ package com.tangem.tap.features.customtoken.impl.di import com.tangem.datasource.api.tangemTech.TangemTechApi +import com.tangem.domain.card.DerivePublicKeysUseCase import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase +import com.tangem.features.tester.api.TesterFeatureToggles import com.tangem.tap.features.customtoken.impl.data.DefaultCustomTokenRepository import com.tangem.tap.features.customtoken.impl.domain.CustomTokenInteractor import com.tangem.tap.features.customtoken.impl.domain.DefaultCustomTokenInteractor @@ -25,8 +27,10 @@ internal object CustomTokenInteractorModule { fun provideCustomTokenInteractor( tangemTechApi: TangemTechApi, appCoroutineDispatcherProvider: AppCoroutineDispatcherProvider, - getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, reduxStateHolder: AppStateHolder, + getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, + derivePublicKeysUseCase: DerivePublicKeysUseCase, + testerFeatureToggles: TesterFeatureToggles, ): CustomTokenInteractor { return DefaultCustomTokenInteractor( featureRepository = DefaultCustomTokenRepository( @@ -35,6 +39,8 @@ internal object CustomTokenInteractorModule { reduxStateHolder = reduxStateHolder, ), getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, + derivePublicKeysUseCase = derivePublicKeysUseCase, + testerFeatureToggles = testerFeatureToggles, ) } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/domain/CustomTokenRepository.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/domain/CustomTokenRepository.kt index 13b5d1c043..cb1959d157 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/domain/CustomTokenRepository.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/domain/CustomTokenRepository.kt @@ -9,6 +9,10 @@ import com.tangem.tap.features.customtoken.impl.domain.models.FoundToken */ interface CustomTokenRepository { - /** Find token by [address] and [networkId] */ + /** + * Find token by [address] and [networkId] + * + * @throws com.tangem.datasource.api.common.response.ApiResponseError + * */ suspend fun findToken(address: String, networkId: String?): FoundToken } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/domain/DefaultCustomTokenInteractor.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/domain/DefaultCustomTokenInteractor.kt index 7de4ee238b..2d6ad2aebf 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/domain/DefaultCustomTokenInteractor.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/domain/DefaultCustomTokenInteractor.kt @@ -6,11 +6,10 @@ import com.tangem.common.CompletionResult import com.tangem.common.card.EllipticCurve import com.tangem.common.core.TangemError import com.tangem.common.extensions.ByteArrayKey -import com.tangem.common.extensions.guard import com.tangem.common.extensions.toMapKey -import com.tangem.common.flatMap import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.data.tokens.utils.CryptoCurrencyFactory +import com.tangem.domain.card.DerivePublicKeysUseCase import com.tangem.domain.common.configs.CardConfig import com.tangem.domain.common.extensions.toNetworkId import com.tangem.domain.common.util.derivationStyleProvider @@ -20,21 +19,22 @@ import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase +import com.tangem.features.tester.api.TesterFeatureToggles import com.tangem.operations.derivation.ExtendedPublicKeysMap -import com.tangem.tap.* import com.tangem.tap.common.extensions.dispatchDebugErrorNotification import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.TapError +import com.tangem.tap.domain.model.Currency import com.tangem.tap.features.customtoken.impl.domain.models.FoundToken import com.tangem.tap.features.tokens.legacy.redux.TokensMiddleware -import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.proxy.redux.DaggerGraphState +import com.tangem.tap.store +import com.tangem.tap.tangemSdkManager +import com.tangem.tap.userWalletsListManager import com.tangem.utils.extensions.DELAY_SDK_DIALOG_CLOSE import kotlinx.coroutines.delay -import kotlinx.coroutines.launch import timber.log.Timber /** @@ -47,6 +47,8 @@ import timber.log.Timber class DefaultCustomTokenInteractor( private val featureRepository: CustomTokenRepository, private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, + private val derivePublicKeysUseCase: DerivePublicKeysUseCase, + private val testerFeatureToggles: TesterFeatureToggles, ) : CustomTokenInteractor { // TODO: Move to DI @@ -66,19 +68,29 @@ class DefaultCustomTokenInteractor( override suspend fun saveToken(customCurrency: CustomCurrency) { val userWallet = getSelectedWalletSyncUseCase().fold(ifLeft = { return }, ifRight = { it }) - val currency = Currency.fromCustomCurrency(customCurrency) - val isNeedToDerive = isNeedToDerive(userWallet, currency) - if (isNeedToDerive) { - deriveMissingBlockchains( - userWallet = userWallet, - currencyList = listOf(currency), - onSuccess = { submitAdd(userWallet = userWallet.copy(scanResponse = it), currency = currency) }, - ) { - throw it - } + + if (testerFeatureToggles.isDerivePublicKeysRefactoringEnabled) { + val currencies = listOfNotNull(element = currency.toCryptoCurrency(userWallet.scanResponse)) + derivePublicKeysUseCase(userWalletId = userWallet.walletId, currencies = currencies) + .onRight { + addCryptoCurrenciesUseCase(userWalletId = userWallet.walletId, currencies = currencies) + } + .onLeft { Timber.e("Failed to derive public keys: $it") } } else { - submitAdd(userWallet, currency) + // TODO: delete [REDACTED_JIRA] + val isNeedToDerive = isNeedToDerive(userWallet, currency) + if (isNeedToDerive) { + deriveMissingBlockchains( + userWallet = userWallet, + currencyList = listOf(currency), + onSuccess = { submitAdd(userWallet = userWallet.copy(scanResponse = it), currency = currency) }, + ) { + throw it + } + } else { + submitAdd(userWallet, currency) + } } } @@ -179,69 +191,36 @@ class DefaultCustomTokenInteractor( private suspend fun submitAdd(userWallet: UserWallet, currency: Currency) { val scanResponse = userWallet.scanResponse - val walletFeatureToggles = store.state.daggerGraphState.get(DaggerGraphState::walletFeatureToggles) + val userWalletId = userWallet.walletId - if (walletFeatureToggles.isRedesignedScreenEnabled) { - val cryptoCurrencyFactory = CryptoCurrencyFactory() + val currencyList = listOfNotNull(element = currency.toCryptoCurrency(scanResponse)) - submitNewAdd( - userWalletId = userWallet.walletId, - updatedScanResponse = scanResponse, - currencyList = listOfNotNull( - when (currency) { - is Currency.Blockchain -> { - cryptoCurrencyFactory.createCoin( - blockchain = currency.blockchain, - extraDerivationPath = currency.derivationPath, - derivationStyleProvider = scanResponse.derivationStyleProvider, - ) - } - is Currency.Token -> { - cryptoCurrencyFactory.createToken( - sdkToken = currency.token, - blockchain = currency.blockchain, - extraDerivationPath = currency.derivationPath, - derivationStyleProvider = scanResponse.derivationStyleProvider, - ) - } - }, - ), - ) - } else { - submitLegacyAdd(scanResponse = scanResponse, currency = currency) + userWalletsListManager.update(userWalletId) { + it.copy(scanResponse = scanResponse) } + + addCryptoCurrenciesUseCase(userWalletId, currencyList) } - private suspend fun submitLegacyAdd(scanResponse: ScanResponse, currency: Currency) { - val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard { - Timber.e("Unable to add currencies, no user wallet selected") - return - } + private fun Currency.toCryptoCurrency(scanResponse: ScanResponse): CryptoCurrency? { + val cryptoCurrencyFactory = CryptoCurrencyFactory() - userWalletsListManager.update( - userWalletId = selectedUserWallet.walletId, - update = { userWallet -> userWallet.copy(scanResponse = scanResponse) }, - ) - .flatMap { updatedUserWallet -> - walletCurrenciesManager.addCurrencies( - userWallet = updatedUserWallet, - currenciesToAdd = listOf(currency), + return when (this) { + is Currency.Blockchain -> { + cryptoCurrencyFactory.createCoin( + blockchain = blockchain, + extraDerivationPath = derivationPath, + derivationStyleProvider = scanResponse.derivationStyleProvider, + ) + } + is Currency.Token -> { + cryptoCurrencyFactory.createToken( + sdkToken = token, + blockchain = blockchain, + extraDerivationPath = derivationPath, + derivationStyleProvider = scanResponse.derivationStyleProvider, ) } - } - - private fun submitNewAdd( - userWalletId: UserWalletId, - updatedScanResponse: ScanResponse, - currencyList: List, - ) { - scope.launch { - userWalletsListManager.update( - userWalletId = userWalletId, - update = { it.copy(scanResponse = updatedScanResponse) }, - ) - - addCryptoCurrenciesUseCase(userWalletId, currencyList) } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt index 484026d744..1e8a78eb4f 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt @@ -25,8 +25,6 @@ import com.tangem.domain.features.addCustomToken.CustomCurrency import com.tangem.domain.tokens.GetCryptoCurrenciesUseCase import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase -import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles -import com.tangem.tap.domain.model.WalletDataModel import com.tangem.tap.features.customtoken.impl.domain.CustomTokenInteractor import com.tangem.tap.features.customtoken.impl.domain.models.FoundToken import com.tangem.tap.features.customtoken.impl.presentation.models.* @@ -38,8 +36,6 @@ import com.tangem.tap.features.customtoken.impl.presentation.states.AddCustomTok import com.tangem.tap.features.customtoken.impl.presentation.validators.ContactAddressValidator import com.tangem.tap.features.customtoken.impl.presentation.validators.ContractAddressValidatorResult import com.tangem.tap.features.details.ui.cardsettings.TextReference -import com.tangem.tap.features.wallet.models.Currency -import com.tangem.tap.store import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider import com.tangem.utils.coroutines.runCatching import com.tangem.wallet.BuildConfig @@ -71,7 +67,6 @@ internal class AddCustomTokenViewModel @Inject constructor( private val featureInteractor: CustomTokenInteractor, private val dispatchers: AppCoroutineDispatcherProvider, private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, - private val walletFeatureToggles: WalletFeatureToggles, ) : ViewModel(), DefaultLifecycleObserver { private val analyticsSender = AddCustomTokenAnalyticsSender(analyticsEventHandler) @@ -88,18 +83,16 @@ internal class AddCustomTokenViewModel @Inject constructor( private var foundToken: FoundToken? = null init { - if (walletFeatureToggles.isRedesignedScreenEnabled) { - viewModelScope.launch(dispatchers.main) { - currentCryptoCurrencies = getSelectedWalletSyncUseCase().fold( - ifLeft = { emptyList() }, - ifRight = { selectedWallet -> - getCurrenciesUseCase(selectedWallet.walletId).fold( - ifLeft = { emptyList() }, - ifRight = { it }, - ) - }, - ) - } + viewModelScope.launch(dispatchers.main) { + currentCryptoCurrencies = getSelectedWalletSyncUseCase().fold( + ifLeft = { emptyList() }, + ifRight = { selectedWallet -> + getCurrenciesUseCase(selectedWallet.walletId).fold( + ifLeft = { emptyList() }, + ifRight = { it }, + ) + }, + ) } } @@ -591,14 +584,6 @@ internal class AddCustomTokenViewModel @Inject constructor( } private fun isTokenAlreadyAdded(): Boolean { - return if (walletFeatureToggles.isRedesignedScreenEnabled) { - isTokenAlreadyAddedNew() - } else { - isTokenAlreadyAddedOld() - } - } - - private fun isTokenAlreadyAddedNew(): Boolean { return currentCryptoCurrencies .filterIsInstance() .any { token -> @@ -621,32 +606,7 @@ internal class AddCustomTokenViewModel @Inject constructor( } } - private fun isTokenAlreadyAddedOld(): Boolean { - return store.state.walletState.walletsStores - .map { walletStore -> walletStore.walletsData.map(WalletDataModel::currency) } - .flatten() - .filterIsInstance() - .any { wrappedCurrency -> - val contractAddress = uiState.form.contractAddressInputField.value - val networkSelectorValue = uiState.form.networkSelectorField.selectedItem.blockchain - val sameId = foundToken?.id == wrappedCurrency.token.id - val sameAddress = contractAddress == wrappedCurrency.token.contractAddress - val sameBlockchain = - Blockchain.fromNetworkId(networkSelectorValue.toNetworkId()) == wrappedCurrency.blockchain - val isSameDerivationPath = getDerivationPath().isSameDerivationPath(wrappedCurrency.derivationPath) - sameId && sameAddress && sameBlockchain && isSameDerivationPath - } - } - private fun isBlockchainAlreadyAdded(): Boolean { - return if (walletFeatureToggles.isRedesignedScreenEnabled) { - isBlockchainAlreadyAddedNew() - } else { - isBlockchainAlreadyAddedOld() - } - } - - private fun isBlockchainAlreadyAddedNew(): Boolean { return currentCryptoCurrencies .filterIsInstance() .any { coin -> @@ -655,22 +615,6 @@ internal class AddCustomTokenViewModel @Inject constructor( } } - private fun isBlockchainAlreadyAddedOld(): Boolean { - return store.state.walletState.walletsStores - .map { walletStore -> walletStore.walletsData.map(WalletDataModel::currency) } - .flatten() - .filterIsInstance() - .any { - val networkSelectorValue = uiState.form.networkSelectorField.selectedItem.blockchain - networkSelectorValue == it.blockchain && - getDerivationPath().isSameDerivationPath(it.derivationPath) - } - } - - private fun DerivationPath?.isSameDerivationPath(rawDerivationPath: String?): Boolean { - return this == rawDerivationPath?.let { DerivationPath(it) } - } - private fun handleContractAddressErrorValidation(type: AddCustomTokenError) { when { isNetworkSelected() && type == AddCustomTokenError.InvalidContractAddress -> { diff --git a/app/src/main/java/com/tangem/tap/features/demo/DemoHelper.kt b/app/src/main/java/com/tangem/tap/features/demo/DemoHelper.kt index 1de357e9df..7dc282cacd 100644 --- a/app/src/main/java/com/tangem/tap/features/demo/DemoHelper.kt +++ b/app/src/main/java/com/tangem/tap/features/demo/DemoHelper.kt @@ -2,13 +2,11 @@ package com.tangem.tap.features.demo import com.tangem.domain.demo.DemoConfig import com.tangem.domain.models.scan.ScanResponse -import com.tangem.domain.tokens.legacy.TradeCryptoAction import com.tangem.tap.common.extensions.dispatchNotification import com.tangem.tap.common.redux.AppState import com.tangem.tap.features.details.redux.DetailsAction import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction -import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.store import com.tangem.wallet.R import org.rekotlin.Action @@ -22,10 +20,7 @@ object DemoHelper { private val disabledActionFeatures = listOf( WalletConnectAction.StartWalletConnect::class.java, - TradeCryptoAction.Buy::class.java, - TradeCryptoAction.Sell::class.java, BackupAction.StartBackup::class.java, - WalletAction.ExploreAddress::class.java, DetailsAction.ResetToFactory.Start::class.java, ) diff --git a/app/src/main/java/com/tangem/tap/features/demo/DemoOnboardingNoteMiddleware.kt b/app/src/main/java/com/tangem/tap/features/demo/DemoOnboardingNoteMiddleware.kt index ae1cf2fa2a..4708050380 100644 --- a/app/src/main/java/com/tangem/tap/features/demo/DemoOnboardingNoteMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/demo/DemoOnboardingNoteMiddleware.kt @@ -5,9 +5,9 @@ import com.tangem.domain.common.extensions.makePrimaryWalletManager import com.tangem.domain.common.extensions.withMainContext import com.tangem.domain.demo.DemoConfig import com.tangem.domain.models.scan.ScanResponse +import com.tangem.tap.common.entities.ProgressState +import com.tangem.tap.domain.model.Currency import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction -import com.tangem.tap.features.wallet.models.Currency -import com.tangem.tap.features.wallet.redux.ProgressState import com.tangem.tap.scope import com.tangem.tap.store import kotlinx.coroutines.launch diff --git a/app/src/main/java/com/tangem/tap/features/details/DarkThemeFeatureToggle.kt b/app/src/main/java/com/tangem/tap/features/details/DarkThemeFeatureToggle.kt deleted file mode 100644 index afb3010ad3..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/DarkThemeFeatureToggle.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.tangem.tap.features.details - -import com.tangem.core.featuretoggle.manager.FeatureTogglesManager - -class DarkThemeFeatureToggle( - private val featureTogglesManager: FeatureTogglesManager, -) { - val isDarkThemeEnabled: Boolean - get() = featureTogglesManager.isFeatureEnabled(name = "DARK_THEME_ENABLED") -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/featuretoggles/DefaultDetailsFeatureToggles.kt b/app/src/main/java/com/tangem/tap/features/details/featuretoggles/DefaultDetailsFeatureToggles.kt deleted file mode 100644 index 2ed04a4c94..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/featuretoggles/DefaultDetailsFeatureToggles.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.tangem.tap.features.details.featuretoggles - -import com.tangem.core.featuretoggle.manager.FeatureTogglesManager - -internal class DefaultDetailsFeatureToggles( - private val featureTogglesManager: FeatureTogglesManager, -) : DetailsFeatureToggles { - - override val isRedesignedAppCurrencySelectorEnabled: Boolean - get() = featureTogglesManager.isFeatureEnabled(name = "REDESIGNED_APP_CURRENCY_SELECTOR_ENABLED") -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/featuretoggles/DetailsFeatureToggles.kt b/app/src/main/java/com/tangem/tap/features/details/featuretoggles/DetailsFeatureToggles.kt deleted file mode 100644 index dd1cd3bbdd..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/featuretoggles/DetailsFeatureToggles.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.tangem.tap.features.details.featuretoggles - -interface DetailsFeatureToggles { - - val isRedesignedAppCurrencySelectorEnabled: Boolean -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/featuretoggles/DetailsFeatureTogglesModule.kt b/app/src/main/java/com/tangem/tap/features/details/featuretoggles/DetailsFeatureTogglesModule.kt deleted file mode 100644 index 2e6fd9874a..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/featuretoggles/DetailsFeatureTogglesModule.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.tangem.tap.features.details.featuretoggles - -import com.tangem.core.featuretoggle.manager.FeatureTogglesManager -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent - -@Module -@InstallIn(SingletonComponent::class) -internal object DetailsFeatureTogglesModule { - - @Provides - fun provideDetailsFeatureToggles(featureTogglesManager: FeatureTogglesManager): DetailsFeatureToggles { - return DefaultDetailsFeatureToggles(featureTogglesManager) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt index 338e8f255c..627b9bf0d5 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt @@ -2,18 +2,17 @@ package com.tangem.tap.features.details.redux import androidx.lifecycle.LifecycleCoroutineScope import com.tangem.core.ui.extensions.TextReference +import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.apptheme.model.AppThemeMode import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ScanResponse -import com.tangem.tap.common.entities.FiatCurrency import org.rekotlin.Action sealed class DetailsAction : Action { data class PrepareScreen( val scanResponse: ScanResponse, - val darkThemeSwitchEnabled: Boolean, val shouldSaveUserWallets: Boolean, ) : DetailsAction() @@ -92,9 +91,9 @@ sealed class DetailsAction : Action { ) : AppSettings() data class ChangeAppCurrency( - val fiatCurrency: FiatCurrency, + val currency: AppCurrency, ) : AppSettings() } - data class ChangeAppCurrency(val fiatCurrency: FiatCurrency) : DetailsAction() + data class ChangeAppCurrency(val currency: AppCurrency) : DetailsAction() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt index 4504a0bb43..dc57a6bb45 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt @@ -22,7 +22,8 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.legacy.isLockedSync import com.tangem.tap.* import com.tangem.tap.common.analytics.events.AnalyticsParam -import com.tangem.tap.common.analytics.events.Basic +import com.tangem.core.analytics.models.AnalyticsParam as CoreAnalyticsParam +import com.tangem.core.analytics.models.Basic import com.tangem.tap.common.analytics.events.Settings import com.tangem.tap.common.extensions.dispatchDialogShow import com.tangem.tap.common.extensions.dispatchOnMain @@ -36,8 +37,6 @@ import com.tangem.tap.domain.userWalletList.di.provideRuntimeImplementation import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.features.walletSelector.redux.WalletSelectorAction import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.utils.coroutines.JobHolder import com.tangem.utils.coroutines.saveIn @@ -233,9 +232,8 @@ class DetailsMiddleware { changeBalanceHiding(action.hideBalance) } is DetailsAction.AppSettings.ChangeAppCurrency -> { - store.dispatch(GlobalAction.ChangeAppCurrency(action.fiatCurrency)) - store.dispatch(DetailsAction.ChangeAppCurrency(action.fiatCurrency)) - store.dispatch(WalletSelectorAction.ChangeAppCurrency(action.fiatCurrency)) + store.dispatch(GlobalAction.ChangeAppCurrency(action.currency)) + store.dispatch(DetailsAction.ChangeAppCurrency(action.currency)) } is DetailsAction.AppSettings.SwitchPrivacySetting.Success, is DetailsAction.AppSettings.SwitchPrivacySetting.Failure, @@ -381,8 +379,6 @@ class DetailsMiddleware { preferencesStorage.shouldShowSaveUserWalletScreen = false store.state.daggerGraphState.get(DaggerGraphState::walletsRepository) .saveShouldSaveUserWallets(item = true) - - store.dispatchWithMain(WalletAction.UpdateCanSaveUserWallets(canSaveUserWallets = true)) } .doOnFailure { error -> Timber.e(error, "Unable to save user wallet") @@ -391,7 +387,6 @@ class DetailsMiddleware { private suspend fun deleteSavedWalletsAndAccessCodes(): CompletionResult { return userWalletsListManager.clear() - .flatMap { walletStoresManager.clear() } .doOnSuccess { Analytics.send(Settings.AppSettings.SaveWalletSwitcherChanged(AnalyticsParam.OnOffState.Off)) deleteSavedAccessCodes() @@ -399,7 +394,6 @@ class DetailsMiddleware { store.state.daggerGraphState.get(DaggerGraphState::walletsRepository) .saveShouldSaveUserWallets(item = false) - store.dispatchWithMain(WalletAction.UpdateCanSaveUserWallets(canSaveUserWallets = true)) store.dispatchWithMain(NavigationAction.PopBackTo(AppScreen.Home)) } .doOnFailure { error -> @@ -524,7 +518,7 @@ class DetailsMiddleware { ) store.state.daggerGraphState.get(DaggerGraphState::scanCardProcessor).scan( - analyticsEvent = Basic.CardWasScanned(AnalyticsParam.ScannedFrom.MyWallets), + analyticsEvent = Basic.CardWasScanned(CoreAnalyticsParam.ScannedFrom.MyWallets), onWalletNotCreated = { // No need to rollback policy, continue with the policy set before the card scan store.dispatchWithMain(DetailsAction.ScanAndSaveUserWallet.Success) diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index 5c5815107f..e6098d1dc9 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -45,7 +45,7 @@ private fun internalReduce(action: Action, state: AppState): DetailsState { } is DetailsAction.ChangeAppCurrency -> detailsState.copy( appSettingsState = detailsState.appSettingsState.copy( - selectedFiatCurrency = action.fiatCurrency, + selectedAppCurrency = action.currency, ), ) is DetailsAction.AccessCodeRecovery -> handleAccessCodeRecoveryAction(action, detailsState) @@ -74,7 +74,7 @@ private fun handlePrepareScreen(action: DetailsAction.PrepareScreen): DetailsSta isBiometricsAvailable = tangemSdkManager.canUseBiometry, saveWallets = action.shouldSaveUserWallets, saveAccessCodes = preferencesStorage.shouldSaveAccessCodes, - selectedFiatCurrency = store.state.globalState.appCurrency, + selectedAppCurrency = store.state.globalState.appCurrency, selectedThemeMode = runBlocking { store.state.daggerGraphState .get { appThemeModeRepository }.getAppThemeMode().firstOrNull() ?: AppThemeMode.DEFAULT @@ -83,7 +83,6 @@ private fun handlePrepareScreen(action: DetailsAction.PrepareScreen): DetailsSta store.state.daggerGraphState .get { balanceHidingRepository }.getBalanceHidingSettings().isHidingEnabledInSettings }, - darkThemeSwitchEnabled = action.darkThemeSwitchEnabled, ), ) } @@ -258,7 +257,7 @@ private fun handlePrivacyAction(action: DetailsAction.AppSettings, state: Detail ) is DetailsAction.AppSettings.ChangeAppCurrency -> state.copy( appSettingsState = state.appSettingsState.copy( - selectedFiatCurrency = action.fiatCurrency, + selectedAppCurrency = action.currency, ), ) is DetailsAction.AppSettings.ChangeBalanceHiding -> state.copy( diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt index c17950e504..1eab7ec856 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt @@ -1,11 +1,11 @@ package com.tangem.tap.features.details.redux import com.tangem.core.ui.extensions.TextReference +import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.apptheme.model.AppThemeMode import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ScanResponse import com.tangem.tap.common.entities.Button -import com.tangem.tap.common.entities.FiatCurrency import org.rekotlin.StateType import java.util.EnumSet @@ -62,9 +62,8 @@ data class AppSettingsState( val needEnrollBiometrics: Boolean = false, val isHidingEnabled: Boolean = false, val isInProgress: Boolean = false, - val selectedFiatCurrency: FiatCurrency = FiatCurrency.Default, + val selectedAppCurrency: AppCurrency = AppCurrency.Default, val selectedThemeMode: AppThemeMode = AppThemeMode.DEFAULT, - val darkThemeSwitchEnabled: Boolean = false, ) enum class SecurityOption { LongTap, PassCode, AccessCode } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt index 6a773800b6..8e1bb3f2e1 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt @@ -74,8 +74,6 @@ sealed class WalletConnectAction : Action { ) : WalletConnectAction() - data class SetDataToSend(val transactionData: WcTransactionData) : WalletConnectAction() - data class HandlePersonalSignRequest( val message: WCEthereumSignMessage, val session: WalletConnectSession, diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt index 3044d21544..60c2e73559 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt @@ -1,20 +1,24 @@ package com.tangem.tap.features.details.redux.walletconnect +import androidx.core.os.bundleOf import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.WalletManager import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.common.extensions.guard import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction -import com.tangem.domain.common.BlockchainNetwork -import com.tangem.domain.common.DerivationStyleProvider +import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.domain.common.extensions.toNetworkId import com.tangem.domain.common.extensions.withMainContext import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.models.scan.ScanResponse +import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.walletconnect.WalletConnectActions import com.tangem.domain.wallets.models.UserWallet +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.feature.qrscanning.QrScanningRouter +import com.tangem.feature.qrscanning.SourceType import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction @@ -29,11 +33,11 @@ import com.tangem.tap.domain.walletconnect2.domain.models.Account import com.tangem.tap.domain.walletconnect2.domain.models.BnbData import com.tangem.tap.domain.walletconnect2.domain.models.WalletConnectError import com.tangem.tap.features.demo.DemoHelper -import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.scope import com.tangem.tap.store import com.tangem.tap.userWalletsListManager +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import org.rekotlin.Action import org.rekotlin.Middleware @@ -97,7 +101,14 @@ class WalletConnectMiddleware { if (uri != null && isWalletConnectUri(uri)) { store.dispatchOnMain(WalletConnectAction.ShowClipboardOrScanQrDialog(uri)) } else { - store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.QrScan)) + store.dispatchOnMain( + NavigationAction.NavigateTo( + screen = AppScreen.QrScanning, + bundle = bundleOf( + QrScanningRouter.SOURCE_KEY to SourceType.WALLET_CONNECT, + ), + ), + ) } } is WalletConnectAction.SelectNetwork -> { @@ -115,7 +126,6 @@ class WalletConnectMiddleware { scope.launch { prepareWalletManager( scanResponse = data.scanResponse, - walletState = store.state.walletState, blockchain = action.blockchain, session = data.session, walletConnectManager = walletConnectManager, @@ -176,8 +186,14 @@ class WalletConnectMiddleware { // ) } is WalletConnectAction.ScanCard -> { - val scanResponse = store.state.globalState.scanResponse ?: return - scanCard(scanResponse, action.session, action.chainId) + val scanResponse = userWalletsListManager.selectedUserWalletSync.guard { + Timber.w("Unable to get selected user wallet for WC session") + return + } + + scope.launch(Dispatchers.Main) { + scanCard(scanResponse, action.session, action.chainId) + } } is WalletConnectAction.ApproveSession -> { walletConnectManager.approve(action.session) @@ -274,7 +290,6 @@ class WalletConnectMiddleware { val walletManager = getWalletManager( wallet = action.session.wallet, blockchain = blockchain, - walletState = store.state.walletState, ).guard { store.dispatchOnMain( GlobalAction.ShowDialog( @@ -372,19 +387,14 @@ class WalletConnectMiddleware { } private suspend fun getWalletManagers(): List { - val walletManagerToggles = store.state.daggerGraphState - .get(DaggerGraphState::walletFeatureToggles) - return if (walletManagerToggles.isRedesignedScreenEnabled) { - val walletManagerFacade = store.state.daggerGraphState - .get(DaggerGraphState::walletManagersFacade) - val userWallet = userWalletsListManager.selectedUserWalletSync ?: return emptyList() - walletManagerFacade.getStoredWalletManagers(userWallet.walletId) - } else { - store.state.walletState.walletManagers - } + val walletManagerFacade = store.state.daggerGraphState + .get(DaggerGraphState::walletManagersFacade) + val userWallet = userWalletsListManager.selectedUserWalletSync ?: return emptyList() + + return walletManagerFacade.getStoredWalletManagers(userWallet.walletId) } - private fun scanCard(scanResponse: ScanResponse, session: WalletConnectSession, chainId: Int?) { + private suspend fun scanCard(userWallet: UserWallet, session: WalletConnectSession, chainId: Int?) { val blockchain = WalletConnectNetworkUtils.parseBlockchain( chainId = chainId, peer = session.peerMeta, @@ -393,27 +403,28 @@ class WalletConnectMiddleware { return } - handleScanResponse(scanResponse = scanResponse, session = session, blockchain = blockchain) + handleScanResponse(userWallet, session, blockchain) } - private fun getAvailableBlockchains( - derivationStyleProvider: DerivationStyleProvider, - walletState: WalletState, - ): List { - return walletState.currencies.filter { - it.isBlockchain() && - !it.isCustomCurrency(derivationStyleProvider.getDerivationStyle()) && it.blockchain.isEvm() - }.map { it.blockchain } + private suspend fun getAvailableEvmBlockchains(userWalletId: UserWalletId): List { + val currenciesRepository = store.state.daggerGraphState.get(DaggerGraphState::currenciesRepository) + + return currenciesRepository.getMultiCurrencyWalletCurrenciesSync(userWalletId) + .asSequence() + .filterIsInstance() + .filterNot { it.isCustom } + .mapNotNull { Blockchain.fromNetworkId(it.network.id.value) } + .filter { it.isEvm() } + .toList() } private suspend fun prepareWalletManager( scanResponse: ScanResponse, - walletState: WalletState, blockchain: Blockchain, session: WalletConnectSession, walletConnectManager: WalletConnectManager, ) { - val walletManager = getWalletManager(session.wallet, blockchain, walletState).guard { + val walletManager = getWalletManager(session.wallet, blockchain).guard { store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(session.session)) store.dispatchOnMain( GlobalAction.ShowDialog( @@ -445,20 +456,25 @@ class WalletConnectMiddleware { } } - private fun handleScanResponse(scanResponse: ScanResponse, session: WalletConnectSession, blockchain: Blockchain) { + private suspend fun handleScanResponse( + userWallet: UserWallet, + session: WalletConnectSession, + blockchain: Blockchain, + ) { + val scanResponse = userWallet.scanResponse + if (!scanResponse.cardTypesResolver.isMultiwalletAllowed()) { store.dispatchOnMain(WalletConnectAction.UnsupportedCard) return } - val walletState = store.state.walletState val updatedSession = session.copy(wallet = session.wallet.copy(blockchain = blockchain)) store.dispatch( WalletConnectAction.SetNewSessionData( - NewWcSessionData(session = updatedSession, scanResponse = scanResponse, blockchain = blockchain), + NewWcSessionData(updatedSession, scanResponse, blockchain), ), ) val blockchains = if (blockchain.isEvm()) { - getAvailableBlockchains(scanResponse.derivationStyleProvider, walletState) + getAvailableEvmBlockchains(userWallet.walletId) } else { emptyList() } @@ -469,11 +485,7 @@ class WalletConnectMiddleware { ) } - private suspend fun getWalletManager( - wallet: WalletForSession, - blockchain: Blockchain, - walletState: WalletState, - ): WalletManager? { + private suspend fun getWalletManager(wallet: WalletForSession, blockchain: Blockchain): WalletManager? { val blockchainToMake = if (blockchain == Blockchain.Ethereum && wallet.isTestNet) { Blockchain.EthereumTestnet } else { @@ -483,25 +495,15 @@ class WalletConnectMiddleware { val derivation = blockchainToMake.derivationPath( style = userWallet.scanResponse.derivationStyleProvider.getDerivationStyle(), )?.rawPath - val walletFeatureToggles = store.state.daggerGraphState - .get(DaggerGraphState::walletFeatureToggles) - return if (walletFeatureToggles.isRedesignedScreenEnabled) { - val walletManagerFacade = store.state.daggerGraphState - .get(DaggerGraphState::walletManagersFacade) - walletManagerFacade.getOrCreateWalletManager( - userWalletId = userWallet.walletId, - blockchain = blockchainToMake, - derivationPath = derivation, - ) - } else { - val blockchainNetwork = BlockchainNetwork( - blockchain = blockchainToMake, - derivationPath = derivation, - tokens = emptyList(), - ) - walletState.getWalletManager(blockchainNetwork) - } + val walletManagerFacade = store.state.daggerGraphState + .get(DaggerGraphState::walletManagersFacade) + + return walletManagerFacade.getOrCreateWalletManager( + userWalletId = userWallet.walletId, + blockchain = blockchainToMake, + derivationPath = derivation, + ) } private fun isWalletConnectUri(uri: String): Boolean { diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt index e9354c13a7..34eed45fd3 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt @@ -73,9 +73,7 @@ data class WalletForSession( } else if (other.derivedPublicKey != null) return false if (derivationPath != other.derivationPath) return false if (isTestNet != other.isTestNet) return false - if (blockchain != other.blockchain) return false - - return true + return blockchain == other.blockchain } override fun hashCode(): Int { diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsFragment.kt index 122e9437d1..8afcd15101 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsFragment.kt @@ -7,7 +7,6 @@ import com.tangem.core.navigation.NavigationAction import com.tangem.core.ui.screen.ComposeFragment import com.tangem.core.ui.theme.AppThemeModeHolder import com.tangem.domain.appcurrency.repository.AppCurrencyRepository -import com.tangem.tap.features.details.featuretoggles.DetailsFeatureToggles import com.tangem.tap.features.details.redux.DetailsAction import com.tangem.tap.features.details.redux.DetailsState import com.tangem.tap.store @@ -21,14 +20,11 @@ internal class AppSettingsFragment : ComposeFragment(), StoreSubscriber, - private val detailsFeatureToggles: DetailsFeatureToggles, private val appCurrencyRepository: AppCurrencyRepository, ) { @@ -45,9 +41,7 @@ internal class AppSettingsViewModel( private set init { - if (detailsFeatureToggles.isRedesignedAppCurrencySelectorEnabled) { - bootstrapAppCurrencyUpdates() - } + bootstrapAppCurrencyUpdates() } fun updateState(state: DetailsState) { @@ -64,11 +58,12 @@ internal class AppSettingsViewModel( private fun buildItems(state: AppSettingsState): ImmutableList { val items = buildList { if (state.needEnrollBiometrics) { + Analytics.send(Settings.AppSettings.EnableBiometrics) itemsFactory.createEnrollBiometricsCard(onClick = ::enrollBiometrics).let(::add) } itemsFactory.createSelectAppCurrencyButton( - currentAppCurrencyName = state.selectedFiatCurrency.name, + currentAppCurrencyName = state.selectedAppCurrency.name, onClick = ::showAppCurrencySelector, ).let(::add) @@ -94,11 +89,9 @@ internal class AppSettingsViewModel( onCheckedChange = ::onFlipToHideBalanceToggled, ).let(::add) - if (state.darkThemeSwitchEnabled) { - itemsFactory.createSelectThemeModeButton(state.selectedThemeMode) { - showThemeModeSelector(state.selectedThemeMode) - }.let(::add) - } + itemsFactory.createSelectThemeModeButton(state.selectedThemeMode) { + showThemeModeSelector(state.selectedThemeMode) + }.let(::add) } return items.toImmutableList() @@ -109,13 +102,7 @@ internal class AppSettingsViewModel( } private fun showAppCurrencySelector() { - val action = if (detailsFeatureToggles.isRedesignedAppCurrencySelectorEnabled) { - NavigationAction.NavigateTo(AppScreen.AppCurrencySelector) - } else { - WalletAction.AppCurrencyAction.ChooseAppCurrency - } - - store.dispatchOnMain(action) + store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.AppCurrencySelector)) } private fun showThemeModeSelector(selectedMode: AppThemeMode) { @@ -179,6 +166,9 @@ internal class AppSettingsViewModel( } private fun onFlipToHideBalanceToggled(enable: Boolean) { + val param = AnalyticsParam.OnOffState(enable) + Analytics.send(Settings.AppSettings.HideBalanceChanged(param)) + store.dispatch(DetailsAction.AppSettings.ChangeBalanceHiding(hideBalance = enable)) } @@ -192,8 +182,7 @@ internal class AppSettingsViewModel( .onEach { if (it.code == store.state.globalState.appCurrency.code) return@onEach - val fiatCurrency = with(it) { FiatCurrency(code, name, symbol) } - store.dispatchWithMain(DetailsAction.AppSettings.ChangeAppCurrency(fiatCurrency)) + store.dispatchWithMain(DetailsAction.AppSettings.ChangeAppCurrency(it)) } .launchIn(scope) .saveIn(appCurrencyUpdatesJobHolder) diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsScreenState.kt b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsScreenState.kt index 1cf79ec5e4..4cf5e68186 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsScreenState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsScreenState.kt @@ -4,7 +4,6 @@ import androidx.annotation.StringRes import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.ui.res.stringResource -import com.tangem.domain.userwallets.Artwork import com.tangem.tap.features.details.redux.AccessCodeRecoveryState import com.tangem.tap.features.details.redux.SecurityOption import com.tangem.tap.features.details.ui.securitymode.toTitleRes @@ -17,7 +16,6 @@ internal data class CardSettingsScreenState( val accessCodeRecoveryState: AccessCodeRecoveryState? = null, val onScanCardClick: () -> Unit, val onElementClick: (CardInfo) -> Unit, - val cardImage: Artwork? = null, ) internal sealed class CardInfo( diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsViewModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsViewModel.kt index 732f7aa06d..64c00b50f4 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsViewModel.kt @@ -12,11 +12,9 @@ import com.tangem.domain.common.getTwinCardIdForUser import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Settings -import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.features.details.redux.CardSettingsState import com.tangem.tap.features.details.redux.DetailsAction import com.tangem.tap.features.details.redux.DetailsState -import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.store import dagger.hilt.android.lifecycle.HiltViewModel import org.rekotlin.StoreSubscriber @@ -37,15 +35,12 @@ internal class CardSettingsViewModel @Inject constructor( is Either.Left -> { Timber.e(selectedWalletEither.value.toString()) } - is Either.Right -> { - store.dispatchOnMain(WalletAction.UpdateUserWalletArtwork(selectedWalletEither.value.walletId)) - } + is Either.Right -> Unit } store.subscribe(this) { state -> state.skipRepeats { oldState, newState -> - oldState.detailsState == newState.detailsState && - oldState.walletState == newState.walletState + oldState.detailsState == newState.detailsState }.select { it.detailsState } } } @@ -67,7 +62,6 @@ internal class CardSettingsViewModel @Inject constructor( onScanCardClick = { store.dispatch(DetailsAction.ScanCard) }, - cardImage = store.state.walletState.cardImage, ) } else { val cardId = if (state.card.isTangemTwins) { @@ -105,7 +99,6 @@ internal class CardSettingsViewModel @Inject constructor( onElementClick = { handleClickingItem(it) }, - cardImage = store.state.walletState.cardImage, ) } } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsFragment.kt index d3d215551c..57376f6c70 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsFragment.kt @@ -9,7 +9,6 @@ import com.tangem.core.ui.screen.ComposeFragment import com.tangem.core.ui.theme.AppThemeModeHolder import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.tap.common.analytics.events.Settings -import com.tangem.tap.features.details.DarkThemeFeatureToggle import com.tangem.tap.features.details.redux.DetailsState import com.tangem.tap.store import dagger.hilt.android.AndroidEntryPoint @@ -22,9 +21,6 @@ internal class DetailsFragment : ComposeFragment(), StoreSubscriber Unit) : EventError() } sealed class SocialNetwork(val id: String, val iconRes: Int) { diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsViewModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsViewModel.kt index 5f12982afd..3e12848581 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsViewModel.kt @@ -2,6 +2,7 @@ package com.tangem.tap.features.details.ui.details import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf +import com.tangem.common.extensions.guard import com.tangem.core.analytics.Analytics import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction @@ -12,19 +13,18 @@ import com.tangem.core.ui.extensions.TextReference import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.tap.common.analytics.events.Settings +import com.tangem.tap.common.extensions.addContext import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.dispatchWithMain import com.tangem.tap.common.feedback.FeedbackEmail import com.tangem.tap.common.feedback.SupportInfo import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.features.details.DarkThemeFeatureToggle import com.tangem.tap.features.details.redux.DetailsAction import com.tangem.tap.features.details.redux.DetailsState import com.tangem.tap.features.disclaimer.redux.DisclaimerAction import com.tangem.tap.features.home.LocaleRegionProvider import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE -import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.scope import com.tangem.tap.userWalletsListManager import com.tangem.wallet.BuildConfig @@ -37,11 +37,11 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import org.rekotlin.Store +import timber.log.Timber // TODO: change to Android ViewModel [REDACTED_JIRA] internal class DetailsViewModel( private val store: Store, - private val darkThemeFeatureToggle: DarkThemeFeatureToggle, private val walletsRepository: WalletsRepository, ) { @@ -158,7 +158,15 @@ internal class DetailsViewModel( private fun linkMoreCards() { Analytics.send(Settings.ButtonCreateBackup()) - store.dispatchOnMain(WalletAction.MultiWallet.BackupWallet) + + val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard { + Timber.e("Unable to backup wallet, no user wallet selected") + return + } + val scanResponse = selectedUserWallet.scanResponse + Analytics.addContext(scanResponse) + store.dispatch(GlobalAction.Onboarding.Start(scanResponse, canSkipBackup = false)) + store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingWallet)) } private fun scanAndSaveUserWallet() { @@ -192,7 +200,6 @@ internal class DetailsViewModel( store.dispatchWithMain( DetailsAction.PrepareScreen( scanResponse = selectedUserWallet.scanResponse, - darkThemeSwitchEnabled = darkThemeFeatureToggle.isDarkThemeEnabled, shouldSaveUserWallets = walletsRepository.shouldSaveUserWalletsSync(), ), ) diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/QrScanFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/QrScanFragment.kt deleted file mode 100644 index 4ba97f4d51..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/QrScanFragment.kt +++ /dev/null @@ -1,108 +0,0 @@ -package com.tangem.tap.features.details.ui.walletconnect - -import android.Manifest -import android.content.pm.PackageManager -import android.os.Bundle -import android.view.* -import androidx.activity.OnBackPressedCallback -import androidx.camera.lifecycle.ProcessCameraProvider -import androidx.core.content.ContextCompat -import androidx.core.view.WindowCompat -import androidx.fragment.app.Fragment -import by.kirich1409.viewbindingdelegate.viewBinding -import com.google.common.util.concurrent.ListenableFuture -import com.otaliastudios.cameraview.CameraView -import com.tangem.core.navigation.NavigationAction -import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction -import com.tangem.tap.features.details.ui.walletconnect.dialogs.PreviewBinder -import com.tangem.tap.store -import com.tangem.wallet.R -import com.tangem.wallet.databinding.LayoutQrScanningBinding -import java.util.concurrent.ExecutorService -import java.util.concurrent.Executors - -internal class QrScanFragment : Fragment(R.layout.layout_qr_scanning) { - - private val binding: LayoutQrScanningBinding by viewBinding(LayoutQrScanningBinding::bind) - - private val binder = PreviewBinder() - - private var cameraProviderFuture: ListenableFuture? = null - private var cameraExecutor: ExecutorService? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setFitSystemWindows(fit = true) - activity?.onBackPressedDispatcher?.addCallback( - this, - object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - store.dispatch(NavigationAction.PopBackTo()) - } - }, - ) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - if (!permissionIsGranted()) requestPermission() - - cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext()) - cameraExecutor = Executors.newSingleThreadExecutor() - - cameraProviderFuture?.addListener( - { - val cameraProvider = cameraProviderFuture?.get() - binder.bindPreview( - context = requireContext(), - binding = binding, - lifecycleOwner = this, - cameraProvider = requireNotNull(cameraProvider), - cameraExecutor = requireNotNull(cameraExecutor), - onScanned = { result -> - store.dispatch(NavigationAction.PopBackTo()) - setFitSystemWindows(fit = false) - if (result.isNotBlank()) { - store.dispatch(WalletConnectAction.OpenSession(result)) - } - }, - ) - }, - ContextCompat.getMainExecutor(requireContext()), - ) - - binding.overlay.post { - binding.overlay.setViewFinder() - } - } - - override fun onDestroy() { - super.onDestroy() - setFitSystemWindows(fit = false) - } - - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { - if (requestCode != CameraView.PERMISSION_REQUEST_CODE) return - - if (grantResults.isEmpty() || grantResults[0] != PackageManager.PERMISSION_GRANTED) { - store.dispatch(WalletConnectAction.NotifyCameraPermissionIsRequired) - store.dispatch(NavigationAction.PopBackTo()) - } - } - - private fun permissionIsGranted(): Boolean { - val cameraPermission = ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA) - return cameraPermission == PackageManager.PERMISSION_GRANTED - } - - private fun requestPermission() { - requestPermissions(arrayOf(Manifest.permission.CAMERA), CameraView.PERMISSION_REQUEST_CODE) - } - - private fun setFitSystemWindows(fit: Boolean) { - activity?.window?.let { - WindowCompat.setDecorFitsSystemWindows(it, fit) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectFragment.kt index 6100201d98..1733d8d946 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectFragment.kt @@ -5,6 +5,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.Modifier +import androidx.fragment.app.viewModels import com.tangem.core.analytics.Analytics import com.tangem.core.navigation.NavigationAction import com.tangem.core.ui.screen.ComposeFragment @@ -23,22 +24,25 @@ internal class WalletConnectFragment : ComposeFragment(), StoreSubscriber = - mutableStateOf(viewModel.updateState(store.state.walletConnectState)) + private val viewModel: WalletConnectViewModel by viewModels() + + private var screenState: MutableState? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Analytics.send(WalletConnect.ScreenOpened()) + lifecycle.addObserver(viewModel) + screenState = mutableStateOf(viewModel.updateState(store.state.walletConnectState)) } @Composable override fun ScreenContent(modifier: Modifier) { + val state = screenState?.value ?: return WalletConnectScreen( modifier = modifier, - state = screenState.value, + state = state, onBackClick = { - if (screenState.value.isLoading) { + if (state.isLoading) { store.dispatch( WalletConnectAction.FailureEstablishingSession( store.state.walletConnectState.newSessionData?.session?.session, @@ -66,6 +70,6 @@ internal class WalletConnectFragment : ComposeFragment(), StoreSubscriber) { fun updateState(state: WalletConnectState): WalletConnectScreenState { Timber.d("WC2 Sessions: ${state.wc2Sessions}") val sessions = state.sessions.map { wcSession -> WcSessionForScreen.fromSession(wcSession) } + state.wc2Sessions diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/ClipboardOrScanQrDialog.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/ClipboardOrScanQrDialog.kt index 301435433b..d3f92e4286 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/ClipboardOrScanQrDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/ClipboardOrScanQrDialog.kt @@ -2,9 +2,12 @@ package com.tangem.tap.features.details.ui.walletconnect.dialogs import android.content.Context import androidx.appcompat.app.AlertDialog +import androidx.core.os.bundleOf import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction +import com.tangem.feature.qrscanning.QrScanningRouter +import com.tangem.feature.qrscanning.SourceType import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction import com.tangem.tap.store @@ -19,7 +22,14 @@ object ClipboardOrScanQrDialog { store.dispatch(WalletConnectAction.OpenSession(wcUri)) } setNegativeButton(context.getText(R.string.wallet_connect_scan_new_code)) { _, _ -> - store.dispatch(NavigationAction.NavigateTo(AppScreen.QrScan)) + store.dispatch( + NavigationAction.NavigateTo( + screen = AppScreen.QrScanning, + bundle = bundleOf( + QrScanningRouter.SOURCE_KEY to SourceType.WALLET_CONNECT, + ), + ), + ) } setOnDismissListener { store.dispatch(GlobalAction.HideDialog) diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/PreviewBinder.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/PreviewBinder.kt deleted file mode 100644 index b958759c58..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/PreviewBinder.kt +++ /dev/null @@ -1,69 +0,0 @@ -package com.tangem.tap.features.details.ui.walletconnect.dialogs - -import android.content.Context -import android.util.Size -import android.view.OrientationEventListener -import android.view.Surface -import androidx.camera.core.CameraSelector -import androidx.camera.core.ImageAnalysis -import androidx.camera.core.Preview -import androidx.camera.lifecycle.ProcessCameraProvider -import androidx.lifecycle.LifecycleOwner -import com.tangem.tap.common.qrCodeScan.MLKitBarcodeAnalyzer -import com.tangem.wallet.databinding.LayoutQrScanningBinding -import java.util.concurrent.ExecutorService - -internal class PreviewBinder { - - @Suppress("LongParameterList") - fun bindPreview( - context: Context, - binding: LayoutQrScanningBinding, - lifecycleOwner: LifecycleOwner, - cameraProvider: ProcessCameraProvider, - cameraExecutor: ExecutorService, - onScanned: (String) -> Unit, - ) { - cameraProvider.unbindAll() - - val preview: Preview = Preview.Builder() - .build() - - val imageAnalysis = ImageAnalysis.Builder() - .setTargetResolution(Size(binding.cameraPreview.width, binding.cameraPreview.height)) - .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) - .build() - - val orientationEventListener = object : OrientationEventListener(context) { - - @Suppress("MagicNumber") - override fun onOrientationChanged(orientation: Int) { - val rotation: Int = when (orientation) { - in 45..134 -> Surface.ROTATION_270 - in 135..224 -> Surface.ROTATION_180 - in 225..314 -> Surface.ROTATION_90 - else -> Surface.ROTATION_0 - } - - imageAnalysis.targetRotation = rotation - } - } - orientationEventListener.enable() - - val analyzer: ImageAnalysis.Analyzer = MLKitBarcodeAnalyzer { - imageAnalysis.clearAnalyzer() - onScanned.invoke(it) - } - - cameraExecutor.let { - imageAnalysis.setAnalyzer(it, analyzer) - } - - preview.setSurfaceProvider(binding.cameraPreview.surfaceProvider) - - val cameraSelector: CameraSelector = CameraSelector.Builder() - .requireLensFacing(CameraSelector.LENS_FACING_BACK) - .build() - cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, imageAnalysis, preview) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerAction.kt b/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerAction.kt index ded89f039b..52e3057f46 100644 --- a/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerAction.kt +++ b/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerAction.kt @@ -1,8 +1,8 @@ package com.tangem.tap.features.disclaimer.redux import com.tangem.core.navigation.AppScreen +import com.tangem.tap.common.entities.ProgressState import com.tangem.tap.features.disclaimer.Disclaimer -import com.tangem.tap.features.wallet.redux.ProgressState import org.rekotlin.Action sealed class DisclaimerAction : Action { diff --git a/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerState.kt b/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerState.kt index ff9ab50f31..8d37c385eb 100644 --- a/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerState.kt +++ b/app/src/main/java/com/tangem/tap/features/disclaimer/redux/DisclaimerState.kt @@ -2,9 +2,9 @@ package com.tangem.tap.features.disclaimer.redux import com.tangem.common.extensions.VoidCallback import com.tangem.core.navigation.AppScreen +import com.tangem.tap.common.entities.ProgressState import com.tangem.tap.features.disclaimer.Disclaimer import com.tangem.tap.features.disclaimer.DummyDisclaimer -import com.tangem.tap.features.wallet.redux.ProgressState import org.rekotlin.StateType data class DisclaimerState( diff --git a/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt b/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt index e2865c1aee..dc438d1872 100644 --- a/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerFragment.kt @@ -8,6 +8,7 @@ import androidx.transition.TransitionInflater import by.kirich1409.viewbindingdelegate.viewBinding import com.tangem.core.navigation.AppScreen import com.tangem.core.ui.extensions.setStatusBarColor +import com.tangem.tap.common.entities.ProgressState import com.tangem.tap.common.extensions.beginDelayedTransition import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show @@ -17,7 +18,6 @@ import com.tangem.tap.features.addBackPressHandler import com.tangem.tap.features.disclaimer.Disclaimer import com.tangem.tap.features.disclaimer.redux.DisclaimerAction import com.tangem.tap.features.disclaimer.redux.DisclaimerState -import com.tangem.tap.features.wallet.redux.ProgressState import com.tangem.tap.store import com.tangem.wallet.R import com.tangem.wallet.databinding.FragmentDisclaimerBinding diff --git a/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerWebViewClient.kt b/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerWebViewClient.kt index abc9d83f27..c64d0b7543 100644 --- a/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerWebViewClient.kt +++ b/app/src/main/java/com/tangem/tap/features/disclaimer/ui/DisclaimerWebViewClient.kt @@ -1,13 +1,9 @@ package com.tangem.tap.features.disclaimer.ui import android.graphics.Bitmap -import android.webkit.WebResourceError -import android.webkit.WebResourceRequest -import android.webkit.WebResourceResponse -import android.webkit.WebView -import android.webkit.WebViewClient +import android.webkit.* import com.tangem.common.extensions.ifNotNull -import com.tangem.tap.features.wallet.redux.ProgressState +import com.tangem.tap.common.entities.ProgressState class DisclaimerWebViewClient : WebViewClient() { 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 6e8db8a3d0..90bb65ce39 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 @@ -34,8 +34,6 @@ class HomeFragment : ComposeFragment(), StoreSubscriber { private var homeState: MutableState = mutableStateOf(store.state.homeState) - // private val learn2earnViewModel by activityViewModels() - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) store.dispatch(HomeAction.OnCreate) @@ -44,11 +42,6 @@ class HomeFragment : ComposeFragment(), StoreSubscriber { @Composable override fun ScreenContent(modifier: Modifier) { - // sync adding story before screen creation - // if (learn2earnViewModel.uiState.storyScreenState.isVisible) { - // store.dispatch(HomeAction.InsertStory(position = 0, Stories.OneInchPromo)) - // homeState.value = store.state.homeState - // } BackHandler(onBack = requireActivity()::finish) SystemBarsEffect { setSystemBarsColor(color = Color.Transparent, darkIcons = false) @@ -83,7 +76,6 @@ class HomeFragment : ComposeFragment(), StoreSubscriber { private fun ScreenContent() { StoriesScreen( homeState = homeState, - onLearn2earnClick = {}, // learn2earnViewModel.uiState.storyScreenState.onClick, onScanButtonClick = { Analytics.send(IntroductionProcess.ButtonScanCard()) store.dispatch(action = HomeAction.ReadCard(scope = requireActivity().lifecycleScope)) 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 ef3ab25517..fb4ae25b2b 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 @@ -1,10 +1,6 @@ package com.tangem.tap.features.home -import android.content.Context -import android.telephony.TelephonyManager -import android.telephony.TelephonyManager.PHONE_TYPE_CDMA import androidx.compose.ui.text.intl.Locale -import java.lang.ref.WeakReference /** [REDACTED_AUTHOR] @@ -13,39 +9,8 @@ interface RegionProvider { fun getRegion(): String? } -class RegionService( - private val providers: List, -) : RegionProvider { - override fun getRegion(): String? { - for (provider in providers) { - val region = provider.getRegion() - if (region != null) return region - } - return null - } -} - -class TelephonyManagerRegionProvider(context: Context) : RegionProvider { - - private val wContext: WeakReference = WeakReference(context) - - override fun getRegion(): String? { - val tm = wContext.get()?.getSystemService(Context.TELEPHONY_SERVICE) as? TelephonyManager ?: return null - - val region = when (tm.phoneType) { - PHONE_TYPE_CDMA -> { - // Result may be unreliable - tm.networkCountryIso - } - else -> tm.networkCountryIso - } - return region.ifEmpty { return null } - } -} - class LocaleRegionProvider : RegionProvider { override fun getRegion(): String = Locale.current.region } -const val RUSSIA_COUNTRY_CODE = "ru" -const val BELARUS_COUNTRY_CODE = "by" \ No newline at end of file +const val RUSSIA_COUNTRY_CODE = "ru" \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt b/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt index 456084062b..79a40b22a9 100644 --- a/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt @@ -20,7 +20,6 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider import com.tangem.core.ui.res.TangemTheme -import com.tangem.feature.learn2earn.presentation.ui.Learn2earnStoriesScreen import com.tangem.tap.features.home.compose.content.* import com.tangem.tap.features.home.compose.views.HomeButtons import com.tangem.tap.features.home.compose.views.SearchCurrenciesButton @@ -33,7 +32,6 @@ import kotlin.math.max @Composable fun StoriesScreen( homeState: MutableState, - onLearn2earnClick: () -> Unit, onScanButtonClick: () -> Unit, onShopButtonClick: () -> Unit, onSearchTokensClick: () -> Unit, @@ -65,7 +63,6 @@ fun StoriesScreen( isScanInProgress = homeState.value.scanInProgress, onGoToPreviousStory = goToPreviousStory, onGoToNextStory = goToNextStory, - onLearn2earnClick = onLearn2earnClick, onSearchTokensClick = onSearchTokensClick, onScanButtonClick = onScanButtonClick, onShopButtonClick = onShopButtonClick, @@ -152,7 +149,6 @@ private fun StoriesScreenContent(config: StoriesScreenContentConfig, modifier: M .align(Alignment.Start), ) when (config.currentStory) { - Stories.OneInchPromo -> Learn2earnStoriesScreen(config.onLearn2earnClick) Stories.TangemIntro -> FirstStoriesContent( isPaused = isPaused, duration = currentStoryDuration, @@ -187,18 +183,12 @@ private fun StoriesScreenContent(config: StoriesScreenContentConfig, modifier: M ) } - AnimatedVisibility( - visible = config.currentStory != Stories.OneInchPromo, - enter = fadeIn(), - exit = fadeOut(), - ) { - HomeButtons( - modifier = Modifier.fillMaxWidth(), - btnScanStateInProgress = config.isScanInProgress, - onScanButtonClick = config.onScanButtonClick, - onShopButtonClick = config.onShopButtonClick, - ) - } + HomeButtons( + modifier = Modifier.fillMaxWidth(), + btnScanStateInProgress = config.isScanInProgress, + onScanButtonClick = config.onScanButtonClick, + onShopButtonClick = config.onShopButtonClick, + ) } } } @@ -210,7 +200,6 @@ private data class StoriesScreenContentConfig( val isScanInProgress: Boolean, val onGoToPreviousStory: () -> Unit = {}, val onGoToNextStory: () -> Unit = {}, - val onLearn2earnClick: () -> Unit = {}, val onSearchTokensClick: () -> Unit = {}, val onScanButtonClick: () -> Unit = {}, val onShopButtonClick: () -> Unit = {}, @@ -265,12 +254,6 @@ private class StoriesScreenContentConfigProvider : CollectionPreviewParameterPro currentStory = Stories.WalletForEveryone, isScanInProgress = false, ), - StoriesScreenContentConfig( - storiesSize = 6, - currentStoryIndex = 6, - currentStory = Stories.OneInchPromo, - isScanInProgress = false, - ), ), ) // endregion Preview \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt index 4b83396c14..e87ba1ee99 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeAction.kt @@ -1,8 +1,8 @@ package com.tangem.tap.features.home.redux import com.tangem.core.analytics.models.AnalyticsEvent -import com.tangem.tap.common.analytics.events.AnalyticsParam -import com.tangem.tap.common.analytics.events.Basic +import com.tangem.core.analytics.models.AnalyticsParam +import com.tangem.core.analytics.models.Basic import com.tangem.tap.common.entities.IndeterminateProgressButton import kotlinx.coroutines.CoroutineScope import org.rekotlin.Action 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 e13c8cff2e..d1210fe911 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 @@ -10,7 +10,7 @@ import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.userwallets.UserWalletBuilder -import com.tangem.tap.common.analytics.events.Basic +import com.tangem.core.analytics.models.Basic import com.tangem.tap.common.analytics.events.IntroductionProcess import com.tangem.tap.common.analytics.events.Shop import com.tangem.tap.common.entities.IndeterminateProgressButton @@ -37,7 +37,6 @@ import timber.log.Timber object HomeMiddleware { val handler = homeMiddleware - const val BUY_WALLET_URL = "https://tangem.com/ru/resellers/" const val NEW_BUY_WALLET_URL = "https://buy.tangem.com/" } diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt index d2674c5f2a..7ffd5d139e 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeState.kt @@ -4,7 +4,6 @@ import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import com.tangem.tap.common.entities.IndeterminateProgressButton import com.tangem.tap.features.send.redux.states.ButtonState -import com.tangem.tap.features.wallet.redux.ProgressState import org.rekotlin.StateType import java.util.Locale @@ -17,9 +16,6 @@ data class HomeState( val firstStory: Stories get() = stories[0] - val btnScanStateInProgress: Boolean - get() = btnScanState.progressState == ProgressState.Loading - fun stepOf(story: Stories): Int = stories.indexOf(story) fun onCountryCodeUpdate(homeState: HomeState, countryCode: String) { @@ -52,7 +48,6 @@ sealed class Stories( val duration: Int, val isNewWalletAvailable: MutableState = mutableStateOf(HomeState.isNewWalletAvailableInit()), ) { - object OneInchPromo : Stories(duration = 8000) object TangemIntro : Stories(duration = 6000) object RevolutionaryWallet : Stories(duration = 6000) object UltraSecureBackup : Stories(duration = 6000) diff --git a/app/src/main/java/com/tangem/tap/features/intentHandler/IntentProcessor.kt b/app/src/main/java/com/tangem/tap/features/intentHandler/IntentProcessor.kt index 47cf6a31fd..696827a0e3 100644 --- a/app/src/main/java/com/tangem/tap/features/intentHandler/IntentProcessor.kt +++ b/app/src/main/java/com/tangem/tap/features/intentHandler/IntentProcessor.kt @@ -15,15 +15,11 @@ class IntentProcessor { intentHandlers.add(handler) } - fun removeIntentHandler(handler: IntentHandler) { - intentHandlers.remove(handler) - } - fun removeAll() { intentHandlers.clear() } - suspend fun handleIntent(intent: Intent?) { + fun handleIntent(intent: Intent?) { intentHandlers.forEach { it.handleIntent(intent) } diff --git a/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/BuyCurrencyIntentHandler.kt b/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/BuyCurrencyIntentHandler.kt index e0c509b3ff..31a7657c79 100644 --- a/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/BuyCurrencyIntentHandler.kt +++ b/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/BuyCurrencyIntentHandler.kt @@ -1,13 +1,7 @@ package com.tangem.tap.features.intentHandler.handlers import android.content.Intent -import android.net.Uri -import com.tangem.core.analytics.Analytics -import com.tangem.domain.tokens.models.analytics.TokenScreenAnalyticsEvent -import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.features.intentHandler.IntentHandler -import com.tangem.tap.network.exchangeServices.ExchangeUrlBuilder -import com.tangem.tap.store /** [REDACTED_AUTHOR] @@ -15,16 +9,19 @@ import com.tangem.tap.store class BuyCurrencyIntentHandler : IntentHandler { override fun handleIntent(intent: Intent?): Boolean { - val data = intent?.data ?: return false - val currency = store.state.walletState.selectedCurrency ?: return false + // FIXME: [REDACTED_JIRA] + // val data = intent?.data ?: return false + // val currency = store.state.walletState.selectedCurrency ?: return false + // + // val successUri = Uri.parse(ExchangeUrlBuilder.SUCCESS_URL) + // return if (data.host == successUri.host && data.authority == successUri.authority) { + // val currencyType = AnalyticsParam.CurrencyType.Currency(currency) + // Analytics.send(TokenScreenAnalyticsEvent.Bought(currencyType.value)) + // true + // } else { + // false + // } - val successUri = Uri.parse(ExchangeUrlBuilder.SUCCESS_URL) - return if (data.host == successUri.host && data.authority == successUri.authority) { - val currencyType = AnalyticsParam.CurrencyType.Currency(currency) - Analytics.send(TokenScreenAnalyticsEvent.Bought(currencyType.value)) - true - } else { - false - } + return false } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/SellCurrencyIntentHandler.kt b/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/SellCurrencyIntentHandler.kt index da1c6cc68a..52e8e12a33 100644 --- a/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/SellCurrencyIntentHandler.kt +++ b/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/SellCurrencyIntentHandler.kt @@ -1,11 +1,7 @@ package com.tangem.tap.features.intentHandler.handlers import android.content.Intent -import com.tangem.domain.tokens.legacy.TradeCryptoAction -import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.features.intentHandler.IntentHandler -import com.tangem.tap.store -import timber.log.Timber /** [REDACTED_AUTHOR] @@ -13,33 +9,36 @@ import timber.log.Timber class SellCurrencyIntentHandler : IntentHandler { override fun handleIntent(intent: Intent?): Boolean { - return try { - val intentData = intent?.data ?: return false - val transactionID = intentData.getQueryParameter(TRANSACTION_ID_PARAM) ?: return false - val currency = intentData.getQueryParameter(CURRENCY_CODE_PARAM) ?: return false - val amount = intentData.getQueryParameter(CURRENCY_AMOUNT_PARAM) ?: return false - val destinationAddress = intentData.getQueryParameter(DEPOSIT_WALLET_ADDRESS_PARAM) ?: return false + // FIXME: [REDACTED_JIRA] + // return try { + // val intentData = intent?.data ?: return false + // val transactionID = intentData.getQueryParameter(TRANSACTION_ID_PARAM) ?: return false + // val currency = intentData.getQueryParameter(CURRENCY_CODE_PARAM) ?: return false + // val amount = intentData.getQueryParameter(CURRENCY_AMOUNT_PARAM) ?: return false + // val destinationAddress = intentData.getQueryParameter(DEPOSIT_WALLET_ADDRESS_PARAM) ?: return false + // + // Timber.d("MoonPay Sell: $amount $currency to $destinationAddress") + // store.dispatchOnMain( + // TradeCryptoAction.SendCrypto( + // currencyId = currency, + // amount = amount, + // destinationAddress = destinationAddress, + // transactionId = transactionID, + // ), + // ) + // true + // } catch (exception: Exception) { + // Timber.d("Not MoonPay URL") + // false + // } - Timber.d("MoonPay Sell: $amount $currency to $destinationAddress") - store.dispatchOnMain( - TradeCryptoAction.SendCrypto( - currencyId = currency, - amount = amount, - destinationAddress = destinationAddress, - transactionId = transactionID, - ), - ) - true - } catch (exception: Exception) { - Timber.d("Not MoonPay URL") - false - } + return false } - private companion object { - private const val TRANSACTION_ID_PARAM = "transactionId" - private const val CURRENCY_CODE_PARAM = "baseCurrencyCode" - private const val CURRENCY_AMOUNT_PARAM = "baseCurrencyAmount" - private const val DEPOSIT_WALLET_ADDRESS_PARAM = "depositWalletAddress" - } + // private companion object { + // private const val TRANSACTION_ID_PARAM = "transactionId" + // private const val CURRENCY_CODE_PARAM = "baseCurrencyCode" + // private const val CURRENCY_AMOUNT_PARAM = "baseCurrencyAmount" + // private const val DEPOSIT_WALLET_ADDRESS_PARAM = "depositWalletAddress" + // } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt index 2f455c7eae..015bf0d5d6 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt @@ -14,7 +14,7 @@ import com.tangem.domain.userwallets.UserWalletBuilder import com.tangem.domain.userwallets.UserWalletIdBuilder import com.tangem.tap.* import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter -import com.tangem.tap.common.analytics.events.Basic +import com.tangem.core.analytics.models.Basic import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.onUserWalletSelected import com.tangem.tap.common.extensions.removeContext @@ -54,7 +54,7 @@ object OnboardingHelper { } fun whereToNavigate(scanResponse: ScanResponse): AppScreen { - return when (scanResponse.productType) { + return when (val type = scanResponse.productType) { ProductType.Note -> AppScreen.OnboardingNote ProductType.Wallet, ProductType.Wallet2, @@ -65,9 +65,9 @@ object OnboardingHelper { AppScreen.OnboardingOther } ProductType.Twins -> AppScreen.OnboardingTwins - ProductType.Start2Coin -> throw java.lang.UnsupportedOperationException( - "Onboarding for Start2Coin cards is not supported", - ) + ProductType.Start2Coin, + ProductType.Visa, + -> throw UnsupportedOperationException("Onboarding for ${type.name} cards is not supported") } } @@ -125,7 +125,7 @@ object OnboardingHelper { val currency = ParamCardCurrencyConverter().convert(scanResponse.cardTypesResolver) if (userWalletId != null && currency != null) { - Analytics.send(Basic.ToppedUp(userWalletId, currency)) + Analytics.send(Basic.ToppedUp(userWalletId.stringValue, currency)) } } 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 c7dfdae94d..467d4d73a5 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 @@ -5,17 +5,17 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.WalletManager import com.tangem.common.extensions.isZero import com.tangem.common.services.Result +import com.tangem.data.source.preferences.storage.UsedCardsPrefStorage import com.tangem.domain.models.scan.ScanResponse import com.tangem.operations.attestation.CardVerifyAndGetInfo import com.tangem.operations.attestation.OnlineCardVerifier +import com.tangem.tap.common.entities.ProgressState import com.tangem.tap.common.extensions.isPositive import com.tangem.tap.common.extensions.safeUpdate import com.tangem.tap.domain.TapError import com.tangem.tap.domain.extensions.getOrLoadCardArtworkUrl -import com.tangem.tap.features.wallet.models.Currency -import com.tangem.tap.features.wallet.models.hasPendingTransactions -import com.tangem.tap.features.wallet.redux.ProgressState -import com.tangem.data.source.preferences.storage.UsedCardsPrefStorage +import com.tangem.tap.domain.model.Currency +import com.tangem.tap.domain.model.hasPendingTransactions import com.tangem.tap.features.demo.isDemoCard import timber.log.Timber import java.math.BigDecimal @@ -25,11 +25,10 @@ import java.math.BigDecimal */ class OnboardingManager( var scanResponse: ScanResponse, - val usedCardsPrefStorage: UsedCardsPrefStorage, + private val usedCardsPrefStorage: UsedCardsPrefStorage, ) { - var cardInfo: Result? = null - private set + private var cardInfo: Result? = null suspend fun loadArtworkUrl(): String { val cardInfo = cardInfo @@ -85,10 +84,6 @@ class OnboardingManager( usedCardsPrefStorage.activationFinished(cardId) } - fun isActivationFinished(cardId: String): Boolean { - return usedCardsPrefStorage.isActivationFinished(cardId) - } - fun isActivationStarted(cardId: String): Boolean { return usedCardsPrefStorage.isActivationStarted(cardId) } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt index 266b627b65..cf01d56658 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt @@ -8,20 +8,18 @@ import com.tangem.domain.common.extensions.makePrimaryWalletManager import com.tangem.domain.common.extensions.withMainContext import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Onboarding +import com.tangem.tap.common.entities.ProgressState import com.tangem.tap.common.extensions.* import com.tangem.tap.common.postUi import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.TapError +import com.tangem.tap.domain.model.Currency import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE import com.tangem.tap.features.onboarding.OnboardingDialog import com.tangem.tap.features.onboarding.OnboardingHelper -import com.tangem.tap.features.wallet.models.Currency -import com.tangem.tap.features.wallet.redux.ProgressState -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.features.wallet.redux.models.WalletDialog import com.tangem.tap.scope import com.tangem.tap.store import com.tangem.tap.tangemSdkManager @@ -104,7 +102,6 @@ private fun handleNoteAction(appState: () -> AppState?, action: Action, dispatch val updatedResponse = scanResponse.copy(card = result.data.card) onboardingManager.scanResponse = updatedResponse onboardingManager.activationStarted(updatedResponse.card.cardId) - store.state.globalState.topUpController?.registerEmptyWallet(updatedResponse) store.dispatch(OnboardingNoteAction.SetStepOfScreen(OnboardingNoteStep.TopUpWallet)) } is CompletionResult.Failure -> Unit @@ -153,8 +150,6 @@ private fun handleNoteAction(appState: () -> AppState?, action: Action, dispatch is OnboardingNoteAction.Balance.Set -> { if (action.balance.balanceIsToppedUp()) { OnboardingHelper.sendToppedUpEvent(scanResponse) - - store.state.globalState.topUpController?.send(scanResponse, AnalyticsParam.CardBalanceState.Full) store.dispatch(OnboardingNoteAction.SetStepOfScreen(OnboardingNoteStep.Done)) } } @@ -178,8 +173,8 @@ private fun handleNoteAction(appState: () -> AppState?, action: Action, dispatch Analytics.send(Onboarding.Topup.ButtonBuyCrypto(currencyType)) if (globalState.userCountryCode == RUSSIA_COUNTRY_CODE) { - val dialogData = WalletDialog.RussianCardholdersWarningDialog.Data(topUpUrl) - store.dispatchOnMain(WalletAction.DialogAction.RussianCardholdersWarningDialog(dialogData)) + val dialogData = AppDialog.RussianCardholdersWarningDialog.Data(topUpUrl) + store.dispatchDialogShow(AppDialog.RussianCardholdersWarningDialog(dialogData)) } else { store.dispatchOpenUrl(topUpUrl) } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt index faf34114d6..3ee9db75ea 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt @@ -1,22 +1,16 @@ package com.tangem.tap.features.onboarding.products.otherCards.redux -import com.tangem.blockchain.common.Blockchain import com.tangem.common.CompletionResult import com.tangem.core.analytics.Analytics -import com.tangem.domain.common.BlockchainNetwork import com.tangem.domain.common.extensions.withMainContext -import com.tangem.domain.common.util.cardTypesResolver -import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.tap.common.analytics.events.Onboarding import com.tangem.tap.common.postUi import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.features.onboarding.OnboardingHelper -import com.tangem.tap.features.wallet.models.toCurrencies import com.tangem.tap.scope import com.tangem.tap.store import com.tangem.tap.tangemSdkManager -import com.tangem.tap.userTokensRepository import com.tangem.utils.extensions.DELAY_SDK_DIALOG_CLOSE import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -89,40 +83,6 @@ private fun handleOtherCardsAction(action: Action) { val updatedCard = updatedResponse.card onboardingManager.scanResponse = updatedResponse onboardingManager.activationStarted(updatedCard.cardId) - store.state.globalState.topUpController?.registerEmptyWallet(updatedResponse) - - val primaryBlockchain = updatedResponse.cardTypesResolver.getBlockchain() - val blockchainNetworks = if (primaryBlockchain != Blockchain.Unknown) { - val primaryToken = updatedResponse.cardTypesResolver.getPrimaryToken() - val blockchainNetwork = - BlockchainNetwork( - blockchain = primaryBlockchain, - derivationStyleProvider = updatedResponse.derivationStyleProvider, - ) - .updateTokens( - listOfNotNull(primaryToken), - ) - listOf(blockchainNetwork) - } else { - listOf( - BlockchainNetwork( - blockchain = Blockchain.Bitcoin, - derivationStyleProvider = updatedResponse.derivationStyleProvider, - ), - BlockchainNetwork( - blockchain = Blockchain.Ethereum, - derivationStyleProvider = updatedResponse.derivationStyleProvider, - ), - ) - } - - scope.launch { - // TODO: Use new repo [REDACTED_JIRA] - userTokensRepository.saveUserTokens( - card = result.data.card, - tokens = blockchainNetworks.toCurrencies(), - ) - } delay(DELAY_SDK_DIALOG_CLOSE) store.dispatch(OnboardingOtherCardsAction.SetStepOfScreen(OnboardingOtherCardsStep.Done)) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt index 31072fcd23..fafb9a473f 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt @@ -14,20 +14,18 @@ import com.tangem.domain.userwallets.UserWalletIdBuilder import com.tangem.domain.wallets.legacy.isLockedSync import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Onboarding +import com.tangem.tap.common.entities.ProgressState import com.tangem.tap.common.extensions.* import com.tangem.tap.common.postUi import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.TapError +import com.tangem.tap.domain.model.Currency import com.tangem.tap.domain.twins.TwinCardsManager import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE import com.tangem.tap.features.onboarding.OnboardingDialog import com.tangem.tap.features.onboarding.OnboardingHelper -import com.tangem.tap.features.wallet.models.Currency -import com.tangem.tap.features.wallet.redux.ProgressState -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.features.wallet.redux.models.WalletDialog import com.tangem.tap.preferencesStorage import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.scope @@ -202,7 +200,6 @@ private fun handle(action: Action, dispatch: DispatchFunction) { is Result.Success -> { Analytics.send(Onboarding.Twins.SetupFinished()) updateScanResponse(result.data) - store.state.globalState.topUpController?.registerEmptyWallet(result.data) delay(DELAY_SDK_DIALOG_CLOSE) withMainContext { @@ -261,8 +258,6 @@ private fun handle(action: Action, dispatch: DispatchFunction) { is TwinCardsAction.Balance.Set -> { if (action.balance.balanceIsToppedUp()) { OnboardingHelper.sendToppedUpEvent(getScanResponse()) - - store.state.globalState.topUpController?.send(getScanResponse(), AnalyticsParam.CardBalanceState.Full) store.dispatchOnMain(TwinCardsAction.SetStepOfScreen(TwinCardsStep.Done)) } } @@ -285,8 +280,8 @@ private fun handle(action: Action, dispatch: DispatchFunction) { Analytics.send(Onboarding.Topup.ButtonBuyCrypto(currencyType)) if (globalState.userCountryCode == RUSSIA_COUNTRY_CODE) { - val dialogData = WalletDialog.RussianCardholdersWarningDialog.Data(topUpUrl) - store.dispatchOnMain(WalletAction.DialogAction.RussianCardholdersWarningDialog(dialogData)) + val dialogData = AppDialog.RussianCardholdersWarningDialog.Data(topUpUrl) + store.dispatchDialogShow(AppDialog.RussianCardholdersWarningDialog(dialogData)) } else { store.dispatchOpenUrl(topUpUrl) } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/TwinsCardsFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/TwinsCardsFragment.kt index 70c389c322..e07b4ab7f3 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/TwinsCardsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/TwinsCardsFragment.kt @@ -207,7 +207,7 @@ class TwinsCardsFragment : BaseOnboardingFragment() { tvHeader.setText(R.string.common_warning) tvBody.setText(R.string.twins_recreate_warning) - chbUnderstand.setOnCheckedChangeListener { buttonView, isChecked -> + chbUnderstand.setOnCheckedChangeListener { _, isChecked -> store.dispatch(TwinCardsAction.SetUserUnderstand(isChecked)) } btnMainAction.isEnabled = state.userWasUnderstandIfWalletRecreate 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 4a6035d3ae..bae2e62550 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 @@ -1,7 +1,6 @@ package com.tangem.tap.features.onboarding.products.wallet.redux import android.net.Uri -import com.tangem.blockchain.common.Blockchain import com.tangem.common.CompletionResult import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.guard @@ -11,11 +10,9 @@ import com.tangem.common.services.Result import com.tangem.core.analytics.Analytics import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction -import com.tangem.domain.common.BlockchainNetwork import com.tangem.domain.common.TapWorkarounds.canSkipBackup import com.tangem.domain.common.extensions.withMainContext import com.tangem.domain.common.util.cardTypesResolver -import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.userwallets.Artwork @@ -36,7 +33,6 @@ import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.onboarding.OnboardingDialog import com.tangem.tap.features.onboarding.OnboardingHelper -import com.tangem.tap.features.wallet.models.toCurrencies import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.wallet.R import kotlinx.coroutines.launch @@ -142,26 +138,7 @@ private fun handleWalletAction(action: Action) { primaryCard = result.data.primaryCard, ) onboardingManager.scanResponse = updatedResponse - store.state.globalState.topUpController?.registerEmptyWallet(updatedResponse) - val blockchainNetworks = if (DemoHelper.isDemoCardId(result.data.card.cardId)) { - DemoHelper.config.demoBlockchains - } else { - listOf(Blockchain.Bitcoin, Blockchain.Ethereum) - }.map { blockchain -> - BlockchainNetwork( - blockchain = blockchain, - derivationStyleProvider = updatedResponse.derivationStyleProvider, - ) - } - - scope.launch { - // TODO: Use new repo [REDACTED_JIRA] - userTokensRepository.saveUserTokens( - card = result.data.card, - tokens = blockchainNetworks.toCurrencies(), - ) - } startCardActivation(updatedResponse) store.dispatch(OnboardingWalletAction.ResumeBackup) } @@ -396,17 +373,22 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) } is CompletionResult.Failure -> { - val error = result.error - if (error is TangemSdkError.BackupFailedNotEmptyWallets && - // todo disabled this check in task with shiba ([REDACTED_TASK_KEY]) and added canSkipBackup - // && onboardingWalletState.wallet2State != null - card?.canSkipBackup == false - ) { - store.dispatchOnMain( - GlobalAction.ShowDialog( - BackupDialog.ResetBackupCard(error.cardId), - ), - ) + when (val error = result.error) { + is TangemSdkError.BackupFailedNotEmptyWallets -> { + if (card?.canSkipBackup == false) { + store.dispatchOnMain( + GlobalAction.ShowDialog( + BackupDialog.ResetBackupCard(error.cardId), + ), + ) + } + } + is TangemSdkError.IssuerSignatureLoadingFailed -> { + store.dispatchOnMain( + GlobalAction.ShowDialog(BackupDialog.AttestationFailed), + ) + } + else -> Unit } } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt index 757196999f..508e40db10 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletState.kt @@ -90,6 +90,7 @@ sealed class BackupStep { } sealed class BackupDialog : StateDialog { + object AttestationFailed : BackupDialog() object AddMoreBackupCards : BackupDialog() object BackupInProgress : BackupDialog() object UnfinishedBackupFound : BackupDialog() diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/BackupAnimator.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/BackupAnimator.kt index 94de063a63..0927376b54 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/BackupAnimator.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/BackupAnimator.kt @@ -250,7 +250,7 @@ class TestBackupAnimation( } @Suppress("MagicNumber") - fun setStep(step: Int, onStepUpdate: (Int) -> Unit = {}) { + private fun setStep(step: Int, onStepUpdate: (Int) -> Unit = {}) { steps = step when (steps) { 0 -> setupCreateWalletState() 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 67a15ded35..fa12a0f3d5 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 @@ -140,7 +140,7 @@ class OnboardingWalletFragment : } private fun initCardsWidget(leapfrogWidget: LeapfrogWidget, deviceScaleFactor: Float, isTest: Boolean = false) { - cardsWidget = WalletCardsWidget(leapfrogWidget, deviceScaleFactor) { 200f * deviceScaleFactor } + cardsWidget = WalletCardsWidget(leapfrogWidget, deviceScaleFactor) animator = if (isTest) { TestBackupAnimation(WalletBackupAnimator(cardsWidget), binding) } else { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/WalletCardsWidget.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/WalletCardsWidget.kt index 8f0bc94325..4a06365222 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/WalletCardsWidget.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/WalletCardsWidget.kt @@ -7,19 +7,16 @@ import android.view.View import android.widget.ImageView import androidx.core.animation.doOnEnd import com.tangem.sdk.ui.widget.leapfrogWidget.LeapView -import com.tangem.sdk.ui.widget.leapfrogWidget.LeapViewState import com.tangem.sdk.ui.widget.leapfrogWidget.LeapfrogWidget class WalletCardsWidget( val leapfrogWidget: LeapfrogWidget, private val deviceScaleFactor: Float = 1f, - val getTopOfAnchorViewForActivateState: () -> Float, ) { private val animDuration: Long = 400 - var currentState: WidgetState? = null - private set + private var currentState: WidgetState? = null fun toWelcome(animate: Boolean = true, onEnd: () -> Unit = {}) { if (currentState == WidgetState.WELCOME) return @@ -239,15 +236,5 @@ private data class CardProperties( ) } - companion object { - fun from(leapViewState: LeapViewState): CardProperties { - val leapViewProperties = leapViewState.properties - - return CardProperties( - yTranslation = leapViewProperties.yTranslation, - elevation = leapViewProperties.elevationEnd, - scale = leapViewProperties.scale, - ) - } - } + companion object } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/AttestationFailedDialog.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/AttestationFailedDialog.kt new file mode 100644 index 0000000000..ef9363f8a0 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/dialogs/AttestationFailedDialog.kt @@ -0,0 +1,24 @@ +package com.tangem.tap.features.onboarding.products.wallet.ui.dialogs + +import android.content.Context +import androidx.appcompat.app.AlertDialog +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.tangem.tap.common.redux.global.GlobalAction +import com.tangem.tap.store +import com.tangem.wallet.R + +internal object AttestationFailedDialog { + + fun create(context: Context): AlertDialog { + return MaterialAlertDialogBuilder(context, R.style.CustomMaterialDialog).apply { + setTitle(R.string.attestation_online_failed_title) + setMessage(R.string.attestation_online_failed_body) + setPositiveButton(R.string.ok) { dialog, _ -> + dialog.dismiss() + } + setOnDismissListener { + store.dispatch(GlobalAction.HideDialog) + } + }.create() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt index 1a1bdd7dd9..92d7685a73 100644 --- a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt @@ -21,7 +21,6 @@ import com.tangem.tap.common.extensions.dispatchWithMain import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.userWalletList.di.provideBiometricImplementation -import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.proxy.redux.DaggerGraphState import kotlinx.coroutines.launch import org.rekotlin.Middleware @@ -119,16 +118,8 @@ internal class SaveWalletMiddleware { ) } - val savedUserWallet = userWalletsListManager.selectedUserWalletSync.guard { - Timber.e("User wallet is not saved") - return@launch - } store.dispatchWithMain(SaveWalletAction.Save.Success) store.dispatchWithMain(NavigationAction.PopBackTo(AppScreen.Wallet)) - store.dispatchWithMain(WalletAction.UpdateCanSaveUserWallets(canSaveUserWallets = true)) - store.dispatchWithMain( - action = WalletAction.MultiWallet.CheckForBackupWarning(savedUserWallet.scanResponse.card), - ) } } } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt index 0dfd830ce1..9fba907beb 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt @@ -11,17 +11,15 @@ import com.tangem.blockchain.common.* import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.extensions.SimpleResult import com.tangem.common.core.TangemSdkError -import com.tangem.common.extensions.guard import com.tangem.core.analytics.Analytics +import com.tangem.core.analytics.models.AnalyticsParam +import com.tangem.core.analytics.models.Basic import com.tangem.core.navigation.NavigationAction import com.tangem.domain.common.TapWorkarounds.isStart2Coin import com.tangem.domain.common.extensions.minimalAmount import com.tangem.domain.common.extensions.withMainContext import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.tokens.legacy.TradeCryptoAction -import com.tangem.tap.common.analytics.events.AnalyticsParam -import com.tangem.tap.common.analytics.events.Basic -import com.tangem.tap.common.analytics.events.Basic.TransactionSent.MemoType import com.tangem.tap.common.analytics.events.Token import com.tangem.tap.common.analytics.events.Token.Send.SelectedCurrency.CurrencyType import com.tangem.tap.common.extensions.dispatchDialogShow @@ -39,20 +37,15 @@ import com.tangem.tap.features.demo.isDemoCard import com.tangem.tap.features.send.redux.* import com.tangem.tap.features.send.redux.FeeAction.RequestFee import com.tangem.tap.features.send.redux.states.* -import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.scope import com.tangem.tap.store -import com.tangem.tap.userWalletsListManager -import com.tangem.tap.walletCurrenciesManager import com.tangem.utils.extensions.DELAY_SDK_DIALOG_CLOSE import com.tangem.wallet.R -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.rekotlin.Action import org.rekotlin.Middleware -import timber.log.Timber import java.util.EnumSet /** @@ -277,9 +270,6 @@ private fun sendTransaction( Analytics.sendSelectedCurrencyEvent(mainCurrencyType) dispatch(NavigationAction.PopBackTo()) } - scope.launch(Dispatchers.IO) { - updateAfterTransaction(walletManager) - } } is SimpleResult.Failure -> { updateFeedbackManagerInfo( @@ -349,11 +339,11 @@ private fun sendTransaction( } } -private fun getMemoType(transactionExtras: TransactionExtrasState): MemoType { +private fun getMemoType(transactionExtras: TransactionExtrasState): Basic.TransactionSent.MemoType { return when { - transactionExtras.isEmpty() -> MemoType.Empty - transactionExtras.isNull() -> MemoType.Null - else -> MemoType.Full + transactionExtras.isEmpty() -> Basic.TransactionSent.MemoType.Empty + transactionExtras.isNull() -> Basic.TransactionSent.MemoType.Null + else -> Basic.TransactionSent.MemoType.Full } } @@ -418,32 +408,4 @@ private fun updateWarnings(dispatch: (Action) -> Unit) { val warnings = warningsManager.getWarnings(WarningMessage.Location.SendScreen, listOf(blockchain)) dispatch(SendAction.Warnings.Set(warnings)) -} - -private suspend fun updateAfterTransaction(walletManager: WalletManager) { - val walletFeatureToggles = store.state.daggerGraphState.get(DaggerGraphState::walletFeatureToggles) - if (!walletFeatureToggles.isRedesignedScreenEnabled) { - updateWalletsLegacy(walletManager) - } -} - -private suspend fun updateWalletsLegacy(walletManager: WalletManager) { - updateWallet(walletManager) - delay(timeMillis = 11000) // more than 10000 to avoid throttling - updateWallet(walletManager) -} - -private suspend fun updateWallet(walletManager: WalletManager) { - val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard { - Timber.e("Unable to update wallet, no user wallet selected") - return - } - val wallet = walletManager.wallet - walletCurrenciesManager.update( - userWallet = selectedUserWallet, - currency = Currency.Blockchain( - blockchain = wallet.blockchain, - derivationPath = wallet.publicKey.derivationPath?.rawPath, - ), - ) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/FeeReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/FeeReducer.kt index 7e2e65171e..e4ac2c2376 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/FeeReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/FeeReducer.kt @@ -2,13 +2,13 @@ package com.tangem.tap.features.send.redux.reducers import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee +import com.tangem.tap.common.entities.ProgressState import com.tangem.tap.features.send.redux.FeeAction import com.tangem.tap.features.send.redux.FeeActionUi import com.tangem.tap.features.send.redux.SendScreenAction import com.tangem.tap.features.send.redux.states.FeeState import com.tangem.tap.features.send.redux.states.FeeType import com.tangem.tap.features.send.redux.states.SendState -import com.tangem.tap.features.wallet.redux.ProgressState /** [REDACTED_AUTHOR] diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt index ebfa2a391a..7d2d583f2f 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt @@ -2,23 +2,12 @@ package com.tangem.tap.features.send.redux.reducers import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.Wallet +import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.tap.common.extensions.scaleToFiat import com.tangem.tap.common.extensions.stripZeroPlainString import com.tangem.tap.features.send.redux.ReceiptAction.RefreshReceipt import com.tangem.tap.features.send.redux.SendScreenAction -import com.tangem.tap.features.send.redux.states.AmountState -import com.tangem.tap.features.send.redux.states.FeeState -import com.tangem.tap.features.send.redux.states.MainCurrencyType -import com.tangem.tap.features.send.redux.states.ReceiptCrypto -import com.tangem.tap.features.send.redux.states.ReceiptFiat -import com.tangem.tap.features.send.redux.states.ReceiptLayoutType -import com.tangem.tap.features.send.redux.states.ReceiptState -import com.tangem.tap.features.send.redux.states.ReceiptSymbols -import com.tangem.tap.features.send.redux.states.ReceiptTokenCrypto -import com.tangem.tap.features.send.redux.states.ReceiptTokenFiat -import com.tangem.tap.features.send.redux.states.SendState -import com.tangem.tap.features.wallet.redux.utils.CAN_BE_LOWER_SIGN -import com.tangem.tap.features.wallet.redux.utils.UNKNOWN_AMOUNT_SIGN +import com.tangem.tap.features.send.redux.states.* import com.tangem.tap.store import java.math.BigDecimal @@ -143,9 +132,9 @@ class ReceiptReducer : SendInternalReducer { ) } else { ReceiptTokenFiat( - amountFiat = UNKNOWN_AMOUNT_SIGN, - feeFiat = UNKNOWN_AMOUNT_SIGN, - totalFiat = UNKNOWN_AMOUNT_SIGN, + amountFiat = BigDecimalFormatter.EMPTY_BALANCE_SIGN, + feeFiat = BigDecimalFormatter.EMPTY_BALANCE_SIGN, + totalFiat = BigDecimalFormatter.EMPTY_BALANCE_SIGN, willSentToken = tokensToSend.stripZeroPlainString(), willSentFeeCoin = feeCoin.stripZeroPlainString(), symbols = symbols, @@ -175,7 +164,7 @@ class ReceiptReducer : SendInternalReducer { ReceiptTokenCrypto( amountToken = tokensToSend.stripZeroPlainString(), feeCoin = feeCoin.stripZeroPlainString().addPrecisionSign(), - totalFiat = UNKNOWN_AMOUNT_SIGN, + totalFiat = BigDecimalFormatter.EMPTY_BALANCE_SIGN, symbols = symbols, ) } @@ -216,7 +205,7 @@ class ReceiptReducer : SendInternalReducer { sendState.tokenConverter!!.toFiatWithPrecision(value).stripZeroPlainString() } else -> { - UNKNOWN_AMOUNT_SIGN + BigDecimalFormatter.EMPTY_BALANCE_SIGN } } } @@ -225,4 +214,8 @@ class ReceiptReducer : SendInternalReducer { val result = if (feeState.feeIsApproximate) "$CAN_BE_LOWER_SIGN $this" else this return result.trim() } + + private companion object { + const val CAN_BE_LOWER_SIGN = "<" + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt index f2d05ab0c2..b8afccf494 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt @@ -2,6 +2,7 @@ package com.tangem.tap.features.send.redux.reducers import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.FeePaidCurrency import com.tangem.tap.common.CurrencyConverter import com.tangem.tap.common.entities.IndeterminateProgressButton import com.tangem.tap.features.send.redux.* @@ -109,7 +110,8 @@ private class PrepareSendScreenStatesReducer : SendInternalReducer { } private fun isFeePaidInNetworkCurrency(blockchain: Blockchain): Boolean = - blockchain.tokenTransactionFeePaidInNetworkCurrency() + // blockchain.tokenTransactionFeePaidInNetworkCurrency() + blockchain.feePaidCurrency() == FeePaidCurrency.SameCurrency // TODO [REDACTED_TASK_KEY] private fun isCoinAmount(typeOfAmount: AmountType): Boolean = typeOfAmount == AmountType.Coin } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt index f7a3a5e41b..27ae5f8a33 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt @@ -2,8 +2,8 @@ package com.tangem.tap.features.send.redux.states import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee -import com.tangem.tap.common.analytics.events.AnalyticsParam -import com.tangem.tap.features.wallet.redux.ProgressState +import com.tangem.core.analytics.models.AnalyticsParam +import com.tangem.tap.common.entities.ProgressState import java.math.BigDecimal /** diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt index c6284ad71d..589a563af2 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt @@ -5,8 +5,8 @@ import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.WalletManager import com.tangem.common.extensions.isZero import com.tangem.core.navigation.StateDialog +import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.tap.common.CurrencyConverter -import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.entities.IndeterminateProgressButton import com.tangem.tap.common.text.DecimalDigitsInputFilter import com.tangem.tap.domain.TapError @@ -128,7 +128,7 @@ data class AmountState( val typeOfAmount: AmountType = AmountType.Coin, val viewAmountValue: InputViewValue = InputViewValue(BigDecimal.ZERO.toPlainString()), val viewBalanceValue: String = BigDecimal.ZERO.toPlainString(), - val mainCurrency: MainCurrency = MainCurrency(MainCurrencyType.FIAT, FiatCurrency.Default.code), + val mainCurrency: MainCurrency = MainCurrency(MainCurrencyType.FIAT, AppCurrency.Default.code), val amountToSendCrypto: BigDecimal = BigDecimal.ZERO, val balanceCrypto: BigDecimal = BigDecimal.ZERO, val hideBalance: Boolean = false, diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt index e115eff66d..3e8b23a5d3 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt @@ -3,30 +3,38 @@ package com.tangem.tap.features.send.ui import android.content.Context -import android.content.Intent import android.os.Bundle import android.text.method.DigitsKeyListener import android.view.View import android.view.inputmethod.EditorInfo import android.widget.EditText +import androidx.core.os.bundleOf import androidx.core.view.postDelayed import androidx.core.widget.addTextChangedListener import androidx.fragment.app.viewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import arrow.core.getOrElse import by.kirich1409.viewbindingdelegate.viewBinding import com.google.android.material.textfield.TextInputEditText import com.tangem.Message import com.tangem.core.analytics.Analytics +import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction +import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.tokens.legacy.TradeCryptoAction +import com.tangem.feature.qrscanning.QrScanningRouter +import com.tangem.feature.qrscanning.SourceType +import com.tangem.feature.qrscanning.usecase.ListenToQrScanningUseCase import com.tangem.sdk.extensions.hideSoftKeyboard import com.tangem.tap.common.KeyboardObserver import com.tangem.tap.common.analytics.events.Token -import com.tangem.tap.common.entities.FiatCurrency +import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.getFromClipboard import com.tangem.tap.common.extensions.setOnImeActionListener -import com.tangem.tap.common.qrCodeScan.ScanQrCodeActivity import com.tangem.tap.common.recyclerView.SpaceItemDecoration import com.tangem.tap.common.snackBar.MaxAmountSnackbar import com.tangem.tap.common.text.truncateMiddleWith @@ -40,8 +48,8 @@ import com.tangem.tap.features.send.redux.AmountActionUi.* import com.tangem.tap.features.send.redux.FeeActionUi.* import com.tangem.tap.features.send.redux.states.FeeType import com.tangem.tap.features.send.redux.states.MainCurrencyType +import com.tangem.tap.features.send.ui.adapters.WarningMessagesAdapter import com.tangem.tap.features.send.ui.stateSubscribers.SendStateSubscriber -import com.tangem.tap.features.wallet.ui.adapters.WarningMessagesAdapter import com.tangem.tap.mainScope import com.tangem.tap.store import com.tangem.wallet.R @@ -49,8 +57,11 @@ import com.tangem.wallet.databinding.FragmentSendBinding import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch import java.text.DecimalFormatSymbols +import javax.inject.Inject private const val EDIT_TEXT_INPUT_DEBOUNCE = 400L @@ -73,11 +84,15 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { val binding: FragmentSendBinding by viewBinding(FragmentSendBinding::bind) + @Inject + lateinit var listenToQrScanningUseCase: ListenToQrScanningUseCase + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) lifecycle.addObserver(viewModel) sendSubscriber.initViewModel(viewModel) Analytics.send(Token.Send.ScreenOpened()) + listenToQrCode() } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -145,13 +160,34 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { } imvQrCode.setOnClickListener { Analytics.send(Token.Send.ButtonQRCode()) - startActivityForResult( - Intent(requireContext(), ScanQrCodeActivity::class.java), - ScanQrCodeActivity.SCAN_QR_REQUEST_CODE, + + store.dispatchOnMain( + NavigationAction.NavigateTo( + screen = AppScreen.QrScanning, + bundle = bundleOf( + QrScanningRouter.SOURCE_KEY to SourceType.SEND, + ), + ), ) } } + private fun listenToQrCode() { + lifecycleScope.launch { + listenToQrScanningUseCase(SourceType.SEND) + .getOrElse { emptyFlow() } + .flowWithLifecycle(this@SendFragment.lifecycle, minActiveState = Lifecycle.State.CREATED) + .collect { + delay(200) + + // Delayed launch is needed in order for the UI to be drawn and to process the sent events. + // If do not use the delay, then etAmount error field is not displayed when + // inserting an incorrect amount by shareUri + onCodeScanned(it) + } + } + } + private fun setupTransactionExtrasLayout() = with(binding.lSendAddress) { // TODO: [REDACTED_TASK_KEY] etXlmMemo.inputtedTextAsFlow() @@ -200,27 +236,16 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { .launchIn(mainScope) } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (requestCode != ScanQrCodeActivity.SCAN_QR_REQUEST_CODE) return - - val scannedCode = data?.getStringExtra(ScanQrCodeActivity.SCAN_RESULT) ?: "" + private fun onCodeScanned(scannedCode: String) { if (scannedCode.isEmpty()) return - // Delayed launch is needed in order for the UI to be drawn and to process the sent events. - // If do not use the delay, then etAmount error field is not displayed when - // inserting an incorrect amount by shareUri - binding.lSendAddress.imvQrCode.postDelayed( - { - store.dispatch( - PasteAddress( - data = scannedCode, - sourceType = Token.Send.AddressEntered.SourceType.QRCode, - ), - ) - store.dispatch(TruncateOrRestore(!binding.lSendAddress.etAddress.isFocused)) - }, - 200, + store.dispatch( + PasteAddress( + data = scannedCode, + sourceType = Token.Send.AddressEntered.SourceType.QRCode, + ), ) + store.dispatch(TruncateOrRestore(!binding.lSendAddress.etAddress.isFocused)) } private fun setupAmountLayout() { @@ -328,7 +353,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { private fun restoreMainCurrency(): MainCurrencyType { val sp = requireContext().getSharedPreferences("SendScreen", Context.MODE_PRIVATE) - val mainCurrency = sp.getString("mainCurrency", FiatCurrency.Default.code) + val mainCurrency = sp.getString("mainCurrency", AppCurrency.Default.code) return MainCurrencyType.values() .firstOrNull { it.name.equals(mainCurrency!!, ignoreCase = true) } ?: MainCurrencyType.CRYPTO 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/send/ui/adapters/WarningMessagesAdapter.kt similarity index 70% rename from app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt rename to app/src/main/java/com/tangem/tap/features/send/ui/adapters/WarningMessagesAdapter.kt index 1534ef1d17..2703f44c00 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/adapters/WarningMessagesAdapter.kt @@ -1,4 +1,4 @@ -package com.tangem.tap.features.wallet.ui.adapters +package com.tangem.tap.features.send.ui.adapters import android.view.LayoutInflater import android.view.View @@ -13,14 +13,13 @@ import com.tangem.tap.common.analytics.events.MainScreen import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.configurable.warningMessage.WarningMessage -import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.store import com.tangem.wallet.R import com.tangem.wallet.databinding.LayoutWarningCardActionBinding import timber.log.Timber -// TODO: Delete with WalletFeatureToggles -@Deprecated(message = "Used only in old wallet screen") +// TODO: Delete with SendFeatureToggles +@Deprecated(message = "Used only in old send screen") class WarningMessagesAdapter : ListAdapter(DiffUtilCallback) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WarningMessageVH { @@ -81,27 +80,15 @@ class WarningMessageVH(val binding: LayoutWarningCardActionBinding) : RecyclerVi private fun setupControlButtons(warning: WarningMessage) = when (warning.type) { WarningMessage.Type.Permanent, WarningMessage.Type.TestCard -> { binding.groupControlsTemporary.hide() - binding.groupControlsRating.hide() binding.btnClose.hide() } WarningMessage.Type.Temporary -> { - binding.groupControlsRating.hide() binding.groupControlsTemporary.show() binding.btnClose.hide() - val buttonAction = - when (warning.titleResId) { - // R.string.warning_important_security_info -> { - // View.OnClickListener { - // store.dispatch(WalletAction.DialogAction.SignedHashesMultiWalletDialog) - // } - // } - else -> { - View.OnClickListener { - store.dispatch(GlobalAction.HideWarningMessage(warning)) - } - } - } + val buttonAction = View.OnClickListener { + store.dispatch(GlobalAction.HideWarningMessage(warning)) + } val buttonTitle = binding.root.getString( warning.buttonTextId ?: R.string.how_to_got_it_button, ) @@ -110,37 +97,19 @@ class WarningMessageVH(val binding: LayoutWarningCardActionBinding) : RecyclerVi } WarningMessage.Type.AppRating -> { binding.groupControlsTemporary.hide() - binding.groupControlsRating.show() binding.btnClose.show() binding.btnClose.setOnClickListener { Analytics.send(MainScreen.NoticeRateAppButton(AnalyticsParam.RateApp.Closed)) store.dispatch(GlobalAction.HideWarningMessage(warning)) - store.dispatch(WalletAction.Warnings.AppRating.RemindLater) } - // binding.btnCanBeBetter.setOnClickListener { - // Analytics.send(MainScreen.NoticeRateAppButton(AnalyticsParam.RateApp.Disliked)) - // store.dispatch(WalletAction.Warnings.AppRating.SetNeverToShow) - // store.dispatch(GlobalAction.HideWarningMessage(warning)) - // store.dispatch(GlobalAction.SendEmail(RateCanBeBetterEmail())) - // } binding.btnReallyCool.setOnClickListener { val activity = binding.root.context.getActivity() ?: return@setOnClickListener Analytics.send(MainScreen.NoticeRateAppButton(AnalyticsParam.RateApp.Liked)) - store.dispatch(WalletAction.Warnings.AppRating.SetNeverToShow) val reviewManager = ReviewManagerFactory.create(activity) val task = reviewManager.requestReviewFlow() task.addOnCompleteListener { - if (it.isSuccessful) { - val reviewFlow = reviewManager.launchReviewFlow(activity, it.result) - reviewFlow.addOnCompleteListener { - if (it.isSuccessful) { - // send review was succeed - } else { - // send fails - } - } - } else { + if (!it.isSuccessful) { Timber.e(task.exception) } }.addOnFailureListener { diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt index 15044dd26f..0802255fb8 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt @@ -7,6 +7,8 @@ import android.view.View import android.view.ViewGroup import androidx.core.text.bold import com.tangem.common.extensions.remove +import com.tangem.core.ui.utils.BigDecimalFormatter +import com.tangem.tap.common.entities.ProgressState import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.getMessageString import com.tangem.tap.common.text.DecimalDigitsInputFilter @@ -19,11 +21,8 @@ import com.tangem.tap.features.send.redux.states.* import com.tangem.tap.features.send.ui.FeeUiHelper import com.tangem.tap.features.send.ui.SendFragment import com.tangem.tap.features.send.ui.SendViewModel +import com.tangem.tap.features.send.ui.adapters.WarningMessagesAdapter import com.tangem.tap.features.send.ui.dialogs.* -import com.tangem.tap.features.wallet.redux.ProgressState -import com.tangem.tap.features.wallet.redux.utils.ROUGH_SIGN -import com.tangem.tap.features.wallet.redux.utils.UNKNOWN_AMOUNT_SIGN -import com.tangem.tap.features.wallet.ui.adapters.WarningMessagesAdapter import com.tangem.wallet.R /** @@ -316,7 +315,7 @@ internal class SendStateSubscriber( fun getString(id: Int, vararg formatStrings: String): String = mainLayout.getString(id, *formatStrings) fun roughOrEmpty(value: String): String { - return if (value == UNKNOWN_AMOUNT_SIGN) value else "$ROUGH_SIGN $value" + return if (value == BigDecimalFormatter.EMPTY_BALANCE_SIGN) value else "$ROUGH_SIGN $value" } when (feeProgressState) { @@ -362,7 +361,7 @@ internal class SendStateSubscriber( llTotalContainer.tvTotalValue.update("${receipt.totalCrypto} ${receipt.symbols.crypto}") } - if (receipt.willSentFiat == UNKNOWN_AMOUNT_SIGN) { + if (receipt.willSentFiat == BigDecimalFormatter.EMPTY_BALANCE_SIGN) { llTotalContainer.tvWillBeSentValue.hide() } else { llTotalContainer.tvWillBeSentValue.show() @@ -414,4 +413,8 @@ internal class SendStateSubscriber( else -> {} } } + + private companion object { + const val ROUGH_SIGN = "≈" + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/signin/redux/SignInAction.kt b/app/src/main/java/com/tangem/tap/features/signin/redux/SignInAction.kt index efbcafb9df..7978ed3f13 100644 --- a/app/src/main/java/com/tangem/tap/features/signin/redux/SignInAction.kt +++ b/app/src/main/java/com/tangem/tap/features/signin/redux/SignInAction.kt @@ -1,6 +1,6 @@ package com.tangem.tap.features.signin.redux -import com.tangem.tap.common.analytics.events.Basic +import com.tangem.core.analytics.models.Basic import org.rekotlin.Action /** diff --git a/app/src/main/java/com/tangem/tap/features/signin/redux/SignInState.kt b/app/src/main/java/com/tangem/tap/features/signin/redux/SignInState.kt index 95b132aab7..27858a8ad7 100644 --- a/app/src/main/java/com/tangem/tap/features/signin/redux/SignInState.kt +++ b/app/src/main/java/com/tangem/tap/features/signin/redux/SignInState.kt @@ -1,6 +1,6 @@ package com.tangem.tap.features.signin.redux -import com.tangem.tap.common.analytics.events.Basic +import com.tangem.core.analytics.models.Basic /** [REDACTED_AUTHOR] 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 5a65f98b6b..4fc0e646b3 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 @@ -3,6 +3,7 @@ package com.tangem.tap.features.tokens.impl.data import androidx.paging.PagingSource import androidx.paging.PagingState import com.tangem.blockchain.common.Blockchain +import com.tangem.datasource.api.common.response.getOrThrow import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.domain.common.extensions.supportedBlockchains import com.tangem.domain.common.extensions.toNetworkId @@ -50,7 +51,7 @@ internal class TangemApiTokensPagingSource( searchText = searchText, offset = page * params.loadSize, limit = params.loadSize, - ) + ).getOrThrow() }.fold( onSuccess = { response -> LoadResult.Page( 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 64739f61a7..35d8f6c47a 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,7 +3,6 @@ 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.tap.domain.tokens.getIconUrl import com.tangem.tap.features.tokens.impl.domain.models.Token import com.tangem.utils.converter.Converter @@ -14,6 +13,8 @@ import com.tangem.utils.converter.Converter */ internal object CoinsResponseConverter : Converter> { + private const val DEFAULT_IMAGE_HOST = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/" + override fun convert(value: CoinsResponse): List { return value.coins.map { token -> Token( @@ -35,4 +36,8 @@ internal object CoinsResponseConverter : Converter> { ) } } + + fun getIconUrl(id: String, imageHost: String? = null): String { + return "${imageHost ?: DEFAULT_IMAGE_HOST}large/$id.png" + } } \ 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 269690c18b..5a532d7012 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 @@ -3,7 +3,6 @@ package com.tangem.tap.features.tokens.impl.data.converters import com.tangem.blockchain.common.Blockchain import com.tangem.datasource.local.testnet.models.TestnetTokensConfig import com.tangem.domain.common.extensions.fromNetworkId -import com.tangem.tap.domain.tokens.getIconUrl import com.tangem.tap.features.tokens.impl.domain.models.Token import com.tangem.utils.converter.Converter @@ -20,7 +19,7 @@ internal object TestnetTokensConfigConverter : Converter val blockchain = Blockchain.fromNetworkId(network.id) ?: return@mapNotNull null @@ -28,7 +27,7 @@ internal object TestnetTokensConfigConverter : Converter> } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/router/DefaultTokensListRouter.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/router/DefaultTokensListRouter.kt index 8c763c93d6..15f39a8914 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/router/DefaultTokensListRouter.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/router/DefaultTokensListRouter.kt @@ -6,7 +6,6 @@ import com.tangem.core.navigation.NavigationAction import com.tangem.tap.common.extensions.dispatchDialogShow import com.tangem.tap.common.extensions.dispatchNotification import com.tangem.tap.common.redux.AppDialog -import com.tangem.tap.features.wallet.redux.models.WalletDialog import com.tangem.tap.store import com.tangem.wallet.R @@ -32,13 +31,13 @@ internal class DefaultTokensListRouter : TokensListRouter { override fun openUnableHideMainTokenAlert(tokenName: String, tokenSymbol: String) { store.dispatchDialogShow( - dialog = WalletDialog.TokensAreLinkedDialog(currencyTitle = tokenName, currencySymbol = tokenSymbol), + dialog = AppDialog.TokensAreLinkedDialog(currencyTitle = tokenName, currencySymbol = tokenSymbol), ) } override fun openRemoveWalletAlert(tokenName: String, onOkClick: () -> Unit) { store.dispatchDialogShow( - dialog = WalletDialog.RemoveWalletDialog(currencyTitle = tokenName, onOk = onOkClick), + dialog = AppDialog.RemoveWalletDialog(currencyTitle = tokenName, onOk = onOkClick), ) } diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListMigration.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListMigration.kt index d4af20b4ea..7ccf604f5a 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListMigration.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListMigration.kt @@ -3,7 +3,6 @@ package com.tangem.tap.features.tokens.impl.presentation.viewmodels import arrow.core.Either import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token -import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.data.tokens.utils.CryptoCurrencyFactory import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.tokens.GetCryptoCurrenciesUseCase @@ -12,9 +11,6 @@ import com.tangem.domain.tokens.TokensAction import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase -import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles -import com.tangem.tap.domain.model.WalletDataModel -import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.store import timber.log.Timber import kotlin.properties.Delegates @@ -22,12 +18,10 @@ import kotlin.properties.Delegates /** * Class that divide a new and legacy logic when user uses tokens list screen * - * @property walletFeatureToggles wallet feature toggles * @property getSelectedWalletSyncUseCase use case that returns selected wallet * @property getCurrenciesUseCase use case that returns crypto currencies of a specified wallet */ internal class TokensListMigration( - private val walletFeatureToggles: WalletFeatureToggles, private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, private val getCurrenciesUseCase: GetCryptoCurrenciesUseCase, ) { @@ -39,14 +33,6 @@ internal class TokensListMigration( private val cryptoCurrencyFactory by lazy { CryptoCurrencyFactory() } suspend fun getCurrentCryptoCurrencies(): TokensListCryptoCurrencies { - return if (walletFeatureToggles.isRedesignedScreenEnabled) { - getNewCryptoCurrencies() - } else { - getLegacyCryptoCurrencies() - } - } - - private suspend fun getNewCryptoCurrencies(): TokensListCryptoCurrencies { return when (val selectedWalletEither = getSelectedWalletSyncUseCase()) { is Either.Left -> { Timber.e(selectedWalletEither.value.toString()) @@ -90,60 +76,12 @@ internal class TokensListMigration( } } - private fun getLegacyCryptoCurrencies(): TokensListCryptoCurrencies { - val wallets = store.state.walletState.walletsDataFromStores - val derivationStyle = store.state.globalState.scanResponse?.derivationStyleProvider?.getDerivationStyle() - - return TokensListCryptoCurrencies( - coins = wallets.toNonCustomBlockchains(derivationStyle), - tokens = wallets.toNonCustomTokensWithBlockchains(derivationStyle), - ) - } - - private fun List.toNonCustomBlockchains(derivationStyle: DerivationStyle?): List { - return this - .mapNotNull { walletDataModel -> - if (walletDataModel.currency.isCustomCurrency(derivationStyle)) { - null - } else { - (walletDataModel.currency as? Currency.Blockchain)?.blockchain - } - } - .distinct() - } - - private fun List.toNonCustomTokensWithBlockchains( - derivationStyle: DerivationStyle?, - ): List { - return this - .mapNotNull { walletDataModel -> - if (walletDataModel.currency !is Currency.Token) return@mapNotNull null - if (walletDataModel.currency.isCustomCurrency(derivationStyle)) return@mapNotNull null - - TokenWithBlockchain(walletDataModel.currency.token, walletDataModel.currency.blockchain) - } - .distinct() - } - fun onSaveButtonClick( - currentTokensList: List, - currentBlockchainList: List, - changedTokensList: MutableList, - changedBlockchainList: List, - ) { - if (walletFeatureToggles.isRedesignedScreenEnabled) { - saveByNewWay(changedTokensList = changedTokensList, changedBlockchainList = changedBlockchainList) - } else { - saveByOldWay(currentTokensList, currentBlockchainList, changedTokensList, changedBlockchainList) - } - } - - private fun saveByNewWay( changedTokensList: MutableList, changedBlockchainList: List, ) { store.dispatch( - action = TokensAction.NewSaveChanges( + action = TokensAction.SaveChanges( currentTokens = currentNewTokens, currentCoins = currentNewCoins, changedTokens = changedTokensList.mapNotNull { @@ -165,23 +103,4 @@ internal class TokensListMigration( ), ) } - - private fun saveByOldWay( - currentTokensList: List, - currentBlockchainList: List, - changedTokensList: MutableList, - changedBlockchainList: List, - ) { - val scanResponse = store.state.globalState.scanResponse ?: return - - store.dispatch( - action = TokensAction.LegacySaveChanges( - currentTokens = currentTokensList, - currentBlockchains = currentBlockchainList, - changedTokens = changedTokensList, - changedBlockchains = changedBlockchainList, - scanResponse = scanResponse, - ), - ) - } } \ 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 f2046c81df..94c4974fc7 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 @@ -22,7 +22,6 @@ import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.tokens.GetCryptoCurrenciesUseCase import com.tangem.domain.tokens.TokenWithBlockchain import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase -import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles import com.tangem.tap.common.extensions.fullNameWithoutTestnet import com.tangem.tap.common.extensions.getNetworkName import com.tangem.tap.features.tokens.impl.domain.TokensListInteractor @@ -70,7 +69,6 @@ internal class TokensListViewModel @Inject constructor( private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, analyticsEventHandler: AnalyticsEventHandler, getCurrenciesUseCase: GetCryptoCurrenciesUseCase, - walletFeatureToggles: WalletFeatureToggles, ) : ViewModel(), DefaultLifecycleObserver { private val isManageAccess = store.state.tokensState.isManageAccess @@ -88,7 +86,6 @@ internal class TokensListViewModel @Inject constructor( private var changedBlockchainList: MutableList = mutableListOf() private val tokensListMigration = TokensListMigration( - walletFeatureToggles = walletFeatureToggles, getSelectedWalletSyncUseCase = getSelectedWalletSyncUseCase, getCurrenciesUseCase = getCurrenciesUseCase, ) @@ -307,8 +304,6 @@ internal class TokensListViewModel @Inject constructor( fun onSaveButtonClick() { analyticsSender.sendWhenSaveButtonClicked() tokensListMigration.onSaveButtonClick( - currentTokensList = currentTokensList, - currentBlockchainList = currentBlockchainList, changedTokensList = changedTokensList, changedBlockchainList = changedBlockchainList, ) diff --git a/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt b/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt index 101c7c51e5..eb39d0d64b 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt @@ -2,14 +2,11 @@ package com.tangem.tap.features.tokens.legacy.redux import com.tangem.blockchain.blockchains.cardano.CardanoUtils import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.common.CompletionResult import com.tangem.common.card.EllipticCurve import com.tangem.common.doOnSuccess import com.tangem.common.extensions.ByteArrayKey -import com.tangem.common.extensions.guard import com.tangem.common.extensions.toMapKey -import com.tangem.common.flatMap import com.tangem.core.navigation.NavigationAction import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.domain.common.configs.CardConfig @@ -17,23 +14,22 @@ import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.common.util.supportsHdWallet import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase -import com.tangem.domain.tokens.TokenWithBlockchain import com.tangem.domain.tokens.TokensAction import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.walletconnect.WalletConnectActions import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import com.tangem.operations.derivation.ExtendedPublicKeysMap -import com.tangem.tap.* import com.tangem.tap.common.extensions.dispatchDebugErrorNotification import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.TapError -import com.tangem.tap.features.wallet.models.Currency +import com.tangem.tap.domain.card.DefaultDerivePublicKeysUseCase import com.tangem.tap.proxy.redux.DaggerGraphState -import com.tangem.utils.extensions.DELAY_SDK_DIALOG_CLOSE -import kotlinx.coroutines.delay +import com.tangem.tap.scope +import com.tangem.tap.store +import com.tangem.tap.tangemSdkManager +import com.tangem.tap.userWalletsListManager import kotlinx.coroutines.launch import org.rekotlin.Middleware import timber.log.Timber @@ -53,15 +49,14 @@ object TokensMiddleware { { next -> { action -> when (action) { - is TokensAction.LegacySaveChanges -> handleLegacySaveChanges(action) - is TokensAction.NewSaveChanges -> handleNewSaveChanges(action) + is TokensAction.SaveChanges -> handleSaveChanges(action) } next(action) } } } - private fun handleNewSaveChanges(action: TokensAction.NewSaveChanges) { + private fun handleSaveChanges(action: TokensAction.SaveChanges) { scope.launch { val scanResponse = action.userWallet.scanResponse @@ -74,7 +69,7 @@ object TokensMiddleware { val tokensToAdd = action.changedTokens.filterNot(currentTokens::contains) val tokensToRemove = currentTokens.filterNot { token -> action.changedTokens.any { it == token } } - removeNewCurrenciesIfNeeded( + removeCurrenciesIfNeeded( userWalletId = action.userWallet.walletId, currencies = blockchainsToRemove + tokensToRemove, ) @@ -89,146 +84,40 @@ object TokensMiddleware { val currencyList = blockchainsToAdd + tokensToAdd - if (scanResponse.supportsHdWallet()) { - deriveMissingCoins(scanResponse = scanResponse, currencyList = currencyList) { - submitNewAdd( - userWallet = action.userWallet, - updatedScanResponse = it, - currencyList = currencyList, - ) - } - } else { - submitNewAdd( - userWallet = action.userWallet, - updatedScanResponse = scanResponse, - currencyList = currencyList, + val featureToggles = store.state.daggerGraphState.get(DaggerGraphState::testerFeatureToggles) + if (featureToggles.isDerivePublicKeysRefactoringEnabled) { + val derivePublicKeys = DefaultDerivePublicKeysUseCase( + tangemSdkManager = tangemSdkManager, + derivationsRepository = store.state.daggerGraphState.get(DaggerGraphState::derivationsRepository), ) - } - } - } - private fun handleLegacySaveChanges(action: TokensAction.LegacySaveChanges) { - scope.launch { - val scanResponse = action.scanResponse - - val currentTokens = action.currentTokens - val currentBlockchains = action.currentBlockchains - - val blockchainsToAdd = action.changedBlockchains.filterNot(currentBlockchains::contains) - val blockchainsToRemove = currentBlockchains.filterNot(action.changedBlockchains::contains) - - val tokensToAdd = action.changedTokens.filterNot(currentTokens::contains) - val tokensToRemove = - currentTokens.filterNot { token -> action.changedTokens.any { it.token == token.token } } - - removeLegacyCurrenciesIfNeeded( - currencies = convertToCurrencies( - blockchains = blockchainsToRemove, - tokens = tokensToRemove, - derivationStyle = scanResponse.derivationStyleProvider.getDerivationStyle(), - ), - ) - - val isNothingToDoWithTokens = tokensToAdd.isEmpty() && tokensToRemove.isEmpty() - val isNothingToDoWithBlockchain = blockchainsToAdd.isEmpty() && blockchainsToRemove.isEmpty() - if (isNothingToDoWithTokens && isNothingToDoWithBlockchain) { - store.dispatchDebugErrorNotification(message = "Nothing to save") - store.dispatchOnMain(NavigationAction.PopBackTo()) - return@launch - } - - val currencyList = convertToCurrencies( - blockchains = blockchainsToAdd, - tokens = tokensToAdd, - derivationStyle = scanResponse.derivationStyleProvider.getDerivationStyle(), - ) - - if (scanResponse.supportsHdWallet()) { - deriveMissingBlockchains(scanResponse, currencyList) { - submitLegacyAdd(it, currencyList) - store.dispatchOnMain(NavigationAction.PopBackTo()) - } - } else { - submitLegacyAdd(scanResponse, currencyList) - store.dispatchOnMain(NavigationAction.PopBackTo()) - } - } - } - - private fun convertToCurrencies( - blockchains: List, - tokens: List, - derivationStyle: DerivationStyle?, - ): List { - return blockchains.map { Currency.Blockchain(it, it.derivationPath(derivationStyle)?.rawPath) } + - tokens.map { - Currency.Token( - token = it.token, - blockchain = it.blockchain, - derivationPath = it.blockchain.derivationPath(derivationStyle)?.rawPath, - ) - } - } - - private fun deriveMissingBlockchains( - scanResponse: ScanResponse, - currencyList: List, - onSuccess: (ScanResponse) -> Unit, - ) { - val config = CardConfig.createConfig(scanResponse.card) - val derivationDataList = currencyList.mapNotNull { currency -> - val curve = config.primaryCurve(currency.blockchain) - curve?.let { getLegacyDerivations(curve, scanResponse, currency) } - } - val derivations = buildMap> { - derivationDataList.forEach { - val current = this[it.derivations.first] - if (current != null) { - current.addAll(it.derivations.second) - current.distinct() - } else { - this[it.derivations.first] = it.derivations.second.toMutableList() - } - } - } - - if (derivations.isEmpty()) { - onSuccess(scanResponse) - return - } - - scope.launch { - val result = tangemSdkManager.derivePublicKeys( - cardId = null, - derivations = derivations, - ) - when (result) { - is CompletionResult.Success -> { - val newDerivedKeys = result.data.entries - val oldDerivedKeys = scanResponse.derivedKeys - - val walletKeys = (newDerivedKeys.keys + oldDerivedKeys.keys).toSet() - - val updatedDerivedKeys = walletKeys.associateWith { walletKey -> - val oldDerivations = ExtendedPublicKeysMap(oldDerivedKeys[walletKey] ?: emptyMap()) - val newDerivations = newDerivedKeys[walletKey] ?: ExtendedPublicKeysMap(emptyMap()) - ExtendedPublicKeysMap(oldDerivations + newDerivations) + derivePublicKeys(userWalletId = action.userWallet.walletId, currencies = currencyList) + .onRight { + addCryptoCurrenciesUseCase( + userWalletId = action.userWallet.walletId, + currencies = currencyList, + ) + store.dispatchOnMain(NavigationAction.PopBackTo()) } - val updatedScanResponse = scanResponse.copy( - derivedKeys = updatedDerivedKeys, - ) - store.dispatchOnMain(GlobalAction.SaveScanResponse(updatedScanResponse)) - delay(DELAY_SDK_DIALOG_CLOSE) - - onSuccess(updatedScanResponse) - } - is CompletionResult.Failure -> { - store.dispatchDebugErrorNotification(TapError.CustomError("Error adding tokens")) + .onLeft { Timber.e("Failed to derive public keys: $it") } + } else { + // TODO: delete [REDACTED_JIRA] + if (scanResponse.supportsHdWallet()) { + deriveMissingCoins(scanResponse = scanResponse, currencyList = currencyList) { + submitAdd( + userWallet = action.userWallet, + updatedScanResponse = it, + currencyList = currencyList, + ) + } + } else { + submitAdd(action.userWallet, scanResponse, currencyList) } } } } + @Deprecated(message = "Use DerivePublicKeysUseCase instead") private fun deriveMissingCoins( scanResponse: ScanResponse, currencyList: List, @@ -237,7 +126,7 @@ object TokensMiddleware { val config = CardConfig.createConfig(scanResponse.card) val derivationDataList = currencyList.mapNotNull { currency -> val curve = config.primaryCurve(blockchain = Blockchain.fromId(currency.network.id.value)) - curve?.let { getNewDerivations(curve, scanResponse, currency) } + curve?.let { getDerivations(curve, scanResponse, currency) } } val derivations = buildMap> { derivationDataList.forEach { @@ -286,42 +175,7 @@ object TokensMiddleware { } } - private fun getLegacyDerivations( - curve: EllipticCurve, - scanResponse: ScanResponse, - currency: Currency, - ): DerivationData? { - val wallet = scanResponse.card.wallets.firstOrNull { it.curve == curve } ?: return null - - val supportedCurves = currency.blockchain.getSupportedCurves() - val path = currency.blockchain.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle()) - .takeIf { supportedCurves.contains(curve) } - - val customPath = currency.derivationPath?.let { - DerivationPath(it) - }.takeIf { supportedCurves.contains(curve) } - - val bothCandidates = listOfNotNull(path, customPath).distinct().toMutableList() - if (bothCandidates.isEmpty()) return null - - if (currency is Currency.Blockchain && currency.blockchain == Blockchain.Cardano) { - currency.derivationPath?.let { - bothCandidates.add(CardanoUtils.extendedDerivationPath(DerivationPath(it))) - } - } - - val mapKeyOfWalletPublicKey = wallet.publicKey.toMapKey() - val alreadyDerivedKeys: ExtendedPublicKeysMap = - scanResponse.derivedKeys[mapKeyOfWalletPublicKey] ?: ExtendedPublicKeysMap(emptyMap()) - val alreadyDerivedPaths = alreadyDerivedKeys.keys.toList() - - val toDerive = bothCandidates.filterNot { alreadyDerivedPaths.contains(it) } - if (toDerive.isEmpty()) return null - - return DerivationData(derivations = mapKeyOfWalletPublicKey to toDerive) - } - - private fun getNewDerivations( + private fun getDerivations( curve: EllipticCurve, scanResponse: ScanResponse, currency: CryptoCurrency, @@ -359,28 +213,7 @@ object TokensMiddleware { class DerivationData(val derivations: Pair>) - private fun submitLegacyAdd(scanResponse: ScanResponse, currencyList: List) { - val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard { - Timber.e("Unable to add currencies, no user wallet selected") - return - } - scope.launch { - userWalletsListManager.update( - userWalletId = selectedUserWallet.walletId, - update = { userWallet -> - userWallet.copy(scanResponse = scanResponse) - }, - ) - .flatMap { updatedUserWallet -> - walletCurrenciesManager.addCurrencies( - userWallet = updatedUserWallet, - currenciesToAdd = currencyList, - ) - } - } - } - - private fun submitNewAdd( + private fun submitAdd( userWallet: UserWallet, updatedScanResponse: ScanResponse, currencyList: List, @@ -390,24 +223,13 @@ object TokensMiddleware { userWalletId = userWallet.walletId, update = { it.copy(scanResponse = updatedScanResponse) }, ).doOnSuccess { - addCryptoCurrenciesUseCase(userWallet.walletId, currencyList).onRight { - store.dispatch(action = WalletConnectActions.New.SetupUserChains(userWallet = userWallet)) - } + addCryptoCurrenciesUseCase(userWallet.walletId, currencyList) } } store.dispatchOnMain(NavigationAction.PopBackTo()) } - private suspend fun removeLegacyCurrenciesIfNeeded(currencies: List) { - if (currencies.isEmpty()) return - val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard { - Timber.e("Unable to remove currencies, no user wallet selected") - return - } - walletCurrenciesManager.removeCurrencies(selectedUserWallet, currencies) - } - - private suspend fun removeNewCurrenciesIfNeeded(userWalletId: UserWalletId, currencies: List) { + private suspend fun removeCurrenciesIfNeeded(userWalletId: UserWalletId, currencies: List) { if (currencies.isEmpty()) return val currenciesRepository = store.state.daggerGraphState.get(DaggerGraphState::currenciesRepository) val walletManagersFacade = store.state.daggerGraphState.get(DaggerGraphState::walletManagersFacade) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/data/WalletRepositoryImpl.kt b/app/src/main/java/com/tangem/tap/features/wallet/data/WalletRepositoryImpl.kt deleted file mode 100644 index e0b6ce6972..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/data/WalletRepositoryImpl.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.tangem.tap.features.wallet.data - -import com.tangem.datasource.api.common.response.getOrThrow -import com.tangem.datasource.api.tangemTech.TangemTechApi -import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse -import com.tangem.tap.features.wallet.domain.WalletRepository -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.withContext - -/** - * Implementation of repository for Wallet feature - * - * @property tangemTechApi API for server requests - * @property dispatchers coroutine dispatcher provider - */ -class WalletRepositoryImpl( - private val tangemTechApi: TangemTechApi, - private val dispatchers: CoroutineDispatcherProvider, -) : WalletRepository { - - override suspend fun getCurrencyList(): CurrenciesResponse = withContext(dispatchers.io) { - tangemTechApi.getCurrencyList().getOrThrow() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/domain/WalletRepository.kt b/app/src/main/java/com/tangem/tap/features/wallet/domain/WalletRepository.kt deleted file mode 100644 index c3ba927d31..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/domain/WalletRepository.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.tangem.tap.features.wallet.domain - -import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse - -/** Repository for Wallet feature */ -interface WalletRepository { - - /** Get list of currency */ - suspend fun getCurrencyList(): CurrenciesResponse -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/models/Currency.kt b/app/src/main/java/com/tangem/tap/features/wallet/models/Currency.kt deleted file mode 100644 index 9262a61187..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/models/Currency.kt +++ /dev/null @@ -1,131 +0,0 @@ -package com.tangem.tap.features.wallet.models - -import com.tangem.blockchain.common.derivation.DerivationStyle -import com.tangem.datasource.api.tangemTech.models.UserTokensResponse -import com.tangem.domain.common.BlockchainNetwork -import com.tangem.domain.common.extensions.fromNetworkId -import com.tangem.domain.common.extensions.toCoinId -import com.tangem.domain.features.addCustomToken.CustomCurrency -import com.tangem.tap.common.redux.global.CryptoCurrencyName -import com.tangem.blockchain.common.Blockchain as SdkBlockchain -import com.tangem.blockchain.common.Token as SdkToken - -sealed interface Currency { - val coinId: String? - get() = when (this) { - is Blockchain -> blockchain.toCoinId() - is Token -> token.id - } - val blockchain: SdkBlockchain - val currencySymbol: CryptoCurrencyName - val derivationPath: String? - val currencyName: String - get() = when (this) { - is Blockchain -> blockchain.fullName - is Token -> token.name - } - val decimals - get() = when (this) { - is Blockchain -> blockchain.decimals() - is Token -> token.decimals - } - - data class Token( - val token: SdkToken, - override val blockchain: SdkBlockchain, - override val derivationPath: String?, - ) : Currency { - override val currencySymbol = token.symbol - } - - data class Blockchain( - override val blockchain: SdkBlockchain, - override val derivationPath: String?, - ) : Currency { - override val currencySymbol: CryptoCurrencyName = blockchain.currency - } - - fun isCustomCurrency(derivationStyle: DerivationStyle?): Boolean { - if (this is Token && this.token.id == null) return true - - if (derivationPath == null || derivationStyle == null) return false - - return derivationPath != blockchain.derivationPath(derivationStyle)?.rawPath - } - - fun isBlockchain(): Boolean = this is Blockchain - fun isToken(): Boolean = this is Token - - companion object { - fun fromBlockchainNetwork(blockchainNetwork: BlockchainNetwork, token: SdkToken? = null): Currency { - return if (token != null) { - Token( - token = token, - blockchain = blockchainNetwork.blockchain, - derivationPath = blockchainNetwork.derivationPath, - ) - } else { - Blockchain( - blockchain = blockchainNetwork.blockchain, - derivationPath = blockchainNetwork.derivationPath, - ) - } - } - - fun fromCustomCurrency(customCurrency: CustomCurrency): Currency { - return when (customCurrency) { - is CustomCurrency.CustomBlockchain -> Blockchain( - blockchain = customCurrency.network, - derivationPath = customCurrency.derivationPath?.rawPath, - ) - is CustomCurrency.CustomToken -> Token( - token = customCurrency.token, - blockchain = customCurrency.network, - derivationPath = customCurrency.derivationPath?.rawPath, - ) - } - } - - fun fromTokenResponse(tokenBody: UserTokensResponse.Token): Currency? { - val blockchain = com.tangem.blockchain.common.Blockchain.fromNetworkId(tokenBody.networkId) - ?: return null - return when { - tokenBody.contractAddress != null -> Token( - token = SdkToken( - name = tokenBody.name, - symbol = tokenBody.symbol, - contractAddress = tokenBody.contractAddress!!, - decimals = tokenBody.decimals, - id = tokenBody.id, - ), - blockchain = blockchain, - derivationPath = tokenBody.derivationPath, - ) - else -> Blockchain( - blockchain = blockchain, - derivationPath = tokenBody.derivationPath, - ) - } - } - } -} - -fun BlockchainNetwork.toCurrencies(): List { - val blockchain = Currency.fromBlockchainNetwork(this) - val tokens = this.tokens.map { Currency.fromBlockchainNetwork(this, it) } - return listOf(blockchain) + tokens -} - -fun List.toCurrencies(): List { - return flatMap { it.toCurrencies() } -} - -fun List.toBlockchainNetworks(): List { - return this.filter { it.isBlockchain() }.map { BlockchainNetwork(it.blockchain, it.derivationPath, getTokens(it)) } -} - -fun List.getTokens(currency: Currency): List { - return this - .filter { it.isToken() && it.blockchain == currency.blockchain && it.derivationPath == currency.derivationPath } - .mapNotNull { if (it is Currency.Token) it.token else null } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/models/WalletWarning.kt b/app/src/main/java/com/tangem/tap/features/wallet/models/WalletWarning.kt deleted file mode 100644 index ef3c3466c4..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/models/WalletWarning.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.tangem.tap.features.wallet.models - -import com.tangem.tap.domain.model.WalletStoreModel - -sealed class WalletWarning(val showingPosition: Int) { - - data class ExistentialDeposit( - val currencyName: String, - val edStringValueWithSymbol: String, - ) : WalletWarning(1) - - data class TransactionInProgress(val currencyName: String) : WalletWarning(showingPosition = 10) - - data class BalanceNotEnoughForFee( - val currencyName: String, - val blockchainFullName: String, - val blockchainSymbol: String, - ) : WalletWarning(showingPosition = 30) - - data class Rent(val walletRent: WalletStoreModel.WalletRent) : WalletWarning(showingPosition = 40) -} - -data class WalletWarningDescription(val title: String, val message: String) \ 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 deleted file mode 100644 index 30b0aaadce..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt +++ /dev/null @@ -1,143 +0,0 @@ -package com.tangem.tap.features.wallet.redux - -import android.content.Context -import androidx.lifecycle.LifecycleCoroutineScope -import com.tangem.blockchain.common.Amount -import com.tangem.blockchain.common.address.AddressType -import com.tangem.core.analytics.models.AnalyticsEvent -import com.tangem.domain.common.BlockchainNetwork -import com.tangem.domain.models.scan.CardDTO -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.common.entities.FiatCurrency -import com.tangem.tap.common.redux.NotificationAction -import com.tangem.tap.domain.TapError -import com.tangem.tap.domain.configurable.warningMessage.WarningMessage -import com.tangem.tap.domain.model.TotalFiatBalance -import com.tangem.tap.domain.model.WalletDataModel -import com.tangem.tap.domain.model.WalletStoreModel -import com.tangem.tap.features.wallet.models.Currency -import com.tangem.tap.features.wallet.redux.models.WalletDialog -import com.tangem.wallet.R -import kotlinx.coroutines.CoroutineScope -import org.rekotlin.Action - -sealed class WalletAction : Action { - - object PopBackToInitialScreen : WalletAction() - - data class UpdateCanSaveUserWallets(val canSaveUserWallets: Boolean) : WalletAction() - - object LoadData : WalletAction() { - object Refresh : WalletAction() - object Success : WalletAction() - data class Failure(val error: TapError?) : WalletAction() - } - - sealed class MultiWallet : WalletAction() { - - data class SelectWallet(val currency: Currency?) : MultiWallet() - - data class TryToRemoveWallet(val currency: Currency) : MultiWallet() - data class RemoveWallet(val currency: Currency) : MultiWallet() - - object BackupWallet : MultiWallet() - data class AddMissingDerivations(val blockchains: List) : MultiWallet() - object ScanToGetDerivations : MultiWallet() - - /** - * Display warning if card has no backup - * - * @param card card to check status - * */ - data class CheckForBackupWarning(val card: CardDTO) : MultiWallet() - } - - sealed class Warnings : WalletAction() { - object CheckHashesCount : Warnings() { - - /** - * Start online verification of signed hashes for single currency wallets if the warning not displayed - * */ - object VerifyOnlineIfNeeded : Warnings() - object SaveCardId : Warnings() - } - - object CheckIfNeeded : Warnings() - object Update : Warnings() - data class Set(val warningList: List) : Warnings() - - object AppRating : Warnings() { - object SetNeverToShow : Warnings() - object RemindLater : Warnings() - } - - class CheckRemainingSignatures(val remainingSignatures: Int?) : Warnings() - } - - data class Scan( - val onScanSuccessEvent: AnalyticsEvent?, - val scope: CoroutineScope, - ) : WalletAction() - - data class Send(val amount: Amount? = null) : WalletAction() - - data class CopyAddress(val address: String, val context: Context) : WalletAction() { - object Success : WalletAction(), NotificationAction { - override val messageResource = R.string.wallet_notification_address_copied - } - } - - data class ShareAddress(val address: String, val context: Context) : WalletAction() - - sealed class DialogAction : WalletAction() { - data class QrCode( - val currency: Currency, - val selectedAddress: WalletDataModel.AddressData, - ) : DialogAction() - - object SignedHashesMultiWalletDialog : DialogAction() - data class ChooseTradeActionDialog( - val buyAllowed: Boolean, - val sellAllowed: Boolean, - val swapAllowed: Boolean, - ) : DialogAction() - - data class ChooseCurrency(val amounts: List) : DialogAction() - data class RussianCardholdersWarningDialog( - val dialogData: WalletDialog.RussianCardholdersWarningDialog.Data? = null, - ) : DialogAction() - - object Hide : DialogAction() - } - - data class ExploreAddress(val exploreUrl: String, val context: Context) : WalletAction() - - object CreateWallet : WalletAction() - data class ChangeWallet(val scope: LifecycleCoroutineScope) : WalletAction() - object ShowSaveWalletIfNeeded : WalletAction() - - sealed class TradeCryptoAction : WalletAction() { - object Sell : TradeCryptoAction() - - data class Buy(val checkUserLocation: Boolean = true) : TradeCryptoAction() - - object Swap : TradeCryptoAction() - } - - data class ChangeSelectedAddress(val type: AddressType) : WalletAction() - - sealed class AppCurrencyAction : WalletAction() { - object ChooseAppCurrency : AppCurrencyAction() - data class SelectAppCurrency(val fiatCurrency: FiatCurrency) : AppCurrencyAction() - } - - data class UserWalletChanged(val userWallet: UserWallet) : WalletAction() - data class WalletStoresChanged(val walletStores: List) : WalletAction() - - data class TotalFiatBalanceChanged(val balance: TotalFiatBalance) : WalletAction() - - data class UpdateUserWalletArtwork(val walletId: UserWalletId) : WalletAction() - - data class SetArtworkUrl(val userWalletId: UserWalletId, val url: String) : WalletAction() -} \ No newline at end of file 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 deleted file mode 100644 index f0bdd8f3f1..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt +++ /dev/null @@ -1,118 +0,0 @@ -package com.tangem.tap.features.wallet.redux - -import com.tangem.blockchain.common.AmountType -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.WalletManager -import com.tangem.domain.common.BlockchainNetwork -import com.tangem.domain.common.util.cardTypesResolver -import com.tangem.domain.userwallets.Artwork -import com.tangem.tap.common.entities.Button -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.model.TotalFiatBalance -import com.tangem.tap.domain.model.WalletDataModel -import com.tangem.tap.domain.model.WalletStoreModel -import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState -import com.tangem.tap.features.wallet.models.Currency -import com.tangem.tap.store -import org.rekotlin.StateType -import java.math.BigDecimal -import kotlin.properties.ReadOnlyProperty - -data class WalletState( - val state: ProgressState = ProgressState.Done, - val error: ErrorType? = null, - val cardImage: Artwork? = null, - val mainWarningsList: List = mutableListOf(), - val walletsStores: List = listOf(), - val isMultiwalletAllowed: Boolean = false, - val cardCurrency: CryptoCurrencyName? = null, - val selectedCurrency: Currency? = null, - val isTestnet: Boolean = false, - val totalBalance: TotalFiatBalance? = null, - val showBackupWarning: Boolean = false, - val missingDerivations: List = emptyList(), - val loadingUserTokens: Boolean = false, - val walletCardsCount: Int? = null, - val canSaveUserWallets: Boolean = false, -) : StateType { - - val walletsDataFromStores: List - get() = walletsStores.flatMap { it.walletsData } - - val selectedWalletData: WalletDataModel? - get() = walletsDataFromStores.firstOrNull { it.currency == selectedCurrency } - - // if you do not delegate - the application crashes on startup, - // because twinCardsState has not been created yet - val twinCardsState: TwinCardsState by ReadOnlyProperty { _, _ -> - store.state.twinCardsState - } - - val isTangemTwins: Boolean - get() = store.state.globalState.scanResponse?.cardTypesResolver?.isTangemTwins() == true - - val isExchangeServiceFeatureOn: Boolean - get() = store.state.globalState.exchangeManager.featureIsSwitchedOn() - - val blockchains: List - get() = walletsStores.mapNotNull { it.walletManager?.wallet?.blockchain } - - val currencies: List - get() = walletsStores.flatMap { it.walletsData }.map { it.currency } - - val walletManagers: List - get() = walletsStores.mapNotNull { it.walletManager } - - private val primaryWalletStore: WalletStoreModel? - get() = if (isMultiwalletAllowed || walletsStores.isEmpty() || walletsStores.size > 1) { - null - } else { - walletsStores[0] - } - - val primaryWalletManager: WalletManager? - get() = primaryWalletStore?.walletManager - - val primaryWalletData: WalletDataModel? - get() = primaryWalletStore?.blockchainWalletData - - val primaryTokenData: WalletDataModel? - get() = primaryWalletStore?.walletsData - ?.firstOrNull { it.currency !is Currency.Blockchain } - - fun getWalletManager(currency: Currency?): WalletManager? { - if (currency?.blockchain == null) return null - return getWalletStore(currency)?.walletManager - } - - fun getWalletManager(blockchain: BlockchainNetwork): WalletManager? { - return walletsStores.firstOrNull { - it.blockchain == blockchain.blockchain && - it.derivationPath?.rawPath == blockchain.derivationPath - }?.walletManager - } - - fun getWalletStore(currency: Currency?): WalletStoreModel? { - if (currency == null) return null - return walletsStores.firstOrNull { - it.blockchain == currency.blockchain && - it.derivationPath?.rawPath == currency.derivationPath - } - } - - fun getBlockchainAmount(currency: Currency): BigDecimal = - getWalletManager(currency)?.wallet?.amounts?.get(AmountType.Coin)?.value ?: BigDecimal.ZERO -} - -enum class ProgressState : WidgetState { Loading, Refreshing, Done, Error } - -enum class ErrorType { - NoInternetConnection, - UnknownBlockchain, -} - -sealed class WalletMainButton(enabled: Boolean) : Button(enabled) { - class SendButton(enabled: Boolean) : WalletMainButton(enabled) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/AppCurrencyMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/AppCurrencyMiddleware.kt deleted file mode 100644 index 857d649336..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/AppCurrencyMiddleware.kt +++ /dev/null @@ -1,157 +0,0 @@ -package com.tangem.tap.features.wallet.redux.middlewares - -import com.tangem.common.extensions.guard -import com.tangem.core.analytics.Analytics -import com.tangem.data.source.preferences.model.DataSourceCurrency -import com.tangem.data.source.preferences.model.DataSourceFiatCurrency -import com.tangem.data.source.preferences.storage.FiatCurrenciesPrefStorage -import com.tangem.domain.appcurrency.repository.AppCurrencyRepository -import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles -import com.tangem.tap.common.analytics.events.AnalyticsParam -import com.tangem.tap.common.analytics.events.MainScreen -import com.tangem.tap.common.entities.FiatCurrency -import com.tangem.tap.common.extensions.dispatchDialogShow -import com.tangem.tap.common.extensions.dispatchWithMain -import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.domain.TapWalletManager -import com.tangem.tap.features.details.redux.DetailsAction -import com.tangem.tap.features.wallet.domain.WalletRepository -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.features.wallet.redux.models.WalletDialog -import com.tangem.tap.features.walletSelector.redux.WalletSelectorAction -import com.tangem.tap.scope -import com.tangem.tap.store -import com.tangem.tap.userWalletsListManager -import com.tangem.utils.coroutines.JobHolder -import com.tangem.utils.coroutines.saveIn -import kotlinx.coroutines.launch -import timber.log.Timber - -class AppCurrencyMiddleware( - private val walletRepository: WalletRepository, - private val tapWalletManager: TapWalletManager, - private val fiatCurrenciesPrefStorage: FiatCurrenciesPrefStorage, - private val featureToggles: WalletFeatureToggles, - private val appCurrencyRepository: AppCurrencyRepository, - private val appCurrencyProvider: () -> FiatCurrency, -) { - private val showSelectorJobHolder = JobHolder() - - fun handle(action: WalletAction.AppCurrencyAction) { - when (action) { - is WalletAction.AppCurrencyAction.ChooseAppCurrency -> showSelector() - is WalletAction.AppCurrencyAction.SelectAppCurrency -> selectCurrency(action) - } - } - - private fun showSelector() { - if (featureToggles.isRedesignedScreenEnabled) { - showSelectorNew() - } else { - showSelectorLegacy() - } - } - - private fun selectCurrency(action: WalletAction.AppCurrencyAction.SelectAppCurrency) { - if (featureToggles.isRedesignedScreenEnabled) { - selectCurrencyNew(action.fiatCurrency) - } else { - selectCurrencyLegacy(action.fiatCurrency) - } - } - - private fun showSelectorNew() { - scope.launch { - val currencies = appCurrencyRepository.getAvailableAppCurrencies() - - store.dispatchDialogShow( - WalletDialog.CurrencySelectionDialog( - currenciesList = currencies.map { appCurrency -> - FiatCurrency( - code = appCurrency.code, - name = appCurrency.name, - symbol = appCurrency.symbol, - ) - }, - currentAppCurrency = appCurrencyProvider.invoke(), - ), - ) - }.saveIn(showSelectorJobHolder) - } - - private fun showSelectorLegacy() { - val storedFiatCurrencies = fiatCurrenciesPrefStorage.restore() - if (storedFiatCurrencies.isNotEmpty()) { - store.dispatchDialogShow( - WalletDialog.CurrencySelectionDialog( - currenciesList = storedFiatCurrencies.mapToUiModel(), - currentAppCurrency = appCurrencyProvider.invoke(), - ), - ) - } - - scope.launch { - runCatching { walletRepository.getCurrencyList() } - .onSuccess { response -> - val currenciesList = response.currencies - .map { with(it) { DataSourceCurrency(id, code, name, rateBTC, unit, type) } } - - if (currenciesList.isNotEmpty() && currenciesList.toSet() != storedFiatCurrencies.toSet()) { - fiatCurrenciesPrefStorage.save(currenciesList) - store.dispatchDialogShow( - WalletDialog.CurrencySelectionDialog( - currenciesList = currenciesList.mapToUiModel(), - currentAppCurrency = appCurrencyProvider.invoke(), - ), - ) - } - } - } - } - - private fun selectCurrencyNew(fiatCurrency: FiatCurrency) { - Analytics.send(MainScreen.MainCurrencyChanged(AnalyticsParam.CurrencyType.FiatCurrency(fiatCurrency))) - - scope.launch { - appCurrencyRepository.changeAppCurrency(fiatCurrency.code) - - store.dispatchWithMain(GlobalAction.ChangeAppCurrency(fiatCurrency)) - store.dispatchWithMain(DetailsAction.ChangeAppCurrency(fiatCurrency)) - store.dispatchWithMain(WalletSelectorAction.ChangeAppCurrency(fiatCurrency)) - - val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard { - Timber.e("Unable to select currency, no user wallet selected") - return@launch - } - - tapWalletManager.loadData(selectedUserWallet, refresh = true) - } - } - - private fun selectCurrencyLegacy(fiatCurrency: FiatCurrency) { - Analytics.send(MainScreen.MainCurrencyChanged(AnalyticsParam.CurrencyType.FiatCurrency(fiatCurrency))) - fiatCurrenciesPrefStorage.saveAppCurrency( - with(fiatCurrency) { DataSourceFiatCurrency(code, name, symbol) }, - ) - store.dispatch(GlobalAction.ChangeAppCurrency(fiatCurrency)) - store.dispatch(DetailsAction.ChangeAppCurrency(fiatCurrency)) - store.dispatch(WalletSelectorAction.ChangeAppCurrency(fiatCurrency)) - val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard { - Timber.e("Unable to select currency, no user wallet selected") - return - } - scope.launch { - tapWalletManager.loadData(selectedUserWallet, refresh = true) - } - } - - private fun List.mapToUiModel(): List { - return this.map { - FiatCurrency( - code = it.code, - name = it.name, - symbol = it.unit, - ) - } - } -} \ No newline at end of file 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 deleted file mode 100644 index 1b3426fda4..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt +++ /dev/null @@ -1,124 +0,0 @@ -package com.tangem.tap.features.wallet.redux.middlewares - -import com.tangem.common.doOnSuccess -import com.tangem.common.extensions.guard -import com.tangem.common.flatMap -import com.tangem.core.analytics.Analytics -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.tap.common.analytics.events.AnalyticsParam -import com.tangem.tap.common.analytics.events.Token.ButtonRemoveToken -import com.tangem.tap.common.extensions.addContext -import com.tangem.tap.common.extensions.dispatchDialogShow -import com.tangem.tap.common.extensions.dispatchErrorNotification -import com.tangem.tap.common.extensions.dispatchWithMain -import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.domain.TapError -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.features.wallet.redux.WalletState -import com.tangem.tap.features.wallet.redux.models.WalletDialog -import com.tangem.tap.proxy.redux.DaggerGraphState -import com.tangem.tap.scope -import com.tangem.tap.store -import com.tangem.tap.userWalletsListManager -import com.tangem.tap.walletCurrenciesManager -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import timber.log.Timber - -class MultiWalletMiddleware { - - @Suppress("LongMethod", "ComplexMethod") - fun handle(action: WalletAction.MultiWallet, walletState: WalletState?) { - when (action) { - is WalletAction.MultiWallet.SelectWallet -> { - if (action.currency != null) { - store.dispatch(NavigationAction.NavigateTo(screen = AppScreen.WalletDetails)) - } - } - is WalletAction.MultiWallet.TryToRemoveWallet -> { - val currency = action.currency - val walletManager = walletState?.getWalletManager(currency).guard { - store.dispatchErrorNotification(TapError.UnsupportedState("walletManager is NULL")) - store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) - return - } - - if (currency.isBlockchain() && walletManager.cardTokens.isNotEmpty()) { - store.dispatchDialogShow( - WalletDialog.TokensAreLinkedDialog( - currencyTitle = currency.currencyName, - currencySymbol = currency.currencySymbol, - ), - ) - } else { - store.dispatchDialogShow( - WalletDialog.RemoveWalletDialog( - currencyTitle = currency.currencyName, - onOk = { - Analytics.send(ButtonRemoveToken(AnalyticsParam.CurrencyType.Currency(currency))) - store.dispatch(WalletAction.MultiWallet.RemoveWallet(currency)) - store.dispatch(NavigationAction.PopBackTo()) - }, - ), - ) - } - } - is WalletAction.MultiWallet.RemoveWallet -> { - val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard { - Timber.e("Unable to remove wallet, no user wallet selected") - return - } - scope.launch { - walletCurrenciesManager.removeCurrency( - userWallet = selectedUserWallet, - currencyToRemove = action.currency, - ) - } - } - is WalletAction.MultiWallet.BackupWallet -> { - val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard { - Timber.e("Unable to backup wallet, no user wallet selected") - return - } - val scanResponse = selectedUserWallet.scanResponse - Analytics.addContext(scanResponse) - store.dispatch(GlobalAction.Onboarding.Start(scanResponse, canSkipBackup = false)) - store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingWallet)) - } - is WalletAction.MultiWallet.AddMissingDerivations -> { - store.state.globalState.topUpController?.addMissingDerivations(action.blockchains) - } - is WalletAction.MultiWallet.ScanToGetDerivations -> { - val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard { - Timber.e("Unable to scan to get derivations, no user wallet selected") - return - } - store.state.globalState.topUpController?.scanToGetDerivations() - scanAndUpdateCard(selectedUserWallet) - } - else -> {} - } - } - - private fun scanAndUpdateCard(selectedUserWallet: UserWallet) = scope.launch(Dispatchers.Default) { - store.state.daggerGraphState.get(DaggerGraphState::scanCardProcessor) - .scan(cardId = selectedUserWallet.cardId, allowsRequestAccessCodeFromRepository = true) - .flatMap { scanResponse -> - userWalletsListManager.update( - userWalletId = selectedUserWallet.walletId, - update = { userWallet -> - userWallet.copy( - scanResponse = scanResponse, - ) - }, - ) - } - .doOnSuccess { updatedUserWallet -> - store.dispatchWithMain(WalletAction.MultiWallet.AddMissingDerivations(emptyList())) - store.dispatchWithMain(GlobalAction.SaveScanResponse(updatedUserWallet.scanResponse)) - store.state.globalState.tapWalletManager.loadData(updatedUserWallet, refresh = true) - } - } -} \ No newline at end of file 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 3864885ded..dac4ffd59d 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 @@ -5,7 +5,6 @@ import com.google.firebase.crashlytics.FirebaseCrashlytics import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.Blockchain -import com.tangem.common.extensions.guard import com.tangem.core.analytics.Analytics import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction @@ -14,45 +13,45 @@ import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.NetworkAddress import com.tangem.feature.swap.presentation.SwapFragment import com.tangem.features.send.api.navigation.SendRouter -import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Token import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder -import com.tangem.tap.common.extensions.dispatchDebugErrorNotification -import com.tangem.tap.common.extensions.dispatchErrorNotification -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.common.extensions.dispatchOpenUrl +import com.tangem.tap.common.extensions.* +import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.TapError 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.models.Currency -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.features.wallet.redux.WalletState -import com.tangem.tap.features.wallet.redux.models.WalletDialog import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager import com.tangem.tap.network.exchangeServices.buyErc20TestnetTokens import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.scope import com.tangem.tap.store import kotlinx.coroutines.launch +import org.rekotlin.Middleware @Suppress("LargeClass") -class TradeCryptoMiddleware { +@Deprecated("Will be removed soon") +object TradeCryptoMiddleware { + + val middleware: Middleware = { _, appState -> + { nextDispatch -> + { action -> + if (action is TradeCryptoAction) { + handle(appState, action) + } + nextDispatch(action) + } + } + } @Suppress("LongMethod", "CyclomaticComplexMethod") - fun handle(state: () -> AppState?, action: TradeCryptoAction) { + private fun handle(state: () -> AppState?, action: TradeCryptoAction) { if (DemoHelper.tryHandle(state, action)) return when (action) { - is TradeCryptoAction.Buy -> proceedBuyAction(state, action) - is TradeCryptoAction.Sell -> proceedSellAction() - is TradeCryptoAction.SendCrypto -> preconfigureAndOpenSendScreen(action) + is TradeCryptoAction.SendCrypto -> preconfigureAndOpenSendScreen() is TradeCryptoAction.FinishSelling -> openReceiptUrl(action.transactionId) - is TradeCryptoAction.Swap -> { - // todo remove old flow - } is TradeCryptoAction.New.Buy -> proceedNewBuyAction(state, action) is TradeCryptoAction.New.Sell -> proceedNewSellAction(action) is TradeCryptoAction.New.Swap -> openSwap( @@ -63,54 +62,6 @@ class TradeCryptoMiddleware { } } - @Deprecated("Use proceedNewBuyAction instead") - private fun proceedBuyAction(state: () -> AppState?, action: TradeCryptoAction.Buy) { - val selectedWalletData = store.state.walletState.selectedWalletData ?: return - val currency = chooseAppropriateCurrency(store.state.walletState) ?: return - - Analytics.send(Token.ButtonBuy(AnalyticsParam.CurrencyType.Currency(currency))) - if (action.checkUserLocation && state()?.globalState?.userCountryCode == RUSSIA_COUNTRY_CODE) { - store.dispatchOnMain(WalletAction.DialogAction.RussianCardholdersWarningDialog()) - return - } - - val card = store.state.globalState.scanResponse?.card ?: return - val addresses = selectedWalletData.walletAddresses?.list.orEmpty() - if (addresses.isEmpty()) return - - val exchangeManager = store.state.globalState.exchangeManager - val appCurrency = store.state.globalState.appCurrency - - 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") - return - } - - scope.launch { - buyErc20TestnetTokens( - card = card, - walletManager = walletManager, - destinationAddress = currency.token.contractAddress, - ) - } - return - } - - exchangeManager.getUrl( - action = CurrencyExchangeManager.Action.Buy, - blockchain = currency.blockchain, - cryptoCurrencyName = currency.currencySymbol, - fiatCurrencyName = appCurrency.code, - walletAddress = addresses[0].address, - isDarkTheme = MutableAppThemeModeHolder.isDarkThemeActive, - )?.let { - store.dispatchOpenUrl(it) - Analytics.send(Token.Topup.ScreenOpened()) - } - } - private fun proceedNewBuyAction(state: () -> AppState?, action: TradeCryptoAction.New.Buy) { val networkAddress = action.cryptoCurrencyStatus.value.networkAddress ?.defaultAddress @@ -132,15 +83,9 @@ class TradeCryptoMiddleware { if (action.checkUserLocation && state()?.globalState?.userCountryCode == RUSSIA_COUNTRY_CODE) { val dialogData = topUrl?.let { - WalletDialog.RussianCardholdersWarningDialog.Data( - topUpUrl = it, - ) + AppDialog.RussianCardholdersWarningDialog.Data(topUpUrl = it) } - store.dispatchOnMain( - WalletAction.DialogAction.RussianCardholdersWarningDialog( - dialogData = dialogData, - ), - ) + store.dispatchDialogShow(AppDialog.RussianCardholdersWarningDialog(data = dialogData)) return } @@ -174,29 +119,6 @@ class TradeCryptoMiddleware { } } - private fun proceedSellAction() { - val selectedWalletData = store.state.walletState.selectedWalletData ?: return - val currency = chooseAppropriateCurrency(store.state.walletState) ?: return - - val appCurrency = store.state.globalState.appCurrency - val addresses = selectedWalletData.walletAddresses?.list.orEmpty() - if (addresses.isEmpty()) return - - Analytics.send(Token.ButtonSell(AnalyticsParam.CurrencyType.Currency(currency))) - - store.state.globalState.exchangeManager.getUrl( - action = CurrencyExchangeManager.Action.Sell, - blockchain = currency.blockchain, - cryptoCurrencyName = currency.currencySymbol, - fiatCurrencyName = appCurrency.code, - walletAddress = addresses[0].address, - isDarkTheme = MutableAppThemeModeHolder.isDarkThemeActive, - )?.let { - store.dispatchOpenUrl(it) - Analytics.send(Token.Withdraw.ScreenOpened()) - } - } - private fun proceedNewSellAction(action: TradeCryptoAction.New.Sell) { val networkAddress = action.cryptoCurrencyStatus.value.networkAddress ?.defaultAddress @@ -217,41 +139,31 @@ class TradeCryptoMiddleware { } } - private fun chooseAppropriateCurrency(walletState: WalletState): Currency? { - return if (walletState.primaryTokenData == null) { - walletState.selectedWalletData?.currency - } else { - walletState.primaryTokenData?.currency as? Currency.Token - }.guard { - store.dispatchDebugErrorNotification("Can't select an appropriate currency for a Trade action") - return null - } - } - - private fun preconfigureAndOpenSendScreen(action: TradeCryptoAction.SendCrypto) { - val selectedWalletData = store.state.walletState.selectedWalletData ?: return - - Analytics.send(Token.ButtonSend(AnalyticsParam.CurrencyType.Currency(selectedWalletData.currency))) - val walletManager = store.state.walletState.getWalletManager(selectedWalletData.currency).guard { - FirebaseCrashlytics.getInstance().recordException(IllegalStateException("WalletManager is null")) - return - } - - store.dispatchOnMain( - PrepareSendScreen( - walletManager = walletManager, - coinAmount = walletManager.wallet.amounts[AmountType.Coin], - coinRate = selectedWalletData.fiatRate, - ), - ) - store.dispatchOnMain( - SendAction.SendSpecificTransaction( - sendAmount = action.amount, - destinationAddress = action.destinationAddress, - transactionId = action.transactionId, - ), - ) - store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Send)) + private fun preconfigureAndOpenSendScreen() = scope.launch { + // FIXME: [REDACTED_JIRA] + // val selectedWalletData = store.state.walletState.selectedWalletData ?: return + // + // Analytics.send(Token.ButtonSend(AnalyticsParam.CurrencyType.Currency(selectedWalletData.currency))) + // val walletManager = store.state.walletState.getWalletManager(selectedWalletData.currency).guard { + // FirebaseCrashlytics.getInstance().recordException(IllegalStateException("WalletManager is null")) + // return + // } + // + // store.dispatchOnMain( + // PrepareSendScreen( + // walletManager = walletManager, + // coinAmount = walletManager.wallet.amounts[AmountType.Coin], + // coinRate = selectedWalletData.fiatRate, + // ), + // ) + // store.dispatchOnMain( + // SendAction.SendSpecificTransaction( + // sendAmount = action.amount, + // destinationAddress = action.destinationAddress, + // transactionId = action.transactionId, + // ), + // ) + // store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Send)) } private fun openReceiptUrl(transactionId: String) { 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 deleted file mode 100644 index b381ec0365..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletDialogsMiddleware.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.tangem.tap.features.wallet.redux.middlewares - -import com.tangem.core.analytics.Analytics -import com.tangem.tap.common.analytics.events.AnalyticsParam -import com.tangem.tap.common.analytics.events.Token -import com.tangem.tap.common.extensions.dispatchDialogHide -import com.tangem.tap.common.extensions.dispatchDialogShow -import com.tangem.tap.common.redux.AppDialog -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.features.wallet.redux.models.WalletDialog -import com.tangem.tap.store - -class WalletDialogsMiddleware { - fun handle(action: WalletAction.DialogAction) { - when (action) { - is WalletAction.DialogAction.SignedHashesMultiWalletDialog -> { - store.dispatchDialogShow(WalletDialog.SignedHashesMultiWalletDialog) - } - is WalletAction.DialogAction.ChooseTradeActionDialog -> { - store.state.walletState.selectedWalletData?.let { - Analytics.send(Token.ButtonExchange(AnalyticsParam.CurrencyType.Currency(it.currency))) - } - store.dispatchDialogShow( - WalletDialog.ChooseTradeActionDialog( - buyAllowed = action.buyAllowed, - sellAllowed = action.sellAllowed, - swapAllowed = action.swapAllowed, - ), - ) - } - is WalletAction.DialogAction.QrCode -> { - store.dispatchDialogShow( - AppDialog.AddressInfoDialog( - currency = action.currency, - addressData = action.selectedAddress, - ), - ) - } - is WalletAction.DialogAction.ChooseCurrency -> { - if (action.amounts.isEmpty()) return - - store.dispatchDialogShow( - WalletDialog.SelectAmountToSendDialog( - amounts = action.amounts, - ), - ) - } - is WalletAction.DialogAction.RussianCardholdersWarningDialog -> { - store.dispatchDialogShow(WalletDialog.RussianCardholdersWarningDialog(action.dialogData)) - } - is WalletAction.DialogAction.Hide -> { - store.dispatchDialogHide() - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt deleted file mode 100644 index ce93801220..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt +++ /dev/null @@ -1,412 +0,0 @@ -package com.tangem.tap.features.wallet.redux.middlewares - -import androidx.lifecycle.LifecycleCoroutineScope -import com.google.firebase.crashlytics.FirebaseCrashlytics -import com.tangem.blockchain.common.Amount -import com.tangem.blockchain.common.AmountType -import com.tangem.blockchain.common.WalletManager -import com.tangem.blockchain.common.address.AddressType -import com.tangem.common.CompletionResult -import com.tangem.common.doOnSuccess -import com.tangem.common.extensions.guard -import com.tangem.core.analytics.Analytics -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction -import com.tangem.datasource.connection.NetworkConnectionManager -import com.tangem.domain.tokens.legacy.TradeCryptoAction -import com.tangem.domain.userwallets.GetCardImageUseCase -import com.tangem.domain.wallets.legacy.lockIfLockable -import com.tangem.tap.* -import com.tangem.tap.common.analytics.events.AnalyticsParam -import com.tangem.tap.common.analytics.events.Basic -import com.tangem.tap.common.analytics.events.MainScreen -import com.tangem.tap.common.analytics.events.Token -import com.tangem.tap.common.extensions.* -import com.tangem.tap.common.redux.AppState -import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.domain.TapError -import com.tangem.tap.domain.model.WalletDataModel -import com.tangem.tap.domain.model.WalletStoreModel -import com.tangem.tap.features.demo.DemoHelper -import com.tangem.tap.features.home.redux.HomeAction -import com.tangem.tap.features.send.redux.PrepareSendScreen -import com.tangem.tap.features.wallet.models.Currency -import com.tangem.tap.features.wallet.models.getSendableAmounts -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.features.wallet.redux.WalletState -import com.tangem.tap.proxy.redux.DaggerGraphState -import com.tangem.utils.coroutines.ifActive -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import org.rekotlin.Action -import org.rekotlin.Middleware -import timber.log.Timber -import java.math.BigDecimal - -@Suppress("LargeClass") -class WalletMiddleware { - private val tradeCryptoMiddleware = TradeCryptoMiddleware() - private val warningsMiddleware = WarningsMiddleware() - private val multiWalletMiddleware = MultiWalletMiddleware() - private val walletDialogMiddleware = WalletDialogsMiddleware() - private val appCurrencyMiddleware by lazy(mode = LazyThreadSafetyMode.NONE) { - AppCurrencyMiddleware( - // TODO("After adding DI") get dependencies by DI - walletRepository = store.state.featureRepositoryProvider.walletRepository, - tapWalletManager = store.state.globalState.tapWalletManager, - fiatCurrenciesPrefStorage = preferencesStorage.fiatCurrenciesPrefStorage, - appCurrencyRepository = store.state.daggerGraphState.get(DaggerGraphState::appCurrencyRepository), - featureToggles = store.state.daggerGraphState.get(DaggerGraphState::walletFeatureToggles), - appCurrencyProvider = { store.state.globalState.appCurrency }, - ) - } - - private val networkConnectionManager: NetworkConnectionManager - get() = store.state.daggerGraphState.get(DaggerGraphState::networkConnectionManager) - - private var updateWalletStoresJob: Job? = null - set(value) { - field?.cancel() - field = value - } - - val walletMiddleware: Middleware = { _, state -> - { next -> - { action -> - handleAction(state, action) - next(action) - } - } - } - - @Suppress("LongMethod", "ComplexMethod") - private fun handleAction(state: () -> AppState?, action: Action) { - if (DemoHelper.tryHandle(state, action)) return - - val globalState = store.state.globalState - val walletState = store.state.walletState - - when (action) { - is TradeCryptoAction -> tradeCryptoMiddleware.handle(state, action) - is WalletAction.Warnings -> warningsMiddleware.handle(action, globalState) - is WalletAction.MultiWallet -> multiWalletMiddleware.handle(action, walletState) - is WalletAction.AppCurrencyAction -> appCurrencyMiddleware.handle(action) - is WalletAction.DialogAction -> walletDialogMiddleware.handle(action) - is WalletAction.CreateWallet -> { - scope.launch { - when (val result = tangemSdkManager.createWallet(globalState.scanResponse?.card?.cardId)) { - is CompletionResult.Success -> { - val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard { - Timber.e("Unable to create wallet, no user wallet selected") - return@launch - } - val updatedScanResponse = selectedUserWallet.scanResponse.copy( - card = result.data, - ) - store.dispatchWithMain(GlobalAction.SaveScanResponse(updatedScanResponse)) - userWalletsListManager.update(selectedUserWallet.walletId) { userWallet -> - userWallet.copy(scanResponse = updatedScanResponse) - } - } - is CompletionResult.Failure -> Unit - } - } - } - is WalletAction.Scan -> { - store.dispatch(NavigationAction.PopBackTo(AppScreen.Home)) - action.scope.launch { - delay(timeMillis = 700) - store.dispatchOnMain(HomeAction.ReadCard(action.onScanSuccessEvent, action.scope)) - } - } - is WalletAction.LoadData, - is WalletAction.LoadData.Refresh, - -> { - val selectedWallet = userWalletsListManager.selectedUserWalletSync.guard { - Timber.e("Unable to load/refresh wallets data, no user wallet selected") - return - } - - scope.launch { - globalState.tapWalletManager.loadData( - userWallet = selectedWallet, - refresh = action is WalletAction.LoadData.Refresh, - ) - } - - store.dispatchOnMain(WalletAction.UpdateUserWalletArtwork(selectedWallet.walletId)) - } - is WalletAction.CopyAddress -> { - Analytics.send(Token.Receive.ButtonCopyAddress()) - action.context.copyToClipboard(action.address) - store.dispatch(WalletAction.CopyAddress.Success) - } - is WalletAction.ShareAddress -> { - Analytics.send(Token.Receive.ButtonShareAddress()) - action.context.shareText(action.address) - } - is WalletAction.ExploreAddress -> { - Analytics.send(Token.ButtonExplore()) - store.dispatchOpenUrl(action.exploreUrl) - } - is WalletAction.Send -> { - val walletStore = walletState.getWalletStore(walletState.selectedCurrency) - val selectedWalletData = walletState.selectedWalletData - val walletManager = walletStore?.walletManager - - if (walletStore == null || walletManager == null || selectedWalletData == null) { - val error = TapError.UnsupportedState( - "WalletAction.Send: walletStore or selectedWalletData or walletManager is null", - ) - FirebaseCrashlytics.getInstance().recordException(IllegalStateException(error.stateError)) - store.dispatchErrorNotification(error) - return - } - if (!networkConnectionManager.isOnline) { - store.dispatchErrorNotification(TapError.NoInternetConnection) - return - } - - val currency = selectedWalletData.currency - val initSendStateAction = if (action.amount == null) { - val sendableAmounts = walletManager.wallet.getSendableAmounts() - if (sendableAmounts.isEmpty()) { - val error = TapError.UnsupportedState("WalletAction.Send: Nothing to send") - FirebaseCrashlytics.getInstance().recordException(IllegalStateException(error.stateError)) - store.dispatchErrorNotification(error) - return - } - - if (walletState.isMultiwalletAllowed) { - val amountToSend = findAmountToSend(currency = currency, amounts = sendableAmounts) - if (amountToSend == null) { - val error = TapError.UnsupportedState("WalletAction.Send: Amount to send is null") - FirebaseCrashlytics.getInstance().recordException(IllegalStateException(error.stateError)) - store.dispatchErrorNotification(error) - return - } - - makeInitSendStateActionByCurrency( - currency = currency, - amount = amountToSend, - walletStore = walletStore, - walletManager = walletManager, - selectedWalletData = selectedWalletData, - ) - } else { - val isSingleAmount = sendableAmounts.size == 1 - if (isSingleAmount) { - makeInitSendStateActionByAmount( - amount = sendableAmounts.first(), - walletStore = walletStore, - walletManager = walletManager, - selectedWalletData = selectedWalletData, - ) - } else { - store.dispatch(WalletAction.DialogAction.ChooseCurrency(sendableAmounts)) - return - } - } - } else { - // action.amount received from the ChooseCurrency dialog - makeInitSendStateActionByAmount( - amount = action.amount, - walletManager = walletManager, - walletStore = walletStore, - selectedWalletData = selectedWalletData, - ) - } - - Analytics.send(Token.ButtonSend(AnalyticsParam.CurrencyType.Currency(currency))) - store.dispatch(initSendStateAction) - store.dispatch(NavigationAction.NavigateTo(AppScreen.Send)) - } - is WalletAction.ShowSaveWalletIfNeeded -> { - showSaveWalletIfNeeded() - } - is WalletAction.ChangeWallet -> { - changeWallet(walletState, action.scope) - } - is WalletAction.UserWalletChanged -> Unit - is WalletAction.WalletStoresChanged -> { - // Cancel update job when new wallet stores received - updateWalletStoresJob = scope.launch(Dispatchers.Default) { - ifActive { fetchTotalFiatBalance(action.walletStores) } - ifActive { findMissedDerivations(action.walletStores) } - ifActive { tryToShowAppRatingWarning(action.walletStores) } - ifActive { store.state.globalState.topUpController?.walletStoresChanged(action.walletStores) } - } - } - is WalletAction.TotalFiatBalanceChanged -> Unit - is WalletAction.PopBackToInitialScreen -> { - userWalletsListManager.lockIfLockable() - val screen = if (walletState.canSaveUserWallets) { - AppScreen.Welcome - } else { - AppScreen.Home - } - - store.dispatchOnMain(NavigationAction.PopBackTo(screen)) - } - is WalletAction.ChangeSelectedAddress -> { - changeSelectedWalletAddress(action.type, walletState) - } - is WalletAction.UpdateUserWalletArtwork -> { - scope.launch { - userWalletsListManager - .update( - userWalletId = action.walletId, - update = { userWallet -> - userWallet.copy( - artworkUrl = GetCardImageUseCase().invoke( - cardId = userWallet.cardId, - cardPublicKey = userWallet.scanResponse.card.cardPublicKey, - ), - ) - }, - ) - .doOnSuccess { - store.dispatch( - WalletAction.SetArtworkUrl(userWalletId = action.walletId, url = it.artworkUrl), - ) - } - } - } - } - } - - private fun findAmountToSend(currency: Currency, amounts: List): Amount? { - return amounts.find { amount -> - val amountType = amount.type - if (amountType is AmountType.Token && currency is Currency.Token) { - val token = amountType.token - token.symbol == currency.currencySymbol && token.contractAddress == currency.token.contractAddress - } else { - amount.currencySymbol == currency.currencySymbol - } - } - } - - private fun makeInitSendStateActionByAmount( - amount: Amount, - walletStore: WalletStoreModel, - walletManager: WalletManager, - selectedWalletData: WalletDataModel, - ): PrepareSendScreen = when (amount.type) { - AmountType.Coin -> - PrepareSendScreen( - walletManager = walletManager, - coinAmount = amount, - coinRate = selectedWalletData.fiatRate, - ) - is AmountType.Token -> { - PrepareSendScreen( - walletManager = walletManager, - coinAmount = walletManager.wallet.amounts[AmountType.Coin], - coinRate = walletStore.blockchainWalletData.fiatRate, - tokenAmount = amount, - tokenRate = selectedWalletData.fiatRate, - ) - } - AmountType.Reserve -> { - val exception = IllegalStateException("WalletAction.Send: Reserve can't be sent") - FirebaseCrashlytics.getInstance().recordException(exception) - throw exception - } - } - - private fun makeInitSendStateActionByCurrency( - currency: Currency, - amount: Amount, - walletStore: WalletStoreModel, - walletManager: WalletManager, - selectedWalletData: WalletDataModel, - ): PrepareSendScreen = when (currency) { - is Currency.Blockchain -> { - PrepareSendScreen( - walletManager = walletManager, - coinAmount = amount, - coinRate = selectedWalletData.fiatRate, - ) - } - is Currency.Token -> { - PrepareSendScreen( - walletManager = walletManager, - coinAmount = walletManager.wallet.amounts[AmountType.Coin], - coinRate = walletStore.blockchainWalletData.fiatRate, - tokenAmount = amount, - tokenRate = selectedWalletData.fiatRate, - ) - } - } - - private fun changeSelectedWalletAddress(type: AddressType, state: WalletState) { - val selectedUserWalletId = userWalletsListManager.selectedUserWalletSync?.walletId.guard { - Timber.e("Unable to change selected wallet address, no user wallet selected") - return - } - val selectedCurrency = state.selectedCurrency.guard { - Timber.e("Unable to change selected wallet address, no currency selected") - return - } - - scope.launch(Dispatchers.Default) { - walletStoresManager.updateSelectedAddress(selectedUserWalletId, selectedCurrency, type) - } - } - - private suspend fun fetchTotalFiatBalance(walletStores: List) { - val totalFiatBalance = totalFiatBalanceCalculator.calculateOrNull(walletStores) - - if (totalFiatBalance != null) { - store.dispatchOnMain(WalletAction.TotalFiatBalanceChanged(totalFiatBalance)) - } - } - - private fun findMissedDerivations(wallStores: List) { - val missedDerivations = wallStores - .filter { store -> - store.walletsData.any { it.status is WalletDataModel.MissedDerivation } - } - .map(WalletStoreModel::blockchainNetwork) - - store.dispatchOnMain(WalletAction.MultiWallet.AddMissingDerivations(missedDerivations)) - } - - private fun tryToShowAppRatingWarning(walletStores: List) { - warningsMiddleware.tryToShowAppRatingWarning( - hasNonZeroWallets = walletStores - .flatMap { it.walletsData } - .any { it.status.amount.isGreaterThan(BigDecimal.ZERO) }, - ) - } - - private fun showSaveWalletIfNeeded() { - if (preferencesStorage.shouldShowSaveUserWalletScreen && - tangemSdkManager.canUseBiometry && - store.state.navigationState.backStack.lastOrNull() == AppScreen.Wallet - ) { - store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.SaveWallet)) - } - } - - private fun changeWallet(state: WalletState, lifecycleScope: LifecycleCoroutineScope) { - when { - state.canSaveUserWallets -> { - Analytics.send(MainScreen.ButtonMyWallets()) - store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.WalletSelector)) - } - else -> { - Analytics.send(MainScreen.ButtonScanCard()) - store.dispatch( - WalletAction.Scan( - onScanSuccessEvent = Basic.CardWasScanned(AnalyticsParam.ScannedFrom.Main), - scope = lifecycleScope, - ), - ) - } - } - } -} \ No newline at end of file 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 deleted file mode 100644 index 39e905255e..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt +++ /dev/null @@ -1,184 +0,0 @@ -package com.tangem.tap.features.wallet.redux.middlewares - -import com.tangem.blockchain.common.BlockchainSdkError -import com.tangem.blockchain.common.SignatureCountValidator -import com.tangem.blockchain.extensions.SimpleResult -import com.tangem.common.card.FirmwareVersion -import com.tangem.domain.common.TapWorkarounds.isTestCard -import com.tangem.domain.common.util.cardTypesResolver -import com.tangem.domain.models.scan.CardDTO -import com.tangem.domain.models.scan.ScanResponse -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.common.redux.global.GlobalState -import com.tangem.tap.domain.configurable.warningMessage.WarningMessage -import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager -import com.tangem.tap.domain.extensions.hasSignedHashes -import com.tangem.tap.features.demo.isDemoCard -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.preferencesStorage -import com.tangem.tap.proxy.redux.DaggerGraphState -import com.tangem.tap.scope -import com.tangem.tap.store -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext - -// TODO: Delete with WalletFeatureToggles -@Deprecated(message = "Used only in old wallet screen") -class WarningsMiddleware { - fun handle(action: WalletAction.Warnings, globalState: GlobalState?) { - when (action) { - is WalletAction.Warnings.Update -> setWarningMessages() - is WalletAction.Warnings.CheckIfNeeded -> { - showCardWarningsIfNeeded(globalState) - val readyToShow = preferencesStorage.appRatingLaunchObserver.isReadyToShow() - if (readyToShow) addWarningMessage(warning = WarningMessagesManager.appRatingWarning, autoUpdate = true) - } - - is WalletAction.Warnings.CheckHashesCount.VerifyOnlineIfNeeded -> checkHashesCountOnlineIfNeeded() - is WalletAction.Warnings.CheckHashesCount.SaveCardId -> { - val cardId = globalState?.scanResponse?.card?.cardId - cardId?.let { preferencesStorage.usedCardsPrefStorage.scanned(it) } - } - is WalletAction.Warnings.AppRating.RemindLater -> { - preferencesStorage.appRatingLaunchObserver.applyDelayedShowing() - } - is WalletAction.Warnings.AppRating.SetNeverToShow -> { - preferencesStorage.appRatingLaunchObserver.setNeverToShow() - } - is WalletAction.Warnings.CheckRemainingSignatures -> { - if (action.remainingSignatures != null && - action.remainingSignatures <= WarningMessagesManager.REMAINING_SIGNATURES_WARNING - ) { - // store.state.globalState.warningManager?.removeWarnings(R.string.warning_low_signatures_format) - addWarningMessage( - warning = WarningMessagesManager.remainingSignaturesNotEnough(action.remainingSignatures), - autoUpdate = true, - ) - } - } - is WalletAction.Warnings.AppRating, - is WalletAction.Warnings.CheckHashesCount, - is WalletAction.Warnings.Set, - -> Unit - } - } - - fun tryToShowAppRatingWarning(hasNonZeroWallets: Boolean) { - if (hasNonZeroWallets) { - preferencesStorage.appRatingLaunchObserver.foundWalletWithFunds() - } - if (preferencesStorage.appRatingLaunchObserver.isReadyToShow()) { - addWarningMessage(WarningMessagesManager.appRatingWarning, true) - } - } - - private fun showCardWarningsIfNeeded(globalState: GlobalState?) { - globalState?.scanResponse?.let { scanResponse -> - val card = scanResponse.card - globalState.warningManager?.removeWarnings(WarningMessage.Origin.Local) - if (card.isTestCard) { - addWarningMessage(WarningMessagesManager.testCardWarning, autoUpdate = true) - return@let - } - - showWarningLowRemainingSignaturesIfNeeded(card) - if (card.firmwareVersion.type != FirmwareVersion.FirmwareType.Release) { - addWarningMessage(WarningMessagesManager.devCardWarning) - } else if (!preferencesStorage.usedCardsPrefStorage.wasScanned(card.cardId)) { - checkIfWarningNeeded(scanResponse)?.let { warning -> addWarningMessage(warning) } - } - if (card.firmwareVersion.type == FirmwareVersion.FirmwareType.Release && !globalState.cardVerifiedOnline) { - addWarningMessage(WarningMessagesManager.onlineVerificationFailed) - } - if (scanResponse.isDemoCard()) { - addWarningMessage(WarningMessagesManager.demoCardWarning) - } - setWarningMessages() - } - } - - private fun showWarningLowRemainingSignaturesIfNeeded(card: CardDTO) { - val remainingSignatures = card.wallets.firstOrNull()?.remainingSignatures - if (remainingSignatures != null && - remainingSignatures <= WarningMessagesManager.REMAINING_SIGNATURES_WARNING - ) { - addWarningMessage(WarningMessagesManager.remainingSignaturesNotEnough(remainingSignatures)) - } - } - - private fun checkIfWarningNeeded(scanResponse: ScanResponse): WarningMessage? { - if (scanResponse.cardTypesResolver.isTangemTwins() || scanResponse.isDemoCard()) return null - - if (scanResponse.cardTypesResolver.isMultiwalletAllowed()) { - val isBackupForbidden = with(scanResponse.card.settings) { !(isBackupAllowed || isHDWalletAllowed) } - return if (scanResponse.card.hasSignedHashes() && isBackupForbidden) { - WarningMessagesManager.signedHashesMultiWalletWarning - } else { - store.dispatch(WalletAction.Warnings.CheckHashesCount.SaveCardId) - null - } - } - - return if (scanResponse.card.hasSignedHashes()) { - WarningMessagesManager.alreadySignedHashesWarning - } else { - store.dispatch(WalletAction.Warnings.CheckHashesCount.SaveCardId) - null - } - } - - private fun checkHashesCountOnlineIfNeeded() { - val alreadySignedHashesWarning = WarningMessagesManager.alreadySignedHashesWarning - val manager = store.state.globalState.warningManager ?: return - if (manager.containsWarning(alreadySignedHashesWarning)) return - - val networkConnectionManager = store.state.daggerGraphState.get(DaggerGraphState::networkConnectionManager) - if (!networkConnectionManager.isOnline) return - - val scanResponse = store.state.globalState.scanResponse - val card = scanResponse?.card - if (card == null || preferencesStorage.usedCardsPrefStorage.wasScanned(card.cardId)) return - - if (scanResponse.cardTypesResolver.isTangemTwins() || scanResponse.cardTypesResolver.isMultiwalletAllowed()) { - return - } - - val validator = store.state.walletState.walletManagers.firstOrNull() - as? SignatureCountValidator - scope.launch { - val signedHashes = card.wallets.firstOrNull()?.totalSignedHashes ?: 0 - val result = validator?.validateSignatureCount(signedHashes) - withContext(Dispatchers.Main) { - when (result) { - SimpleResult.Success -> { - store.dispatch(WalletAction.Warnings.CheckHashesCount.SaveCardId) - } - is SimpleResult.Failure -> - if (signedHashes > 0 || result.error is BlockchainSdkError.SignatureCountNotMatched) { - alreadySignedHashesWarning.isHidden = false - addWarningMessage(alreadySignedHashesWarning, true) - } - null -> Unit - } - } - } - } - - private fun addWarningMessage(warning: WarningMessage, autoUpdate: Boolean = false) { - store.state.globalState.warningManager?.addWarning(warning) - if (autoUpdate) setWarningMessages() - } - - private fun setWarningMessages() { - store.dispatchOnMain(WalletAction.Warnings.Set(getWarnings())) - } - - private fun getWarnings(): List { - val warningManager = store.state.globalState.warningManager ?: return emptyList() - return warningManager.getWarnings( - WarningMessage.Location.MainScreen, - store.state.walletState.blockchains, - ) - } -} \ No newline at end of file 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 deleted file mode 100644 index c8b5747a94..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/models/WalletDialog.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.tangem.tap.features.wallet.redux.models - -import com.tangem.blockchain.common.Amount -import com.tangem.core.navigation.StateDialog -import com.tangem.tap.common.entities.FiatCurrency -import com.tangem.wallet.R - -sealed interface WalletDialog : StateDialog { - data class SelectAmountToSendDialog(val amounts: List) : WalletDialog - object SignedHashesMultiWalletDialog : WalletDialog - data class ChooseTradeActionDialog( - val buyAllowed: Boolean, - val sellAllowed: Boolean, - val swapAllowed: Boolean, - ) : WalletDialog - - data class CurrencySelectionDialog( - val currenciesList: List, - val currentAppCurrency: FiatCurrency, - ) : WalletDialog - - data class RemoveWalletDialog( - val currencyTitle: String, - val onOk: () -> Unit, - ) : WalletDialog { - val messageRes: Int = R.string.token_details_hide_alert_message - val titleRes: Int = R.string.token_details_hide_alert_title - val primaryButtonRes: Int = R.string.token_details_hide_alert_hide - } - - data class TokensAreLinkedDialog( - val currencyTitle: String, - val currencySymbol: String, - ) : WalletDialog { - val messageRes: Int = R.string.token_details_unable_hide_alert_message - val titleRes: Int = R.string.token_details_unable_hide_alert_title - } - - data class RussianCardholdersWarningDialog(val data: Data?) : WalletDialog { - data class Data(val topUpUrl: String) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/AppCurrencyReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/AppCurrencyReducer.kt deleted file mode 100644 index 49ba3fa007..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/AppCurrencyReducer.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.tangem.tap.features.wallet.redux.reducers - -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.features.wallet.redux.WalletState - -class AppCurrencyReducer { - fun reduce(action: WalletAction.AppCurrencyAction, state: WalletState): WalletState { - return when (action) { - is WalletAction.AppCurrencyAction.SelectAppCurrency, - is WalletAction.AppCurrencyAction.ChooseAppCurrency, - -> state - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt deleted file mode 100644 index a0bc33e948..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.tangem.tap.features.wallet.redux.reducers - -import com.tangem.domain.models.scan.CardDTO -import com.tangem.tap.features.wallet.redux.ProgressState -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.features.wallet.redux.WalletState - -class MultiWalletReducer { - @Suppress("LongMethod", "ComplexMethod") - fun reduce(action: WalletAction.MultiWallet, state: WalletState): WalletState { - return when (action) { - is WalletAction.MultiWallet.SelectWallet -> { - state.copy(selectedCurrency = action.currency) - } - - is WalletAction.MultiWallet.TryToRemoveWallet -> state - is WalletAction.MultiWallet.AddMissingDerivations -> state.copy( - missingDerivations = action.blockchains, - ) - - is WalletAction.MultiWallet.BackupWallet -> state - is WalletAction.MultiWallet.ScanToGetDerivations -> state.copy(state = ProgressState.Loading) - is WalletAction.MultiWallet.CheckForBackupWarning -> state.copy( - showBackupWarning = action.card.settings.isBackupAllowed && - action.card.backupStatus == CardDTO.BackupStatus.NoBackup, - ) - - else -> state - } - } -} \ No newline at end of file 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 deleted file mode 100644 index 3fb8a17540..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt +++ /dev/null @@ -1,172 +0,0 @@ -package com.tangem.tap.features.wallet.redux.reducers - -import com.tangem.blockchain.common.Wallet -import com.tangem.blockchain.common.address.AddressType -import com.tangem.domain.common.TapWorkarounds.isTestCard -import com.tangem.domain.models.scan.CardDTO -import com.tangem.domain.userwallets.Artwork -import com.tangem.tap.common.redux.AppState -import com.tangem.tap.domain.TapError -import com.tangem.tap.domain.model.WalletDataModel -import com.tangem.tap.domain.model.WalletStoreModel -import com.tangem.tap.features.wallet.models.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.WalletState -import com.tangem.tap.proxy.AppStateHolder -import com.tangem.tap.userWalletsListManager -import org.rekotlin.Action - -object WalletReducer { - fun reduce(action: Action, state: AppState, appStateHolder: AppStateHolder): WalletState = - internalReduce(action, state, appStateHolder) -} - -@Suppress("LongMethod", "ComplexMethod") -private fun internalReduce(action: Action, state: AppState, appStateHolder: AppStateHolder): WalletState { - val multiWalletReducer = MultiWalletReducer() - val appCurrencyReducer = AppCurrencyReducer() - - if (action !is WalletAction) return state.walletState - - var newState = state.walletState - - when (action) { - is WalletAction.Warnings -> newState = handleCheckSignedHashesActions(action, newState) - is WalletAction.MultiWallet -> newState = multiWalletReducer.reduce(action, newState) - is WalletAction.LoadData.Failure -> { - when (action.error) { - is TapError.NoInternetConnection -> { - newState = newState.copy( - state = ProgressState.Error, - error = ErrorType.NoInternetConnection, - ) - } - is TapError.UnknownBlockchain -> { - newState = newState.copy( - state = ProgressState.Error, - error = ErrorType.UnknownBlockchain, - ) - } - else -> { - newState = newState.copy( - state = ProgressState.Error, - ) - } - } - } - is WalletAction.LoadData -> { - newState = newState.copy( - state = ProgressState.Loading, - error = null, - ) - } - is WalletAction.LoadData.Refresh -> { - newState = newState.copy( - state = ProgressState.Refreshing, - error = null, - ) - } - is WalletAction.AppCurrencyAction -> { - newState = appCurrencyReducer.reduce(action, newState) - } - is WalletAction.UserWalletChanged -> with(action.userWallet) { - val card = scanResponse.card - newState = WalletState( - isMultiwalletAllowed = isMultiCurrency, - cardImage = Artwork( - artworkId = artworkUrl, - ), - isTestnet = card.isTestCard, - state = ProgressState.Loading, - showBackupWarning = isMultiCurrency && - card.settings.isBackupAllowed && - card.backupStatus == CardDTO.BackupStatus.NoBackup, - walletCardsCount = card.findCardsCount(), - walletsStores = newState.walletsStores, - totalBalance = if (isMultiCurrency) { - newState.totalBalance - } else { - null - }, - ) - } - is WalletAction.WalletStoresChanged -> { - newState = newState.copy( - walletsStores = action.walletStores, - selectedCurrency = findSelectedCurrency( - walletsStores = action.walletStores, - currentSelectedCurrency = newState.selectedCurrency, - isMultiWalletAllowed = newState.isMultiwalletAllowed, - ), - ) - } - is WalletAction.TotalFiatBalanceChanged -> { - newState = newState.copy( - totalBalance = action.balance, - ) - } - is WalletAction.LoadData.Success -> { - newState = newState.copy(state = ProgressState.Done) - } - is WalletAction.UpdateCanSaveUserWallets -> { - newState = newState.copy(canSaveUserWallets = action.canSaveUserWallets) - } - is WalletAction.SetArtworkUrl -> { - val selectedUserWallet = userWalletsListManager.selectedUserWalletSync?.walletId - - if (selectedUserWallet == action.userWalletId) { - newState = newState.copy( - cardImage = Artwork(artworkId = action.url), - ) - } - } - else -> Unit - } - appStateHolder.walletState = newState - return newState -} - -fun findSelectedCurrency( - walletsStores: List, - currentSelectedCurrency: Currency?, - isMultiWalletAllowed: Boolean, -): Currency? = if (isMultiWalletAllowed) { - currentSelectedCurrency -} else { - walletsStores.firstOrNull() - ?.walletsData - ?.firstOrNull() - ?.currency -} - -private fun CardDTO.findCardsCount(): Int? { - return (this.backupStatus as? CardDTO.BackupStatus.Active)?.cardCount?.inc() -} - -fun Wallet.createAddressesData(): List { - val listOfAddressData = mutableListOf() - // put a defaultAddress at the first place - addresses.forEach { - val addressData = WalletDataModel.AddressData( - it.value, - it.type, - getShareUri(it.value), - getExploreUrl(it.value), - ) - if (it.type == AddressType.Default) { - listOfAddressData.add(0, addressData) - } else { - listOfAddressData.add(addressData) - } - } - return listOfAddressData -} - -private fun handleCheckSignedHashesActions(action: WalletAction.Warnings, state: WalletState): WalletState { - return when (action) { - is WalletAction.Warnings.Set -> state.copy(mainWarningsList = action.warningList) - else -> state - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/utils/Constants.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/utils/Constants.kt deleted file mode 100644 index dfab28f18a..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/utils/Constants.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.tangem.tap.features.wallet.redux.utils - -const val UNKNOWN_AMOUNT_SIGN = "—" -const val ROUGH_SIGN = "≈" -const val CAN_BE_LOWER_SIGN = "<" \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/BalanceWidget.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/BalanceWidget.kt deleted file mode 100644 index eb7dfd7126..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/BalanceWidget.kt +++ /dev/null @@ -1,123 +0,0 @@ -package com.tangem.tap.features.wallet.ui - -import androidx.annotation.IdRes -import com.tangem.tap.common.extensions.hide -import com.tangem.tap.common.extensions.show -import com.tangem.tap.domain.model.WalletDataModel -import com.tangem.tap.features.wallet.ui.utils.getFormattedCryptoAmount -import com.tangem.tap.features.wallet.ui.utils.getFormattedFiatAmount -import com.tangem.tap.store -import com.tangem.wallet.R -import com.tangem.wallet.databinding.CardBalanceBinding - -// TODO: Delete with WalletFeatureToggles -@Deprecated(message = "Used only in old wallet screen") -class BalanceWidget( - private val binding: CardBalanceBinding, - private val fragment: WalletFragment, - private val blockchainWalletData: WalletDataModel, - private val tokenWalletData: WalletDataModel?, -) { - - @Suppress("LongMethod", "ComplexMethod") - fun setup() { - when (blockchainWalletData.status) { - is WalletDataModel.Loading -> { - with(binding) { - lBalance.root.show() - lBalanceError.root.hide() - lBalance.tvFiatAmount.hide() - - lBalance.tvCurrency.text = blockchainWalletData.currency.currencyName - lBalance.tvAmount.text = "" - } - - showStatus(R.id.tv_status_loading) - - if (tokenWalletData != null) { - showBalanceWithToken(blockchainWalletData, false) - } else { - showBalanceWithoutToken(blockchainWalletData, false) - } - } - is WalletDataModel.VerifiedOnline, - is WalletDataModel.TransactionInProgress, - -> with(binding.lBalance) { - root.show() - binding.lBalanceError.root.hide() - val statusView = if (blockchainWalletData.status is WalletDataModel.VerifiedOnline) { - R.id.tv_status_verified - } else { - // tvStatusError.text = fragment.getText(R.string.wallet_balance_tx_in_progress) - R.id.group_error - } - showStatus(statusView) - // tvStatusErrorMessage.hide() - - if (tokenWalletData != null) { - showBalanceWithToken(blockchainWalletData, true) - } else { - showBalanceWithoutToken(blockchainWalletData, true) - } - } - is WalletDataModel.Unreachable -> with(binding.lBalance) { - root.show() - binding.lBalanceError.root.hide() - tvFiatAmount.hide() - groupBaseCurrency.hide() - - val currency = tokenWalletData?.currency?.currencySymbol - ?: blockchainWalletData.currency.currencyName - tvCurrency.text = currency - tvAmount.text = "" - - // tvStatusErrorMessage.text = blockchainWalletData.status.errorMessage - // TODO: Delete with WalletFeatureToggles - // tvStatusError.text = fragment.getString(R.string.wallet_balance_blockchain_unreachable) - - showStatus(R.id.group_error) - // tvStatusErrorMessage.show(!blockchainWalletData.status.errorMessage.isNullOrBlank()) - } - is WalletDataModel.NoAccount -> with(binding.lBalanceError) { - binding.lBalance.root.hide() - binding.lBalanceError.root.show() - tvErrorTitle.text = fragment.getText(R.string.wallet_error_no_account) - tvErrorDescriptions.text = - fragment.getString( - R.string.no_account_generic, - blockchainWalletData.status.amountToCreateAccount, - blockchainWalletData.currency.currencySymbol, - ) - } - else -> {} - } - } - - private fun showStatus(@IdRes viewRes: Int) = with(binding.lBalance) { - // groupError.show(viewRes == R.id.group_error) - tvStatusLoading.show(viewRes == R.id.tv_status_loading) - tvStatusVerified.show(viewRes == R.id.tv_status_verified) - } - - private fun showBalanceWithToken(data: WalletDataModel, showAmount: Boolean) = with(binding.lBalance) { - groupBaseCurrency.show() - tvCurrency.text = tokenWalletData?.currency?.currencySymbol - tvBaseCurrency.text = data.currency.currencyName - tvAmount.text = if (showAmount) tokenWalletData?.getFormattedCryptoAmount() else "" - tvBaseAmount.text = if (showAmount) data.getFormattedCryptoAmount() else "" - if (showAmount) { - tvFiatAmount.show() - tvFiatAmount.text = tokenWalletData?.getFormattedFiatAmount(store.state.globalState.appCurrency) - } - } - - private fun showBalanceWithoutToken(data: WalletDataModel, showAmount: Boolean) = with(binding.lBalance) { - groupBaseCurrency.hide() - tvCurrency.text = data.currency.currencyName - tvAmount.text = if (showAmount) data.getFormattedCryptoAmount() else "" - if (showAmount) { - tvFiatAmount.show() - tvFiatAmount.text = data.getFormattedFiatAmount(store.state.globalState.appCurrency) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/MultipleAddressUiHelper.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/MultipleAddressUiHelper.kt deleted file mode 100644 index 9531a726e3..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/MultipleAddressUiHelper.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.tangem.tap.features.wallet.ui - -import android.view.View -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.address.AddressType -import com.tangem.wallet.R - -object MultipleAddressUiHelper { - - private val blockchainsSupportingSplit = listOf( - Blockchain.Bitcoin, - Blockchain.BitcoinTestnet, - Blockchain.Litecoin, - Blockchain.BitcoinCash, - Blockchain.Cardano, - ) - - fun typeToId(type: AddressType, blockchain: Blockchain): Int { - return if (blockchain in blockchainsSupportingSplit) { - if (type == AddressType.Legacy) { - R.id.chip_legacy - } else { - R.id.chip_default - } - } else { - View.NO_ID - } - } - - fun idToType(id: Int, blockchain: Blockchain): AddressType? { - return when (blockchain) { - in blockchainsSupportingSplit -> { - when (id) { - R.id.chip_default -> AddressType.Default - R.id.chip_legacy -> AddressType.Legacy - else -> null - } - } - else -> null - } - } -} \ No newline at end of file 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 deleted file mode 100644 index de08710457..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt +++ /dev/null @@ -1,509 +0,0 @@ -package com.tangem.tap.features.wallet.ui - -import android.os.Bundle -import android.view.* -import android.widget.TextView -import androidx.activity.OnBackPressedCallback -import androidx.annotation.ColorRes -import androidx.annotation.DrawableRes -import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.Fragment -import androidx.lifecycle.lifecycleScope -import androidx.recyclerview.widget.LinearLayoutManager -import by.kirich1409.viewbindingdelegate.viewBinding -import com.badoo.mvicore.DiffStrategy -import com.badoo.mvicore.ModelWatcher -import com.badoo.mvicore.modelWatcher -import com.tangem.common.doOnResult -import com.tangem.common.extensions.guard -import com.tangem.core.analytics.Analytics -import com.tangem.core.navigation.NavigationAction -import com.tangem.domain.common.extensions.withMainContext -import com.tangem.domain.common.util.derivationStyleProvider -import com.tangem.domain.tokens.legacy.TradeCryptoAction -import com.tangem.feature.swap.api.SwapFeatureToggleManager -import com.tangem.feature.swap.domain.SwapInteractor -import com.tangem.sdk.extensions.dpToPx -import com.tangem.tap.common.SnackbarHandler -import com.tangem.tap.common.TestActions -import com.tangem.tap.common.analytics.events.DetailsScreen -import com.tangem.tap.common.analytics.events.Token -import com.tangem.tap.common.extensions.* -import com.tangem.tap.common.recyclerView.SpaceItemDecoration -import com.tangem.tap.common.redux.AppState -import com.tangem.tap.common.utils.SafeStoreSubscriber -import com.tangem.tap.domain.model.WalletDataModel -import com.tangem.tap.features.wallet.models.Currency -import com.tangem.tap.features.wallet.models.PendingTransaction -import com.tangem.tap.features.wallet.models.PendingTransactionType -import com.tangem.tap.features.wallet.models.WalletWarning -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.WalletState -import com.tangem.tap.features.wallet.redux.utils.UNKNOWN_AMOUNT_SIGN -import com.tangem.tap.features.wallet.ui.adapters.PendingTransactionsAdapter -import com.tangem.tap.features.wallet.ui.adapters.WalletDetailWarningMessagesAdapter -import com.tangem.tap.features.wallet.ui.images.load -import com.tangem.tap.features.wallet.ui.test.TestWallet -import com.tangem.tap.features.wallet.ui.utils.* -import com.tangem.tap.store -import com.tangem.tap.userWalletsListManagerSafe -import com.tangem.tap.walletCurrenciesManager -import com.tangem.wallet.R -import com.tangem.wallet.databinding.FragmentWalletDetailsBinding -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import timber.log.Timber -import java.math.BigDecimal -import javax.inject.Inject - -/** - * Wallet details fragment - use only for MultiWallet - */ -// TODO: Delete with WalletFeatureToggles -@Suppress("LargeClass", "MagicNumber") -@Deprecated(message = "Used only in old wallet screen") -@AndroidEntryPoint -class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), SafeStoreSubscriber { - - @Inject - lateinit var swapInteractor: SwapInteractor - - @Inject - lateinit var swapFeatureToggleManager: SwapFeatureToggleManager - - private lateinit var pendingTransactionAdapter: PendingTransactionsAdapter - private lateinit var warningMessagesAdapter: WalletDetailWarningMessagesAdapter - - private val binding: FragmentWalletDetailsBinding by viewBinding(FragmentWalletDetailsBinding::bind) - - private val walletDataWatcher: ModelWatcher = modelWatcher { - val addressCardStrategy: DiffStrategy = { old, new -> - old.currency != new.currency || old.walletAddresses != new.walletAddresses - } - - WalletDataModel::currency { - handleCurrencyIcon(it) - } - WalletDataModel::walletAddresses { walletAddresses -> - setupCopyAndShareButtons(walletAddresses?.selectedAddress?.address) - } - WalletDataModel::currency { currency -> - setupCurrency(currency) - } - watch({ it }, addressCardStrategy) { walletData -> - setupAddressCard( - shouldShowMultipleAddress = walletData.shouldShowMultipleAddress(), - selectedAddress = walletData.walletAddresses?.selectedAddress, - currency = walletData.currency, - ) - } - } - - private val walletStateWatcher: ModelWatcher = modelWatcher { - val walletDataStrategy: DiffStrategy = { old, new -> - new.walletsStores.isNotEmpty() && - new.selectedCurrency != null && - (old.selectedCurrency != new.selectedCurrency || old.walletsStores != new.walletsStores) - } - - watch({ it }, walletDataStrategy) { state -> - val selectedWallet = state.selectedWalletData - if (selectedWallet != null) { - setupBalanceData(selectedWallet) - setupSwipeRefresh(selectedWallet) - walletDataWatcher.invoke(selectedWallet) - - val walletStore = state.getWalletStore(state.selectedCurrency) - if (walletStore != null) { - handleWarnings( - selectedWallet.assembleWarnings( - blockchainAmount = walletStore.blockchainWalletData.status.amount, - blockchainWalletRent = walletStore.walletRent, - ), - ) - } - } - } - (WalletState::selectedWalletData or WalletState::isExchangeServiceFeatureOn) { state -> - val selectedWallet = state.selectedWalletData - if (selectedWallet != null) { - val blockchainAmount: BigDecimal = state.getBlockchainAmount(selectedWallet.currency) - setupButtonsRow(selectedWallet, state.isExchangeServiceFeatureOn, blockchainAmount) - } - } - (WalletState::state or WalletState::error) { state -> - setupNoInternetHandling(state.state, state.error) - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setHasOptionsMenu(true) - - Analytics.send(DetailsScreen.ScreenOpened()) - activity?.onBackPressedDispatcher?.addCallback( - this, - object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - store.dispatch(WalletAction.MultiWallet.SelectWallet(null)) - store.dispatch(NavigationAction.PopBackTo()) - } - }, - ) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - (activity as? AppCompatActivity)?.setSupportActionBar(binding.toolbar) - binding.toolbar.setNavigationOnClickListener { activity?.onBackPressed() } - - setupTransactionsRecyclerView() - setupButtons() - setupWarningsRecyclerView() - setupTestActionButton() - } - - override fun onStart() { - super.onStart() - store.subscribe(this) { state -> state.select(AppState::walletState) } - } - - override fun onStop() { - super.onStop() - store.unsubscribe(this) - } - - override fun onDestroyView() { - super.onDestroyView() - clearWatchers() - } - - private fun setupTransactionsRecyclerView() = with(binding) { - pendingTransactionAdapter = PendingTransactionsAdapter() - rvPendingTransaction.layoutManager = LinearLayoutManager(requireContext()) - rvPendingTransaction.adapter = pendingTransactionAdapter - } - - private fun setupWarningsRecyclerView() = with(binding) { - warningMessagesAdapter = WalletDetailWarningMessagesAdapter() - rvWarningMessages.layoutManager = LinearLayoutManager(requireContext()) - rvWarningMessages.adapter = warningMessagesAdapter - rvWarningMessages.addItemDecoration(SpaceItemDecoration.vertical(8f)) - } - - private fun setupButtons() { - binding.rowButtons.onSendClick = { store.dispatch(WalletAction.Send()) } - } - - private fun setupTestActionButton() { - view?.findViewById(R.id.l_balance)?.let { view -> - TestActions.initFor(view = view, actions = TestWallet.solanaRentExemptWarning()) - } - } - - override fun newStateOnMain(state: WalletState) { - if (activity == null || view == null) return - if (state.selectedWalletData == null) return - walletStateWatcher.invoke(state) - - updateViewMeasurements() - } - - private fun updateViewMeasurements() { - val tvFiatAmount = binding.lWalletDetails.lBalance.tvFiatAmount - val paddingStart = if (tvFiatAmount.text == UNKNOWN_AMOUNT_SIGN) 16f else 12f - - tvFiatAmount.setPadding( - tvFiatAmount.dpToPx(paddingStart).toInt(), - tvFiatAmount.paddingTop, - tvFiatAmount.paddingEnd, - tvFiatAmount.paddingBottom, - ) - } - - private fun setupCurrency(currency: Currency) = with(binding) { - tvCurrencyTitle.text = currency.currencyName - - if (currency is Currency.Token) { - tvCurrencySubtitle.text = tvCurrencySubtitle.getString( - R.string.wallet_currency_subtitle, - currency.blockchain.fullName, - ) - tvCurrencySubtitle.show() - } else { - tvCurrencySubtitle.hide() - } - } - - private fun setupSwipeRefresh(walletData: WalletDataModel) { - binding.srlWalletDetails.setOnRefreshListener { - if (walletData.status !is WalletDataModel.Loading) { - Analytics.send(Token.Refreshed()) - val selectedUserWallet = userWalletsListManagerSafe?.selectedUserWalletSync.guard { - Timber.e("Unable to refresh wallet details screen, no user wallet selected") - return@setOnRefreshListener - } - binding.srlWalletDetails.isRefreshing = true - lifecycleScope.launch(Dispatchers.Default) { - walletCurrenciesManager.update(selectedUserWallet, walletData.currency).doOnResult { - withMainContext { - binding.srlWalletDetails.isRefreshing = false - } - } - } - } - } - } - - private fun setupCopyAndShareButtons(walletAddress: String?) { - binding.lWalletDetails.btnCopy.setOnClickListener { - if (walletAddress != null) store.dispatch(WalletAction.CopyAddress(walletAddress, requireContext())) - } - - binding.lWalletDetails.btnShare.setOnClickListener { - if (walletAddress != null) store.dispatch(WalletAction.ShareAddress(walletAddress, requireContext())) - } - } - - private fun setupButtonsRow( - selectedWallet: WalletDataModel, - isExchangeServiceFeatureOn: Boolean, - blockchainAmount: BigDecimal, - ) { - val exchangeManager = store.state.globalState.exchangeManager - binding.rowButtons.apply { - onBuyClick = { store.dispatch(TradeCryptoAction.Buy()) } - onSellClick = { store.dispatch(TradeCryptoAction.Sell) } - onSwapClick = { store.dispatch(TradeCryptoAction.Swap) } - onTradeClick = { - store.dispatch( - WalletAction.DialogAction.ChooseTradeActionDialog( - buyAllowed = selectedWallet.isAvailableToBuy(exchangeManager), - sellAllowed = selectedWallet.isAvailableToSell(exchangeManager), - swapAllowed = selectedWallet.isAvailableToSwap( - swapFeatureToggleManager = swapFeatureToggleManager, - swapInteractor = swapInteractor, - isSingleWallet = false, - ), - ), - ) - } - } - val actions = selectedWallet.getAvailableActions( - swapInteractor = swapInteractor, - exchangeManager = exchangeManager, - swapFeatureToggleManager = swapFeatureToggleManager, - isSingleWallet = false, - ) - binding.rowButtons.updateButtonsVisibility( - actions = actions, - exchangeServiceFeatureOn = isExchangeServiceFeatureOn, - sendAllowed = selectedWallet.mainButton(blockchainAmount).enabled, - ) - } - - private fun handleWarnings(warnings: List) = with(binding) { - val converter = WalletWarningConverter(requireContext()) - val warningDetails = warnings.map { converter.convert(it) } - - warningMessagesAdapter.submitList(warningDetails) - rvWarningMessages.show(warningDetails.isNotEmpty()) - } - - private fun handleCurrencyIcon(currency: Currency) = with(binding.lWalletDetails.lBalance) { - ivCurrency.load( - currency = currency, - derivationStyle = store.state.globalState.scanResponse - ?.derivationStyleProvider?.getDerivationStyle(), - ) - } - - private fun showPendingTransactionsIfPresent(pendingTransactions: List) { - val knownTransactions = pendingTransactions.filterNot { it.type == PendingTransactionType.Unknown } - pendingTransactionAdapter.submitList(knownTransactions) - binding.rvPendingTransaction.show(knownTransactions.isNotEmpty()) - } - - private fun setupAddressCard( - shouldShowMultipleAddress: Boolean, - selectedAddress: WalletDataModel.AddressData?, - currency: Currency, - ) = with(binding.lWalletDetails) { - if (selectedAddress == null) return@with - - setupAddressTypeChips(shouldShowMultipleAddress, selectedAddress, currency) - - tvAddress.text = selectedAddress.address - tvExplore.setOnClickListener { - store.dispatch(WalletAction.ExploreAddress(selectedAddress.exploreUrl, requireContext())) - } - ivQrCode.setImageBitmap(selectedAddress.shareUrl.toQrCode()) - - tvReceiveMessage.text = when (currency) { - is Currency.Blockchain -> tvReceiveMessage.getString( - id = R.string.address_qr_code_message_format, - currency.blockchain.fullName, - currency.currencySymbol, - currency.blockchain.fullName, - ) - is Currency.Token -> tvReceiveMessage.getString( - id = R.string.address_qr_code_message_format, - currency.token.name, - currency.currencySymbol, - currency.blockchain.fullName, - ) - } - } - - private fun setupAddressTypeChips( - shouldShowMultipleAddress: Boolean, - selectedAddress: WalletDataModel.AddressData, - currency: Currency, - ) = with(binding.lWalletDetails) { - if (shouldShowMultipleAddress && currency is Currency.Blockchain) { - (cardBalance as? ViewGroup)?.beginDelayedTransition() - chipGroupAddressType.show() - chipGroupAddressType.fitChipsByGroupWidth() - - val checkedId = MultipleAddressUiHelper.typeToId(selectedAddress.type, currency.blockchain) - if (checkedId != View.NO_ID) chipGroupAddressType.check(checkedId) - - chipGroupAddressType.setOnCheckedChangeListener { _, checkedId -> - if (checkedId == -1) return@setOnCheckedChangeListener - val type = - MultipleAddressUiHelper.idToType(checkedId, currency.blockchain) - type?.let { store.dispatch(WalletAction.ChangeSelectedAddress(type)) } - } - } else { - chipGroupAddressType.hide() - } - } - - private fun setupNoInternetHandling(progressState: ProgressState, errorType: ErrorType?) { - if (progressState == ProgressState.Error) { - if (errorType == ErrorType.NoInternetConnection) { - binding.srlWalletDetails.isRefreshing = false - (activity as? SnackbarHandler)?.showSnackbar( - text = R.string.wallet_notification_no_internet, - buttonTitle = R.string.common_retry, - ) { store.dispatch(WalletAction.LoadData) } - } - } else { - (activity as? SnackbarHandler)?.dismissSnackbar() - } - } - - private fun setupBalanceData(walletData: WalletDataModel) = with(binding.lWalletDetails) { - when (val status = walletData.status) { - is WalletDataModel.Loading -> { - lBalanceError.root.hide() - lBalance.root.show() - lBalance.groupBalance.show() - lBalance.tvError.hide() - lBalance.tvAmount.text = walletData.getFormattedCryptoAmount() - lBalance.tvFiatAmount.text = walletData.getFormattedFiatAmount(store.state.globalState.appCurrency) - lBalance.tvStatus.setLoadingStatus(R.string.wallet_balance_loading) - } - is WalletDataModel.VerifiedOnline, - is WalletDataModel.SameCurrencyTransactionInProgress, - is WalletDataModel.TransactionInProgress, - -> { - lBalanceError.root.hide() - lBalance.root.show() - lBalance.groupBalance.show() - lBalance.tvError.hide() - lBalance.tvAmount.text = walletData.getFormattedCryptoAmount() - lBalance.tvFiatAmount.text = walletData.getFormattedFiatAmount(store.state.globalState.appCurrency) - when (status) { - is WalletDataModel.VerifiedOnline, - is WalletDataModel.SameCurrencyTransactionInProgress, - -> { - lBalance.tvStatus.setVerifiedBalanceStatus(R.string.wallet_balance_verified) - showPendingTransactionsIfPresent(status.pendingTransactions) - } - - is WalletDataModel.TransactionInProgress -> { - lBalance.tvStatus.setWarningStatus(R.string.wallet_balance_tx_in_progress) - showPendingTransactionsIfPresent(status.pendingTransactions) - } - - else -> Unit - } - } - is WalletDataModel.Unreachable -> { - lBalanceError.root.hide() - lBalance.root.show() - lBalance.groupBalance.hide() - lBalance.tvError.show() - // TODO: Delete with WalletFeatureToggles - // lBalance.tvError.setWarningStatus( - // R.string.wallet_balance_blockchain_unreachable, - // status.errorMessage, - // ) - } - is WalletDataModel.NoAccount -> { - lBalance.root.hide() - lBalanceError.root.show() - lBalanceError.tvErrorTitle.text = getText(R.string.wallet_error_no_account) - lBalanceError.tvErrorDescriptions.text = - getString( - R.string.no_account_generic, - status.amountToCreateAccount, - walletData.currency.currencySymbol, - ) - } - - else -> Unit - } - } - - @Deprecated("Deprecated in Java") - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - R.id.menu_remove -> { - store.state.walletState.selectedWalletData?.let { walletData -> - store.dispatch(WalletAction.MultiWallet.TryToRemoveWallet(walletData.currency)) - true - } - false - } - - else -> super.onOptionsItemSelected(item) - } - } - - @Deprecated( - message = "Deprecated in Java", - replaceWith = ReplaceWith("inflater.inflate(R.menu.menu_wallet_details, menu)", "com.tangem.wallet.R"), - ) - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - inflater.inflate(R.menu.menu_wallet_details, menu) - } - - private fun clearWatchers() { - walletDataWatcher.clear() - walletStateWatcher.clear() - } - - private fun TextView.setWarningStatus(mainMessage: Int, error: String? = null) { - val text = getString(mainMessage).appendIfNotNull(error, "\nError: ") - setStatus(text, R.color.warning, R.drawable.ic_warning_small) - } - - private fun TextView.setVerifiedBalanceStatus(mainMessage: Int) { - setStatus(getString(mainMessage), R.color.accent, R.drawable.ic_ok) - } - - private fun TextView.setLoadingStatus(mainMessage: Int) { - setStatus(getString(mainMessage), R.color.darkGray4, null) - } - - private fun TextView.setStatus(text: String, @ColorRes color: Int, @DrawableRes drawable: Int?) { - this.text = text - setTextColor(getColor(color)) - setCompoundDrawablesWithIntrinsicBounds(drawable ?: 0, 0, 0, 0) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt deleted file mode 100644 index a02a218d62..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt +++ /dev/null @@ -1,326 +0,0 @@ -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 androidx.activity.OnBackPressedCallback -import androidx.appcompat.app.AppCompatActivity -import androidx.compose.runtime.mutableStateOf -import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.flowWithLifecycle -import androidx.lifecycle.lifecycleScope -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import androidx.transition.TransitionInflater -import by.kirich1409.viewbindingdelegate.viewBinding -import coil.load -import coil.size.Scale -import com.badoo.mvicore.modelWatcher -import com.tangem.core.analytics.Analytics -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction -import com.tangem.core.ui.extensions.setStatusBarColor -import com.tangem.core.ui.utils.OneTouchClickListener -import com.tangem.datasource.connection.NetworkConnectionManager -import com.tangem.feature.swap.api.SwapFeatureToggleManager -import com.tangem.feature.swap.domain.SwapInteractor -import com.tangem.tap.MainActivity -import com.tangem.tap.common.analytics.events.Portfolio -import com.tangem.tap.common.extensions.show -import com.tangem.tap.common.recyclerView.SpaceItemDecoration -import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.common.utils.SafeStoreSubscriber -import com.tangem.tap.domain.configurable.warningMessage.WarningMessage -import com.tangem.tap.domain.statePrinter.printScanResponseState -import com.tangem.tap.domain.statePrinter.printWalletState -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.WalletState -import com.tangem.tap.features.wallet.ui.adapters.WarningMessagesAdapter -import com.tangem.tap.features.wallet.ui.wallet.MultiWalletView -import com.tangem.tap.features.wallet.ui.wallet.SingleWalletView -import com.tangem.tap.features.wallet.ui.wallet.WalletView -import com.tangem.tap.store -import com.tangem.wallet.BuildConfig -import com.tangem.wallet.R -import com.tangem.wallet.databinding.FragmentWalletBinding -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.launch -import javax.inject.Inject - -@AndroidEntryPoint -class WalletFragment : Fragment(R.layout.fragment_wallet), SafeStoreSubscriber { - - @Inject - lateinit var swapInteractor: SwapInteractor - - @Inject - lateinit var swapFeatureToggleManager: SwapFeatureToggleManager - - @Inject - lateinit var networkConnectionManager: NetworkConnectionManager - - private lateinit var warningsAdapter: WarningMessagesAdapter - - private val binding: FragmentWalletBinding by viewBinding(FragmentWalletBinding::bind) - - private var walletView: WalletView = MultiWalletView() - - private val viewModel by viewModels() - - private val totalBalanceWatcher = modelWatcher { - (WalletState::totalBalance) { totalBalance -> - totalBalance?.let { - viewModel.onBalanceLoaded(totalBalance) - store.state.globalState.topUpController?.totalBalanceStateChanged(it) - } - } - } - - private val isNetworkConnectionError = mutableStateOf(false) - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setHasOptionsMenu(true) - activity?.lifecycle?.addObserver(viewModel) - - activity?.onBackPressedDispatcher?.addCallback( - this, - object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - store.dispatch(WalletAction.PopBackToInitialScreen) - } - }, - ) - val inflater = TransitionInflater.from(requireContext()) - enterTransition = inflater.inflateTransition(R.transition.fade) - exitTransition = inflater.inflateTransition(R.transition.fade) - } - - override fun onStart() { - super.onStart() - - setStatusBarColor(R.color.background_secondary) - - subscribeOnNetworkStateChanging() - - store.subscribe(this) { state -> - state.select { it.walletState } - } - walletView.setFragment(this, binding) - } - - override fun onStop() { - super.onStop() - store.unsubscribe(this) - walletView.removeFragment() - } - - override fun onDestroy() { - walletView.onDestroyFragment() - super.onDestroy() - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - (activity as? AppCompatActivity)?.setSupportActionBar(binding.toolbar) - - binding.toolbar.setNavigationOnClickListener( - OneTouchClickListener { - store.dispatch(WalletAction.ChangeWallet(scope = requireActivity().lifecycleScope)) - }, - ) - setupWarningsRecyclerView() - walletView.changeWalletView(this, binding) - addCustomActionOnCard() - } - - private fun addCustomActionOnCard() { - if (!BuildConfig.TEST_ACTION_ENABLED) return - - binding.ivCard.setOnClickListener { - printScanResponseState() - printWalletState() - } - } - - @Suppress("MagicNumber") - private fun setupWarningsRecyclerView() { - warningsAdapter = WarningMessagesAdapter() - val layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false) - with(binding) { - rvWarningMessages.layoutManager = layoutManager - rvWarningMessages.addItemDecoration(SpaceItemDecoration.all(16f)) - rvWarningMessages.adapter = warningsAdapter - } - } - - @Suppress("ComplexMethod") - override fun newStateOnMain(state: WalletState) { - if (activity == null || view == null) return - - when { - state.isMultiwalletAllowed && walletView !is MultiWalletView -> { - walletView.onViewDestroy() - walletView = MultiWalletView() - walletView.changeWalletView(this, binding) - } - !state.isMultiwalletAllowed && walletView !is SingleWalletView -> { - walletView.onViewDestroy() - walletView = SingleWalletView() - walletView.changeWalletView(this, binding) - } - else -> {} // we keep the same view unless we scan a card that requires a different view - } - totalBalanceWatcher.invoke(state) - - walletView.swapInteractor = swapInteractor - walletView.swapFeatureToggleManager = swapFeatureToggleManager - - walletView.onNewState(state) - - if (binding.toolbar.menu.findItem(R.id.details_menu) == null) { - binding.toolbar.inflateMenu(R.menu.menu_wallet) - } - - setupCardImage(state) - - showWarningsIfPresent(state.mainWarningsList) - - setupPullToRefreshLayout(state) - - binding.toolbar.setNavigationIcon( - if (state.canSaveUserWallets) R.drawable.ic_wallet_24 else R.drawable.ic_tap_card_24, - ) - - // showLearn2earnView() - } - - // private fun showLearn2earnView() { - // val isShowing = learn2earnViewModel.uiState.mainScreenState.isVisible - // if (!isShowing) return - // - // binding.composeLearnToEarnContainer.show(true) { binding.llWarnings.beginDelayedTransition() } - // binding.composeLearnToEarnContainer.apply { - // setViewCompositionStrategy( - // strategy = ViewCompositionStrategy.DisposeOnLifecycleDestroyed( - // lifecycle = [REDACTED_EMAIL], - // ), - // ) - // setContent { - // TangemTheme { - // Learn2earnMainPageScreen(learn2earnViewModel.uiState) - // } - // } - // } - // } - - private fun setupPullToRefreshLayout(state: WalletState) { - setupErrorPullToRefreshState(state) - - binding.pullToRefreshLayout.isRefreshing = state.state == ProgressState.Refreshing - - binding.pullToRefreshLayout.setOnRefreshListener { - if (state.state != ProgressState.Loading && state.state != ProgressState.Refreshing) { - refreshWalletData() - } - } - } - - private fun setupErrorPullToRefreshState(state: WalletState) { - if (state.state == ProgressState.Error) { - when (state.error) { - ErrorType.NoInternetConnection -> { - isNetworkConnectionError.value = true - binding.pullToRefreshLayout.isRefreshing = false - - (activity as? MainActivity)?.showSnackbar( - text = R.string.wallet_notification_no_internet, - buttonTitle = R.string.common_retry, - ) - // because was added logic of autoupdate mainscreen data, remove retry - // TODO("remove comment after release 4.6") - // { store.dispatch(WalletAction.LoadData) } - } - else -> isNetworkConnectionError.value = false - } - } else { - isNetworkConnectionError.value = false - (activity as? MainActivity)?.dismissSnackbar() - } - } - - private fun refreshWalletData() { - Analytics.send(Portfolio.Refreshed()) - store.dispatch(WalletAction.LoadData.Refresh) - // learn2earnViewModel.onMainScreenRefreshed() - } - - private fun showWarningsIfPresent(warnings: List) { - warningsAdapter.submitList(warnings) - binding.rvWarningMessages.show(warnings.isNotEmpty()) - } - - private fun setupCardImage(state: WalletState) { - binding.ivCard.load(state.cardImage?.artworkId) { - scale(Scale.FIT) - crossfade(enable = true) - placeholder(R.drawable.card_placeholder_black) - error(R.drawable.card_placeholder_black) - fallback(R.drawable.card_placeholder_black) - } - } - - private fun subscribeOnNetworkStateChanging() { - viewLifecycleOwner.lifecycleScope.launch { - networkConnectionManager.isOnlineFlow - .flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.STARTED) - .distinctUntilChanged() - .collect { isOnline -> - if (isOnline) { - (activity as? MainActivity)?.dismissSnackbar() - } else { - isNetworkConnectionError.value = true - binding.pullToRefreshLayout.isRefreshing = false - (activity as? MainActivity)?.showSnackbar( - text = R.string.wallet_notification_no_internet, - buttonTitle = R.string.common_retry, - ) - } - if (isOnline && isNetworkConnectionError.value) { - refreshWalletData() - } - } - } - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - R.id.details_menu -> { - store.dispatch(GlobalAction.UpdateFeedbackInfo(store.state.walletState.walletManagers)) - store.dispatch(NavigationAction.NavigateTo(AppScreen.Details)) - - true - } - else -> super.onOptionsItemSelected(item) - } - } - - @Deprecated( - message = "Deprecated in Java", - replaceWith = ReplaceWith( - "if (store.state.walletState.shouldShowDetails) inflater.inflate(R.menu.menu_wallet, menu)", - "com.tangem.tap.store", - "com.tangem.wallet.R", - ), - ) - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - inflater.inflate(R.menu.menu_wallet, menu) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletViewModel.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletViewModel.kt deleted file mode 100644 index 271bc8aaae..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletViewModel.kt +++ /dev/null @@ -1,132 +0,0 @@ -package com.tangem.tap.features.wallet.ui - -import androidx.lifecycle.DefaultLifecycleObserver -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.tangem.core.analytics.api.AnalyticsEventHandler -import com.tangem.domain.common.util.cardTypesResolver -import com.tangem.domain.wallets.legacy.UserWalletsListManager -import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter -import com.tangem.tap.common.analytics.events.Basic -import com.tangem.tap.common.analytics.events.MainScreen -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.domain.model.TotalFiatBalance -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.features.wallet.ui.analytics.WalletAnalyticsEventsMapper -import com.tangem.tap.store -import com.tangem.tap.walletStoresManager -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.* -import kotlinx.coroutines.flow.* -import org.rekotlin.StoreSubscriber -import javax.inject.Inject - -// TODO: Kill me, please -@OptIn(ExperimentalCoroutinesApi::class) -@HiltViewModel -internal class WalletViewModel @Inject constructor( - private val analyticsEventHandler: AnalyticsEventHandler, -) : ViewModel(), StoreSubscriber, DefaultLifecycleObserver { - private var observeWalletStoresUpdatesJob: Job? = null - set(value) { - field?.cancel() - field = value - } - - private val walletAnalyticsEventsMapper = WalletAnalyticsEventsMapper() - - init { - subscribeToUserWalletsListManagerUpdates() - } - - override fun onCleared() { - store.unsubscribe(this) - } - - override fun newState(state: UserWalletsListManager?) { - // Restarting observing of wallet store updates when the manager changes - if (state != null) { - bootstrapSelectedWalletStoresChanges(state) - } - } - - override fun onCreate(owner: LifecycleOwner) { - launch() - val scanResponse = store.state.globalState.scanResponse - if (scanResponse != null) { - val currency = ParamCardCurrencyConverter().convert(scanResponse.cardTypesResolver) - val signInType = store.state.signInState.type - if (currency != null && signInType != null) { - analyticsEventHandler.send( - Basic.SignedIn( - currency = currency, - batch = scanResponse.card.batchId, - signInType = signInType, - walletsCount = store.state.globalState.userWalletsListManager?.walletsCount.toString(), - hasBackup = scanResponse.card.backupStatus?.isActive, - ), - ) - } - } - } - - override fun onStart(owner: LifecycleOwner) { - analyticsEventHandler.send(MainScreen.ScreenOpened()) - } - - fun onBalanceLoaded(totalBalance: TotalFiatBalance?) { - if (totalBalance != null) { - walletAnalyticsEventsMapper.convert(totalBalance)?.let { balanceParam -> - analyticsEventHandler.send( - Basic.BalanceLoaded( - balance = balanceParam, - ), - ) - } - } - } - - private fun launch() { - val manager = store.state.globalState.userWalletsListManager - if (manager != null) { - bootstrapSelectedWalletStoresChanges(manager) - } - bootstrapShowSaveWalletIfNeeded() - } - - @OptIn(FlowPreview::class) - private fun bootstrapSelectedWalletStoresChanges(manager: UserWalletsListManager) { - observeWalletStoresUpdatesJob = manager.selectedUserWallet - .map { it.walletId } - .flatMapLatest(walletStoresManager::get) - .debounce { walletStores -> - if (walletStores.isNotEmpty()) WALLET_STORES_DEBOUNCE_TIMEOUT else 0 - } - .onEach { walletStores -> - store.dispatchOnMain(WalletAction.WalletStoresChanged(walletStores)) - } - .launchIn(viewModelScope) - } - - private fun bootstrapShowSaveWalletIfNeeded() { - viewModelScope.launch { - delay(timeMillis = 1_800) - store.dispatchOnMain(WalletAction.ShowSaveWalletIfNeeded) - } - } - - private fun subscribeToUserWalletsListManagerUpdates() { - store.subscribe(this) { appState -> - appState - .skip { old, new -> - old.globalState.userWalletsListManager == new.globalState.userWalletsListManager - } - .select { it.globalState.userWalletsListManager } - } - } - - companion object { - private const val WALLET_STORES_DEBOUNCE_TIMEOUT = 100L - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletWarningConverter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletWarningConverter.kt deleted file mode 100644 index 5f98692fca..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletWarningConverter.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.tangem.tap.features.wallet.ui - -import android.content.Context -import com.tangem.common.module.ModuleMessageConverter -import com.tangem.tap.features.wallet.models.WalletWarning -import com.tangem.tap.features.wallet.models.WalletWarningDescription -import com.tangem.wallet.R - -/** -[REDACTED_AUTHOR] - */ -// TODO: Delete with WalletFeatureToggles -@Deprecated(message = "Used only in old wallet screen") -class WalletWarningConverter( - private val context: Context, -) : ModuleMessageConverter { - - override fun convert(message: WalletWarning): WalletWarningDescription { - // val warningMessage = when (message) { - // is WalletWarning.ExistentialDeposit -> { - // context.getString( - // R.string.warning_existential_deposit_message, - // message.currencyName, - // message.edStringValueWithSymbol, - // ) - // } - // is WalletWarning.BalanceNotEnoughForFee -> { - // context.getString( - // R.string.token_details_send_blocked_fee_format, - // message.currencyName, - // message.blockchainFullName, - // message.currencyName, - // message.blockchainFullName, - // message.blockchainSymbol, - // ) - // } - // is WalletWarning.TransactionInProgress -> { - // context.getString( - // R.string.token_details_send_blocked_tx_format, - // message.currencyName, - // ) - // } - // is WalletWarning.Rent -> { - // context.getString( - // R.string.solana_rent_warning, - // message.walletRent.rent, - // message.walletRent.exemptionAmount, - // ) - // } - // } - return WalletWarningDescription(context.getString(R.string.common_warning), "") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/PendingTransactionsAdapter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/PendingTransactionsAdapter.kt deleted file mode 100644 index 0a59da666a..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/PendingTransactionsAdapter.kt +++ /dev/null @@ -1,74 +0,0 @@ -package com.tangem.tap.features.wallet.ui.adapters - -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView -import com.tangem.tap.common.extensions.getDrawableCompat -import com.tangem.tap.common.extensions.getString -import com.tangem.tap.common.extensions.hide -import com.tangem.tap.features.wallet.models.PendingTransaction -import com.tangem.tap.features.wallet.models.PendingTransactionType -import com.tangem.wallet.R -import com.tangem.wallet.databinding.ItemPendingTransactionBinding - -class PendingTransactionsAdapter : - ListAdapter(DiffUtilCallback) { - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TransactionsViewHolder { - val binding = ItemPendingTransactionBinding.inflate( - LayoutInflater.from(parent.context), - parent, - false, - ) - return TransactionsViewHolder(binding) - } - - override fun onBindViewHolder(holder: TransactionsViewHolder, position: Int) { - holder.bind(currentList[position]) - } - - object DiffUtilCallback : DiffUtil.ItemCallback() { - override fun areContentsTheSame(oldItem: PendingTransaction, newItem: PendingTransaction) = oldItem == newItem - - override fun areItemsTheSame(oldItem: PendingTransaction, newItem: PendingTransaction) = oldItem == newItem - } - - class TransactionsViewHolder(val binding: ItemPendingTransactionBinding) : - RecyclerView.ViewHolder(binding.root) { - - fun bind(transaction: PendingTransaction) { - if (transaction.type == PendingTransactionType.Unknown) { - binding.root.hide() - } - - val transactionDescriptionRes = when (transaction.type) { - PendingTransactionType.Incoming -> R.string.wallet_pending_tx_receiving - PendingTransactionType.Outgoing -> R.string.wallet_pending_tx_sending - PendingTransactionType.Unknown -> return - } - val transactionAddressRes = when (transaction.type) { - PendingTransactionType.Incoming -> R.string.wallet_pending_tx_receiving_address_format - PendingTransactionType.Outgoing -> R.string.wallet_pending_tx_sending_address_format - PendingTransactionType.Unknown -> return - } - val image = when (transaction.type) { - PendingTransactionType.Incoming -> R.drawable.ic_arrow_left - PendingTransactionType.Outgoing -> R.drawable.ic_arrow_right_20 - PendingTransactionType.Unknown -> return - } - binding.tvPendingTransaction.text = - binding.root.getString(transactionDescriptionRes).let { "$it " } - - transaction.amountValueUi?.let { binding.tvPendingTransactionAmount.text = "$it " } - binding.tvPendingTransactionCurrency.text = transaction.currency - - if (transaction.address != null) { - binding.tvPendingTransactionAddress.text = - binding.root.getString(transactionAddressRes, transaction.address) - } - binding.ivPendingTransaction.setImageDrawable(binding.root.context.getDrawableCompat(image)) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt deleted file mode 100644 index 0b1e671dab..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt +++ /dev/null @@ -1,110 +0,0 @@ -package com.tangem.tap.features.wallet.ui.adapters - -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.core.view.isVisible -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView -import com.tangem.core.analytics.Analytics -import com.tangem.domain.common.util.derivationStyleProvider -import com.tangem.tap.common.analytics.events.Portfolio -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.domain.model.WalletDataModel -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.features.wallet.ui.images.load -import com.tangem.tap.features.wallet.ui.utils.getFormattedCryptoAmount -import com.tangem.tap.features.wallet.ui.utils.getFormattedFiatAmount -import com.tangem.tap.features.wallet.ui.utils.getFormattedFiatRate -import com.tangem.tap.store -import com.tangem.wallet.R -import com.tangem.wallet.databinding.ItemCurrencyWalletBinding - -// TODO: Delete with WalletFeatureToggles -@Deprecated(message = "Used only in old wallet screen") -class WalletAdapter : ListAdapter(DiffUtilCallback) { - - override fun getItemId(position: Int): Long { - return currentList[position].currency.currencySymbol.hashCode().toLong() - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WalletsViewHolder { - val layout = ItemCurrencyWalletBinding.inflate( - LayoutInflater.from(parent.context), - parent, - false, - ) - return WalletsViewHolder(layout) - } - - override fun onBindViewHolder(holder: WalletsViewHolder, position: Int) { - holder.bind(currentList[position]) - } - - object DiffUtilCallback : DiffUtil.ItemCallback() { - override fun areContentsTheSame(oldItem: WalletDataModel, newItem: WalletDataModel) = oldItem == newItem - - override fun areItemsTheSame(oldItem: WalletDataModel, newItem: WalletDataModel) = oldItem == newItem - } - - class WalletsViewHolder(val binding: ItemCurrencyWalletBinding) : - RecyclerView.ViewHolder(binding.root) { - - fun bind(wallet: WalletDataModel) = with(binding) { - val status = wallet.status - val fiatCurrency = store.state.globalState.appCurrency - - val statusMessage = when (status) { - is WalletDataModel.TransactionInProgress -> { - root.getString(R.string.wallet_balance_tx_in_progress) - } - is WalletDataModel.Unreachable -> { - // TODO: Delete with WalletFeatureToggles - // root.getString(R.string.wallet_balance_blockchain_unreachable) - } - is WalletDataModel.MissedDerivation -> { - root.getString(R.string.wallet_balance_missing_derivation) - } - else -> null - } - - if (status is WalletDataModel.Loading) { - lContent.root.hide() - lShimmer.root.veil() - } else { - lShimmer.root.unVeil() - lContent.root.show() - } - - ivCurrency.load( - currency = wallet.currency, - derivationStyle = store.state.globalState.scanResponse - ?.derivationStyleProvider?.getDerivationStyle(), - ) - - lContent.tvCurrency.text = wallet.currency.currencyName - lContent.tvAmountFiat.text = wallet.getFormattedFiatAmount(fiatCurrency) - lContent.tvAmount.text = wallet.getFormattedCryptoAmount() - - lContent.tvStatus.isVisible = statusMessage != null - // lContent.tvStatus.text = statusMessage - - lContent.tvExchangeRate.isVisible = statusMessage == null - lContent.tvExchangeRate.text = wallet.getFormattedFiatRate( - fiatCurrency = fiatCurrency, - noRateValue = root.getString(id = R.string.token_item_no_rate), - ) - - if (wallet.walletAddresses != null) { - cardWallet.setOnClickListener { - Analytics.send(Portfolio.TokenTapped()) - store.dispatch(WalletAction.MultiWallet.SelectWallet(wallet.currency)) - } - } else { - cardWallet.setOnClickListener(null) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletDetailWarningMessagesAdapter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletDetailWarningMessagesAdapter.kt deleted file mode 100644 index 95be9ce597..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletDetailWarningMessagesAdapter.kt +++ /dev/null @@ -1,52 +0,0 @@ -package com.tangem.tap.features.wallet.ui.adapters - -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView -import com.tangem.tap.common.extensions.getColor -import com.tangem.tap.features.wallet.models.WalletWarningDescription -import com.tangem.wallet.R -import com.tangem.wallet.databinding.LayoutWarningCardBinding - -/** -[REDACTED_AUTHOR] - */ -class WalletDetailWarningMessagesAdapter : - ListAdapter(DiffUtilCallback()) { - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WalletDetailsWarningMessageVH { - val inflater = LayoutInflater.from(parent.context) - val binding = LayoutWarningCardBinding.inflate(inflater, parent, false) - - return WalletDetailsWarningMessageVH(binding) - } - - override fun onBindViewHolder(holder: WalletDetailsWarningMessageVH, position: Int) { - holder.bind(currentList[position]) - } - - private class DiffUtilCallback : DiffUtil.ItemCallback() { - override fun areContentsTheSame(oldItem: WalletWarningDescription, newItem: WalletWarningDescription) = - oldItem == newItem - - override fun areItemsTheSame(oldItem: WalletWarningDescription, newItem: WalletWarningDescription) = - oldItem == newItem - } -} - -class WalletDetailsWarningMessageVH( - val binding: LayoutWarningCardBinding, -) : RecyclerView.ViewHolder(binding.root) { - - fun bind(warning: WalletWarningDescription) { - binding.warningCard.setCardBackgroundColor(binding.root.getColor(R.color.darkGray2)) - setText(warning) - } - - private fun setText(warning: WalletWarningDescription) = with(binding.warningContentContainer) { - tvTitle.text = warning.title - tvMessage.text = warning.message - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/analytics/WalletAnalyticsEventsMapper.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/analytics/WalletAnalyticsEventsMapper.kt deleted file mode 100644 index 184e85983c..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/analytics/WalletAnalyticsEventsMapper.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.tangem.tap.features.wallet.ui.analytics - -import com.tangem.tap.common.analytics.events.AnalyticsParam -import com.tangem.tap.common.extensions.isGreaterThan -import com.tangem.tap.domain.model.TotalFiatBalance -import com.tangem.utils.converter.Converter -import java.math.BigDecimal - -class WalletAnalyticsEventsMapper : Converter { - - override fun convert(value: TotalFiatBalance): AnalyticsParam.CardBalanceState? { - return when (value) { - is TotalFiatBalance.Failed -> AnalyticsParam.CardBalanceState.BlockchainError - is TotalFiatBalance.Loaded -> when { - value.isWarning -> AnalyticsParam.CardBalanceState.CustomToken - value.amount.isGreaterThan(BigDecimal.ZERO) -> AnalyticsParam.CardBalanceState.Full - else -> AnalyticsParam.CardBalanceState.Empty - } - is TotalFiatBalance.Loading -> null - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/AmountToSendBottomSheetDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/AmountToSendBottomSheetDialog.kt deleted file mode 100644 index 75cbe3010a..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/AmountToSendBottomSheetDialog.kt +++ /dev/null @@ -1,88 +0,0 @@ -package com.tangem.tap.features.wallet.ui.dialogs - -import android.content.Context -import android.os.Bundle -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.appcompat.view.ContextThemeWrapper -import androidx.recyclerview.widget.* -import com.google.android.material.bottomsheet.BottomSheetDialog -import com.tangem.blockchain.common.Amount -import com.tangem.tap.common.extensions.toFormattedString -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.features.wallet.redux.models.WalletDialog -import com.tangem.tap.store -import com.tangem.wallet.R -import com.tangem.wallet.databinding.DialogWalletSendBinding -import com.tangem.wallet.databinding.ItemWalletAmountToSendBinding - -class AmountToSendBottomSheetDialog( - context: Context, - private val dialog: WalletDialog.SelectAmountToSendDialog, -) : BottomSheetDialog(context) { - - var binding: DialogWalletSendBinding? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding = DialogWalletSendBinding.inflate(LayoutInflater.from(context)) - setContentView(binding!!.root) - } - - override fun show() { - super.show() - - setOnDismissListener { - binding = null - store.dispatch(WalletAction.DialogAction.Hide) - } - - binding!!.rvAmountsToSend.layoutManager = LinearLayoutManager(context) - val dividerItemDecoration = DividerItemDecoration( - ContextThemeWrapper(binding!!.root.context, R.style.AppTheme), - DividerItemDecoration.VERTICAL, - ) - binding!!.rvAmountsToSend.addItemDecoration(dividerItemDecoration) - - val viewAdapter = ChooseAmountAdapter() - binding!!.rvAmountsToSend.adapter = viewAdapter - - viewAdapter.submitList(dialog.amounts) - } -} - -private class ChooseAmountAdapter : ListAdapter(DiffUtilCallback) { - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AmountViewHolder { - val binding = ItemWalletAmountToSendBinding.inflate( - LayoutInflater.from(parent.context), - parent, - false, - ) - return AmountViewHolder(binding) - } - - override fun onBindViewHolder(holder: AmountViewHolder, position: Int) { - holder.bind(currentList[position]) - } - - object DiffUtilCallback : DiffUtil.ItemCallback() { - override fun areContentsTheSame(oldItem: Amount, newItem: Amount) = - oldItem.currencySymbol == newItem.currencySymbol - - override fun areItemsTheSame(oldItem: Amount, newItem: Amount) = oldItem == newItem - } - - class AmountViewHolder(val binding: ItemWalletAmountToSendBinding) : - RecyclerView.ViewHolder(binding.root) { - - fun bind(amount: Amount) = with(binding) { - tvCurrencySymbol.text = amount.currencySymbol - tvAmount.text = amount.value?.toFormattedString(amount.decimals) - root.setOnClickListener { - store.dispatch(WalletAction.DialogAction.Hide) - store.dispatch(WalletAction.Send(amount)) - } - } - } -} \ No newline at end of file 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 deleted file mode 100644 index 25ce55e44c..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ChooseTradeActionBottomSheetDialog.kt +++ /dev/null @@ -1,56 +0,0 @@ -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.domain.tokens.legacy.TradeCryptoAction -import com.tangem.tap.common.extensions.show -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.features.wallet.redux.models.WalletDialog -import com.tangem.tap.store -import com.tangem.wallet.databinding.DialogWalletTradeBinding - -class ChooseTradeActionBottomSheetDialog( - context: Context, - private val dialogData: WalletDialog.ChooseTradeActionDialog, -) : BottomSheetDialog(context) { - - var binding: DialogWalletTradeBinding? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding = DialogWalletTradeBinding.inflate(LayoutInflater.from(context)) - setContentView(binding!!.root) - } - - override fun show() { - super.show() - - this.setOnDismissListener { - binding = null - store.dispatch(WalletAction.DialogAction.Hide) - } - - binding?.let { - with(it) { - dialogBtnBuy.show(dialogData.buyAllowed) - dialogBtnSell.show(dialogData.sellAllowed) - dialogBtnSwap.show(dialogData.swapAllowed) - - dialogBtnBuy.setOnClickListener { - dismiss() - store.dispatch(TradeCryptoAction.Buy()) - } - dialogBtnSell.setOnClickListener { - dismiss() - store.dispatch(TradeCryptoAction.Sell) - } - dialogBtnSwap.setOnClickListener { - dismiss() - store.dispatch(TradeCryptoAction.Swap) - } - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/SignedHashesWarningDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/SignedHashesWarningDialog.kt deleted file mode 100644 index 8ebbffeb54..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/SignedHashesWarningDialog.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.tangem.tap.features.wallet.ui.dialogs - -import android.content.Context -import androidx.appcompat.app.AlertDialog -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.tangem.tap.common.redux.global.GlobalAction -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 - -// TODO: Delete with WalletFeatureToggles -@Deprecated(message = "Used only in old wallet screen") -object SignedHashesWarningDialog { - fun create(context: Context): AlertDialog { - return MaterialAlertDialogBuilder(context, R.style.CustomMaterialDialog).apply { - // setTitle(context.getString(R.string.warning_important_security_info, "\u26A0")) - // setMessage(R.string.alert_signed_hashes_message) - setPositiveButton(R.string.common_understand) { _, _ -> - store.dispatch(WalletAction.Warnings.CheckHashesCount.SaveCardId) - store.dispatch( - GlobalAction.HideWarningMessage(WarningMessagesManager.signedHashesMultiWalletWarning), - ) - } - setNegativeButton(R.string.common_cancel) { _, _ -> } - setOnDismissListener { - store.dispatch(WalletAction.DialogAction.Hide) - } - }.create() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/SimpleOkDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/SimpleOkDialog.kt deleted file mode 100644 index 9abf465eb0..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/SimpleOkDialog.kt +++ /dev/null @@ -1,75 +0,0 @@ -package com.tangem.tap.features.wallet.ui.dialogs - -import android.content.Context -import androidx.appcompat.app.AlertDialog -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.tangem.tap.common.extensions.dispatchDialogHide -import com.tangem.tap.common.redux.AppDialog -import com.tangem.tap.store -import com.tangem.wallet.R - -/** -[REDACTED_AUTHOR] - */ -object SimpleOkDialog { - - fun create(dialog: AppDialog.SimpleOkDialog, context: Context): AlertDialog { - return MaterialAlertDialogBuilder(context, R.style.CustomMaterialDialog).apply { - setTitle(dialog.header) - setMessage(dialog.message) - setPositiveButton(R.string.common_ok) { _, _ -> } - setOnDismissListener { - store.dispatchDialogHide() - dialog.onOk?.invoke() - } - }.create() - } - - fun create(dialog: AppDialog.SimpleOkDialogRes, context: Context): AlertDialog { - val message = if (dialog.args.isEmpty()) { - context.getString(dialog.messageId) - } else { - context.getString(dialog.messageId, *dialog.args.toTypedArray()) - } - return AlertDialog.Builder(context).apply { - setTitle(context.getString(dialog.headerId)) - setMessage(message) - setPositiveButton(R.string.common_ok) { _, _ -> } - setOnDismissListener { - store.dispatchDialogHide() - dialog.onOk?.invoke() - } - }.create() - } - - fun create(dialog: AppDialog.SimpleOkErrorDialog, context: Context): AlertDialog = create( - dialog = AppDialog.SimpleOkDialog( - header = context.getString(R.string.common_error), - message = dialog.message, - onOk = dialog.onOk, - ), - context = context, - ) - - fun create(dialog: AppDialog.SimpleOkWarningDialog, context: Context): AlertDialog = create( - dialog = AppDialog.SimpleOkDialog( - header = context.getString(R.string.common_warning), - message = dialog.message, - onOk = dialog.onOk, - ), - context = context, - ) - - fun create(dialog: AppDialog.OkCancelDialogRes, context: Context): AlertDialog { - return MaterialAlertDialogBuilder(context, R.style.CustomMaterialDialog).apply { - setTitle(context.getString(dialog.headerId)) - setMessage(dialog.messageId) - setPositiveButton(dialog.okButton.title) { _, _ -> dialog.okButton.action?.invoke() } - setNegativeButton(dialog.cancelButton.title) { _, _ -> dialog.cancelButton.action?.invoke() } - setOnDismissListener { - store.dispatchDialogHide() - dialog.cancelButton.action?.invoke() - } - }.create() - } -} \ No newline at end of file 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 deleted file mode 100644 index 77ac36e571..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconRequest.kt +++ /dev/null @@ -1,172 +0,0 @@ -package com.tangem.tap.features.wallet.ui.images - -import android.widget.ImageView -import android.widget.TextView -import androidx.constraintlayout.utils.widget.ImageFilterView -import coil.imageLoader -import coil.request.ImageRequest -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.IconsUtil -import com.tangem.blockchain.common.Token -import com.tangem.core.ui.extensions.getActiveIconRes -import com.tangem.domain.common.extensions.toCoinId -import com.tangem.domain.common.extensions.toNetworkId -import com.tangem.tap.common.extensions.getColor -import com.tangem.tap.common.extensions.getTextColor -import com.tangem.tap.domain.extensions.getCustomIconUrl -import com.tangem.tap.domain.tokens.getIconUrl -import com.tangem.wallet.R - -private const val QCX = "QCX" -private const val VOYR = "VOYRME" - -class CurrencyIconRequest( - private val currencyImageView: ImageFilterView, - private val currencyTextView: TextView?, - private val token: Token?, - private val blockchain: Blockchain, - private val getLocalImage: Boolean = false, -) { - fun load() { - when { - token == null && blockchain.isTestnet() -> loadTestnetBlockchainIcon() - token == null -> loadBlockchainIcon() - blockchain.isTestnet() -> loadTestnetTokenIcon() - else -> loadTokenIcon() - } - } - - private fun loadBlockchainIcon() { - loadBlockchainIconBase( - onStart = { - currencyImageView.colorFilter = null - }, - ) - } - - private fun loadTestnetBlockchainIcon() { - loadBlockchainIconBase( - onStart = { - currencyImageView.saturation = 0f - }, - ) - } - - private fun loadTokenIcon() { - loadTokenIconBase( - onStart = { - currencyImageView.setColorFilter(it.getColor()) - }, - onSuccess = { - currencyImageView.colorFilter = null - }, - onError = { - currencyImageView.setColorFilter(it.getColor()) - currencyTextView?.setTextColor(it.getTextColor()) - }, - ) - } - - private fun loadTestnetTokenIcon() { - loadTokenIconBase( - onStart = { - currencyImageView.saturation = 0f - }, - onError = { - currencyImageView.saturation = 0f - currencyImageView.setColorFilter(it.getColor(true)) - currencyTextView?.setTextColor(it.getTextColor(true)) - }, - ) - } - - private inline fun loadBlockchainIconBase( - crossinline onStart: (Blockchain) -> Unit = {}, - crossinline onSuccess: (Blockchain) -> Unit = {}, - crossinline onError: (Blockchain) -> Unit = {}, - ) { - currencyImageView.loadIcon( - data = getBlockchainIconData(blockchain), - placeholderRes = getActiveIconRes(blockchain.id), - onStart = { onStart(blockchain) }, - onSuccess = { onSuccess(blockchain) }, - onError = { onError(blockchain) }, - ) - } - - private fun getBlockchainIconData(blockchain: Blockchain): Any { - return if (getLocalImage) { - when (blockchain) { - Blockchain.TerraV1, Blockchain.TerraV2 -> getActiveIconRes(blockchain.toCoinId()) - else -> getActiveIconRes(blockchain.id) - } - } else { - when (blockchain) { - Blockchain.TerraV1, Blockchain.TerraV2 -> getIconUrl(blockchain.toCoinId()) - else -> getIconUrl(blockchain.toNetworkId()) - } - } - } - - 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.name.take(1) - currencyTextView?.setTextColor(token.getTextColor()) - onStart(token) - }, - onSuccess = { - currencyTextView?.text = null - onSuccess(token) - }, - onError = { - // for some reason the onStart doesn't call if an error occurs - currencyTextView?.text = token.name.take(1) - onError(token) - }, - ) - } -} - -private inline fun ImageView.loadIcon( - data: Any?, - placeholderRes: Int, - crossinline onStart: () -> Unit = {}, - crossinline onSuccess: () -> Unit = {}, - crossinline onError: () -> Unit = {}, -) { - ImageRequest.Builder(context) - .data(data) - .placeholder(placeholderRes) - .error(placeholderRes) - .fallback(placeholderRes) - .listener( - onStart = { onStart() }, - onSuccess = { _, _ -> onSuccess() }, - onError = { _, _ -> onError() }, - ) - .target(imageView = this) - .build() - .also(context.imageLoader::enqueue) -} - -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 diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconView.kt deleted file mode 100644 index 1eb8c65fd9..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/images/CurrencyIconView.kt +++ /dev/null @@ -1,74 +0,0 @@ -package com.tangem.tap.features.wallet.ui.images - -import android.content.Context -import android.util.AttributeSet -import android.view.LayoutInflater -import android.widget.TextView -import androidx.constraintlayout.utils.widget.ImageFilterView -import androidx.constraintlayout.widget.ConstraintLayout -import androidx.core.view.isVisible -import com.tangem.blockchain.common.derivation.DerivationStyle -import com.tangem.sdk.extensions.dpToPx -import com.tangem.tap.features.wallet.models.Currency -import com.tangem.wallet.databinding.ViewCurrencyIconBinding -import kotlin.math.roundToInt - -@Suppress("MagicNumber") -class CurrencyIconView @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0, -) : ConstraintLayout(context, attrs, defStyleAttr) { - private val binding = ViewCurrencyIconBinding.inflate( - LayoutInflater.from(context), - this, - ) - - val currencyImageView: ImageFilterView - get() = binding.ivCurrency - - val currencyTextView: TextView - get() = binding.tvTokenLetter - - val blockchainBadge: ImageFilterView - get() = binding.ivBlockchainBadge - - var isBlockchainBadgeVisible: Boolean - get() = binding.ivBlockchainBadge.isVisible - set(value) = binding.ivBlockchainBadge::isVisible.set(value) - - var isCustomCurrencyBadgeVisible: Boolean - get() = binding.customBadge.isVisible - set(value) = binding.customBadge::isVisible.set(value) - - init { - minWidth = dpToPx(48f).roundToInt() - minHeight = dpToPx(48f).roundToInt() - } -} - -fun CurrencyIconView.load(currency: Currency, derivationStyle: DerivationStyle?) { - isCustomCurrencyBadgeVisible = currency.isCustomCurrency(derivationStyle) - - CurrencyIconRequest( - currencyImageView = currencyImageView, - currencyTextView = currencyTextView, - token = (currency as? Currency.Token)?.token, - blockchain = currency.blockchain, - ).load() - - if (currency.isToken()) { - // load a blockchain icon into the blockchain badge - isBlockchainBadgeVisible = true - - CurrencyIconRequest( - currencyImageView = blockchainBadge, - currencyTextView = null, - token = null, - blockchain = currency.blockchain, - getLocalImage = true, - ).load() - } else { - isBlockchainBadgeVisible = false - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/test/TestWallet.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/test/TestWallet.kt deleted file mode 100644 index 188cf35d91..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/test/TestWallet.kt +++ /dev/null @@ -1,160 +0,0 @@ -package com.tangem.tap.features.wallet.ui.test - -import com.tangem.blockchain.common.Amount -import com.tangem.blockchain.common.AmountType -import com.tangem.blockchain.common.Token -import com.tangem.blockchain.common.WalletManager -import com.tangem.common.extensions.guard -import com.tangem.domain.common.BlockchainNetwork -import com.tangem.tap.common.TestAction -import com.tangem.tap.common.TestActions -import com.tangem.tap.common.extensions.dispatchDebugErrorNotification -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.store -import java.math.BigDecimal -import kotlin.random.Random - -/** -[REDACTED_AUTHOR] - */ -object TestWallet { - fun solanaRentExemptWarning(): List { - val checker = SolanaRentWarningActionEmitter() - return listOf( - "BALANCE = 0.0" to { checker.setZeroBalance() }, - "BALANCE < 0.00089088" to { checker.setLessThanRentExempt() }, - "BALANCE > 0.00089088" to { checker.setMoreThanRentExempt() }, - "BALANCE = 0.00089087" to { checker.setLessThanRentExemptByOne() }, - "BALANCE = 0.00089088 (rent exempt)" to { checker.setForRentExemptBarrier() }, - "BALANCE = 0.00089089" to { checker.setMoreThanRentExemptByOne() }, - ) - } - - fun getBlockchainBalanceActions(blockchainNetwork: BlockchainNetwork): List { - return getBlockchainBalanceActions( - getWalletManager(blockchainNetwork), - blockchainNetwork.blockchain.decimals(), - ) - } - - fun getTokenBalanceAction(blockchainNetwork: BlockchainNetwork, token: Token): List { - return getTokenBalanceAction(getWalletManager(blockchainNetwork), token) - } - - fun getBlockchainBalanceActions(walletManager: WalletManager?, decimals: Int): List { - val minValue = BigDecimal.ONE.movePointLeft(decimals) - val averageValue = BigDecimal.ONE.movePointRight(decimals) - .divide(BigDecimal(2)).movePointLeft(decimals) - val maxValue = BigDecimal(2).pow(32) - - return listOf( - "0.0" to { setBalance(walletManager, BigDecimal.ZERO) }, - "Min value" to { setBalance(walletManager, minValue) }, - "Average value" to { setBalance(walletManager, averageValue) }, - "Max value" to { setBalance(walletManager, maxValue) }, - "1234567890123.01234567890123" to { setBalance(walletManager, BigDecimal.ZERO) }, - ) - } - - fun getTokenBalanceAction(walletManager: WalletManager?, token: Token): List { - val minValue = BigDecimal.ONE.movePointLeft(token.decimals) - val averageValue = BigDecimal.ONE.movePointRight(token.decimals) - .divide(BigDecimal(2)).movePointLeft(token.decimals) - val maxValue = BigDecimal(2).pow(32) - - return listOf( - "0.0" to { setTokenBalance(walletManager, BigDecimal.ZERO, token) }, - "Min value" to { setTokenBalance(walletManager, minValue, token) }, - "Average value" to { setTokenBalance(walletManager, averageValue, token) }, - "Max value" to { setTokenBalance(walletManager, maxValue, token) }, - "1234567890123.01234567890123" to { setTokenBalance(walletManager, BigDecimal.ZERO, token) }, - ) - } - - fun setBalance(walletManager: WalletManager?, value: BigDecimal) { - val manager = walletManager.guard { - store.dispatchDebugErrorNotification("WalletManager not found") - return - } - val amount = manager.wallet.amounts[AmountType.Coin] ?: Amount(manager.wallet.blockchain) - setBalance(manager, amount, value) - } - - private fun setTokenBalance(walletManager: WalletManager?, value: BigDecimal, token: Token? = null) { - val manager = walletManager.guard { - store.dispatchDebugErrorNotification("WalletManager not found") - return - } - val token = token.guard { - store.dispatchDebugErrorNotification("Token not found") - return - } - - val amount = manager.wallet.amounts[AmountType.Token(token)] ?: Amount(token) - setBalance(manager, amount, value) - } - - private fun setBalance(walletManager: WalletManager, amount: Amount, value: BigDecimal) { - TestActions.testAmountInjectionForWalletManagerEnabled = true - walletManager.wallet.setAmount(amount.copy(value = value)) - store.dispatch(WalletAction.LoadData) - } - - private fun getWalletManager(blockchainNetwork: BlockchainNetwork): WalletManager? { - return store.state.walletState.getWalletManager(blockchainNetwork) - } -} - -private class SolanaRentWarningActionEmitter { - - private val zero = BigDecimal.ZERO - private val one = BigDecimal(0.00000001) - private val rentExemptBarrier = BigDecimal(0.00089088) - private val lessThanRentExemptByOne = rentExemptBarrier.minus(one) - private val moreThanRentExemptByOne = rentExemptBarrier.plus(one) - private val moreThanRentExempt = rentExemptBarrier.plus(Random.nextDouble().toBigDecimal()) - private val lessThanRentExempt = rentExemptBarrier - .minus(Random.nextDouble(0.0, rentExemptBarrier.minus(one).toDouble()).toBigDecimal()) - - fun setZeroBalance() { - setBalance(zero) - } - - fun setForRentExemptBarrier() { - setBalance(rentExemptBarrier) - } - - fun setLessThanRentExemptByOne() { - setBalance(lessThanRentExemptByOne) - } - - fun setMoreThanRentExemptByOne() { - setBalance(moreThanRentExemptByOne) - } - - fun setMoreThanRentExempt() { - setBalance(moreThanRentExempt) - } - - fun setLessThanRentExempt() { - setBalance(lessThanRentExempt) - } - - private fun setBalance(value: BigDecimal) { - val amount = Amount(getBlockchainNetwork().blockchain).copy(value = value) - getWalletManager().apply { - TestActions.testAmountInjectionForWalletManagerEnabled = true - wallet.setAmount(amount) - } - store.dispatch(WalletAction.LoadData) - } - - private fun getWalletManager(): WalletManager { - return store.state.walletState.getWalletManager(getBlockchainNetwork())!! - } - - private fun getBlockchainNetwork(): BlockchainNetwork { - val currency = store.state.walletState.selectedWalletData!!.currency - return BlockchainNetwork(currency.blockchain, currency.derivationPath, listOf()) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/utils/WalletDataOperations.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/utils/WalletDataOperations.kt deleted file mode 100644 index 7a43921862..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/utils/WalletDataOperations.kt +++ /dev/null @@ -1,164 +0,0 @@ -package com.tangem.tap.features.wallet.ui.utils - -import com.tangem.blockchain.common.Blockchain -import com.tangem.common.extensions.isZero -import com.tangem.domain.common.extensions.toNetworkId -import com.tangem.feature.swap.api.SwapFeatureToggleManager -import com.tangem.feature.swap.domain.SwapInteractor -import com.tangem.tap.common.entities.FiatCurrency -import com.tangem.tap.common.extensions.toFiatRateString -import com.tangem.tap.common.extensions.toFiatValue -import com.tangem.tap.common.extensions.toFormattedCryptoCurrencyString -import com.tangem.tap.common.extensions.toFormattedFiatValue -import com.tangem.tap.domain.model.WalletDataModel -import com.tangem.tap.domain.model.WalletStoreModel -import com.tangem.tap.features.wallet.models.PendingTransactionType -import com.tangem.tap.features.wallet.models.WalletWarning -import com.tangem.tap.features.wallet.redux.WalletMainButton -import com.tangem.tap.features.wallet.redux.utils.UNKNOWN_AMOUNT_SIGN -import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager -import java.math.BigDecimal - -internal fun WalletDataModel.mainButton(blockchainAmount: BigDecimal): WalletMainButton = WalletMainButton.SendButton( - enabled = !isEmptyAmount && - hasPendingTransactions() && - !blockchainAmount.isZero(), -) - -internal fun WalletDataModel.hasPendingTransactions(): Boolean { - // for now check pending ongoing only just for BTC, later test and add other utxo networks - val isBitcoinBlockchain = - currency.blockchain == Blockchain.Bitcoin || currency.blockchain == Blockchain.BitcoinTestnet - if (currency.isBlockchain() && isBitcoinBlockchain) { - val outgoingTransactions = status.pendingTransactions.filter { - it.type == PendingTransactionType.Outgoing - } - return outgoingTransactions.isEmpty() - } - return status.pendingTransactions.isEmpty() -} - -internal fun WalletDataModel.getFormattedCryptoAmount(): String { - return status.amount.toFormattedCryptoCurrencyString( - decimals = currency.decimals, - currency = currency.currencySymbol, - ) -} - -internal fun WalletDataModel.getFormattedFiatAmount( - fiatCurrency: FiatCurrency, - unknownAmountSign: String = UNKNOWN_AMOUNT_SIGN, -): String { - return this.fiatRate?.let { status.amount.toFiatValue(it) } - ?.takeIf { !status.isErrorStatus } - ?.toFormattedFiatValue(fiatCurrencyName = fiatCurrency.symbol, fiatCode = fiatCurrency.code) - ?: unknownAmountSign -} - -internal fun WalletDataModel.getFormattedFiatRate(fiatCurrency: FiatCurrency, noRateValue: String): String { - return fiatRate?.toFiatRateString(fiatCurrency.symbol, fiatCurrency.code) - ?: noRateValue -} - -internal fun WalletDataModel.isAvailableToBuy(exchangeManager: CurrencyExchangeManager): Boolean { - return exchangeManager.availableForBuy(currency) -} - -internal fun WalletDataModel.isAvailableToSell(exchangeManager: CurrencyExchangeManager): Boolean { - return exchangeManager.availableForSell(currency) -} - -internal fun WalletDataModel.isAvailableToSwap( - swapFeatureToggleManager: SwapFeatureToggleManager, - swapInteractor: SwapInteractor, - isSingleWallet: Boolean, -): Boolean { - if (isSingleWallet) { - return false - } - if (currency.blockchain.id == Blockchain.Optimism.id && !swapFeatureToggleManager.isOptimismSwapEnabled) { - return false - } - return swapInteractor.isAvailableToSwap(currency.blockchain.toNetworkId()) && - !currency.isCustomCurrency(null) -} - -internal fun WalletDataModel.getAvailableActions( - swapInteractor: SwapInteractor, - exchangeManager: CurrencyExchangeManager, - swapFeatureToggleManager: SwapFeatureToggleManager, - isSingleWallet: Boolean, -): Set { - return setOfNotNull( - if (isAvailableToBuy(exchangeManager)) CurrencyAction.Buy else null, - if (isAvailableToSell(exchangeManager)) CurrencyAction.Sell else null, - if (isAvailableToSwap(swapFeatureToggleManager, swapInteractor, isSingleWallet)) CurrencyAction.Swap else null, - ) -} - -internal fun WalletDataModel.shouldShowMultipleAddress(): Boolean { - val listOfAddresses = walletAddresses?.list.orEmpty() - return listOfAddresses.size > 1 -} - -internal fun WalletDataModel.assembleWarnings( - blockchainAmount: BigDecimal, - blockchainWalletRent: WalletStoreModel.WalletRent?, -): List { - val walletWarnings = mutableListOf() - assembleNonTypedWarnings(walletWarnings, blockchainWalletRent) - assembleBlockchainWarnings(walletWarnings) - assembleTokenWarnings(walletWarnings, blockchainAmount) - - return walletWarnings.sortedBy { it.showingPosition } -} - -private fun WalletDataModel.assembleNonTypedWarnings( - walletWarnings: MutableList, - walletRent: WalletStoreModel.WalletRent?, -) { - if (this.status is WalletDataModel.SameCurrencyTransactionInProgress) { - walletWarnings.add(WalletWarning.TransactionInProgress(currency.currencyName)) - } - if (walletRent != null) { - walletWarnings.add(WalletWarning.Rent(walletRent)) - } -} - -private fun WalletDataModel.assembleBlockchainWarnings(walletWarnings: MutableList) { - with(currency) { - if (!isBlockchain()) return - - if (existentialDeposit != null) { - val warning = WalletWarning.ExistentialDeposit( - currencyName = currencyName, - edStringValueWithSymbol = "${existentialDeposit.toPlainString()} $currencySymbol", - ) - walletWarnings.add(warning) - } - } -} - -private fun WalletDataModel.assembleTokenWarnings( - walletWarnings: MutableList, - blockchainAmount: BigDecimal, -) { - if (!currency.isToken()) return - - if (!this.isEmptyAmount && blockchainAmount.isZero()) { - walletWarnings.add( - WalletWarning.BalanceNotEnoughForFee( - currencyName = currency.currencyName, - blockchainFullName = currency.blockchain.fullName, - blockchainSymbol = currency.blockchain.currency, - ), - ) - } -} - -private val WalletDataModel.isEmptyAmount: Boolean - get() = this.status.amount.isZero() - -enum class CurrencyAction { - Buy, Sell, Swap -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/view/TotalBalanceCard.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/view/TotalBalanceCard.kt deleted file mode 100644 index d39bf43e05..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/view/TotalBalanceCard.kt +++ /dev/null @@ -1,355 +0,0 @@ -package com.tangem.tap.features.wallet.ui.view - -import android.content.Context -import android.util.AttributeSet -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.material.Divider -import androidx.compose.material.Surface -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.AbstractComposeView -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.buildAnnotatedString -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.tooling.preview.Preview -import com.tangem.core.ui.components.* -import com.tangem.core.ui.res.TangemTheme -import com.tangem.tap.common.entities.FiatCurrency -import com.tangem.tap.domain.model.TotalFiatBalance -import com.tangem.tap.features.wallet.redux.utils.UNKNOWN_AMOUNT_SIGN -import com.tangem.wallet.R -import com.valentinilk.shimmer.shimmer -import timber.log.Timber -import java.math.BigDecimal -import java.math.RoundingMode -import java.text.DecimalFormat -import java.text.NumberFormat -import java.util.Currency -import java.util.Locale - -internal class TotalBalanceCard @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0, -) : AbstractComposeView(context, attrs, defStyleAttr) { - private var state by mutableStateOf(TotalBalanceCardState.Empty) - - var status: TotalFiatBalance? = null - set(value) { - if (field == value) return - field = value - updateState(value, fiatCurrency, onChangeFiatCurrencyClick) - } - - var onChangeFiatCurrencyClick: () -> Unit = { /* no-op */ } - set(value) { - if (field == value) return - field = value - updateState(status, fiatCurrency, value) - } - - var fiatCurrency: FiatCurrency = FiatCurrency.Default - set(value) { - if (field == value) return - field = value - updateState(status, value, onChangeFiatCurrencyClick) - } - - @Composable - override fun Content() { - TangemTheme { - TotalBalanceCardContent(state = state) - } - } - - override fun getAccessibilityClassName(): CharSequence { - return javaClass.name - } - - private fun updateState(status: TotalFiatBalance?, fiatCurrency: FiatCurrency, onChangeCurrencyClick: () -> Unit) { - state = when (status) { - null -> TotalBalanceCardState.Empty - is TotalFiatBalance.Failed -> TotalBalanceCardState.Failure( - fiatCurrency = fiatCurrency, - onChangeFiatCurrencyClick = onChangeCurrencyClick, - ) - is TotalFiatBalance.Loading -> TotalBalanceCardState.Loading( - fiatCurrency = fiatCurrency, - onChangeFiatCurrencyClick = onChangeCurrencyClick, - ) - is TotalFiatBalance.Loaded -> TotalBalanceCardState.Success( - amount = status.amount, - showWarning = status.isWarning, - onChangeFiatCurrencyClick = onChangeCurrencyClick, - fiatCurrency = fiatCurrency, - ) - } - } -} - -@Composable -private fun TotalBalanceCardContent(state: TotalBalanceCardState, modifier: Modifier = Modifier) { - TotalBalanceCardScaffold( - modifier = modifier, - title = { - Text( - text = stringResource(id = R.string.main_page_balance), - style = TangemTheme.typography.subtitle2, - color = TangemTheme.colors.text.tertiary, - ) - }, - amount = { - when (state) { - is TotalBalanceCardState.Empty, - is TotalBalanceCardState.Loading, - -> LoadingAmount() - is TotalBalanceCardState.Failure, - is TotalBalanceCardState.Success, - -> LoadedAmount( - amount = buildAmountString( - amount = state.amount, - fiatCurrency = state.fiatCurrency, - ), - ) - } - }, - currencySelector = { - if (state !is TotalBalanceCardState.Empty) { - SelectorButton( - text = state.fiatCurrency.code, - onClick = state.onChangeFiatCurrencyClick, - ) - } - }, - warningText = { - AnimatedVisibility(visible = state.showWarning) { - Text( - modifier = Modifier.fillMaxWidth(), - text = stringResource(id = R.string.main_processing_full_amount), - style = TangemTheme.typography.caption2, - color = TangemTheme.colors.text.attention, - ) - } - }, - ) -} - -@Composable -private fun TotalBalanceCardScaffold( - title: @Composable () -> Unit, - amount: @Composable () -> Unit, - currencySelector: @Composable () -> Unit, - warningText: @Composable () -> Unit, - modifier: Modifier = Modifier, - amountWeight: Float = 0.8f, -) { - Surface( - modifier = modifier, - shape = TangemTheme.shapes.roundedCornersMedium, - color = TangemTheme.colors.background.plain, - elevation = TangemTheme.dimens.elevation1, - ) { - Column( - modifier = Modifier.fillMaxWidth(), - ) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.Top, - ) { - SpacerW16() - Column( - modifier = Modifier.weight(amountWeight), - ) { - SpacerH12() - title() - SpacerH4() - amount() - } - currencySelector() - SpacerW4() - } - SpacerH4() - Box( - modifier = Modifier.padding( - horizontal = TangemTheme.dimens.spacing16, - ), - ) { - warningText() - } - SpacerH12() - } - } -} - -@Composable -private fun LoadingAmount(modifier: Modifier = Modifier) { - Box(modifier = modifier.shimmer()) { - Box( - modifier = Modifier - .width(TangemTheme.dimens.size116) - .height(TangemTheme.dimens.size32) - .background( - color = TangemTheme.colors.stroke.primary, - shape = TangemTheme.shapes.roundedCornersSmall2, - ), - ) - } -} - -@Composable -private fun LoadedAmount(amount: AnnotatedString, modifier: Modifier = Modifier) { - Box(modifier = modifier) { - Text( - text = amount, - style = TangemTheme.typography.h2, - color = TangemTheme.colors.text.primary1, - overflow = TextOverflow.Ellipsis, - maxLines = 1, - ) - } -} - -@Composable -private fun buildAmountString(amount: BigDecimal?, fiatCurrency: FiatCurrency): AnnotatedString { - if (amount == null) return AnnotatedString(text = UNKNOWN_AMOUNT_SIGN) - - val locale = Locale.getDefault() - val fractionDigits = 2 - val formatter = NumberFormat.getCurrencyInstance(locale) as? DecimalFormat - ?: return AnnotatedString("${amount.toPlainString()} ${fiatCurrency.symbol}") - - val currencyToShow = "${fiatCurrency.symbol} " - val scaledAmount = try { - Currency.getInstance(fiatCurrency.code)?.let { currency -> - formatter.currency = currency - formatter.maximumFractionDigits = fractionDigits - formatter.minimumFractionDigits = fractionDigits - formatter.isGroupingUsed = true - formatter.roundingMode = RoundingMode.HALF_UP - formatter.format(amount).replace(currency.symbol, currencyToShow) - } ?: formatter.format(amount) - } catch (e: IllegalArgumentException) { - Timber.e("TotalBalanceCard buildAmountString currencyCode is not a supported ISO 4217 code: $e") - formatter.currency?.let { - formatter.format(amount).replace(it.symbol, currencyToShow) - } ?: formatter.format(amount) - } - - val integer = scaledAmount.substringBefore(formatter.decimalFormatSymbols.decimalSeparator) - var reminder = scaledAmount.substringAfter(formatter.decimalFormatSymbols.decimalSeparator) - - // if locale formatted currency at the end, remember it and place out of AnnotatedString - val currency = if (reminder.endsWith(currencyToShow)) { - reminder = reminder.dropLast(currencyToShow.length) - currencyToShow - } else { - "" - } - - return buildAnnotatedString { - append(integer) - append(formatter.decimalFormatSymbols.decimalSeparator) - append( - AnnotatedString( - text = reminder, - spanStyle = TangemTheme.typography.h3.toSpanStyle(), - ), - ) - append(currency) // it is not empty if was placed at the end after locale formatting - } -} - -private sealed interface TotalBalanceCardState { - val amount: BigDecimal? - val showWarning: Boolean - val fiatCurrency: FiatCurrency - val onChangeFiatCurrencyClick: () -> Unit - - object Empty : TotalBalanceCardState { - override val amount: BigDecimal? = null - override val showWarning: Boolean = false - override val fiatCurrency: FiatCurrency = FiatCurrency.Default - override val onChangeFiatCurrencyClick: () -> Unit = { /* no-op */ } - } - - data class Loading( - override val fiatCurrency: FiatCurrency, - override val onChangeFiatCurrencyClick: () -> Unit, - ) : TotalBalanceCardState { - override val amount: BigDecimal = BigDecimal.ZERO - override val showWarning: Boolean = false - } - - data class Failure( - override val fiatCurrency: FiatCurrency, - override val onChangeFiatCurrencyClick: () -> Unit, - ) : TotalBalanceCardState { - override val amount: BigDecimal? = null - override val showWarning: Boolean = true - } - - data class Success( - override val amount: BigDecimal, - override val showWarning: Boolean, - override val fiatCurrency: FiatCurrency, - override val onChangeFiatCurrencyClick: () -> Unit, - ) : TotalBalanceCardState -} - -// region Preview -@Composable -private fun TotalBalanceCardContentSample(modifier: Modifier = Modifier) { - Column( - modifier = modifier - .background(TangemTheme.colors.background.primary) - .padding(all = TangemTheme.dimens.spacing16), - ) { - TotalBalanceCardContent(state = TotalBalanceCardState.Empty) - Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) - TotalBalanceCardContent( - state = TotalBalanceCardState.Loading(FiatCurrency("USD", "USD", "$")) {}, - ) - Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) - TotalBalanceCardContent( - state = TotalBalanceCardState.Failure( - onChangeFiatCurrencyClick = {}, - fiatCurrency = FiatCurrency("USD", "USD", "$"), - ), - ) - Divider(modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing8)) - TotalBalanceCardContent( - state = TotalBalanceCardState.Success( - amount = BigDecimal("9917.72"), - showWarning = false, - onChangeFiatCurrencyClick = {}, - fiatCurrency = FiatCurrency("USD", "USD", "$"), - ), - ) - } -} - -@Preview(showBackground = true, widthDp = 360) -@Composable -private fun TotalBalanceCardContentPreview_Light() { - TangemTheme { - TotalBalanceCardContentSample() - } -} - -@Preview(showBackground = true, widthDp = 360) -@Composable -private fun TotalBalanceCardContentPreview_Dark() { - TangemTheme(isDark = true) { - TotalBalanceCardContentSample() - } -} -// endregion Preview \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/view/WalletDetailsButtonsRow.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/view/WalletDetailsButtonsRow.kt deleted file mode 100644 index d9bf31f00c..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/view/WalletDetailsButtonsRow.kt +++ /dev/null @@ -1,77 +0,0 @@ -package com.tangem.tap.features.wallet.ui.view - -import android.content.Context -import android.util.AttributeSet -import android.view.Gravity -import android.view.LayoutInflater -import android.widget.LinearLayout -import androidx.core.view.isVisible -import com.google.android.material.button.MaterialButton -import com.tangem.tap.common.extensions.hide -import com.tangem.tap.common.extensions.show -import com.tangem.tap.features.wallet.ui.utils.CurrencyAction -import com.tangem.wallet.databinding.ViewWalletDetailsButtonsRowBinding - -internal class WalletDetailsButtonsRow @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0, -) : LinearLayout(context, attrs, defStyleAttr) { - private val binding = ViewWalletDetailsButtonsRowBinding.inflate( - LayoutInflater.from(context), - this, - ) - var onBuyClick: (() -> Unit)? = null - var onSellClick: (() -> Unit)? = null - var onTradeClick: (() -> Unit)? = null - var onSwapClick: (() -> Unit)? = null - var onSendClick: (() -> Unit)? = null - - init { - orientation = HORIZONTAL - - with(binding) { - btnBuy.setOnClickListener { onBuyClick?.invoke() } - btnSell.setOnClickListener { onSellClick?.invoke() } - btnSwap.setOnClickListener { onSwapClick?.invoke() } - btnTrade.setOnClickListener { onTradeClick?.invoke() } - btnSend.setOnClickListener { onSendClick?.invoke() } - } - } - - fun updateButtonsVisibility( - actions: Set, - exchangeServiceFeatureOn: Boolean, - sendAllowed: Boolean, - ) = with(binding) { - containerActionButtons.isVisible = exchangeServiceFeatureOn - - when { - actions.isEmpty() -> { - containerActionButtons.hide() - } - actions.size == 1 -> { - val action = actions.first() - btnTrade.hide() - btnBuy.show(action == CurrencyAction.Buy) - btnSell.show(action == CurrencyAction.Sell) - btnSwap.show(action == CurrencyAction.Swap) - } - else -> { - btnBuy.hide() - btnSell.hide() - btnSwap.hide() - btnTrade.show() - } - } - - if (containerActionButtons.isVisible) { - btnSend.gravity = Gravity.START or Gravity.CENTER_VERTICAL - btnSend.iconGravity = MaterialButton.ICON_GRAVITY_END - } else { - btnSend.gravity = Gravity.CENTER - btnSend.iconGravity = MaterialButton.ICON_GRAVITY_TEXT_END - } - btnSend.isEnabled = sendAllowed - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/CurrencySelectionDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/CurrencySelectionDialog.kt deleted file mode 100644 index 51a29dfdba..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/CurrencySelectionDialog.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.tangem.tap.features.wallet.ui.wallet - -import android.content.Context -import androidx.appcompat.app.AlertDialog -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.features.wallet.redux.models.WalletDialog -import com.tangem.tap.store -import com.tangem.wallet.R - -object CurrencySelectionDialog { - fun create(dialog: WalletDialog.CurrencySelectionDialog, context: Context): AlertDialog { - val currenciesToShow = dialog.currenciesList - .map { it.displayName } - .toTypedArray() - val currentSelection = dialog.currenciesList - .indexOfFirst { it.code == dialog.currentAppCurrency.code } - - return MaterialAlertDialogBuilder(context, R.style.CustomMaterialDialog) - .setTitle(context.getString(R.string.details_row_title_currency)) - .setNegativeButton(context.getString(R.string.common_cancel)) { _, _ -> /* no-op */ } - .setOnDismissListener { - store.dispatch(WalletAction.DialogAction.Hide) - } - .setSingleChoiceItems(currenciesToShow, currentSelection) { _, which -> - dialog.currenciesList.getOrNull(which)?.let { selectedCurrency -> - store.dispatch( - WalletAction.AppCurrencyAction.SelectAppCurrency( - fiatCurrency = selectedCurrency, - ), - ) - store.dispatch(WalletAction.DialogAction.Hide) - } - } - .create() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt deleted file mode 100644 index 37789a8792..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt +++ /dev/null @@ -1,191 +0,0 @@ -package com.tangem.tap.features.wallet.ui.wallet - -import androidx.core.view.isVisible -import androidx.recyclerview.widget.LinearLayoutManager -import com.badoo.mvicore.modelWatcher -import com.tangem.core.analytics.Analytics -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction -import com.tangem.domain.tokens.TokensAction -import com.tangem.tap.common.analytics.events.MainScreen -import com.tangem.tap.common.analytics.events.Portfolio -import com.tangem.tap.common.entities.FiatCurrency -import com.tangem.tap.common.extensions.getQuantityString -import com.tangem.tap.common.extensions.hide -import com.tangem.tap.common.extensions.show -import com.tangem.tap.domain.model.TotalFiatBalance -import com.tangem.tap.features.wallet.redux.ErrorType -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.features.wallet.redux.WalletState -import com.tangem.tap.features.wallet.ui.WalletFragment -import com.tangem.tap.features.wallet.ui.adapters.WalletAdapter -import com.tangem.tap.store -import com.tangem.wallet.R -import com.tangem.wallet.databinding.FragmentWalletBinding - -class MultiWalletView : WalletView() { - - private lateinit var walletsAdapter: WalletAdapter - - private val watcher = modelWatcher { - // !!! Workaround !!! - // Checking state properties instead of state params can reduce application performance, - // but here it is necessary because the WalletStore has an unsuitable equals method - WalletState::walletsDataFromStores { - walletsAdapter.submitList(it) - } - WalletState::loadingUserTokens { - binding?.pbLoadingUserTokens?.show(it) - } - WalletState::walletCardsCount { walletCardsCount -> - binding?.let { - setupWalletCardNumber(it, walletCardsCount) - } - } - WalletState::missingDerivations { missingDerivations -> - binding?.let { - handleRescanWarning(it, missingDerivations.isNotEmpty()) - } - } - WalletState::showBackupWarning { showBackupWarnings -> - binding?.let { - handleBackupWarning(it, showBackupWarnings) - } - } - (WalletState::totalBalance or WalletState::walletsDataFromStores) { walletState -> - binding?.let { - handleTotalBalance( - binding = it, - totalBalance = walletState.totalBalance, - walletsCount = walletState.walletsDataFromStores.size, - appFiatCurrency = store.state.globalState.appCurrency, - ) - } - } - } - - override fun changeWalletView(fragment: WalletFragment, binding: FragmentWalletBinding) { - setFragment(fragment, binding) - onViewCreated() - showMultiWalletView(binding) - } - - private fun showMultiWalletView(binding: FragmentWalletBinding) = with(binding) { - watcher.clear() - tvTwinCardNumber.hide() - rvPendingTransaction.hide() - lCardBalance.root.hide() - lAddress.root.hide() - rowButtons.hide() - lSingleWalletBalance.root.hide() - lCardTotalBalance.show() - rvMultiwallet.show() - btnAddToken.show() - } - - override fun onViewCreated() { - setupWalletsRecyclerView() - } - - private fun setupWalletsRecyclerView() { - val fragment = fragment ?: return - walletsAdapter = WalletAdapter() - walletsAdapter.setHasStableIds(true) - binding?.rvMultiwallet?.layoutManager = LinearLayoutManager(fragment.requireContext()) - binding?.rvMultiwallet?.adapter = walletsAdapter - binding?.rvMultiwallet?.itemAnimator = null - } - - override fun onNewState(state: WalletState) { - val fragment = fragment ?: return - val binding = binding ?: return - - watcher.invoke(state) - - binding.btnAddToken.setOnClickListener { - Analytics.send(Portfolio.ButtonManageTokens()) - - store.dispatch(action = TokensAction.SetArgs.ManageAccess) - store.dispatch(action = NavigationAction.NavigateTo(screen = AppScreen.ManageTokens)) - } - handleErrorStates(state = state, binding = binding, fragment = fragment) - } - - private fun setupWalletCardNumber(binding: FragmentWalletBinding, walletCardsCount: Int?) = with(binding) { - if (walletCardsCount != null) { - tvTwinCardNumber.show() - tvTwinCardNumber.text = - tvTwinCardNumber.getQuantityString(R.plurals.card_label_card_count, walletCardsCount) - } else { - tvTwinCardNumber.hide() - } - } - - private fun handleBackupWarning(binding: FragmentWalletBinding, showBackupWarning: Boolean) = - with(binding.lWalletBackupWarning) { - root.isVisible = showBackupWarning - root.setOnClickListener { - Analytics.send(MainScreen.NoticeBackupYourWalletTapped()) - store.dispatch(WalletAction.MultiWallet.BackupWallet) - } - } - - private fun handleRescanWarning(binding: FragmentWalletBinding, showRescanWarning: Boolean) = - with(binding.lWalletRescanWarning) { - root.isVisible = showRescanWarning - root.setOnClickListener { - Analytics.send(MainScreen.NoticeScanYourCardTapped()) - store.dispatch(WalletAction.MultiWallet.ScanToGetDerivations) - } - } - - private fun handleTotalBalance( - binding: FragmentWalletBinding, - totalBalance: TotalFiatBalance?, - walletsCount: Int, - appFiatCurrency: FiatCurrency, - ) = with(binding.lCardTotalBalance) { - isVisible = walletsCount > 0 - - onChangeFiatCurrencyClick = { - store.dispatch(WalletAction.AppCurrencyAction.ChooseAppCurrency) - } - status = totalBalance - fiatCurrency = appFiatCurrency - } - - private fun handleErrorStates(state: WalletState, binding: FragmentWalletBinding, fragment: WalletFragment) { - when (state.error) { - ErrorType.UnknownBlockchain -> { - showErrorState( - binding, - fragment.getText(R.string.wallet_error_unsupported_blockchain), - fragment.getString(R.string.wallet_error_unsupported_blockchain_subtitle), - ) - } - - else -> Unit - } - } - - private fun showErrorState( - binding: FragmentWalletBinding, - errorTitle: CharSequence, - errorDescription: CharSequence, - ) = with(binding) { - lCardBalance.root.show() - with(lCardBalance) { - lBalance.root.hide() - lBalanceError.root.show() - rvMultiwallet.show() - btnAddToken.hide() - lBalanceError.tvErrorTitle.text = errorTitle - lBalanceError.tvErrorDescriptions.text = errorDescription - } - } - - override fun onDestroyFragment() { - super.onDestroyFragment() - watcher.clear() - } -} \ 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 deleted file mode 100644 index 7ce1a4d49e..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt +++ /dev/null @@ -1,235 +0,0 @@ -package com.tangem.tap.features.wallet.ui.wallet - -import android.view.View -import android.view.ViewGroup -import androidx.recyclerview.widget.LinearLayoutManager -import com.tangem.core.analytics.Analytics -import com.tangem.domain.tokens.legacy.TradeCryptoAction -import com.tangem.tap.common.analytics.events.Token -import com.tangem.tap.common.extensions.* -import com.tangem.tap.domain.model.WalletDataModel -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.PendingTransactionType -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.features.wallet.redux.WalletState -import com.tangem.tap.features.wallet.ui.BalanceWidget -import com.tangem.tap.features.wallet.ui.MultipleAddressUiHelper -import com.tangem.tap.features.wallet.ui.WalletFragment -import com.tangem.tap.features.wallet.ui.adapters.PendingTransactionsAdapter -import com.tangem.tap.features.wallet.ui.utils.* -import com.tangem.tap.features.wallet.ui.view.WalletDetailsButtonsRow -import com.tangem.tap.store -import com.tangem.wallet.R -import com.tangem.wallet.databinding.FragmentWalletBinding - -class SingleWalletView : WalletView() { - private lateinit var pendingTransactionAdapter: PendingTransactionsAdapter - - // FIXME: Move to model watcher - private var watchedPrimaryWalletForAddressCard: WalletDataModel? = null - private var watchedPrimaryWalletForBalance: WalletDataModel? = null - - override fun changeWalletView(fragment: WalletFragment, binding: FragmentWalletBinding) { - setFragment(fragment, binding) - onViewCreated() - showSingleWalletView(binding) - } - - private fun showSingleWalletView(binding: FragmentWalletBinding) = with(binding) { - watchedPrimaryWalletForAddressCard = null - watchedPrimaryWalletForBalance = null - tvTwinCardNumber.hide() - rvMultiwallet.hide() - btnAddToken.hide() - rvPendingTransaction.hide() - pbLoadingUserTokens.hide() - lCardTotalBalance.hide() - lSingleWalletBalance.root.hide() - lWalletRescanWarning.root.hide() - lWalletBackupWarning.root.hide() - lCardBalance.root.show() - lAddress.root.show() - rowButtons.show() - } - - override fun onViewCreated() { - setupTransactionsRecyclerView() - } - - override fun onDestroyFragment() { - super.onDestroyFragment() - watchedPrimaryWalletForAddressCard = null - watchedPrimaryWalletForBalance = null - } - - private fun setupTransactionsRecyclerView() { - val fragment = fragment ?: return - pendingTransactionAdapter = PendingTransactionsAdapter() - binding?.rvPendingTransaction?.layoutManager = - LinearLayoutManager(fragment.requireContext()) - binding?.rvPendingTransaction?.adapter = pendingTransactionAdapter - } - - override fun onNewState(state: WalletState) { - val binding = binding ?: return - val primaryWalletData = state.primaryWalletData ?: return - - setupTwinCards(state.twinCardsState, binding) - setupButtons(primaryWalletData, binding, state.isExchangeServiceFeatureOn) - setupAddressCard(state, binding) - showPendingTransactionsIfPresent(primaryWalletData.status.pendingTransactions) - setupBalance(state, primaryWalletData) - } - - private fun showPendingTransactionsIfPresent(pendingTransactions: List) { - val knownTransactions = pendingTransactions.filterNot { - it.type == PendingTransactionType.Unknown - } - pendingTransactionAdapter.submitList(knownTransactions) - binding?.rvPendingTransaction?.show(knownTransactions.isNotEmpty()) - } - - private fun setupBalance(state: WalletState, primaryWallet: WalletDataModel) { - if (watchedPrimaryWalletForBalance == primaryWallet) return - watchedPrimaryWalletForBalance = primaryWallet - - val fragment = fragment ?: return - binding?.apply { - lCardBalance.lBalance.root.show() - BalanceWidget( - binding = this.lCardBalance, - fragment = fragment, - blockchainWalletData = primaryWallet, - tokenWalletData = state.primaryTokenData, - ).setup() - } - } - - private fun setupTwinCards(twinCardsState: TwinCardsState?, binding: FragmentWalletBinding) = with(binding) { - if (twinCardsState?.cardNumber == null) { - tvTwinCardNumber.hide() - } else { - tvTwinCardNumber.show() - tvTwinCardNumber.text = tvTwinCardNumber.getQuantityString(R.plurals.card_label_card_count, 2) - } - } - - private fun setupButtons( - walletData: WalletDataModel, - binding: FragmentWalletBinding, - isExchangeServiceFeatureEnabled: Boolean, - ) = with(binding) { - setupRowButtons(walletData, rowButtons, isExchangeServiceFeatureEnabled) - - lAddress.btnCopy.setOnClickListener { - walletData.walletAddresses?.selectedAddress?.address?.let { addressString -> - store.dispatch(WalletAction.CopyAddress(addressString, fragment!!.requireContext())) - } - } - lAddress.btnShowQr.setOnClickListener { - Analytics.send(Token.ShowWalletAddress) - - walletData.walletAddresses?.selectedAddress?.let { selectedAddress -> - store.dispatch( - WalletAction.DialogAction.QrCode( - currency = walletData.currency, - selectedAddress = selectedAddress, - ), - ) - } - } - } - - private fun setupRowButtons( - walletData: WalletDataModel, - rowButtons: WalletDetailsButtonsRow, - isExchangeServiceFeatureEnabled: Boolean, - ) { - val swapInteractor = this.swapInteractor ?: return - val swapFeatureToggleManager = this.swapFeatureToggleManager ?: return - - val exchangeManager = store.state.globalState.exchangeManager - binding?.rowButtons?.apply { - onBuyClick = { store.dispatch(TradeCryptoAction.Buy()) } - onSellClick = { store.dispatch(TradeCryptoAction.Sell) } - onSwapClick = { store.dispatch(TradeCryptoAction.Swap) } - onTradeClick = { - store.dispatch( - WalletAction.DialogAction.ChooseTradeActionDialog( - buyAllowed = walletData.isAvailableToBuy(exchangeManager), - sellAllowed = walletData.isAvailableToSell(exchangeManager), - swapAllowed = false, // always disable for single wallet - ), - ) - } - } - val actions = walletData.getAvailableActions( - swapInteractor = swapInteractor, - exchangeManager = exchangeManager, - swapFeatureToggleManager = swapFeatureToggleManager, - isSingleWallet = true, - ) - binding?.rowButtons?.updateButtonsVisibility( - actions = actions, - exchangeServiceFeatureOn = isExchangeServiceFeatureEnabled, - sendAllowed = walletData.mainButton(walletData.status.amount).enabled, - ) - - rowButtons.onSendClick = { store.dispatch(WalletAction.Send()) } - } - - private fun setupAddressCard(state: WalletState, binding: FragmentWalletBinding) = with(binding.lAddress) { - val primaryWallet = state.primaryWalletData - if (primaryWallet == watchedPrimaryWalletForAddressCard) return@with - watchedPrimaryWalletForAddressCard = primaryWallet - - if (primaryWallet?.walletAddresses != null && primaryWallet.currency is Currency.Blockchain) { - binding.lAddress.root.show() - if (primaryWallet.shouldShowMultipleAddress()) { - (binding.lAddress.root as? ViewGroup)?.beginDelayedTransition() - chipGroupAddressType.show() - chipGroupAddressType.fitChipsByGroupWidth() - val checkedId = MultipleAddressUiHelper.typeToId( - primaryWallet.walletAddresses.selectedAddress.type, - primaryWallet.currency.blockchain, - ) - if (checkedId != View.NO_ID) chipGroupAddressType.check(checkedId) - - chipGroupAddressType.setOnCheckedChangeListener { group, checkedId -> - if (checkedId == -1) return@setOnCheckedChangeListener - val type = MultipleAddressUiHelper.idToType(checkedId, primaryWallet.currency.blockchain) - type?.let { store.dispatch(WalletAction.ChangeSelectedAddress(type)) } - } - } else { - chipGroupAddressType.hide() - } - tvAddress.text = primaryWallet.walletAddresses.selectedAddress.address - tvExplore.setOnClickListener { - store.dispatch( - WalletAction.ExploreAddress( - primaryWallet.walletAddresses.selectedAddress.exploreUrl, - fragment!!.requireContext(), - ), - ) - } - setupCardInfo(primaryWallet) - } else { - binding.lAddress.root.hide() - } - } - - private fun setupCardInfo(walletData: WalletDataModel) { - val textView = binding?.lAddress?.tvInfo - val blockchain = walletData.currency.blockchain - if (textView != null) { - textView.text = textView.getString( - id = R.string.address_qr_code_message_format, - blockchain.fullName, - blockchain.currency, - blockchain.fullName, - ) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/WalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/WalletView.kt deleted file mode 100644 index c3cb5a7c74..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/WalletView.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.tangem.tap.features.wallet.ui.wallet - -import com.tangem.feature.swap.api.SwapFeatureToggleManager -import com.tangem.feature.swap.domain.SwapInteractor -import com.tangem.tap.features.wallet.redux.WalletState -import com.tangem.tap.features.wallet.ui.WalletFragment -import com.tangem.wallet.databinding.FragmentWalletBinding - -abstract class WalletView { - - var swapInteractor: SwapInteractor? = null - var swapFeatureToggleManager: SwapFeatureToggleManager? = null - - protected var fragment: WalletFragment? = null - protected var binding: FragmentWalletBinding? = null - - fun setFragment(fragment: WalletFragment, binding: FragmentWalletBinding) { - this.fragment = fragment - this.binding = binding - } - - fun removeFragment() { - fragment = null - binding = null - } - - open fun onViewDestroy() { - removeFragment() - } - - open fun onDestroyFragment() {} - - abstract fun changeWalletView(fragment: WalletFragment, binding: FragmentWalletBinding) - abstract fun onViewCreated() - abstract fun onNewState(state: WalletState) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/UserWalletModel.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/redux/UserWalletModel.kt deleted file mode 100644 index d1300ffa43..0000000000 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/UserWalletModel.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.tangem.tap.features.walletSelector.redux - -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.domain.model.TotalFiatBalance - -data class UserWalletModel( - val id: UserWalletId, - val name: String, - val artworkUrl: String, - val type: Type, - val fiatBalance: TotalFiatBalance, - val isLocked: Boolean, -) { - sealed interface Type { - data class SingleCurrency( - val blockchainName: String, - ) : Type - - data class MultiCurrency( - val cardsInWallet: Int, - val tokensCount: Int, - ) : Type - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorAction.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorAction.kt deleted file mode 100644 index 9acb28b1d6..0000000000 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorAction.kt +++ /dev/null @@ -1,62 +0,0 @@ -package com.tangem.tap.features.walletSelector.redux - -import com.tangem.common.core.TangemError -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.common.entities.FiatCurrency -import com.tangem.tap.domain.model.WalletStoreModel -import org.rekotlin.Action - -internal sealed interface WalletSelectorAction : Action { - data class UserWalletsLoaded( - val userWallets: List, - ) : WalletSelectorAction - - data class SelectedWalletChanged( - val selectedWallet: UserWallet, - ) : WalletSelectorAction - - data class IsLockedChanged( - val isLocked: Boolean, - ) : WalletSelectorAction - - data class WalletStoresChanged( - val walletsStores: Map>, - ) : WalletSelectorAction - - data class BalancesLoaded( - val userWalletModels: List, - ) : WalletSelectorAction - - object UnlockWithBiometry : WalletSelectorAction { - object Success : WalletSelectorAction - data class Error(val error: TangemError) : WalletSelectorAction - } - - data class SelectWallet( - val userWalletId: UserWalletId, - val sendAnalyticsEvent: Boolean = false, - ) : WalletSelectorAction - - data class RenameWallet( - val userWalletId: UserWalletId, - val newName: String, - ) : WalletSelectorAction - - data class RemoveWallets( - val userWalletsIds: List, - ) : WalletSelectorAction - - object AddWallet : WalletSelectorAction { - object Success : WalletSelectorAction - data class Error(val error: TangemError) : WalletSelectorAction - } - - data class ChangeAppCurrency( - val fiatCurrency: FiatCurrency, - ) : WalletSelectorAction - - object CloseError : WalletSelectorAction - - object ClearUserWallets : WalletSelectorAction -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorMiddleware.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorMiddleware.kt deleted file mode 100644 index 1593e811f9..0000000000 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorMiddleware.kt +++ /dev/null @@ -1,397 +0,0 @@ -package com.tangem.tap.features.walletSelector.redux - -import com.tangem.common.* -import com.tangem.common.core.TangemSdkError -import com.tangem.core.analytics.Analytics -import com.tangem.core.navigation.AppScreen -import com.tangem.core.navigation.NavigationAction -import com.tangem.domain.models.scan.ScanResponse -import com.tangem.domain.userwallets.UserWalletBuilder -import com.tangem.domain.userwallets.UserWalletIdBuilder -import com.tangem.domain.wallets.legacy.unlockIfLockable -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.* -import com.tangem.tap.common.analytics.events.AnalyticsParam -import com.tangem.tap.common.analytics.events.Basic -import com.tangem.tap.common.analytics.events.MyWallets -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.common.extensions.dispatchWithMain -import com.tangem.tap.common.extensions.onUserWalletSelected -import com.tangem.tap.common.redux.AppState -import com.tangem.tap.domain.model.TotalFiatBalance -import com.tangem.tap.domain.model.WalletStoreModel -import com.tangem.tap.proxy.redux.DaggerGraphState -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.launch -import org.rekotlin.Middleware -import timber.log.Timber - -// Refactoring is coming -@Suppress("LargeClass") -internal class WalletSelectorMiddleware { - val middleware: Middleware = { _, appStateProvider -> - { next -> - { action -> - val appState = appStateProvider() - if (action is WalletSelectorAction && appState != null) { - handleAction(action, appState.walletSelectorState) - } - next(action) - } - } - } - - private fun handleAction(action: WalletSelectorAction, state: WalletSelectorState) { - when (action) { - is WalletSelectorAction.UserWalletsLoaded -> { - fetchWalletStores(action.userWallets) - } - is WalletSelectorAction.WalletStoresChanged -> { - updateBalances(action.walletsStores, state) - } - is WalletSelectorAction.UnlockWithBiometry -> { - unlockWallets() - } - is WalletSelectorAction.AddWallet -> { - addWallet() - } - is WalletSelectorAction.SelectWallet -> { - selectWallet(action.userWalletId) - } - is WalletSelectorAction.RemoveWallets -> { - deleteWallets(action.userWalletsIds, state) - } - is WalletSelectorAction.RenameWallet -> { - renameWallet(action.userWalletId, action.newName) - } - is WalletSelectorAction.ChangeAppCurrency -> { - refreshUserWalletsAmounts() - } - is WalletSelectorAction.ClearUserWallets -> { - clearUserWalletsAndCloseError() - } - is WalletSelectorAction.AddWallet.Success, - is WalletSelectorAction.AddWallet.Error, - is WalletSelectorAction.SelectedWalletChanged, - is WalletSelectorAction.UnlockWithBiometry.Error, - is WalletSelectorAction.UnlockWithBiometry.Success, - is WalletSelectorAction.BalancesLoaded, - is WalletSelectorAction.IsLockedChanged, - is WalletSelectorAction.CloseError, - -> Unit - } - } - - private fun fetchWalletStores(userWallets: List) { - scope.launch { - walletStoresManager.fetch(userWallets) - .doOnFailure { error -> - Timber.e(error, "Unable to fetch wallet stores") - } - } - } - - private fun updateBalances( - updatedWalletStores: Map>, - state: WalletSelectorState, - ) { - if (updatedWalletStores.isNotEmpty()) { - scope.launch(Dispatchers.Default) { - val updatedWallets = state.wallets.calculateBalanceAndUpdateWalletStores(updatedWalletStores) - if (updatedWallets != state.wallets) { - store.dispatchOnMain(WalletSelectorAction.BalancesLoaded(updatedWallets)) - } - } - } - } - - private fun unlockWallets() { - Analytics.send(MyWallets.Button.UnlockWithBiometrics()) - - scope.launch { - userWalletsListManager.unlockIfLockable() - .doOnFailure { error -> - Timber.e(error, "Unable to unlock all user wallets") - store.dispatchOnMain(WalletSelectorAction.UnlockWithBiometry.Error(error)) - } - .doOnSuccess { - store.dispatchOnMain(WalletSelectorAction.UnlockWithBiometry.Success) - } - } - } - - private fun addWallet() = scope.launch { - Analytics.send(MyWallets.Button.ScanNewCard()) - - val cardSdkConfigRepository = store.state.daggerGraphState.get(DaggerGraphState::cardSdkConfigRepository) - - val prevUseBiometricsForAccessCode = cardSdkConfigRepository.isBiometricsRequestPolicy() - - // Update access code policy for access code saving when a card was scanned - cardSdkConfigRepository.setAccessCodeRequestPolicy( - isBiometricsRequestPolicy = preferencesStorage.shouldSaveAccessCodes, - ) - - store.state.daggerGraphState.get(DaggerGraphState::scanCardProcessor).scan( - analyticsEvent = Basic.CardWasScanned(AnalyticsParam.ScannedFrom.MyWallets), - onWalletNotCreated = { - // No need to rollback policy, continue with the policy set before the card scan - store.dispatchOnMain(WalletSelectorAction.AddWallet.Success) - store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Wallet)) - }, - disclaimerWillShow = { - store.dispatchOnMain(NavigationAction.PopBackTo()) - }, - onSuccess = { scanResponse -> - saveUserWalletAndPopBackToWalletScreen(scanResponse) - .doOnFailure { error -> - // Rollback policy if card saving was failed - cardSdkConfigRepository.setAccessCodeRequestPolicy(prevUseBiometricsForAccessCode) - Timber.e(error, "Unable to save user wallet") - store.dispatchOnMain(WalletSelectorAction.AddWallet.Error(error)) - } - }, - onFailure = { error -> - // Rollback policy if card scanning was failed - cardSdkConfigRepository.setAccessCodeRequestPolicy(prevUseBiometricsForAccessCode) - Timber.e(error, "Unable to scan card") - store.dispatchOnMain(WalletSelectorAction.AddWallet.Error(error)) - }, - ) - } - - private suspend fun saveUserWalletAndPopBackToWalletScreen(scanResponse: ScanResponse): CompletionResult { - val userWallet = UserWalletBuilder(scanResponse).build() - ?: return CompletionResult.Failure(TangemSdkError.WalletIsNotCreated()) - - return userWalletsListManager.save(userWallet) - .doOnSuccess { - store.dispatchOnMain(WalletSelectorAction.AddWallet.Success) - store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Wallet)) - store.onUserWalletSelected(userWallet) - } - } - - private fun selectWallet(userWalletId: UserWalletId) { - scope.launch { - userWalletsListManager.get(userWalletId) - .flatMap { userWallet -> - if (userWallet.isLocked) { - unlockUserWalletWithScannedCard(userWallet) - } else { - userWalletsListManager.select(userWalletId) - } - } - .doOnFailure { error -> - Timber.e( - error, - """ - Unable to select user wallet - |- Wallet ID to select: $userWalletId - """.trimIndent(), - ) - } - .doOnSuccess { - val selectedUserWallet = userWalletsListManager.selectedUserWalletSync - if (selectedUserWallet != null) { - store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Wallet)) - store.onUserWalletSelected( - userWallet = selectedUserWallet, - sendAnalyticsEvent = true, - ) - } - } - } - } - - private suspend fun unlockUserWalletWithScannedCard(userWallet: UserWallet): CompletionResult { - Analytics.send(MyWallets.Button.WalletUnlockTapped()) - tangemSdkManager.changeDisplayedCardIdNumbersCount(userWallet.scanResponse) - return store.state.daggerGraphState.get(DaggerGraphState::scanCardProcessor).scan() - .map { scanResponse -> - val scannedUserWalletId = UserWalletIdBuilder.scanResponse(scanResponse).build() - if (scannedUserWalletId == userWallet.walletId) { - userWallet.updateCardWallets(scanResponse) - } else { - // TODO: Display error - Timber.e( - """ - Unable to unlock and select user wallet - |- Excepted ID: ${userWallet.walletId} - |- Received ID: $scannedUserWalletId - """.trimIndent(), - ) - error("Wrong card") - } - } - .flatMap { updatedUserWallet -> - userWalletsListManager.save(updatedUserWallet, canOverride = true) - } - .doOnFailure { - tangemSdkManager.changeDisplayedCardIdNumbersCount( - scanResponse = userWalletsListManager.selectedUserWalletSync?.scanResponse, - ) - } - } - - private fun UserWallet.updateCardWallets(scanResponse: ScanResponse): UserWallet { - return this.copy( - scanResponse = this.scanResponse.copy( - card = this.scanResponse.card.copy( - wallets = scanResponse.card.wallets, - ), - ), - ) - } - - private fun deleteWallets(userWalletsIds: List, state: WalletSelectorState) { - Analytics.send(MyWallets.Button.DeleteWalletTapped()) - - scope.launch { - when (userWalletsIds.size) { - state.wallets.size -> clearUserWalletsAndPopBackToHomeScreen() - else -> deleteUserWallets( - userWalletsIds = userWalletsIds, - currentSelectedWalletId = state.selectedWalletId, - ) - } - .doOnFailure { error -> - Timber.e( - error, - """ - Unable to delete user wallets - |- Wallets IDs to delete: $userWalletsIds - |- Current wallets IDs: ${state.wallets.map { it.id }} - """.trimIndent(), - ) - } - } - } - - private fun renameWallet(userWalletId: UserWalletId, newName: String) { - scope.launch { - userWalletsListManager.update(userWalletId) { it.copy(name = newName) } - .doOnFailure { error -> - Timber.e( - error, - """ - Unable to rename user wallet - |- Wallet ID: $userWalletId - |- New name: $newName - """.trimIndent(), - ) - } - } - } - - private fun refreshUserWalletsAmounts() { - val featureToggles = store.state.daggerGraphState.get(DaggerGraphState::walletFeatureToggles) - if (featureToggles.isRedesignedScreenEnabled) return - - scope.launch { - walletStoresManager.updateAmounts( - userWallets = userWalletsListManager.userWallets.firstOrNull().orEmpty(), - ) - .doOnFailure { error -> - Timber.e(error, "Unable to refresh user wallets amounts") - } - } - } - - private fun clearUserWalletsAndCloseError() = scope.launch { - clearUserWallets() - .doOnSuccess { - store.dispatchWithMain(WalletSelectorAction.CloseError) - popBackToHome() - } - .doOnFailure { e -> - Timber.e(e, "Unable to clear user wallets") - } - } - - private suspend fun clearUserWalletsAndPopBackToHomeScreen(): CompletionResult { - return clearUserWallets() - .doOnSuccess { popBackToHome() } - } - - private suspend fun clearUserWallets(): CompletionResult { - return userWalletsListManager.clear() - .flatMap { walletStoresManager.clear() } - .flatMap { tangemSdkManager.clearSavedUserCodes() } - } - - private suspend fun popBackToHome() { - // !!! Workaround !!! - store.dispatchWithMain(NavigationAction.PopBackTo(AppScreen.Wallet)) - delay(timeMillis = 280) - store.dispatchWithMain(NavigationAction.PopBackTo(AppScreen.Home)) - } - - private suspend fun deleteUserWallets( - userWalletsIds: List, - currentSelectedWalletId: UserWalletId?, - ): CompletionResult { - return userWalletsListManager.delete(userWalletsIds) - .flatMap { walletStoresManager.delete(userWalletsIds) } - .flatMap { deleteAccessCodes(userWalletsIds) } - .doOnSuccess { - val selectedUserWallet = userWalletsListManager.selectedUserWalletSync - - when { - selectedUserWallet == null -> { - store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Welcome)) - } - currentSelectedWalletId != selectedUserWallet.walletId -> { - store.onUserWalletSelected( - userWallet = selectedUserWallet, - sendAnalyticsEvent = true, - ) - } - } - } - } - - private suspend fun deleteAccessCodes(userWalletsIds: List): CompletionResult { - val cardsIds = userWalletsListManager.userWallets.firstOrNull() - ?.asSequence() - ?.filter { it.walletId in userWalletsIds } - ?.flatMap { it.cardsInWallet } - ?.toSet() - - return if (cardsIds.isNullOrEmpty()) { - CompletionResult.Success(Unit) - } else { - tangemSdkManager.deleteSavedUserCodes(cardsIds.toSet()) - } - } - - private suspend fun List.calculateBalanceAndUpdateWalletStores( - walletStores: Map>, - ): List { - return this - .associateWith { walletStores[it.id] } - .map { (wallet, walletStores) -> - wallet.calculateBalanceAndUpdateWalletStores(walletStores) - } - } - - private suspend fun UserWalletModel.calculateBalanceAndUpdateWalletStores( - walletStores: List?, - ): UserWalletModel { - return this.copy( - type = when (type) { - is UserWalletModel.Type.MultiCurrency -> type.copy( - tokensCount = walletStores?.flatMap { it.walletsData }?.size ?: 0, - ) - is UserWalletModel.Type.SingleCurrency -> type - }, - fiatBalance = totalFiatBalanceCalculator.calculate( - walletStores = walletStores.orEmpty(), - initial = TotalFiatBalance.Loading, - ), - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorReducer.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorReducer.kt deleted file mode 100644 index 13daba6567..0000000000 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorReducer.kt +++ /dev/null @@ -1,106 +0,0 @@ -package com.tangem.tap.features.walletSelector.redux - -import com.tangem.domain.common.util.cardTypesResolver -import com.tangem.domain.models.scan.CardDTO -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.tap.common.redux.AppState -import com.tangem.tap.domain.model.TotalFiatBalance -import org.rekotlin.Action - -internal object WalletSelectorReducer { - fun reduce(action: Action, state: AppState): WalletSelectorState { - return if (action is WalletSelectorAction) { - internalReduce(action, state.walletSelectorState) - } else { - state.walletSelectorState - } - } - - @Suppress("ComplexMethod") - private fun internalReduce(action: WalletSelectorAction, state: WalletSelectorState): WalletSelectorState { - return when (action) { - is WalletSelectorAction.UserWalletsLoaded -> state.copy( - wallets = action.userWallets.updateWalletsModels(state.wallets), - ) - is WalletSelectorAction.SelectedWalletChanged -> state.copy( - selectedWalletId = action.selectedWallet.walletId, - ) - is WalletSelectorAction.IsLockedChanged -> state.copy( - isLocked = action.isLocked, - ) - is WalletSelectorAction.BalancesLoaded -> state.copy( - wallets = action.userWalletModels, - ) - is WalletSelectorAction.CloseError -> state.copy(error = null) - is WalletSelectorAction.UnlockWithBiometry -> state.copy( - isUnlockInProgress = true, - ) - is WalletSelectorAction.UnlockWithBiometry.Error -> state.copy( - isUnlockInProgress = false, - error = action.error, - ) - is WalletSelectorAction.UnlockWithBiometry.Success -> state.copy( - isUnlockInProgress = false, - ) - is WalletSelectorAction.AddWallet -> state.copy( - isCardSavingInProgress = true, - ) - is WalletSelectorAction.AddWallet.Error -> state.copy( - isCardSavingInProgress = false, - error = action.error, - ) - is WalletSelectorAction.AddWallet.Success -> state.copy( - isCardSavingInProgress = false, - ) - is WalletSelectorAction.ChangeAppCurrency -> state.copy( - fiatCurrency = action.fiatCurrency, - ) - is WalletSelectorAction.WalletStoresChanged, - is WalletSelectorAction.SelectWallet, - is WalletSelectorAction.RemoveWallets, - is WalletSelectorAction.RenameWallet, - is WalletSelectorAction.ClearUserWallets, - -> state - } - } - - private fun List.updateWalletsModels(prevWallets: List): List { - return this.map { userWallet -> - prevWallets - .find { it.id == userWallet.walletId } - ?.let { - it.copy( - name = userWallet.name, - artworkUrl = userWallet.artworkUrl, - isLocked = userWallet.isLocked, - type = userWallet.getType(prevType = it.type), - ) - } - ?: with(userWallet) { - UserWalletModel( - id = walletId, - name = name, - artworkUrl = artworkUrl, - type = getType(), - fiatBalance = TotalFiatBalance.Loading, - isLocked = isLocked, - ) - } - } - } - - private fun UserWallet.getType(prevType: UserWalletModel.Type? = null): UserWalletModel.Type { - return if (isMultiCurrency) { - UserWalletModel.Type.MultiCurrency( - cardsInWallet = (scanResponse.card.backupStatus as? CardDTO.BackupStatus.Active) - ?.cardCount?.inc() - ?: 1, - tokensCount = (prevType as? UserWalletModel.Type.MultiCurrency)?.tokensCount ?: 0, - ) - } else { - UserWalletModel.Type.SingleCurrency( - blockchainName = scanResponse.cardTypesResolver.getBlockchain().fullName, - ) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorState.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorState.kt deleted file mode 100644 index 49e1f753a3..0000000000 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorState.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.tangem.tap.features.walletSelector.redux - -import com.tangem.common.core.TangemError -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.common.entities.FiatCurrency -import org.rekotlin.StateType - -data class WalletSelectorState( - val wallets: List = emptyList(), - val selectedWalletId: UserWalletId? = null, - val isLocked: Boolean = false, - val fiatCurrency: FiatCurrency = FiatCurrency.Default, - val isCardSavingInProgress: Boolean = false, - val isUnlockInProgress: Boolean = false, - val error: TangemError? = null, -) : StateType \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/Mapper.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/Mapper.kt deleted file mode 100644 index d01e48d352..0000000000 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/Mapper.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.tangem.tap.features.walletSelector.ui - -import com.tangem.tap.common.entities.FiatCurrency -import com.tangem.tap.common.extensions.toFormattedFiatValue -import com.tangem.tap.domain.model.TotalFiatBalance -import com.tangem.tap.features.walletSelector.redux.UserWalletModel -import com.tangem.tap.features.walletSelector.ui.model.MultiCurrencyUserWalletItem -import com.tangem.tap.features.walletSelector.ui.model.SingleCurrencyUserWalletItem -import com.tangem.tap.features.walletSelector.ui.model.UserWalletItem - -internal fun List.toUiModels(appCurrency: FiatCurrency): Sequence { - return this.asSequence().map { userWalletModel -> - with(userWalletModel) { - val balance = when (fiatBalance) { - is TotalFiatBalance.Failed -> UserWalletItem.Balance.Failed - is TotalFiatBalance.Loading -> UserWalletItem.Balance.Loading - is TotalFiatBalance.Loaded -> UserWalletItem.Balance.Loaded( - amount = fiatBalance.amount.toFormattedFiatValue(appCurrency.symbol, appCurrency.code), - showWarning = fiatBalance.isWarning, - ) - } - when (type) { - is UserWalletModel.Type.MultiCurrency -> MultiCurrencyUserWalletItem( - id = id, - name = name, - imageUrl = artworkUrl, - balance = balance, - isLocked = isLocked, - cardsInWallet = type.cardsInWallet, - tokensCount = type.tokensCount, - ) - is UserWalletModel.Type.SingleCurrency -> SingleCurrencyUserWalletItem( - id = id, - name = name, - imageUrl = artworkUrl, - balance = balance, - isLocked = isLocked, - tokenName = type.blockchainName, - ) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorBottomSheetFragment.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorBottomSheetFragment.kt deleted file mode 100644 index 2e80036cd4..0000000000 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorBottomSheetFragment.kt +++ /dev/null @@ -1,100 +0,0 @@ -package com.tangem.tap.features.walletSelector.ui - -import android.app.Dialog -import android.os.Bundle -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material.SnackbarHost -import androidx.compose.material.SnackbarHostState -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.platform.rememberNestedScrollInteropConnection -import androidx.fragment.app.viewModels -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.tangem.core.analytics.Analytics -import com.tangem.core.ui.components.wallets.RenameWalletDialogContent -import com.tangem.core.ui.res.TangemTheme -import com.tangem.core.ui.screen.ComposeBottomSheetFragment -import com.tangem.core.ui.theme.AppThemeModeHolder -import com.tangem.tap.common.analytics.events.MyWallets -import com.tangem.tap.features.details.ui.cardsettings.resolveReference -import com.tangem.tap.features.walletSelector.ui.components.* -import com.tangem.tap.features.walletSelector.ui.model.DialogModel -import com.tangem.tap.features.walletSelector.ui.model.WarningModel -import dagger.hilt.android.AndroidEntryPoint -import javax.inject.Inject - -@AndroidEntryPoint -internal class WalletSelectorBottomSheetFragment : ComposeBottomSheetFragment() { - - @Inject - override lateinit var appThemeModeHolder: AppThemeModeHolder - - private val viewModel by viewModels() - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - Analytics.send(MyWallets.MyWalletsScreenOpened()) - - return super.onCreateDialog(savedInstanceState) - } - - @Composable - override fun ScreenContent(modifier: Modifier) { - val state by viewModel.state.collectAsStateWithLifecycle() - val snackbarHostState = remember { SnackbarHostState() } - val errorMessage by rememberUpdatedState(newValue = state.error?.resolveReference()) - val dialog by rememberUpdatedState(newValue = state.dialog) - - Box(modifier = modifier.nestedScroll(rememberNestedScrollInteropConnection())) { - WalletSelectorScreenContent( - state = state, - onWalletClick = viewModel::walletClicked, - onWalletLongClick = viewModel::walletLongClicked, - onUnlockClick = viewModel::unlock, - onAddCardClick = viewModel::addWallet, - onClearSelectedClick = viewModel::cancelWalletsEditing, - onEditSelectedWalletClick = viewModel::renameWallet, - onDeleteSelectedWalletsClick = viewModel::deleteWallets, - ) - - SnackbarHost( - modifier = Modifier - .align(Alignment.BottomCenter) - .padding(vertical = TangemTheme.dimens.spacing16) - .fillMaxWidth(), - hostState = snackbarHostState, - ) - } - - Dialog(dialog = dialog) - - LaunchedEffect(key1 = errorMessage) { - errorMessage?.let { - snackbarHostState.showSnackbar(it) - viewModel.closeError() - } - } - } - - @Suppress("TopLevelComposableFunctions") - @Composable - private fun Dialog(dialog: DialogModel?) { - if (dialog == null) return - when (dialog) { - is DialogModel.RemoveWalletDialog -> RemoveWalletDialogContent(dialog) - is DialogModel.RenameWalletDialog -> { - RenameWalletDialogContent( - name = dialog.currentName, - onConfirm = dialog.onConfirm, - onDismiss = dialog.onDismiss, - ) - } - is WarningModel.BiometricsLockoutWarning -> BiometricsLockoutWarningContent(dialog) - is WarningModel.KeyInvalidatedWarning -> KeyInvalidatedWarningContent(dialog) - is WarningModel.BiometricsDisabledWarning -> BiometricsDisabledWarningContent(dialog) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorScreenState.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorScreenState.kt deleted file mode 100644 index 796c2eac3f..0000000000 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorScreenState.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.tangem.tap.features.walletSelector.ui - -import androidx.compose.runtime.Immutable -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.features.details.ui.cardsettings.TextReference -import com.tangem.tap.features.walletSelector.ui.model.DialogModel -import com.tangem.tap.features.walletSelector.ui.model.MultiCurrencyUserWalletItem -import com.tangem.tap.features.walletSelector.ui.model.SingleCurrencyUserWalletItem - -@Immutable -internal data class WalletSelectorScreenState( - val multiCurrencyWallets: List = emptyList(), - val singleCurrencyWallets: List = emptyList(), - val selectedUserWalletId: UserWalletId? = null, - val isLocked: Boolean = false, - val editingUserWalletsIds: List = listOf(), - val dialog: DialogModel? = null, - val showAddCardProgress: Boolean = false, - val showUnlockProgress: Boolean = false, - val error: TextReference? = null, -) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorViewModel.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorViewModel.kt deleted file mode 100644 index 7a83d7bbd4..0000000000 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorViewModel.kt +++ /dev/null @@ -1,278 +0,0 @@ -package com.tangem.tap.features.walletSelector.ui - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.tangem.common.core.TangemError -import com.tangem.core.analytics.Analytics -import com.tangem.domain.wallets.legacy.UserWalletsListError -import com.tangem.domain.wallets.legacy.isLocked -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.common.analytics.events.MyWallets -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.features.details.ui.cardsettings.TextReference -import com.tangem.tap.features.walletSelector.redux.WalletSelectorAction -import com.tangem.tap.features.walletSelector.redux.WalletSelectorState -import com.tangem.tap.features.walletSelector.ui.model.DialogModel -import com.tangem.tap.features.walletSelector.ui.model.MultiCurrencyUserWalletItem -import com.tangem.tap.features.walletSelector.ui.model.SingleCurrencyUserWalletItem -import com.tangem.tap.features.walletSelector.ui.model.WarningModel -import com.tangem.tap.store -import com.tangem.tap.userWalletsListManager -import com.tangem.tap.walletStoresManager -import kotlinx.coroutines.flow.* -import org.rekotlin.StoreSubscriber - -internal class WalletSelectorViewModel : ViewModel(), StoreSubscriber { - private val stateInternal = MutableStateFlow(WalletSelectorScreenState()) - val state: StateFlow = stateInternal - - init { - subscribeToStoreChanges() - bootstrapWalletListChanges() - bootstrapWalletsStoresChanges() - bootstrapAppFiatCurrency() - } - - fun unlock() { - store.dispatch(WalletSelectorAction.UnlockWithBiometry) - } - - fun addWallet() { - store.dispatch(WalletSelectorAction.AddWallet) - } - - fun walletClicked(userWalletId: UserWalletId) = with(state.value) { - when { - editingUserWalletsIds.isNotEmpty() && !editingUserWalletsIds.contains(userWalletId) -> { - editWallet(userWalletId) - } - editingUserWalletsIds.isNotEmpty() && editingUserWalletsIds.contains(userWalletId) -> { - cancelWalletEditing(userWalletId) - } - selectedUserWalletId != userWalletId -> { - store.dispatch( - WalletSelectorAction.SelectWallet( - userWalletId = userWalletId, - sendAnalyticsEvent = true, - ), - ) - } - } - } - - fun walletLongClicked(userWalletId: UserWalletId) = with(state.value) { - if (editingUserWalletsIds.isEmpty()) { - editWallet(userWalletId) - } - } - - fun cancelWalletsEditing() { - stateInternal.update { prevState -> - prevState.copy( - editingUserWalletsIds = emptyList(), - ) - } - } - - fun renameWallet() = with(state.value) { - if (editingUserWalletsIds.isNotEmpty() && dialog == null) { - val editedUserWalletId = editingUserWalletsIds.first() - val editedUserWallet = (multiCurrencyWallets + singleCurrencyWallets) - .find { it.id == editedUserWalletId } - - if (editedUserWallet != null) { - Analytics.send(MyWallets.Button.EditWalletTapped()) - val dialog = DialogModel.RenameWalletDialog( - currentName = editedUserWallet.name, - onConfirm = { newName -> - store.dispatch(WalletSelectorAction.RenameWallet(editedUserWalletId, newName)) - stateInternal.update { prevState -> - prevState.copy( - dialog = null, - editingUserWalletsIds = emptyList(), - ) - } - }, - onDismiss = { - stateInternal.update { prevState -> - prevState.copy( - dialog = null, - ) - } - }, - ) - - stateInternal.update { prevState -> - prevState.copy( - dialog = dialog, - ) - } - } - } - } - - fun deleteWallets() = with(state.value) { - if (editingUserWalletsIds.isNotEmpty()) { - val dialog = DialogModel.RemoveWalletDialog( - onConfirm = { - store.dispatch(WalletSelectorAction.RemoveWallets(editingUserWalletsIds)) - stateInternal.update { prevState -> - prevState.copy( - dialog = null, - editingUserWalletsIds = emptyList(), - ) - } - }, - onDismiss = { - stateInternal.update { prevState -> - prevState.copy( - dialog = null, - ) - } - }, - ) - - stateInternal.update { prevState -> - prevState.copy( - dialog = dialog, - ) - } - } - } - - fun closeError() { - store.dispatch(WalletSelectorAction.CloseError) - } - - // TODO: Refactor errors handling - override fun newState(state: WalletSelectorState) { - stateInternal.update { prevState -> - val walletsUi = state.wallets.toUiModels(state.fiatCurrency) - val walletsIds = walletsUi.map { it.id } - val multiCurrencyWallets = arrayListOf() - val singleCurrencyWallets = arrayListOf() - walletsUi.forEach { wallet -> - when (wallet) { - is MultiCurrencyUserWalletItem -> { - multiCurrencyWallets.add(wallet) - } - is SingleCurrencyUserWalletItem -> { - singleCurrencyWallets.add(wallet) - } - } - } - val warningDialog = createWarningDialogIfNeeded(state.error, prevState.dialog) - - prevState.copy( - multiCurrencyWallets = multiCurrencyWallets, - singleCurrencyWallets = singleCurrencyWallets, - selectedUserWalletId = state.selectedWalletId, - editingUserWalletsIds = prevState.editingUserWalletsIds.filter { it in walletsIds }, - isLocked = state.isLocked, - showUnlockProgress = state.isUnlockInProgress, - showAddCardProgress = state.isCardSavingInProgress, - dialog = warningDialog, - error = state.error - ?.takeIf { !it.silent && warningDialog == null } - ?.let { error -> - error.messageResId?.let { TextReference.Res(it) } - ?: TextReference.Str(error.customMessage) - }, - ) - } - } - - override fun onCleared() { - store.unsubscribe(this) - } - - private fun createWarningDialogIfNeeded(error: TangemError?, currentDialog: DialogModel?): DialogModel? { - return when (error) { - is UserWalletsListError.BiometricsAuthenticationLockout -> WarningModel.BiometricsLockoutWarning( - isPermanent = error.isPermanent, - onDismiss = this::dismissWarningDialog, - ) - is UserWalletsListError.AllKeysInvalidated, - is UserWalletsListError.NoUserWalletSelected, - -> WarningModel.KeyInvalidatedWarning( - onDismiss = this::dismissWarningDialog, - ) - is UserWalletsListError.BiometricsAuthenticationDisabled -> WarningModel.BiometricsDisabledWarning( - onDismiss = this::clearUserWallets, - ) - else -> currentDialog - } - } - - private fun clearUserWallets() { - store.dispatch(WalletSelectorAction.ClearUserWallets) - } - - private fun dismissWarningDialog() { - stateInternal.update { prevState -> - prevState.copy( - dialog = null, - ) - } - closeError() - } - - private fun editWallet(userWalletId: UserWalletId) { - stateInternal.update { prevState -> - prevState.copy( - editingUserWalletsIds = prevState.editingUserWalletsIds + userWalletId, - ) - } - } - - private fun cancelWalletEditing(userWalletId: UserWalletId) { - stateInternal.update { prevState -> - prevState.copy( - editingUserWalletsIds = prevState.editingUserWalletsIds - userWalletId, - ) - } - } - - private fun subscribeToStoreChanges() { - store.subscribe(this) { appState -> - appState.skip { old, new -> old.walletSelectorState == new.walletSelectorState } - .select { it.walletSelectorState } - } - } - - private fun bootstrapWalletListChanges() { - userWalletsListManager.userWallets - .onEach { - store.dispatchOnMain(WalletSelectorAction.UserWalletsLoaded(userWallets = it)) - } - .launchIn(viewModelScope) - - userWalletsListManager.selectedUserWallet - .onEach { - store.dispatchOnMain(WalletSelectorAction.SelectedWalletChanged(selectedWallet = it)) - } - .launchIn(viewModelScope) - - userWalletsListManager.isLocked - .onEach { - store.dispatchOnMain(WalletSelectorAction.IsLockedChanged(isLocked = it)) - } - .launchIn(viewModelScope) - } - - private fun bootstrapWalletsStoresChanges() { - walletStoresManager.getAll() - .onEach { walletStores -> - store.dispatchOnMain(WalletSelectorAction.WalletStoresChanged(walletStores)) - } - .launchIn(viewModelScope) - } - - private fun bootstrapAppFiatCurrency() { - store.dispatch( - WalletSelectorAction.ChangeAppCurrency( - fiatCurrency = store.state.globalState.appCurrency, - ), - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/BiometricsDisabledWarningContent.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/BiometricsDisabledWarningContent.kt deleted file mode 100644 index 61e8bd9000..0000000000 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/BiometricsDisabledWarningContent.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.tangem.tap.features.walletSelector.ui.components - -import androidx.compose.foundation.layout.Column -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import com.tangem.core.ui.components.BasicDialog -import com.tangem.core.ui.components.DialogButton -import com.tangem.core.ui.res.TangemTheme -import com.tangem.tap.features.walletSelector.ui.model.WarningModel -import com.tangem.wallet.R - -@Composable -internal fun BiometricsDisabledWarningContent(warning: WarningModel.BiometricsDisabledWarning) { - BasicDialog( - title = stringResource(id = R.string.common_warning), - message = stringResource(id = R.string.biometric_unavailable_warning), - onDismissDialog = warning.onDismiss, - isDismissable = false, - confirmButton = DialogButton( - title = stringResource(id = R.string.common_ok), - onClick = warning.onDismiss, - ), - ) -} - -// region Preview -@Composable -private fun BiometricsDisabledWarningContentSample(modifier: Modifier = Modifier) { - Column(modifier = modifier) { - BiometricsDisabledWarningContent( - warning = WarningModel.BiometricsDisabledWarning( - onDismiss = {}, - ), - ) - } -} - -@Preview(showBackground = true, widthDp = 360) -@Composable -private fun BiometricsDisabledWarningContentPreview_Light() { - TangemTheme { - BiometricsDisabledWarningContentSample() - } -} - -@Preview(showBackground = true, widthDp = 360) -@Composable -private fun BiometricsDisabledWarningContentPreview_Dark() { - TangemTheme(isDark = true) { - BiometricsDisabledWarningContentSample() - } -} -// endregion Preview \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/BiometricsLockoutWarningContent.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/BiometricsLockoutWarningContent.kt deleted file mode 100644 index 3abfb1ade2..0000000000 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/BiometricsLockoutWarningContent.kt +++ /dev/null @@ -1,89 +0,0 @@ -package com.tangem.tap.features.walletSelector.ui.components - -import androidx.compose.foundation.layout.Column -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import com.tangem.core.ui.components.BasicDialog -import com.tangem.core.ui.components.DialogButton -import com.tangem.core.ui.res.TangemTheme -import com.tangem.tap.features.walletSelector.ui.model.WarningModel -import com.tangem.wallet.R - -@Composable -internal fun BiometricsLockoutWarningContent(warning: WarningModel.BiometricsLockoutWarning) { - BasicDialog( - title = stringResource(id = R.string.biometric_lockout_warning_title), - message = stringResource( - id = if (warning.isPermanent) { - R.string.biometric_lockout_permanent_warning_description - } else { - R.string.biometric_lockout_warning_description - }, - ), - onDismissDialog = warning.onDismiss, - confirmButton = DialogButton( - title = stringResource(id = R.string.common_ok), - onClick = warning.onDismiss, - ), - ) -} - -// region Preview -@Composable -private fun BiometricsLockoutDialogSample(modifier: Modifier = Modifier) { - Column(modifier = modifier) { - BiometricsLockoutWarningContent( - warning = WarningModel.BiometricsLockoutWarning( - isPermanent = false, - onDismiss = {}, - ), - ) - } -} - -@Preview(showBackground = true, widthDp = 360) -@Composable -private fun BiometricsLockoutDialogPreview_Light() { - TangemTheme { - BiometricsLockoutDialogSample() - } -} - -@Preview(showBackground = true, widthDp = 360) -@Composable -private fun BiometricsLockoutDialogPreview_Dark() { - TangemTheme(isDark = true) { - BiometricsLockoutDialogSample() - } -} - -@Composable -private fun BiometricsLockoutDialog_Permanent_Sample(modifier: Modifier = Modifier) { - Column(modifier = modifier) { - BiometricsLockoutWarningContent( - warning = WarningModel.BiometricsLockoutWarning( - isPermanent = true, - onDismiss = {}, - ), - ) - } -} - -@Preview(showBackground = true, widthDp = 360) -@Composable -private fun BiometricsLockoutDialog_Permanent_Preview_Light() { - TangemTheme { - BiometricsLockoutDialog_Permanent_Sample() - } -} - -@Preview(showBackground = true, widthDp = 360) -@Composable -private fun BiometricsLockoutDialog_Permanent_Preview_Dark() { - TangemTheme(isDark = true) { - BiometricsLockoutDialog_Permanent_Sample() - } -} -// endregion Preview \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/KeyInvalidatedWarningContent.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/KeyInvalidatedWarningContent.kt deleted file mode 100644 index 4459f55a1d..0000000000 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/KeyInvalidatedWarningContent.kt +++ /dev/null @@ -1,52 +0,0 @@ -package com.tangem.tap.features.walletSelector.ui.components - -import androidx.compose.foundation.layout.Column -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview -import com.tangem.core.ui.components.BasicDialog -import com.tangem.core.ui.components.DialogButton -import com.tangem.core.ui.res.TangemTheme -import com.tangem.tap.features.walletSelector.ui.model.WarningModel -import com.tangem.wallet.R - -@Composable -internal fun KeyInvalidatedWarningContent(warning: WarningModel.KeyInvalidatedWarning) { - BasicDialog( - title = stringResource(id = R.string.common_attention), - message = stringResource(id = R.string.key_invalidated_warning_description), - onDismissDialog = warning.onDismiss, - confirmButton = DialogButton( - title = stringResource(id = R.string.common_ok), - onClick = warning.onDismiss, - ), - ) -} - -// region Preview -@Composable -private fun KeyInvalidatedWarningSample(modifier: Modifier = Modifier) { - Column(modifier = modifier) { - KeyInvalidatedWarningContent( - warning = WarningModel.KeyInvalidatedWarning(onDismiss = {}), - ) - } -} - -@Preview(showBackground = true, widthDp = 360) -@Composable -private fun KeyInvalidatedWarningPreview_Light() { - TangemTheme { - KeyInvalidatedWarningSample() - } -} - -@Preview(showBackground = true, widthDp = 360) -@Composable -private fun KeyInvalidatedWarningPreview_Dark() { - TangemTheme(isDark = true) { - KeyInvalidatedWarningSample() - } -} -// endregion Preview \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/MockData.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/MockData.kt deleted file mode 100644 index 0099f47c84..0000000000 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/MockData.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.tangem.tap.features.walletSelector.ui.components - -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.features.walletSelector.ui.WalletSelectorScreenState -import com.tangem.tap.features.walletSelector.ui.model.MultiCurrencyUserWalletItem -import com.tangem.tap.features.walletSelector.ui.model.SingleCurrencyUserWalletItem -import com.tangem.tap.features.walletSelector.ui.model.UserWalletItem - -internal object MockData { - private val multiCurrencyUserWallet = MultiCurrencyUserWalletItem( - id = UserWalletId("wallet_1"), - balance = UserWalletItem.Balance.Loaded( - amount = "6781.05 $", - showWarning = true, - ), - name = "Wallet", - imageUrl = "https://app.tangem.com/cards/card_default.png", - isLocked = false, - tokensCount = 12, - cardsInWallet = 3, - ) - - private val singleCurrencyUserWallet = SingleCurrencyUserWalletItem( - id = UserWalletId("wallet_4"), - balance = UserWalletItem.Balance.Loaded( - amount = "6781.05 $", - showWarning = false, - ), - name = "Wallet", - imageUrl = "https://app.tangem.com/cards/card_default.png", - isLocked = false, - tokenName = "Ethereum", - ) - - val state = WalletSelectorScreenState( - multiCurrencyWallets = listOf( - multiCurrencyUserWallet, - multiCurrencyUserWallet.copy(id = UserWalletId("wallet_2")), - multiCurrencyUserWallet.copy(id = UserWalletId("wallet_3"), tokensCount = 2, cardsInWallet = 1), - ), - singleCurrencyWallets = listOf(singleCurrencyUserWallet), - selectedUserWalletId = multiCurrencyUserWallet.id, - isLocked = false, - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/RemoveWalletDialogContent.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/RemoveWalletDialogContent.kt deleted file mode 100644 index cedbb48278..0000000000 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/RemoveWalletDialogContent.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.tangem.tap.features.walletSelector.ui.components - -import androidx.compose.runtime.Composable -import androidx.compose.ui.res.stringResource -import com.tangem.core.ui.components.BasicDialog -import com.tangem.core.ui.components.DialogButton -import com.tangem.tap.features.walletSelector.ui.model.DialogModel -import com.tangem.wallet.R - -@Composable -internal fun RemoveWalletDialogContent(dialog: DialogModel.RemoveWalletDialog) { - BasicDialog( - message = stringResource(id = R.string.user_wallet_list_delete_prompt), - confirmButton = DialogButton( - title = stringResource(id = R.string.common_delete), - warning = true, - onClick = dialog.onConfirm, - ), - dismissButton = DialogButton( - onClick = dialog.onDismiss, - ), - onDismissDialog = dialog.onDismiss, - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WallectSelectorScreenContent.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WallectSelectorScreenContent.kt deleted file mode 100644 index 0993a1c9ea..0000000000 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WallectSelectorScreenContent.kt +++ /dev/null @@ -1,330 +0,0 @@ -package com.tangem.tap.features.walletSelector.ui.components - -import androidx.annotation.StringRes -import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.itemsIndexed -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberUpdatedState -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import com.tangem.core.ui.components.PrimaryButton -import com.tangem.core.ui.components.SecondaryButtonIconEnd -import com.tangem.core.ui.components.SpacerWMax -import com.tangem.core.ui.components.atoms.Hand -import com.tangem.core.ui.res.TangemTheme -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.features.walletSelector.ui.WalletSelectorScreenState -import com.tangem.wallet.R - -@Suppress("LongParameterList") -@OptIn(ExperimentalFoundationApi::class) -@Composable -internal fun WalletSelectorScreenContent( - state: WalletSelectorScreenState, - onWalletClick: (UserWalletId) -> Unit, - onWalletLongClick: (UserWalletId) -> Unit, - onUnlockClick: () -> Unit, - onAddCardClick: () -> Unit, - onClearSelectedClick: () -> Unit, - onEditSelectedWalletClick: () -> Unit, - onDeleteSelectedWalletsClick: () -> Unit, -) { - LazyColumn { - stickyHeader { - Header( - editingWalletsIds = state.editingUserWalletsIds, - onClearSelectedClick = onClearSelectedClick, - onEditSelectedWalletClick = onEditSelectedWalletClick, - onDeleteSelectedWalletsClick = onDeleteSelectedWalletsClick, - ) - } - - item { - WalletsTitle(textResId = R.string.user_wallet_list_multi_header, wallets = state.multiCurrencyWallets) - } - - itemsIndexed( - items = state.multiCurrencyWallets, - key = { _, wallet -> wallet.id.stringValue }, - ) { _, wallet -> - - WalletItem( - wallet = wallet, - isSelected = remember(state.selectedUserWalletId) { wallet.id == state.selectedUserWalletId }, - isChecked = remember(state.editingUserWalletsIds) { wallet.id in state.editingUserWalletsIds }, - onWalletClick = { onWalletClick(wallet.id) }, - onWalletLongClick = { onWalletLongClick(wallet.id) }, - ) - } - - item { - WalletsTitle(textResId = R.string.user_wallet_list_single_header, wallets = state.singleCurrencyWallets) - } - - itemsIndexed( - items = state.singleCurrencyWallets, - key = { _, wallet -> wallet.id.stringValue }, - ) { _, wallet -> - WalletItem( - wallet = wallet, - isSelected = remember(state.selectedUserWalletId) { wallet.id == state.selectedUserWalletId }, - isChecked = remember(state.editingUserWalletsIds) { wallet.id in state.editingUserWalletsIds }, - onWalletClick = { onWalletClick(wallet.id) }, - onWalletLongClick = { onWalletLongClick(wallet.id) }, - ) - } - - item { - Footer( - isLocked = state.isLocked, - showUnlockProgress = state.showUnlockProgress, - showAddCardProgress = state.showAddCardProgress, - onUnlockClick = onUnlockClick, - onAddCardClick = onAddCardClick, - ) - } - } -} - -@Composable -private fun Header( - editingWalletsIds: List, - onClearSelectedClick: () -> Unit, - onEditSelectedWalletClick: () -> Unit, - onDeleteSelectedWalletsClick: () -> Unit, -) { - val editingWalletsSize by rememberUpdatedState(newValue = editingWalletsIds.size) - val hasEditingWallets by remember { derivedStateOf { editingWalletsSize > 0 } } - - Column( - modifier = Modifier - .fillMaxWidth() - .background( - color = TangemTheme.colors.background.plain, - shape = TangemTheme.shapes.bottomSheet, - ), - ) { - Hand() - Box( - modifier = Modifier - .fillMaxWidth() - .height(TangemTheme.dimens.size44), - contentAlignment = Alignment.Center, - ) { - if (hasEditingWallets) { - EditWalletsBar( - editingWalletsSize = editingWalletsSize, - onClearSelectedClick = onClearSelectedClick, - onEditSelectedWalletClick = onEditSelectedWalletClick, - onDeleteSelectedWalletsClick = onDeleteSelectedWalletsClick, - ) - } else { - Text( - text = stringResource(R.string.user_wallet_list_title), - style = TangemTheme.typography.subtitle1, - color = TangemTheme.colors.text.primary1, - textAlign = TextAlign.Center, - ) - } - } - } -} - -@Composable -private fun WalletsTitle(@StringRes textResId: Int, wallets: List<*>) { - if (wallets.isNotEmpty()) { - Text( - modifier = Modifier.padding(start = TangemTheme.dimens.spacing16), - text = stringResource(id = textResId), - style = TangemTheme.typography.subtitle2, - color = TangemTheme.colors.text.tertiary, - ) - } -} - -@Composable -private fun Footer( - isLocked: Boolean, - showUnlockProgress: Boolean, - showAddCardProgress: Boolean, - onUnlockClick: () -> Unit, - onAddCardClick: () -> Unit, -) { - Column( - modifier = Modifier - .padding( - top = TangemTheme.dimens.spacing24, - bottom = TangemTheme.dimens.spacing16, - ) - .padding(horizontal = TangemTheme.dimens.spacing16) - .fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12), - ) { - if (isLocked) { - PrimaryButton( - modifier = Modifier.fillMaxWidth(), - text = stringResource( - id = R.string.user_wallet_list_unlock_all_with, - stringResource(id = R.string.common_biometrics), - ), - showProgress = showUnlockProgress, - onClick = onUnlockClick, - ) - } - SecondaryButtonIconEnd( - modifier = Modifier.fillMaxWidth(), - text = stringResource(R.string.user_wallet_list_add_button), - showProgress = showAddCardProgress, - iconResId = R.drawable.ic_tangem_24, - onClick = onAddCardClick, - ) - } -} - -@Composable -private fun EditWalletsBar( - editingWalletsSize: Int, - onClearSelectedClick: () -> Unit, - onEditSelectedWalletClick: () -> Unit, - onDeleteSelectedWalletsClick: () -> Unit, -) { - val showEditAction by remember(editingWalletsSize) { - derivedStateOf { editingWalletsSize in 1 until 2 } - } - - Row( - modifier = Modifier - .padding(horizontal = TangemTheme.dimens.spacing16) - .fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12), - ) { - IconButton( - modifier = Modifier.size(TangemTheme.dimens.size32), - onClick = onClearSelectedClick, - ) { - Icon( - modifier = Modifier.size(TangemTheme.dimens.size24), - painter = painterResource(id = R.drawable.ic_close_24), - tint = TangemTheme.colors.icon.primary1, - contentDescription = "Unselect wallets", - ) - } - Text( - text = stringResource(id = R.string.user_wallet_list_editing_count, editingWalletsSize), - style = TangemTheme.typography.subtitle1, - color = TangemTheme.colors.text.primary1, - ) - SpacerWMax() - if (showEditAction) { - IconButton( - modifier = Modifier.size(TangemTheme.dimens.size32), - onClick = onEditSelectedWalletClick, - ) { - Icon( - modifier = Modifier.size(TangemTheme.dimens.size24), - painter = painterResource(id = R.drawable.ic_pencil_outline_24), - tint = TangemTheme.colors.icon.primary1, - contentDescription = "Change wallet name", - ) - } - } - IconButton( - modifier = Modifier.size(TangemTheme.dimens.size32), - onClick = onDeleteSelectedWalletsClick, - ) { - Icon( - modifier = Modifier.size(TangemTheme.dimens.size24), - painter = painterResource(id = R.drawable.ic_trash), - tint = TangemTheme.colors.icon.warning, - contentDescription = "Delete selected wallets", - ) - } - } -} - -// region Preview -@Composable -private fun WalletSelectorScreenContentSample(modifier: Modifier = Modifier) { - Column( - modifier = modifier - .background(color = TangemTheme.colors.background.primary), - ) { - WalletSelectorScreenContent( - state = MockData.state.copy(isLocked = true), - onWalletClick = { /* no-op */ }, - onWalletLongClick = { /* no-op */ }, - onUnlockClick = { /* no-op */ }, - onAddCardClick = { /* no-op */ }, - onEditSelectedWalletClick = { /* no-op */ }, - onClearSelectedClick = { /* no-op */ }, - onDeleteSelectedWalletsClick = { /* no-op */ }, - ) - } -} - -@Preview(showBackground = true, widthDp = 360) -@Composable -private fun WalletSelectorScreenContentPreview_Light() { - TangemTheme { - WalletSelectorScreenContentSample() - } -} - -@Preview(showBackground = true, widthDp = 360) -@Composable -private fun WalletSelectorScreenContentPreview_Dark() { - TangemTheme(isDark = true) { - WalletSelectorScreenContentSample() - } -} - -@Composable -private fun WalletSelectorScreenContent_EditWallets_Sample(modifier: Modifier = Modifier) { - Column( - modifier = modifier - .background(TangemTheme.colors.background.primary), - ) { - WalletSelectorScreenContent( - state = MockData.state - .copy(editingUserWalletsIds = listOf(MockData.state.multiCurrencyWallets[2].id)), - onWalletClick = { /* no-op */ }, - onWalletLongClick = { /* no-op */ }, - onUnlockClick = { /* no-op */ }, - onAddCardClick = { /* no-op */ }, - onEditSelectedWalletClick = { /* no-op */ }, - onClearSelectedClick = { /* no-op */ }, - onDeleteSelectedWalletsClick = { /* no-op */ }, - ) - } -} - -@Preview(showBackground = true, widthDp = 360) -@Composable -private fun WalletSelectorScreenContent_EditWallets_Preview_Light() { - TangemTheme { - WalletSelectorScreenContent_EditWallets_Sample() - } -} - -@Preview(showBackground = true, widthDp = 360) -@Composable -private fun WalletSelectorScreenContent_EditWallets_Preview_Dark() { - TangemTheme(isDark = true) { - WalletSelectorScreenContent_EditWallets_Sample() - } -} -// endregion Preview \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletItem.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletItem.kt deleted file mode 100644 index 0224ee04fc..0000000000 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletItem.kt +++ /dev/null @@ -1,459 +0,0 @@ -package com.tangem.tap.features.walletSelector.ui.components - -import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.combinedClickable -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.Divider -import androidx.compose.material.Icon -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.BlendMode -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.pluralStringResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import coil.compose.SubcomposeAsyncImage -import coil.request.ImageRequest -import com.tangem.core.ui.components.SpacerH2 -import com.tangem.core.ui.components.SpacerW6 -import com.tangem.core.ui.components.SpacerW8 -import com.tangem.core.ui.res.TangemTheme -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.common.extensions.cardImageData -import com.tangem.tap.features.walletSelector.ui.model.MultiCurrencyUserWalletItem -import com.tangem.tap.features.walletSelector.ui.model.SingleCurrencyUserWalletItem -import com.tangem.tap.features.walletSelector.ui.model.UserWalletItem -import com.tangem.wallet.R -import com.valentinilk.shimmer.shimmer - -@OptIn(ExperimentalFoundationApi::class) -@Composable -internal fun WalletItem( - wallet: UserWalletItem, - isSelected: Boolean, - isChecked: Boolean, - onWalletClick: () -> Unit, - onWalletLongClick: () -> Unit, -) { - Row( - modifier = Modifier - .combinedClickable( - onClick = onWalletClick, - onLongClick = onWalletLongClick, - ) - .heightIn(min = TangemTheme.dimens.size72) - .padding(all = TangemTheme.dimens.spacing16), - verticalAlignment = Alignment.CenterVertically, - ) { - WalletCardImage( - cardImageUrl = wallet.imageUrl, - isChecked = isChecked, - isSelected = isSelected, - ) - SpacerW8() - WalletInfo(wallet = wallet, isSelected = isSelected) - SpacerW6() - TokensInfo( - isLocked = wallet.isLocked, - balance = wallet.balance, - tokensCount = (wallet as? MultiCurrencyUserWalletItem)?.tokensCount, - ) - } -} - -@Composable -private fun WalletCardImage(cardImageUrl: String, isChecked: Boolean, isSelected: Boolean) { - Box( - modifier = Modifier - .width(TangemTheme.dimens.size62) - .height(TangemTheme.dimens.size42), - ) { - val cardImageModifier = Modifier - .align(Alignment.Center) - .width(TangemTheme.dimens.size56) - .height(TangemTheme.dimens.size32) - .clip(TangemTheme.shapes.roundedCornersSmall2) - - val tintColor = TangemTheme.colors.icon.accent.copy(alpha = .6f) - val checkedColorFilter = remember(isChecked) { - if (isChecked) { - ColorFilter.tint( - color = tintColor, - blendMode = BlendMode.SrcOver, - ) - } else { - null - } - } - - SubcomposeAsyncImage( - modifier = cardImageModifier, - model = ImageRequest.Builder(LocalContext.current) - .cardImageData(cardImageUrl) - .crossfade(true) - .build(), - loading = { CardImageShimmer(modifier = cardImageModifier) }, - error = { CardImagePlaceholder(modifier = cardImageModifier) }, - colorFilter = checkedColorFilter, - contentDescription = null, - ) - - when { - isChecked -> { - CheckedWalletMark( - modifier = Modifier.matchParentSize(), - ) - } - isSelected -> { - SelectedWalletBadge( - modifier = Modifier.align(Alignment.TopEnd), - ) - } - } - } -} - -@Composable -private fun RowScope.WalletInfo(wallet: UserWalletItem, isSelected: Boolean) { - Column( - modifier = Modifier.weight(weight = 2f), - verticalArrangement = Arrangement.SpaceAround, - horizontalAlignment = Alignment.Start, - ) { - Text( - text = wallet.name, - color = if (isSelected) TangemTheme.colors.text.accent else TangemTheme.colors.text.primary1, - overflow = TextOverflow.Ellipsis, - maxLines = 1, - style = TangemTheme.typography.subtitle1, - ) - SpacerH2() - Text( - text = when (wallet) { - is MultiCurrencyUserWalletItem -> pluralStringResource( - id = R.plurals.card_label_card_count, - count = wallet.cardsInWallet, - wallet.cardsInWallet, - ) - is SingleCurrencyUserWalletItem -> wallet.tokenName - }, - color = TangemTheme.colors.text.tertiary, - maxLines = 1, - style = TangemTheme.typography.caption2, - ) - } -} - -@Composable -private fun RowScope.TokensInfo(isLocked: Boolean, balance: UserWalletItem.Balance, tokensCount: Int?) { - Column( - modifier = Modifier.weight(weight = 3f), - verticalArrangement = Arrangement.SpaceAround, - horizontalAlignment = Alignment.End, - ) { - if (isLocked) { - LockedPlaceholder() - } else { - when (balance) { - is UserWalletItem.Balance.Failed, - is UserWalletItem.Balance.Loaded, - -> { - LoadedTokensInfo( - balanceAmount = balance.amount, - tokensCount = tokensCount, - showWarning = balance.showWarning, - ) - } - is UserWalletItem.Balance.Loading -> { - LoadingTokensInfo(isMultiCurrencyWallet = tokensCount != null) - } - } - } - } -} - -@Composable -private fun LoadingTokensInfo(isMultiCurrencyWallet: Boolean) { - Column( - modifier = Modifier.shimmer(), - horizontalAlignment = Alignment.End, - verticalArrangement = Arrangement.SpaceAround, - ) { - Box( - modifier = Modifier - .width(TangemTheme.dimens.size62) - .height(TangemTheme.dimens.size16) - .background( - color = TangemTheme.colors.button.secondary, - shape = TangemTheme.shapes.roundedCornersSmall2, - ), - ) - if (isMultiCurrencyWallet) { - SpacerH2() - Box( - modifier = Modifier - .width(TangemTheme.dimens.size48) - .height(TangemTheme.dimens.size12) - .background( - color = TangemTheme.colors.button.secondary, - shape = TangemTheme.shapes.roundedCornersSmall2, - ), - ) - } - } -} - -@Composable -private fun LoadedTokensInfo( - balanceAmount: String, - tokensCount: Int?, - showWarning: Boolean, - modifier: Modifier = Modifier, -) { - Column( - modifier = modifier, - horizontalAlignment = Alignment.End, - ) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4), - ) { - Text( - text = balanceAmount, - color = TangemTheme.colors.text.primary1, - textAlign = TextAlign.End, - overflow = TextOverflow.Ellipsis, - maxLines = 1, - style = TangemTheme.typography.subtitle1, - ) - if (showWarning) { - Icon( - modifier = Modifier.size(TangemTheme.dimens.size16), - painter = painterResource(id = R.drawable.ic_alert_24), - tint = TangemTheme.colors.icon.attention, - contentDescription = null, - ) - } - } - if (tokensCount != null) { - SpacerH2() - Text( - text = pluralStringResource( - id = R.plurals.token_count, - count = tokensCount, - tokensCount, - ), - style = TangemTheme.typography.caption2, - color = TangemTheme.colors.text.tertiary, - textAlign = TextAlign.End, - ) - } - } -} - -@Composable -private fun LockedPlaceholder(modifier: Modifier = Modifier) { - Box( - modifier = modifier - .padding(vertical = TangemTheme.dimens.spacing2) - .background( - color = TangemTheme.colors.button.secondary, - shape = TangemTheme.shapes.roundedCornersMedium, - ), - ) { - Icon( - modifier = Modifier - .padding( - vertical = TangemTheme.dimens.spacing8, - horizontal = TangemTheme.dimens.spacing12, - ) - .size(TangemTheme.dimens.size20), - painter = painterResource(id = R.drawable.ic_locked_24), - tint = TangemTheme.colors.icon.primary1, - contentDescription = null, - ) - } -} - -@Composable -private fun CardImagePlaceholder(modifier: Modifier = Modifier) { - Box(modifier = modifier) { - Image( - modifier = Modifier.matchParentSize(), - painter = painterResource(id = R.drawable.card_placeholder_black), - contentDescription = null, - ) - } -} - -@Composable -private fun CardImageShimmer(modifier: Modifier = Modifier) { - Box(modifier = modifier.shimmer()) { - Box( - modifier = Modifier - .matchParentSize() - .background(TangemTheme.colors.button.secondary), - ) - } -} - -@Composable -private fun SelectedWalletBadge(modifier: Modifier = Modifier) { - Box( - modifier = modifier - .size(TangemTheme.dimens.size18) - .background( - color = Color.White, - shape = CircleShape, - ), - contentAlignment = Alignment.Center, - ) { - Icon( - modifier = Modifier.padding(all = 0.5.dp), - painter = painterResource(id = R.drawable.ic_check_circle_18), - tint = TangemTheme.colors.icon.accent, - contentDescription = null, - ) - } -} - -@Composable -private fun CheckedWalletMark(modifier: Modifier = Modifier) { - Box( - modifier = modifier, - contentAlignment = Alignment.Center, - ) { - Icon( - painter = painterResource(id = R.drawable.ic_check_24), - tint = TangemTheme.colors.icon.primary2, - contentDescription = null, - ) - } -} - -// region Preview -@Composable -@Suppress("LongMethod") // preview -private fun WalletItemSample(modifier: Modifier = Modifier) { - Column( - modifier = modifier - .background(TangemTheme.colors.background.primary), - ) { - WalletItem( - wallet = MultiCurrencyUserWalletItem( - id = UserWalletId(value = null), - name = "Tangem Card", - imageUrl = "", - balance = UserWalletItem.Balance.Loaded( - amount = "1412121218 BTC", - showWarning = false, - ), - isLocked = false, - tokensCount = 2, - cardsInWallet = 1, - ), - isSelected = false, - isChecked = false, - onWalletClick = {}, - onWalletLongClick = {}, - ) - Divider(modifier = Modifier.padding(vertical = 4.dp)) - WalletItem( - wallet = MultiCurrencyUserWalletItem( - id = UserWalletId(value = null), - name = "Tangem Card", - imageUrl = "", - balance = UserWalletItem.Balance.Loaded( - amount = "1412121218 BTC", - showWarning = true, - ), - isLocked = false, - tokensCount = 2, - cardsInWallet = 1, - ), - isSelected = false, - isChecked = false, - onWalletClick = {}, - onWalletLongClick = {}, - ) - Divider(modifier = Modifier.padding(vertical = 4.dp)) - WalletItem( - wallet = MultiCurrencyUserWalletItem( - id = UserWalletId(value = null), - name = "Tangem Card", - imageUrl = "", - balance = UserWalletItem.Balance.Loading, - isLocked = true, - tokensCount = 2, - cardsInWallet = 1, - ), - isSelected = false, - isChecked = false, - onWalletClick = {}, - onWalletLongClick = {}, - ) - Divider(modifier = Modifier.padding(vertical = 4.dp)) - WalletItem( - wallet = MultiCurrencyUserWalletItem( - id = UserWalletId(value = null), - name = "Tangem Card", - imageUrl = "", - balance = UserWalletItem.Balance.Failed, - isLocked = false, - tokensCount = 2, - cardsInWallet = 1, - ), - isSelected = true, - isChecked = false, - onWalletClick = {}, - onWalletLongClick = {}, - ) - Divider(modifier = Modifier.padding(vertical = 4.dp)) - WalletItem( - wallet = SingleCurrencyUserWalletItem( - id = UserWalletId(value = null), - name = "Tangem Card", - imageUrl = "", - balance = UserWalletItem.Balance.Loaded( - amount = "141212121888 BTC", - showWarning = false, - ), - isLocked = false, - tokenName = "Bitcoin", - ), - isSelected = false, - isChecked = true, - onWalletClick = {}, - onWalletLongClick = {}, - ) - } -} - -@Preview(showBackground = true, widthDp = 360) -@Composable -private fun WalletItemPreview_Light() { - TangemTheme { - WalletItemSample() - } -} - -@Preview(showBackground = true, widthDp = 360) -@Composable -private fun WalletItemPreview_Dark() { - TangemTheme(isDark = true) { - WalletItemSample() - } -} -// endregion Preview \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/DialogModel.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/DialogModel.kt deleted file mode 100644 index 87039dc113..0000000000 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/DialogModel.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.tangem.tap.features.walletSelector.ui.model - -internal sealed interface DialogModel { - data class RenameWalletDialog( - val currentName: String, - val onConfirm: (newName: String) -> Unit, - val onDismiss: () -> Unit, - ) : DialogModel - - data class RemoveWalletDialog( - val onConfirm: () -> Unit, - val onDismiss: () -> Unit, - ) : DialogModel -} - -internal sealed interface WarningModel : DialogModel { - data class BiometricsLockoutWarning( - val isPermanent: Boolean, - val onDismiss: () -> Unit, - ) : WarningModel - - data class KeyInvalidatedWarning( - val onDismiss: () -> Unit, - ) : WarningModel - - data class BiometricsDisabledWarning( - val onDismiss: () -> Unit, - ) : WarningModel -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/UserWalletItem.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/UserWalletItem.kt deleted file mode 100644 index e5e3033115..0000000000 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/UserWalletItem.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.tangem.tap.features.walletSelector.ui.model - -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.features.wallet.redux.utils.UNKNOWN_AMOUNT_SIGN - -internal sealed interface UserWalletItem { - val id: UserWalletId - val name: String - val imageUrl: String - val balance: Balance - val isLocked: Boolean - - sealed class Balance { - open val amount: String = UNKNOWN_AMOUNT_SIGN - open val showWarning: Boolean = false - - object Loading : Balance() - - object Failed : Balance() { - override val showWarning: Boolean = true - } - - data class Loaded( - override val amount: String, - override val showWarning: Boolean, - ) : Balance() - } -} - -internal data class MultiCurrencyUserWalletItem( - override val id: UserWalletId, - override val name: String, - override val imageUrl: String, - override val balance: UserWalletItem.Balance, - override val isLocked: Boolean, - val tokensCount: Int, - val cardsInWallet: Int, -) : UserWalletItem - -internal data class SingleCurrencyUserWalletItem( - override val id: UserWalletId, - override val name: String, - override val imageUrl: String, - override val balance: UserWalletItem.Balance, - override val isLocked: Boolean, - val tokenName: String, -) : UserWalletItem \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt index 482671ce51..555f73988c 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt @@ -7,6 +7,7 @@ import com.tangem.common.doOnResult import com.tangem.common.doOnSuccess import com.tangem.common.flatMap import com.tangem.core.analytics.Analytics +import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction import com.tangem.domain.common.util.cardTypesResolver @@ -15,8 +16,7 @@ import com.tangem.domain.userwallets.UserWalletBuilder import com.tangem.domain.wallets.legacy.unlockIfLockable import com.tangem.tap.* import com.tangem.tap.common.analytics.converters.ParamCardCurrencyConverter -import com.tangem.tap.common.analytics.events.AnalyticsParam -import com.tangem.tap.common.analytics.events.Basic +import com.tangem.core.analytics.models.Basic import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.dispatchWithMain import com.tangem.tap.common.extensions.onUserWalletSelected @@ -158,7 +158,6 @@ internal class WelcomeMiddleware { private suspend fun disableUserWalletsSaving() { userWalletsListManager.clear() - .flatMap { walletStoresManager.clear() } .flatMap { tangemSdkManager.clearSavedUserCodes() } .doOnFailure { e -> Timber.e(e, "Unable to clear user wallets") diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/BuyExchangeService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/BuyExchangeService.kt index f9d2d16492..5f48db2717 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/BuyExchangeService.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/BuyExchangeService.kt @@ -1,7 +1,7 @@ package com.tangem.tap.network.exchangeServices import com.tangem.blockchain.common.Blockchain -import com.tangem.tap.features.wallet.models.Currency +import com.tangem.tap.domain.model.Currency import com.tangem.tap.network.exchangeServices.mercuryo.MercuryoService import com.tangem.tap.scope import kotlinx.coroutines.launch diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/CardExchangeRules.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/CardExchangeRules.kt index 3e0d7ab0a5..0f4fd42f7d 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/CardExchangeRules.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/CardExchangeRules.kt @@ -1,9 +1,9 @@ package com.tangem.tap.network.exchangeServices -import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.common.TapWorkarounds.isStart2Coin +import com.tangem.domain.models.scan.CardDTO +import com.tangem.tap.domain.model.Currency import com.tangem.tap.features.demo.isDemoCard -import com.tangem.tap.features.wallet.models.Currency /** [REDACTED_AUTHOR] diff --git a/app/src/main/java/com/tangem/tap/features/wallet/converters/CryptoCurrencyConverter.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/CryptoCurrencyConverter.kt similarity index 96% rename from app/src/main/java/com/tangem/tap/features/wallet/converters/CryptoCurrencyConverter.kt rename to app/src/main/java/com/tangem/tap/network/exchangeServices/CryptoCurrencyConverter.kt index 891841469c..7ed890dda8 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/converters/CryptoCurrencyConverter.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/CryptoCurrencyConverter.kt @@ -1,11 +1,11 @@ -package com.tangem.tap.features.wallet.converters +package com.tangem.tap.network.exchangeServices import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token import com.tangem.data.tokens.utils.CryptoCurrencyFactory import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.tap.features.wallet.models.Currency +import com.tangem.tap.domain.model.Currency import com.tangem.tap.store import com.tangem.utils.converter.TwoWayConverter 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 09274aa9ac..7b0d4d5a93 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 @@ -12,8 +12,8 @@ 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.domain.model.Currency import com.tangem.tap.features.demo.isDemoCard -import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.store import java.math.BigDecimal diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/DefaultRampManager.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/DefaultRampManager.kt index b8a3e32d55..76de7375da 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/DefaultRampManager.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/DefaultRampManager.kt @@ -2,7 +2,6 @@ package com.tangem.tap.network.exchangeServices import com.tangem.domain.exchange.RampStateManager import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.tap.features.wallet.converters.CryptoCurrencyConverter class DefaultRampManager(private val exchangeService: ExchangeService?) : RampStateManager { 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 442a0a6db0..b139b14200 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 @@ -2,7 +2,7 @@ package com.tangem.tap.network.exchangeServices import com.tangem.blockchain.common.Blockchain import com.tangem.tap.common.feature.Feature -import com.tangem.tap.features.wallet.models.Currency +import com.tangem.tap.domain.model.Currency interface Exchanger { fun isBuyAllowed(): Boolean 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 6ded86b438..9446f5c5cf 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 @@ -7,7 +7,7 @@ import com.tangem.common.extensions.toHexString import com.tangem.common.services.Result import com.tangem.common.services.performRequest import com.tangem.tap.common.redux.global.CryptoCurrencyName -import com.tangem.tap.features.wallet.models.Currency +import com.tangem.tap.domain.model.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 2d4694c975..664f31e958 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 @@ -8,7 +8,7 @@ import com.tangem.common.services.performRequest import com.tangem.datasource.api.common.createRetrofitInstance import com.tangem.domain.common.extensions.withIOContext import com.tangem.tap.common.redux.global.CryptoCurrencyName -import com.tangem.tap.features.wallet.models.Currency +import com.tangem.tap.domain.model.Currency import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager import com.tangem.tap.network.exchangeServices.ExchangeService import com.tangem.tap.network.exchangeServices.ExchangeUrlBuilder.Companion.SCHEME diff --git a/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt b/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt index 953ee95b85..022b56497b 100644 --- a/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt +++ b/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt @@ -3,21 +3,18 @@ package com.tangem.tap.proxy import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction import com.tangem.core.navigation.ReduxNavController +import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.redux.ReduxStateHolder import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.legacy.WalletsStateHolder import com.tangem.domain.wallets.models.UserWallet -import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.dispatchWithMain import com.tangem.tap.common.extensions.onUserWalletSelected import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.TangemSdkManager -import com.tangem.tap.domain.tokens.UserTokensRepository -import com.tangem.tap.domain.walletStores.WalletStoresManager -import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.network.exchangeServices.ExchangeService import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -44,12 +41,9 @@ class AppStateHolder @Inject constructor() : WalletsStateHolder, ReduxNavControl @Deprecated("Use scan response from selected user wallet") var scanResponse: ScanResponse? = null - var walletState: WalletState? = null - var userTokensRepository: UserTokensRepository? = null var mainStore: Store? = null var tangemSdkManager: TangemSdkManager? = null - var walletStoresManager: WalletStoresManager? = null - var appFiatCurrency: FiatCurrency = FiatCurrency.Default + var appFiatCurrency: AppCurrency = AppCurrency.Default var exchangeService: ExchangeService? = null fun getActualCard(): CardDTO? { diff --git a/app/src/main/java/com/tangem/tap/proxy/DerivationManagerImpl.kt b/app/src/main/java/com/tangem/tap/proxy/DerivationManagerImpl.kt index 47c99bfb4b..63b07213d1 100644 --- a/app/src/main/java/com/tangem/tap/proxy/DerivationManagerImpl.kt +++ b/app/src/main/java/com/tangem/tap/proxy/DerivationManagerImpl.kt @@ -39,7 +39,7 @@ import com.tangem.utils.extensions.DELAY_SDK_DIALOG_CLOSE import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlin.coroutines.suspendCoroutine -import com.tangem.tap.features.wallet.models.Currency as WalletModelCurrency +import com.tangem.tap.domain.model.Currency as WalletModelCurrency class DerivationManagerImpl( private val appStateHolder: AppStateHolder, @@ -52,31 +52,6 @@ class DerivationManagerImpl( AddCryptoCurrenciesUseCase(currenciesRepository, networksRepository) } - override suspend fun deriveMissingBlockchains(currency: Currency) = suspendCoroutine { continuation -> - val blockchain = Blockchain.fromNetworkId(currency.networkId) - val card = appStateHolder.getActualCard() - if (blockchain != null && card != null) { - val appToken = getAppToken(currency) - val scanResponse = appStateHolder.scanResponse - if (scanResponse != null) { - val blockchainNetwork = BlockchainNetwork(blockchain, scanResponse.derivationStyleProvider) - val appCurrency = WalletModelCurrency.fromBlockchainNetwork( - blockchainNetwork, - appToken, - ) - deriveMissingBlockchains( - scanResponse = scanResponse, - currencyList = listOf(appCurrency), - onSuccess = { continuation.resumeWith(Result.success(true)) }, - ) { - continuation.resumeWith(Result.failure(it)) - } - } - } else { - continuation.resumeWith(Result.failure(IllegalStateException("no blockchain or card found"))) - } - } - override suspend fun deriveAndAddTokens(currency: Currency) = suspendCoroutine { continuation -> val selectedUserWallet = requireNotNull( userWalletsListManager.selectedUserWalletSync, @@ -106,7 +81,7 @@ class DerivationManagerImpl( } } else { val blockchainNetwork = BlockchainNetwork(blockchain, scanResponse.derivationStyleProvider) - val appCurrency = WalletModelCurrency.fromBlockchainNetwork( + val appCurrency = com.tangem.tap.domain.model.Currency.fromBlockchainNetwork( blockchainNetwork, getAppToken(currency), ) @@ -136,27 +111,6 @@ class DerivationManagerImpl( } } - override fun getDerivationPathForBlockchain(networkId: String): String? { - val scanResponse = appStateHolder.scanResponse - val blockchain = Blockchain.fromNetworkId(networkId) - if (scanResponse != null && blockchain != null) { - return blockchain.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle())?.rawPath - } - return null - } - - override fun hasDerivation(networkId: String, derivationPath: String): Boolean { - val scanResponse = appStateHolder.scanResponse - val blockchain = Blockchain.fromNetworkId(networkId) - if (scanResponse != null && blockchain != null) { - return scanResponse.hasDerivation( - blockchain, - derivationPath, - ) - } - return false - } - private suspend fun addToken( userWalletId: UserWalletId, blockchain: Blockchain, @@ -284,7 +238,7 @@ class DerivationManagerImpl( private fun getDerivations( curve: EllipticCurve, scanResponse: ScanResponse, - currency: com.tangem.tap.features.wallet.models.Currency, + currency: com.tangem.tap.domain.model.Currency, ): TokensMiddleware.DerivationData? { val wallet = scanResponse.card.wallets.firstOrNull { it.curve == curve } ?: return null @@ -341,8 +295,4 @@ class DerivationManagerImpl( } return IllegalStateException(error.customMessage) } - - private class DerivationData( - val derivations: Pair>, - ) } \ No newline at end of file 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 a0db65cfba..ec450b5f39 100644 --- a/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt +++ b/app/src/main/java/com/tangem/tap/proxy/TransactionManagerImpl.kt @@ -1,5 +1,6 @@ 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.binance.BinanceTransactionExtras @@ -21,18 +22,11 @@ import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.hexToBytes import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.domain.card.repository.CardSdkConfigRepository -import com.tangem.domain.common.BlockchainNetwork import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.domain.walletmanager.WalletManagersFacade -import com.tangem.features.send.impl.presentation.viewmodel.XlmMemoType -import com.tangem.features.send.impl.presentation.viewmodel.determineXlmMemoType -import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles import com.tangem.lib.crypto.TransactionManager import com.tangem.lib.crypto.models.* import com.tangem.lib.crypto.models.transactions.SendTxResult -import com.tangem.tap.common.analytics.events.AnalyticsParam -import com.tangem.tap.common.analytics.events.Basic -import com.tangem.tap.common.analytics.events.Basic.TransactionSent.MemoType import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.TangemSigner import com.tangem.tap.userWalletsListManager @@ -47,7 +41,6 @@ class TransactionManagerImpl( private val analytics: AnalyticsEventHandler, private val cardSdkConfigRepository: CardSdkConfigRepository, private val walletManagersFacade: WalletManagersFacade, - private val walletFeatureToggles: WalletFeatureToggles, ) : TransactionManager { override suspend fun sendApproveTransaction( @@ -67,12 +60,6 @@ class TransactionManagerImpl( gasLimit = txData.gasLimit, destinationAddress = txData.destinationAddress, dataToSign = txData.dataToSign, - successEvent = AnalyticsParam.TxSentFrom.Approve( - blockchain = blockchain.fullName, - token = analyticsData.tokenSymbol, - feeType = AnalyticsParam.FeeType.fromString(analyticsData.feeType), - permissionType = analyticsData.permissionType ?: "", - ), ) } @@ -90,19 +77,6 @@ class TransactionManagerImpl( } else { createAmount(txData.amountToSend, txData.currencyToSend, blockchain) } - val successEvent = if (isSwap) { - AnalyticsParam.TxSentFrom.Swap( - blockchain = blockchain.fullName, - token = analyticsData.tokenSymbol, - feeType = AnalyticsParam.FeeType.fromString(analyticsData.feeType), - ) - } else { - AnalyticsParam.TxSentFrom.Send( - blockchain = blockchain.fullName, - token = analyticsData.tokenSymbol, - feeType = AnalyticsParam.FeeType.fromString(analyticsData.feeType), - ) - } return sendTransactionInternal( walletManager = walletManager, amount = amount, @@ -111,7 +85,6 @@ class TransactionManagerImpl( gasLimit = txData.gasLimit, destinationAddress = txData.destinationAddress, dataToSign = txData.dataToSign, - successEvent = successEvent, ) } @@ -124,7 +97,6 @@ class TransactionManagerImpl( gasLimit: Int, destinationAddress: String, dataToSign: String, - successEvent: AnalyticsParam.TxSentFrom, ): SendTxResult { val txData = walletManager.createTransaction( amount = amount, @@ -140,10 +112,7 @@ class TransactionManagerImpl( FirebaseCrashlytics.getInstance().recordException(ex) return SendTxResult.UnknownError(ex) } - return handleSendResult( - result = sendResult, - successEvent = successEvent, - ) + return handleSendResult(result = sendResult) } override fun getExplorerTransactionLink(networkId: String, txAddress: String): String { @@ -156,11 +125,12 @@ class TransactionManagerImpl( if (memo == null) return null return when (blockchain) { Blockchain.Stellar -> { - val xmlMemo = when (determineXlmMemoType(memo)) { - XlmMemoType.TEXT -> StellarMemo.Text(memo) - XlmMemoType.ID -> StellarMemo.Id(memo.toBigInteger()) + val xlmMemo = if (memo.isNotEmpty() && memo.isDigitsOnly()) { + StellarMemo.Id(memo.toBigInteger()) + } else { + StellarMemo.Text(memo) } - StellarTransactionExtras(xmlMemo) + StellarTransactionExtras(xlmMemo) } Blockchain.Binance -> BinanceTransactionExtras(memo) Blockchain.XRP -> memo.toLongOrNull()?.let { XrpTransactionBuilder.XrpTransactionExtras(it) } @@ -384,10 +354,9 @@ class TransactionManagerImpl( } } - private fun handleSendResult(result: SimpleResult, successEvent: AnalyticsParam.TxSentFrom): SendTxResult { + private fun handleSendResult(result: SimpleResult): SendTxResult { when (result) { is SimpleResult.Success -> { - analytics.send(Basic.TransactionSent(sentFrom = successEvent, memoType = MemoType.Null)) return SendTxResult.Success } is SimpleResult.Failure -> { @@ -440,19 +409,15 @@ class TransactionManagerImpl( } private suspend fun getActualWalletManager(blockchain: Blockchain, derivationPath: String?): WalletManager { - val walletManager = if (walletFeatureToggles.isRedesignedScreenEnabled) { - val selectedUserWallet = requireNotNull( - userWalletsListManager.selectedUserWalletSync, - ) { "userWallet or userWalletsListManager is null" } - walletManagersFacade.getOrCreateWalletManager( - selectedUserWallet.walletId, - blockchain, - derivationPath, - ) - } else { - val blockchainNetwork = BlockchainNetwork(blockchain, derivationPath, emptyList()) - appStateHolder.walletState?.getWalletManager(blockchainNetwork) - } + val selectedUserWallet = requireNotNull( + userWalletsListManager.selectedUserWalletSync, + ) { "userWallet or userWalletsListManager is null" } + val walletManager = walletManagersFacade.getOrCreateWalletManager( + selectedUserWallet.walletId, + blockchain, + derivationPath, + ) + return requireNotNull(walletManager) { "no wallet manager found" } } diff --git a/app/src/main/java/com/tangem/tap/proxy/UserWalletManagerImpl.kt b/app/src/main/java/com/tangem/tap/proxy/UserWalletManagerImpl.kt index 74fce7b587..836356c40e 100644 --- a/app/src/main/java/com/tangem/tap/proxy/UserWalletManagerImpl.kt +++ b/app/src/main/java/com/tangem/tap/proxy/UserWalletManagerImpl.kt @@ -4,34 +4,28 @@ import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token import com.tangem.blockchain.common.WalletManager -import com.tangem.common.doOnFailure -import com.tangem.common.extensions.guard -import com.tangem.domain.common.BlockchainNetwork +import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.domain.common.extensions.toCoinId import com.tangem.domain.common.extensions.toNetworkId +import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.walletmanager.WalletManagersFacade -import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles import com.tangem.lib.crypto.UserWalletManager import com.tangem.lib.crypto.models.Currency import com.tangem.lib.crypto.models.Currency.NativeToken import com.tangem.lib.crypto.models.Currency.NonNativeToken import com.tangem.lib.crypto.models.ProxyAmount import com.tangem.lib.crypto.models.ProxyFiatCurrency -import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.userWalletsListManager -import com.tangem.tap.walletCurrenciesManager -import com.tangem.tap.walletStoresManager -import kotlinx.coroutines.flow.firstOrNull import timber.log.Timber import java.math.BigDecimal -import com.tangem.tap.features.wallet.models.Currency as WalletCurrency class UserWalletManagerImpl( private val appStateHolder: AppStateHolder, private val walletManagersFacade: WalletManagersFacade, - private val walletFeatureToggles: WalletFeatureToggles, + private val currenciesRepository: CurrenciesRepository, + private val userWalletsStore: UserWalletsStore, ) : UserWalletManager { override suspend fun getUserTokens( @@ -39,43 +33,44 @@ class UserWalletManagerImpl( derivationPath: String?, isExcludeCustom: Boolean, ): List { - val card = appStateHolder.getActualCard() - val userTokensRepository = - requireNotNull(appStateHolder.userTokensRepository) { "userTokensRepository is null" } - return if (card != null) { - userTokensRepository.getUserTokens(card, null) // refactor in [REDACTED_JIRA] - .filter { - val checkCustom = if (isExcludeCustom) { - !it.isCustomCurrency(null) - } else { - true - } - it.blockchain.toNetworkId() == networkId && - checkCustom && - it.derivationPath == derivationPath - } - .map { - if (it is com.tangem.tap.features.wallet.models.Currency.Token) { - NonNativeToken( - id = it.coinId ?: "", - name = it.currencyName, - symbol = it.currencySymbol, - networkId = it.blockchain.toNetworkId(), - contractAddress = it.token.contractAddress, - decimalCount = it.token.decimals, - ) - } else { - NativeToken( - id = it.coinId ?: "", - name = it.currencyName, - symbol = it.currencySymbol, - networkId = it.blockchain.toNetworkId(), - ) - } - } - } else { - emptyList() + // FIXME: Find user wallet by ID + val userWallet = requireNotNull(userWalletsStore.selectedUserWalletOrNull) { + "No user wallet selected" } + return currenciesRepository.getMultiCurrencyWalletCurrenciesSync(userWallet.walletId) + .filter { + val checkCustom = if (isExcludeCustom) { + !it.isCustom + } else { + true + } + val blockchain = Blockchain.fromId(it.network.id.value) + + blockchain.toNetworkId() == networkId && + checkCustom && + it.network.derivationPath.value == derivationPath + } + .map { + val blockchain = Blockchain.fromId(it.network.id.value) + + if (it is CryptoCurrency.Token) { + NonNativeToken( + id = it.id.rawCurrencyId ?: "", + name = it.name, + symbol = it.symbol, + networkId = blockchain.toNetworkId(), + contractAddress = it.contractAddress, + decimalCount = it.decimals, + ) + } else { + NativeToken( + id = it.id.rawCurrencyId ?: "", + name = it.name, + symbol = it.symbol, + networkId = blockchain.toNetworkId(), + ) + } + } } override fun getNativeTokenForNetwork(networkId: String): Currency { @@ -107,40 +102,9 @@ class UserWalletManagerImpl( } } - override suspend fun addToken(currency: Currency, derivationPath: String?) { - val blockchain = requireNotNull(Blockchain.fromNetworkId(currency.networkId)) { "blockchain not found" } - val blockchainNetwork = BlockchainNetwork(blockchain, derivationPath, emptyList()) - - val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard { - Timber.e("Unable to add token, no user wallet selected") - return - } - walletCurrenciesManager.addCurrencies( - userWallet = selectedUserWallet, - currenciesToAdd = listOf(currency.toWalletCurrency(blockchainNetwork)), - ) - } - override suspend fun hideAllTokens() { - val userWallet = userWalletsListManager.selectedUserWalletSync.guard { - Timber.e("No user wallets selected") - return - } - - val currencies = walletStoresManager.get(userWallet.walletId) - .firstOrNull() - ?.flatMap { walletStore -> - walletStore.walletsData.map { it.currency } - } - .guard { - Timber.d("No currencies found") - return - } - - walletCurrenciesManager.removeCurrencies(userWallet, currenciesToRemove = currencies) - .doOnFailure { e -> - Timber.e(e, "Unable to delete all currencies") - } + // FIXME: Used only in Tester Actions + Timber.w("Not implemented") } override suspend fun getWalletAddress(networkId: String, derivationPath: String?): String { @@ -216,28 +180,17 @@ class UserWalletManagerImpl( ) } - override fun refreshWallet() { - // workaround, should update wallet after transaction - appStateHolder.mainStore?.dispatchOnMain(WalletAction.LoadData.Refresh) - } - @Throws(IllegalArgumentException::class) private suspend fun getActualWalletManager(blockchain: Blockchain, derivationPath: String?): WalletManager { - val walletManager = if (walletFeatureToggles.isRedesignedScreenEnabled) { - val selectedUserWallet = requireNotNull( - userWalletsListManager.selectedUserWalletSync, - ) { "userWallet or userWalletsListManager is null" } - walletManagersFacade.getOrCreateWalletManager( - selectedUserWallet.walletId, - blockchain, - derivationPath, - ) - } else { - val blockchainNetwork = BlockchainNetwork(blockchain, derivationPath, emptyList()) - return requireNotNull(appStateHolder.walletState?.getWalletManager(blockchainNetwork)) { - "No wallet manager found" - } - } + val selectedUserWallet = requireNotNull( + userWalletsListManager.selectedUserWalletSync, + ) { "userWallet or userWalletsListManager is null" } + val walletManager = walletManagersFacade.getOrCreateWalletManager( + selectedUserWallet.walletId, + blockchain, + derivationPath, + ) + return requireNotNull(walletManager) { "No wallet manager found" } @@ -252,18 +205,4 @@ private fun NonNativeToken.toSdkToken(): Token { contractAddress = this.contractAddress, decimals = this.decimalCount, ) -} - -private fun Currency.toWalletCurrency(network: BlockchainNetwork): WalletCurrency { - return when (this) { - is NativeToken -> WalletCurrency.Blockchain( - blockchain = network.blockchain, - derivationPath = network.derivationPath, - ) - is NonNativeToken -> WalletCurrency.Token( - token = this.toSdkToken(), - blockchain = network.blockchain, - derivationPath = network.derivationPath, - ) - } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/proxy/di/ProxyModule.kt b/app/src/main/java/com/tangem/tap/proxy/di/ProxyModule.kt index b64680c54c..263fc1eec8 100644 --- a/app/src/main/java/com/tangem/tap/proxy/di/ProxyModule.kt +++ b/app/src/main/java/com/tangem/tap/proxy/di/ProxyModule.kt @@ -1,32 +1,25 @@ package com.tangem.tap.proxy.di -import com.tangem.common.Provider import com.tangem.core.analytics.api.AnalyticsEventHandler -import com.tangem.core.featuretoggle.manager.FeatureTogglesManager +import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.card.repository.CardSdkConfigRepository -import com.tangem.domain.common.CardTypesResolver -import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.walletmanager.WalletManagersFacade -import com.tangem.feature.learn2earn.domain.api.Learn2earnDependencyProvider -import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles import com.tangem.lib.crypto.DerivationManager import com.tangem.lib.crypto.TransactionManager import com.tangem.lib.crypto.UserWalletManager -import com.tangem.tap.features.details.DarkThemeFeatureToggle import com.tangem.tap.proxy.* import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.* import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -class ProxyModule { +internal object ProxyModule { @Provides @Singleton @@ -39,12 +32,14 @@ class ProxyModule { fun provideUserWalletManager( appStateHolder: AppStateHolder, walletManagersFacade: WalletManagersFacade, - walletFeatureToggles: WalletFeatureToggles, + currenciesRepository: CurrenciesRepository, + userWalletsStore: UserWalletsStore, ): UserWalletManager { return UserWalletManagerImpl( appStateHolder = appStateHolder, walletManagersFacade = walletManagersFacade, - walletFeatureToggles = walletFeatureToggles, + currenciesRepository = currenciesRepository, + userWalletsStore = userWalletsStore, ) } @@ -55,14 +50,12 @@ class ProxyModule { analytics: AnalyticsEventHandler, cardSdkConfigRepository: CardSdkConfigRepository, walletManagersFacade: WalletManagersFacade, - walletFeatureToggles: WalletFeatureToggles, ): TransactionManager { return TransactionManagerImpl( appStateHolder = appStateHolder, analytics = analytics, cardSdkConfigRepository = cardSdkConfigRepository, walletManagersFacade = walletManagersFacade, - walletFeatureToggles = walletFeatureToggles, ) } @@ -79,33 +72,4 @@ class ProxyModule { networksRepository = networksRepository, ) } - - @Provides - @Singleton - fun provideDarkThemeFeatureToggle(featureTogglesManager: FeatureTogglesManager): DarkThemeFeatureToggle { - return DarkThemeFeatureToggle(featureTogglesManager) - } - - // regions FeatureConsumers - @Provides - @Singleton - fun provideLear2earnDependencies(appStateHolder: AppStateHolder): Learn2earnDependencyProvider { - return object : Learn2earnDependencyProvider { - - @OptIn(ExperimentalCoroutinesApi::class) - override fun getCardTypeResolverFlow(): Flow { - return appStateHolder.userWalletListManagerFlow - .flatMapLatest { manager -> - manager?.selectedUserWallet - ?.map { it.scanResponse.cardTypesResolver } - ?: flowOf(null) - } - } - - override fun getWebViewAuthCredentialsProvider(): Provider = Provider { - appStateHolder.mainStore?.state?.globalState?.configManager?.config?.tangemComAuthorization - } - } - } - // endregion FeatureConsumers } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt index 3392c82127..9dab1b838d 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphAction.kt @@ -1,5 +1,6 @@ package com.tangem.tap.proxy.redux +import com.tangem.feature.qrscanning.QrScanningRouter import com.tangem.domain.card.ScanCardUseCase import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.features.managetokens.navigation.ManageTokensRouter @@ -21,5 +22,6 @@ sealed interface DaggerGraphAction : Action { val manageTokensRouter: ManageTokensRouter, val cardSdkConfigRepository: CardSdkConfigRepository, val sendRouter: SendRouter, + val qrScanningRouter: QrScanningRouter, ) : DaggerGraphAction } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt index df08899bf0..5f2030f390 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphReducer.kt @@ -21,6 +21,7 @@ object DaggerGraphReducer { manageTokensRouter = action.manageTokensRouter, cardSdkConfigRepository = action.cardSdkConfigRepository, sendRouter = action.sendRouter, + qrScanningRouter = action.qrScanningRouter, ) } } 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 e7a479dfa0..6bd651f70c 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 @@ -1,6 +1,6 @@ package com.tangem.tap.proxy.redux -import com.tangem.datasource.asset.AssetReader +import com.tangem.feature.qrscanning.QrScanningRouter import com.tangem.datasource.connection.NetworkConnectionManager import com.tangem.domain.appcurrency.repository.AppCurrencyRepository import com.tangem.domain.apptheme.repository.AppThemeModeRepository @@ -8,6 +8,7 @@ import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository import com.tangem.domain.card.ScanCardProcessor import com.tangem.domain.card.ScanCardUseCase import com.tangem.domain.card.repository.CardSdkConfigRepository +import com.tangem.domain.card.repository.DerivationsRepository import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.walletmanager.WalletManagersFacade @@ -16,25 +17,22 @@ import com.tangem.features.managetokens.featuretoggles.ManageTokensFeatureToggle import com.tangem.features.managetokens.navigation.ManageTokensRouter import com.tangem.features.send.api.featuretoggles.SendFeatureToggles import com.tangem.features.send.api.navigation.SendRouter +import com.tangem.features.tester.api.TesterFeatureToggles import com.tangem.features.tester.api.TesterRouter import com.tangem.features.tokendetails.navigation.TokenDetailsRouter -import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles import com.tangem.features.wallet.navigation.WalletRouter import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor import com.tangem.tap.domain.walletconnect2.domain.WalletConnectRepository import com.tangem.tap.domain.walletconnect2.domain.WalletConnectSessionsRepository import com.tangem.tap.features.customtoken.api.featuretoggles.CustomTokenFeatureToggles -import com.tangem.tap.features.details.featuretoggles.DetailsFeatureToggles import com.tangem.tap.proxy.AppStateHolder import org.rekotlin.StateType data class DaggerGraphState( - val assetReader: AssetReader? = null, val testerRouter: TesterRouter? = null, val networkConnectionManager: NetworkConnectionManager? = null, val customTokenFeatureToggles: CustomTokenFeatureToggles? = null, val scanCardUseCase: ScanCardUseCase? = null, - val walletFeatureToggles: WalletFeatureToggles? = null, val walletRouter: WalletRouter? = null, val walletConnectRepository: WalletConnectRepository? = null, val walletConnectSessionsRepository: WalletConnectSessionsRepository? = null, @@ -49,14 +47,16 @@ data class DaggerGraphState( val appStateHolder: AppStateHolder? = null, val appThemeModeRepository: AppThemeModeRepository? = null, val balanceHidingRepository: BalanceHidingRepository? = null, - val detailsFeatureToggles: DetailsFeatureToggles? = null, val walletsRepository: WalletsRepository? = null, val networksRepository: NetworksRepository? = null, val sendFeatureToggles: SendFeatureToggles? = null, val sendRouter: SendRouter? = null, + val qrScanningRouter: QrScanningRouter? = null, // FIXME: It is used only for TokensList screen. Remove after refactoring of TokensList val currenciesRepository: CurrenciesRepository? = null, + val derivationsRepository: DerivationsRepository? = null, + val testerFeatureToggles: TesterFeatureToggles? = null, ) : StateType { inline fun get(getDependency: DaggerGraphState.() -> T?): T { diff --git a/app/src/main/res/color/menu_item_color.xml b/app/src/main/res/color/menu_item_color.xml deleted file mode 100644 index 2b59ebcebe..0000000000 --- a/app/src/main/res/color/menu_item_color.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/color/selector_chip_background.xml b/app/src/main/res/color/selector_chip_background.xml deleted file mode 100644 index 16be87b913..0000000000 --- a/app/src/main/res/color/selector_chip_background.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable-hdpi/ic_eth.webp b/app/src/main/res/drawable-hdpi/ic_eth.webp deleted file mode 100644 index ffe1bc6185..0000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_eth.webp and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/wallet_for_everyone.webp b/app/src/main/res/drawable-hdpi/wallet_for_everyone.webp deleted file mode 100644 index e50874dfbf..0000000000 Binary files a/app/src/main/res/drawable-hdpi/wallet_for_everyone.webp and /dev/null differ diff --git a/app/src/main/res/drawable-ldpi/ic_eth.webp b/app/src/main/res/drawable-ldpi/ic_eth.webp deleted file mode 100644 index dc633be363..0000000000 Binary files a/app/src/main/res/drawable-ldpi/ic_eth.webp and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_eth.webp b/app/src/main/res/drawable-mdpi/ic_eth.webp deleted file mode 100644 index befd41d571..0000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_eth.webp and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/wallet_for_everyone.webp b/app/src/main/res/drawable-mdpi/wallet_for_everyone.webp deleted file mode 100644 index bf0ea6cf64..0000000000 Binary files a/app/src/main/res/drawable-mdpi/wallet_for_everyone.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_eth.webp b/app/src/main/res/drawable-xhdpi/ic_eth.webp deleted file mode 100644 index ba04cee99d..0000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_eth.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/wallet_for_everyone.webp b/app/src/main/res/drawable-xhdpi/wallet_for_everyone.webp deleted file mode 100644 index 2eec0c7074..0000000000 Binary files a/app/src/main/res/drawable-xhdpi/wallet_for_everyone.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_eth.webp b/app/src/main/res/drawable-xxhdpi/ic_eth.webp deleted file mode 100644 index 440f1fea5c..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_eth.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/wallet_for_everyone.webp b/app/src/main/res/drawable-xxhdpi/wallet_for_everyone.webp deleted file mode 100644 index 408f94af76..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/wallet_for_everyone.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_eth.webp b/app/src/main/res/drawable-xxxhdpi/ic_eth.webp deleted file mode 100644 index 8a3417ce7b..0000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_eth.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/wallet_for_everyone.webp b/app/src/main/res/drawable-xxxhdpi/wallet_for_everyone.webp deleted file mode 100644 index 5dc7baec82..0000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/wallet_for_everyone.webp and /dev/null differ diff --git a/app/src/main/res/drawable/dapps0.webp b/app/src/main/res/drawable/dapps0.webp deleted file mode 100644 index 5a5ddd3913..0000000000 Binary files a/app/src/main/res/drawable/dapps0.webp and /dev/null differ diff --git a/app/src/main/res/drawable/ic_accept_coin.xml b/app/src/main/res/drawable/ic_accept_coin.xml deleted file mode 100644 index 6df78b4b77..0000000000 --- a/app/src/main/res/drawable/ic_accept_coin.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_add.xml b/app/src/main/res/drawable/ic_add.xml deleted file mode 100644 index 9b975701fe..0000000000 --- a/app/src/main/res/drawable/ic_add.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_angle_bracket_right.xml b/app/src/main/res/drawable/ic_angle_bracket_right.xml deleted file mode 100644 index 1cfb0d7374..0000000000 --- a/app/src/main/res/drawable/ic_angle_bracket_right.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_arrow_angle_down.xml b/app/src/main/res/drawable/ic_arrow_angle_down.xml deleted file mode 100644 index e773e9862f..0000000000 --- a/app/src/main/res/drawable/ic_arrow_angle_down.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_arrow_angle_right.xml b/app/src/main/res/drawable/ic_arrow_angle_right.xml deleted file mode 100644 index c009fd11a9..0000000000 --- a/app/src/main/res/drawable/ic_arrow_angle_right.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_arrow_down.xml b/app/src/main/res/drawable/ic_arrow_down.xml deleted file mode 100644 index 9e169488af..0000000000 --- a/app/src/main/res/drawable/ic_arrow_down.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/ic_arrow_left.xml b/app/src/main/res/drawable/ic_arrow_left.xml deleted file mode 100644 index c9d25ccc99..0000000000 --- a/app/src/main/res/drawable/ic_arrow_left.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_arrow_right_20.xml b/app/src/main/res/drawable/ic_arrow_right_20.xml deleted file mode 100644 index b10fdaf7c6..0000000000 --- a/app/src/main/res/drawable/ic_arrow_right_20.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_arrow_up.xml b/app/src/main/res/drawable/ic_arrow_up.xml deleted file mode 100644 index 34f1d028fe..0000000000 --- a/app/src/main/res/drawable/ic_arrow_up.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/ic_check_circle_18.xml b/app/src/main/res/drawable/ic_check_circle_18.xml deleted file mode 100644 index 2c3e494f73..0000000000 --- a/app/src/main/res/drawable/ic_check_circle_18.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_connecting_socket.xml b/app/src/main/res/drawable/ic_connecting_socket.xml deleted file mode 100644 index a07fbe5515..0000000000 --- a/app/src/main/res/drawable/ic_connecting_socket.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_copy.xml b/app/src/main/res/drawable/ic_copy.xml deleted file mode 100644 index db40f4b5b9..0000000000 --- a/app/src/main/res/drawable/ic_copy.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_copy_chip.xml b/app/src/main/res/drawable/ic_copy_chip.xml deleted file mode 100644 index 83325e0952..0000000000 --- a/app/src/main/res/drawable/ic_copy_chip.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_currency.xml b/app/src/main/res/drawable/ic_currency.xml deleted file mode 100644 index 7508a16997..0000000000 --- a/app/src/main/res/drawable/ic_currency.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_in_progress.xml b/app/src/main/res/drawable/ic_in_progress.xml deleted file mode 100644 index 17f814a06d..0000000000 --- a/app/src/main/res/drawable/ic_in_progress.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/ic_inactive.xml b/app/src/main/res/drawable/ic_inactive.xml deleted file mode 100644 index eb19f0ce6d..0000000000 --- a/app/src/main/res/drawable/ic_inactive.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_near_no_color.xml b/app/src/main/res/drawable/ic_near_no_color.xml deleted file mode 100644 index 573a4ef9be..0000000000 --- a/app/src/main/res/drawable/ic_near_no_color.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_ok.xml b/app/src/main/res/drawable/ic_ok.xml deleted file mode 100644 index adc3c59467..0000000000 --- a/app/src/main/res/drawable/ic_ok.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_overlay.xml b/app/src/main/res/drawable/ic_overlay.xml deleted file mode 100644 index 5487da88f8..0000000000 --- a/app/src/main/res/drawable/ic_overlay.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_passport.xml b/app/src/main/res/drawable/ic_passport.xml deleted file mode 100644 index e793f05866..0000000000 --- a/app/src/main/res/drawable/ic_passport.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/drawable/ic_payid.xml b/app/src/main/res/drawable/ic_payid.xml deleted file mode 100644 index 822e97afc9..0000000000 --- a/app/src/main/res/drawable/ic_payid.xml +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_pencil_outline_24.xml b/app/src/main/res/drawable/ic_pencil_outline_24.xml deleted file mode 100644 index 039a5d5463..0000000000 --- a/app/src/main/res/drawable/ic_pencil_outline_24.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_promo_code.xml b/app/src/main/res/drawable/ic_promo_code.xml deleted file mode 100644 index 149d59df21..0000000000 --- a/app/src/main/res/drawable/ic_promo_code.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/ic_reject.xml b/app/src/main/res/drawable/ic_reject.xml deleted file mode 100644 index 776aa18595..0000000000 --- a/app/src/main/res/drawable/ic_reject.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/ic_scan_card.xml b/app/src/main/res/drawable/ic_scan_card.xml deleted file mode 100644 index d190690243..0000000000 --- a/app/src/main/res/drawable/ic_scan_card.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_search.xml b/app/src/main/res/drawable/ic_search.xml deleted file mode 100644 index d698df7efe..0000000000 --- a/app/src/main/res/drawable/ic_search.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/ic_send.xml b/app/src/main/res/drawable/ic_send.xml deleted file mode 100644 index e0329e1862..0000000000 --- a/app/src/main/res/drawable/ic_send.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_shipping.xml b/app/src/main/res/drawable/ic_shipping.xml deleted file mode 100644 index b36ffe1bed..0000000000 --- a/app/src/main/res/drawable/ic_shipping.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_tangem_warning.xml b/app/src/main/res/drawable/ic_tangem_warning.xml deleted file mode 100644 index aa1e0c73ee..0000000000 --- a/app/src/main/res/drawable/ic_tangem_warning.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - diff --git a/app/src/main/res/drawable/ic_trash.xml b/app/src/main/res/drawable/ic_trash.xml deleted file mode 100644 index 38e732373b..0000000000 --- a/app/src/main/res/drawable/ic_trash.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_tx_explore.xml b/app/src/main/res/drawable/ic_tx_explore.xml deleted file mode 100644 index 0c0c7403f5..0000000000 --- a/app/src/main/res/drawable/ic_tx_explore.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_tx_incoming.xml b/app/src/main/res/drawable/ic_tx_incoming.xml deleted file mode 100644 index 1f8d16c0ff..0000000000 --- a/app/src/main/res/drawable/ic_tx_incoming.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_tx_outgoing.xml b/app/src/main/res/drawable/ic_tx_outgoing.xml deleted file mode 100644 index e6b391e594..0000000000 --- a/app/src/main/res/drawable/ic_tx_outgoing.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_wallet_24.xml b/app/src/main/res/drawable/ic_wallet_24.xml deleted file mode 100644 index b1c1514913..0000000000 --- a/app/src/main/res/drawable/ic_wallet_24.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_warning.xml b/app/src/main/res/drawable/ic_warning.xml deleted file mode 100644 index 81f2751c0c..0000000000 --- a/app/src/main/res/drawable/ic_warning.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/ic_warning_small.xml b/app/src/main/res/drawable/ic_warning_small.xml deleted file mode 100644 index a42413f750..0000000000 --- a/app/src/main/res/drawable/ic_warning_small.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - diff --git a/app/src/main/res/drawable/img_warning_triangle_24.xml b/app/src/main/res/drawable/img_warning_triangle_24.xml deleted file mode 100644 index c54d7d8f8b..0000000000 --- a/app/src/main/res/drawable/img_warning_triangle_24.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/shape_badge_custom_currency.xml b/app/src/main/res/drawable/shape_badge_custom_currency.xml deleted file mode 100644 index cfd740103d..0000000000 --- a/app/src/main/res/drawable/shape_badge_custom_currency.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - diff --git a/app/src/main/res/drawable/shape_chip.xml b/app/src/main/res/drawable/shape_chip.xml deleted file mode 100644 index 658e94c9a7..0000000000 --- a/app/src/main/res/drawable/shape_chip.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/card_balance.xml b/app/src/main/res/layout/card_balance.xml deleted file mode 100644 index 051fdb2e53..0000000000 --- a/app/src/main/res/layout/card_balance.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/card_total_balance_shimmer.xml b/app/src/main/res/layout/card_total_balance_shimmer.xml deleted file mode 100644 index 76dfc5e5a3..0000000000 --- a/app/src/main/res/layout/card_total_balance_shimmer.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/app/src/main/res/layout/dialog_onboarding_address_info.xml b/app/src/main/res/layout/dialog_onboarding_address_info.xml index b4a422e77b..258b3fbbcc 100644 --- a/app/src/main/res/layout/dialog_onboarding_address_info.xml +++ b/app/src/main/res/layout/dialog_onboarding_address_info.xml @@ -129,4 +129,4 @@ app:layout_constraintBaseline_toBaselineOf="parent" app:layout_constraintTop_toBottomOf="@+id/btn_fl_copy_address" /> - \ No newline at end of file + diff --git a/app/src/main/res/layout/dialog_wallet_send.xml b/app/src/main/res/layout/dialog_wallet_send.xml deleted file mode 100644 index d204d38de3..0000000000 --- a/app/src/main/res/layout/dialog_wallet_send.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_wallet_trade.xml b/app/src/main/res/layout/dialog_wallet_trade.xml deleted file mode 100644 index 96dd6d34c1..0000000000 --- a/app/src/main/res/layout/dialog_wallet_trade.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_wallet.xml b/app/src/main/res/layout/fragment_wallet.xml deleted file mode 100644 index 22df751c87..0000000000 --- a/app/src/main/res/layout/fragment_wallet.xml +++ /dev/null @@ -1,250 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/fragment_wallet_details.xml b/app/src/main/res/layout/fragment_wallet_details.xml deleted file mode 100644 index f1cdf9a73a..0000000000 --- a/app/src/main/res/layout/fragment_wallet_details.xml +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/item_currency_wallet.xml b/app/src/main/res/layout/item_currency_wallet.xml deleted file mode 100644 index 0c6ef1fa3f..0000000000 --- a/app/src/main/res/layout/item_currency_wallet.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/item_currency_wallet_content.xml b/app/src/main/res/layout/item_currency_wallet_content.xml deleted file mode 100644 index 4bb15bb54b..0000000000 --- a/app/src/main/res/layout/item_currency_wallet_content.xml +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/item_currency_wallet_shimmer.xml b/app/src/main/res/layout/item_currency_wallet_shimmer.xml deleted file mode 100644 index a37bd8c956..0000000000 --- a/app/src/main/res/layout/item_currency_wallet_shimmer.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/item_pending_transaction.xml b/app/src/main/res/layout/item_pending_transaction.xml deleted file mode 100644 index c514bfc677..0000000000 --- a/app/src/main/res/layout/item_pending_transaction.xml +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/item_wallet_amount_to_send.xml b/app/src/main/res/layout/item_wallet_amount_to_send.xml deleted file mode 100644 index c225c828e4..0000000000 --- a/app/src/main/res/layout/item_wallet_amount_to_send.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/layout_address.xml b/app/src/main/res/layout/layout_address.xml deleted file mode 100644 index 713c384939..0000000000 --- a/app/src/main/res/layout/layout_address.xml +++ /dev/null @@ -1,157 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/layout_balance.xml b/app/src/main/res/layout/layout_balance.xml deleted file mode 100644 index 4141bd7302..0000000000 --- a/app/src/main/res/layout/layout_balance.xml +++ /dev/null @@ -1,196 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/layout_balance_error.xml b/app/src/main/res/layout/layout_balance_error.xml deleted file mode 100644 index 1341d89e1f..0000000000 --- a/app/src/main/res/layout/layout_balance_error.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - diff --git a/app/src/main/res/layout/layout_balance_wallet_details.xml b/app/src/main/res/layout/layout_balance_wallet_details.xml deleted file mode 100644 index 9c4656d92e..0000000000 --- a/app/src/main/res/layout/layout_balance_wallet_details.xml +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/layout_qr_scanning.xml b/app/src/main/res/layout/layout_qr_scanning.xml deleted file mode 100644 index 964d9acae6..0000000000 --- a/app/src/main/res/layout/layout_qr_scanning.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/layout_send_fee.xml b/app/src/main/res/layout/layout_send_fee.xml index bba30587f9..66c3b128f1 100644 --- a/app/src/main/res/layout/layout_send_fee.xml +++ b/app/src/main/res/layout/layout_send_fee.xml @@ -12,7 +12,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" - android:text="@string/send_network_fee_title" + android:text="@string/common_network_fee_title" android:textSize="14sp" app:layout_constraintBottom_toBottomOf="@+id/flExpandCollapse" app:layout_constraintStart_toStartOf="parent" diff --git a/app/src/main/res/layout/layout_single_wallet_balance.xml b/app/src/main/res/layout/layout_single_wallet_balance.xml deleted file mode 100644 index d3c6de4ff3..0000000000 --- a/app/src/main/res/layout/layout_single_wallet_balance.xml +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/layout_wallet_backup_warning.xml b/app/src/main/res/layout/layout_wallet_backup_warning.xml deleted file mode 100644 index 4fb8f8360e..0000000000 --- a/app/src/main/res/layout/layout_wallet_backup_warning.xml +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/layout_wallet_details.xml b/app/src/main/res/layout/layout_wallet_details.xml deleted file mode 100644 index afff5b42e5..0000000000 --- a/app/src/main/res/layout/layout_wallet_details.xml +++ /dev/null @@ -1,183 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/layout_wallet_rescan_warning.xml b/app/src/main/res/layout/layout_wallet_rescan_warning.xml deleted file mode 100644 index c795c2c22a..0000000000 --- a/app/src/main/res/layout/layout_wallet_rescan_warning.xml +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/layout_warning.xml b/app/src/main/res/layout/layout_warning.xml index 7ba7e096ef..9c19796c5b 100644 --- a/app/src/main/res/layout/layout_warning.xml +++ b/app/src/main/res/layout/layout_warning.xml @@ -35,7 +35,6 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tv_title" app:layout_constraintVertical_bias="0.0" - app:lineHeight="18dp" - tools:text="@string/lorem_ipsum" /> + app:lineHeight="18dp" /> diff --git a/app/src/main/res/layout/layout_warning_card.xml b/app/src/main/res/layout/layout_warning_card.xml deleted file mode 100644 index e1fca38ee8..0000000000 --- a/app/src/main/res/layout/layout_warning_card.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/layout_warning_card_action.xml b/app/src/main/res/layout/layout_warning_card_action.xml index bbb58fab01..3b9177f441 100644 --- a/app/src/main/res/layout/layout_warning_card_action.xml +++ b/app/src/main/res/layout/layout_warning_card_action.xml @@ -45,19 +45,6 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="@+id/warning_content_container" /> - - - - - - - - - - - - -