From fd128f3bd784f4e25ce50312711c3e3f775e4ff7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 3 Jun 2024 14:09:13 +0400 Subject: [PATCH 1/5] Updated on 2026-08-14 --- .../com/tangem/tap/proxy/AppStateHolder.kt | 6 ++ .../tangem/domain/redux/ReduxStateHolder.kt | 2 + .../features/details/DetailsEntryPoint.kt | 5 ++ features/details/impl/build.gradle.kts | 4 ++ .../features/details/DetailsFragment.kt | 51 ++++++++++++++++- .../tangem/features/details/DetailsRouter.kt | 55 +++++++++++++++++++ .../details/component/DetailsComponent.kt | 3 + .../preview/PreviewDetailsComponent.kt | 12 +++- .../component/preview/PreviewRouter.kt | 18 ++++++ .../features/details/routing/DetailsRoute.kt | 20 +++++++ .../features/details/ui/DetailsScreen.kt | 9 ++- .../features/details/utils/ItemsBuilder.kt | 20 +++++-- .../features/details/utils/SocialsBuilder.kt | 12 ++-- features/wallet/impl/build.gradle.kts | 1 + .../router/DefaultWalletRouter.kt | 12 +++- .../presentation/router/InnerWalletRouter.kt | 2 +- .../intents/WalletContentClickIntents.kt | 2 +- 17 files changed, 215 insertions(+), 19 deletions(-) create mode 100644 features/details/impl/src/main/kotlin/com/tangem/features/details/DetailsRouter.kt create mode 100644 features/details/impl/src/main/kotlin/com/tangem/features/details/component/preview/PreviewRouter.kt create mode 100644 features/details/impl/src/main/kotlin/com/tangem/features/details/routing/DetailsRoute.kt 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 5faa769457..cf7de3bfe1 100644 --- a/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt +++ b/app/src/main/java/com/tangem/tap/proxy/AppStateHolder.kt @@ -10,7 +10,9 @@ import com.tangem.domain.wallets.models.UserWallet 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.feedback.FeedbackEmail import com.tangem.tap.common.redux.AppState +import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.sdk.TangemSdkManager import com.tangem.tap.network.exchangeServices.ExchangeService import org.rekotlin.Action @@ -54,4 +56,8 @@ class AppStateHolder @Inject constructor() : ReduxNavController, ReduxStateHolde override suspend fun onUserWalletSelected(userWallet: UserWallet) { mainStore?.onUserWalletSelected(userWallet) } + + override fun sendFeedbackEmail() { + mainStore?.dispatch(GlobalAction.SendEmail(FeedbackEmail())) + } } \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/redux/ReduxStateHolder.kt b/domain/legacy/src/main/java/com/tangem/domain/redux/ReduxStateHolder.kt index 58c0bb0dde..80e03d9347 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/redux/ReduxStateHolder.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/redux/ReduxStateHolder.kt @@ -10,4 +10,6 @@ interface ReduxStateHolder { suspend fun dispatchWithMain(action: Action) suspend fun onUserWalletSelected(userWallet: UserWallet) + + fun sendFeedbackEmail() } \ No newline at end of file diff --git a/features/details/api/src/main/kotlin/com/tangem/features/details/DetailsEntryPoint.kt b/features/details/api/src/main/kotlin/com/tangem/features/details/DetailsEntryPoint.kt index f2cf124e1c..d3ac9fe6f7 100644 --- a/features/details/api/src/main/kotlin/com/tangem/features/details/DetailsEntryPoint.kt +++ b/features/details/api/src/main/kotlin/com/tangem/features/details/DetailsEntryPoint.kt @@ -5,4 +5,9 @@ import androidx.fragment.app.Fragment interface DetailsEntryPoint { fun entryFragment(): Fragment + + companion object { + + const val USER_WALLET_ID_KEY = "user_wallet_id" + } } \ No newline at end of file diff --git a/features/details/impl/build.gradle.kts b/features/details/impl/build.gradle.kts index 4f9498a097..5060249621 100644 --- a/features/details/impl/build.gradle.kts +++ b/features/details/impl/build.gradle.kts @@ -15,14 +15,18 @@ dependencies { /* Project - API */ implementation(projects.features.details.api) + implementation(projects.features.tester.api) /* Project - Core */ implementation(projects.core.decompose) implementation(projects.core.ui) implementation(projects.core.featuretoggles) + implementation(projects.core.navigation) + implementation(projects.core.analytics.models) /* Project - Domain */ implementation(projects.domain.wallets.models) + implementation(projects.domain.legacy) /* AndroidX */ implementation(deps.androidx.fragment.ktx) diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/DetailsFragment.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/DetailsFragment.kt index 6e6b812683..28db59fc3d 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/DetailsFragment.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/DetailsFragment.kt @@ -3,8 +3,13 @@ package com.tangem.features.details import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.fragment.app.Fragment +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.di.RootAppComponentContext import com.tangem.core.ui.UiDependencies +import com.tangem.core.ui.message.EventMessageEffect +import com.tangem.core.ui.message.EventMessageHandler import com.tangem.core.ui.screen.ComposeFragment +import com.tangem.features.details.component.DetailsComponent import com.tangem.features.details.component.preview.PreviewDetailsComponent import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject @@ -16,10 +21,52 @@ internal class DetailsFragment : ComposeFragment() { @Inject override lateinit var uiDependencies: UiDependencies + // @Inject + // internal lateinit var componentFactory: DetailsComponent.Factory + + @Inject + internal lateinit var detailsRouter: DetailsRouter + + @Inject + @RootAppComponentContext + internal lateinit var rootContext: AppComponentContext + + private val component: DetailsComponent by lazy { initComponent() } + + private val messageHandler = EventMessageHandler() + @Composable override fun ScreenContent(modifier: Modifier) { - // TODO: Update to use the actual component in [REDACTED_TASK_KEY] - PreviewDetailsComponent().View(modifier = modifier) + component.View(modifier = modifier) + + EventMessageEffect( + messageHandler = messageHandler, + snackbarHostState = component.snackbarHostState, + ) + } + + private fun initComponent(): DetailsComponent { + // TODO: Uncomment in [REDACTED_JIRA] + // val selectedUserWalletId = arguments?.getString(DetailsEntryPoint.USER_WALLET_ID_KEY) + // ?.let(::UserWalletId) + // + // + // requireNotNull(selectedUserWalletId) { "UserWalletId must be provided" } + // + // val context = rootContext.childByContext( + // componentContext = defaultComponentContext(requireActivity().onBackPressedDispatcher), + // messageHandler = messageHandler, + // router = detailsRouter, + // ) + // + // return componentFactory.create( + // context = context, + // params = DetailsComponent.Params( + // selectedUserWalletId = selectedUserWalletId, + // ), + // ) + + return PreviewDetailsComponent() } companion object : DetailsEntryPoint { diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/DetailsRouter.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/DetailsRouter.kt new file mode 100644 index 0000000000..fbd703a49a --- /dev/null +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/DetailsRouter.kt @@ -0,0 +1,55 @@ +package com.tangem.features.details + +import com.tangem.core.decompose.navigation.Route +import com.tangem.core.decompose.navigation.Router +import com.tangem.core.navigation.NavigationAction +import com.tangem.core.navigation.ReduxNavController +import com.tangem.domain.redux.ReduxStateHolder +import com.tangem.features.details.routing.DetailsRoute +import com.tangem.features.tester.api.TesterRouter +import javax.inject.Inject + +// TODO: Remove after [REDACTED_JIRA] +internal class DetailsRouter @Inject constructor( + private val reduxNavController: ReduxNavController, + private val reduxStateHolder: ReduxStateHolder, + private val testerRouter: TesterRouter, +) : Router { + + override fun push(route: Route, onComplete: (isSuccess: Boolean) -> Unit) { + if (route is DetailsRoute) { + when (route) { + is DetailsRoute.Screen -> { + reduxNavController.navigate(NavigationAction.NavigateTo(route.screen, bundle = route.params)) + } + is DetailsRoute.Feedback -> { + reduxStateHolder.sendFeedbackEmail() + } + is DetailsRoute.TesterMenu -> { + testerRouter.startTesterScreen() + } + is DetailsRoute.Url -> { + reduxNavController.navigate(NavigationAction.OpenUrl(route.url)) + } + } + onComplete(true) + } else { + onComplete(false) + } + } + + override fun pop(onComplete: (isSuccess: Boolean) -> Unit) { + reduxNavController.popBackStack() + onComplete(true) + } + + override fun popTo(route: Route, onComplete: (isSuccess: Boolean) -> Unit) { + if (route is DetailsRoute.Screen) { + reduxNavController.popBackStack(route.screen) + onComplete(true) + } else { + reduxNavController.getBackStack() + onComplete(false) + } + } +} \ No newline at end of file diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/component/DetailsComponent.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/component/DetailsComponent.kt index fbe165d89e..9731597b9b 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/component/DetailsComponent.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/component/DetailsComponent.kt @@ -1,5 +1,6 @@ package com.tangem.features.details.component +import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.tangem.core.decompose.context.AppComponentContext @@ -7,6 +8,8 @@ import com.tangem.domain.wallets.models.UserWalletId interface DetailsComponent { + val snackbarHostState: SnackbarHostState + @Composable @Suppress("TopLevelComposableFunctions") // TODO: Remove this check fun View(modifier: Modifier) diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/component/preview/PreviewDetailsComponent.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/component/preview/PreviewDetailsComponent.kt index 59a69914fd..9c6aff4521 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/component/preview/PreviewDetailsComponent.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/component/preview/PreviewDetailsComponent.kt @@ -1,5 +1,6 @@ package com.tangem.features.details.component.preview +import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.tangem.features.details.component.DetailsComponent @@ -12,15 +13,18 @@ import kotlinx.coroutines.runBlocking internal class PreviewDetailsComponent : DetailsComponent { + override val snackbarHostState: SnackbarHostState = SnackbarHostState() + private val previewBlocks = runBlocking { ItemsBuilder( walletConnectComponent = PreviewWalletConnectComponent(), userWalletListComponent = PreviewUserWalletListComponent(), + router = PreviewRouter(), ).buldAll() } private val previewFooter = DetailsFooterUM( - socials = SocialsBuilder(urlOpener = { /* no-op */ }).buildAll(), + socials = SocialsBuilder(PreviewRouter()).buildAll(), appVersion = "1.0.0-preview", ) @@ -33,6 +37,10 @@ internal class PreviewDetailsComponent : DetailsComponent { @Composable @Suppress("TopLevelComposableFunctions") // TODO: Remove this check override fun View(modifier: Modifier) { - DetailsScreen(state = previewState, modifier = modifier) + DetailsScreen( + modifier = modifier, + state = previewState, + snackbarHostState = snackbarHostState, + ) } } \ No newline at end of file diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/component/preview/PreviewRouter.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/component/preview/PreviewRouter.kt new file mode 100644 index 0000000000..49366db923 --- /dev/null +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/component/preview/PreviewRouter.kt @@ -0,0 +1,18 @@ +package com.tangem.features.details.component.preview + +import com.tangem.core.decompose.navigation.Route +import com.tangem.core.decompose.navigation.Router + +internal class PreviewRouter : Router { + override fun push(route: Route, onComplete: (isSuccess: Boolean) -> Unit) { + /* no-op */ + } + + override fun pop(onComplete: (isSuccess: Boolean) -> Unit) { + /* no-op */ + } + + override fun popTo(route: Route, onComplete: (isSuccess: Boolean) -> Unit) { + /* no-op */ + } +} \ No newline at end of file diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/routing/DetailsRoute.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/routing/DetailsRoute.kt new file mode 100644 index 0000000000..878987aca5 --- /dev/null +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/routing/DetailsRoute.kt @@ -0,0 +1,20 @@ +package com.tangem.features.details.routing + +import android.os.Bundle +import com.tangem.core.decompose.navigation.Route +import com.tangem.core.navigation.AppScreen + +// TODO: Remove after [REDACTED_JIRA] +internal sealed class DetailsRoute : Route { + + data class Screen( + val screen: AppScreen, + val params: Bundle? = null, + ) : DetailsRoute() + + data class Url(val url: String) : DetailsRoute() + + data object Feedback : DetailsRoute() + + data object TesterMenu : DetailsRoute() +} \ No newline at end of file diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/ui/DetailsScreen.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/ui/DetailsScreen.kt index ff67ebbd9e..0b03305816 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/ui/DetailsScreen.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/ui/DetailsScreen.kt @@ -19,6 +19,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import com.tangem.core.ui.components.SystemBarsEffect +import com.tangem.core.ui.components.snackbar.TangemSnackbarHost import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.features.details.component.preview.PreviewDetailsComponent @@ -31,7 +32,7 @@ private const val COLLAPSED_APP_BAR_THRESHOLD = 0.4f @OptIn(ExperimentalMaterial3Api::class) @Composable -internal fun DetailsScreen(state: DetailsUM, modifier: Modifier = Modifier) { +internal fun DetailsScreen(state: DetailsUM, snackbarHostState: SnackbarHostState, modifier: Modifier = Modifier) { val backgroundColor = TangemTheme.colors.background.secondary val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() @@ -44,6 +45,12 @@ internal fun DetailsScreen(state: DetailsUM, modifier: Modifier = Modifier) { Scaffold( modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection), containerColor = backgroundColor, + snackbarHost = { + TangemSnackbarHost( + modifier = Modifier.padding(all = TangemTheme.dimens.spacing16), + hostState = snackbarHostState, + ) + }, topBar = { TopBar(state, scrollBehavior) }, ) { paddingValues -> Content( diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/ItemsBuilder.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/ItemsBuilder.kt index 9ef2760ab6..9bd974e94c 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/ItemsBuilder.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/ItemsBuilder.kt @@ -1,5 +1,7 @@ package com.tangem.features.details.utils +import com.tangem.core.decompose.navigation.Router +import com.tangem.core.navigation.AppScreen import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.stringReference import com.tangem.features.details.component.UserWalletListComponent @@ -7,6 +9,7 @@ import com.tangem.features.details.component.WalletConnectComponent import com.tangem.features.details.entity.DetailsItemUM import com.tangem.features.details.impl.BuildConfig import com.tangem.features.details.impl.R +import com.tangem.features.details.routing.DetailsRoute import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList @@ -14,6 +17,7 @@ import kotlinx.collections.immutable.toImmutableList internal class ItemsBuilder( private val walletConnectComponent: WalletConnectComponent, private val userWalletListComponent: UserWalletListComponent, + private val router: Router, ) { suspend fun buldAll(): ImmutableList = buildList { @@ -37,7 +41,7 @@ internal class ItemsBuilder( } } - fun buildUserWalletListBlock(): DetailsItemUM = DetailsItemUM.Component( + private fun buildUserWalletListBlock(): DetailsItemUM = DetailsItemUM.Component( id = "user_wallet_list", content = { userWalletListComponent.View(modifier = it) @@ -51,7 +55,7 @@ internal class ItemsBuilder( id = "buy_tangem_wallet", title = stringReference("Buy Tangem Wallet"), // TODO: Move to resources in [REDACTED_TASK_KEY] iconRes = R.drawable.ic_tangem_24, - onClick = { /* [REDACTED_TODO_COMMENT] */ }, + onClick = { router.push(DetailsRoute.Url(BUY_TANGEM_URL)) }, ), ), ) @@ -63,7 +67,7 @@ internal class ItemsBuilder( id = "app_settings", title = resourceReference(R.string.app_settings_title), iconRes = R.drawable.ic_settings_24, - onClick = { /* [REDACTED_TODO_COMMENT] */ }, + onClick = { router.push(DetailsRoute.Screen(AppScreen.AppSettings)) }, ).let(::add) if (BuildConfig.TESTER_MENU_ENABLED) { @@ -71,7 +75,7 @@ internal class ItemsBuilder( id = "tester_menu", title = stringReference(value = "Tester menu"), iconRes = R.drawable.ic_alert_24, - onClick = { /* [REDACTED_TODO_COMMENT] */ }, + onClick = { router.push(DetailsRoute.TesterMenu) }, ).let(::add) } }.toImmutableList(), @@ -84,14 +88,18 @@ internal class ItemsBuilder( id = "send_feedback", title = stringReference("Send feedback"), // TODO: Move to resources in [REDACTED_TASK_KEY] iconRes = R.drawable.ic_comment_24, - onClick = { /* [REDACTED_TODO_COMMENT] */ }, + onClick = { router.push(DetailsRoute.Feedback) }, ), DetailsItemUM.Basic.Item( id = "disclaimer", title = resourceReference(R.string.disclaimer_title), iconRes = R.drawable.ic_text_24, - onClick = { /* [REDACTED_TODO_COMMENT] */ }, + onClick = { router.push(DetailsRoute.Screen(AppScreen.Disclaimer)) }, ), ), ) + + private companion object { + const val BUY_TANGEM_URL = "https://buy.tangem.com/" + } } \ No newline at end of file diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/SocialsBuilder.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/SocialsBuilder.kt index d2666a03e7..6896f1e7d0 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/SocialsBuilder.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/utils/SocialsBuilder.kt @@ -1,13 +1,15 @@ package com.tangem.features.details.utils import androidx.compose.ui.text.intl.Locale +import com.tangem.core.decompose.navigation.Router import com.tangem.features.details.entity.DetailsFooterUM import com.tangem.features.details.impl.R +import com.tangem.features.details.routing.DetailsRoute import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList internal class SocialsBuilder( - private val urlOpener: (url: String) -> Unit, + private val router: Router, ) { fun buildAll(): ImmutableList = Social.all.map { social -> @@ -21,11 +23,13 @@ internal class SocialsBuilder( private fun openUrl(social: Social) { val locale = Locale.current.region - if (locale == RUSSIA_LOCALE && social.urlRu != null) { - urlOpener(social.urlRu) + val url = if (locale == RUSSIA_LOCALE && social.urlRu != null) { + social.urlRu } else { - urlOpener(social.url) + social.url } + + router.push(DetailsRoute.Url(url)) } private enum class Social( diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts index 301b8984f8..dad4976a7d 100644 --- a/features/wallet/impl/build.gradle.kts +++ b/features/wallet/impl/build.gradle.kts @@ -87,4 +87,5 @@ dependencies { implementation(projects.features.send.api) implementation(projects.features.tester.api) implementation(projects.features.manageTokens.api) + implementation(projects.features.details.api) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index c0f4910bfe..6276cdb2e9 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -28,6 +28,7 @@ import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensScree import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensViewModel import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletViewModel +import com.tangem.features.details.DetailsEntryPoint import com.tangem.features.managetokens.navigation.ExpandableState import com.tangem.features.managetokens.navigation.ManageTokensUi import com.tangem.features.tokendetails.navigation.TokenDetailsRouter @@ -116,8 +117,15 @@ internal class DefaultWalletRouter( navController.navigate(WalletRoute.OrganizeTokens.createRoute(userWalletId)) } - override fun openDetailsScreen() { - reduxNavController.navigate(action = NavigationAction.NavigateTo(AppScreen.Details)) + override fun openDetailsScreen(selectedWalletId: UserWalletId) { + reduxNavController.navigate( + action = NavigationAction.NavigateTo( + screen = AppScreen.Details, + bundle = bundleOf( + DetailsEntryPoint.USER_WALLET_ID_KEY to selectedWalletId.stringValue, + ), + ), + ) } override fun openOnboardingScreen() { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt index a8b311b067..bf8aba542f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt @@ -35,7 +35,7 @@ internal interface InnerWalletRouter : WalletRouter { fun openOrganizeTokensScreen(userWalletId: UserWalletId) /** Open details screen */ - fun openDetailsScreen() + fun openDetailsScreen(selectedWalletId: UserWalletId) /** Open onboarding screen */ fun openOnboardingScreen() diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletContentClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletContentClickIntents.kt index cae3373a31..5c192d9a44 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletContentClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/intents/WalletContentClickIntents.kt @@ -82,7 +82,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor( ), ) } else { - router.openDetailsScreen() + router.openDetailsScreen(stateHolder.getSelectedWalletId()) } } } From 8b0f0e589edfe150efd6a81649ce653cb0da65dc Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 30 May 2024 15:48:27 +0500 Subject: [PATCH 2/5] Updated on 2026-08-14 --- .../tap/di/domain/WalletsDomainModule.kt | 7 ++- .../DefaultWalletAddressServiceRepository.kt | 4 +- .../data/wallets/di/WalletsDataModule.kt | 11 ----- .../DefaultWalletManagersFacade.kt | 11 +++++ .../walletmanager/WalletManagersFacade.kt | 3 ++ .../WalletAddressServiceRepository.kt | 2 +- .../transaction/error/ValidateAddressError.kt | 7 +++ .../usecase/ParseSharedAddressUseCase.kt | 4 +- .../usecase/ValidateWalletAddressUseCase.kt | 43 +++++++++++++++++++ .../usecase/ValidateWalletMemoUseCase.kt | 4 +- .../usecase/ValidateWalletAddressUseCase.kt | 27 ------------ 11 files changed, 77 insertions(+), 46 deletions(-) rename data/{wallets/src/main/java/com/tangem/data/wallets => transaction/src/main/java/com/tangem/data/transaction}/DefaultWalletAddressServiceRepository.kt (97%) rename domain/{wallets/src/main/java/com/tangem/domain/wallets/repository => transaction/src/main/java/com/tangem/domain/transaction}/WalletAddressServiceRepository.kt (91%) create mode 100644 domain/transaction/src/main/java/com/tangem/domain/transaction/error/ValidateAddressError.kt rename domain/{wallets/src/main/java/com/tangem/domain/wallets => transaction/src/main/java/com/tangem/domain/transaction}/usecase/ParseSharedAddressUseCase.kt (85%) create mode 100644 domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/ValidateWalletAddressUseCase.kt rename domain/{wallets/src/main/java/com/tangem/domain/wallets => transaction/src/main/java/com/tangem/domain/transaction}/usecase/ValidateWalletMemoUseCase.kt (77%) delete mode 100644 domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ValidateWalletAddressUseCase.kt 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 2f5322f914..c539df5afc 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,7 +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.UserWalletsListManager -import com.tangem.domain.wallets.repository.WalletAddressServiceRepository +import com.tangem.domain.transaction.WalletAddressServiceRepository +import com.tangem.domain.transaction.usecase.ParseSharedAddressUseCase +import com.tangem.domain.transaction.usecase.ValidateWalletAddressUseCase +import com.tangem.domain.transaction.usecase.ValidateWalletMemoUseCase import com.tangem.domain.wallets.repository.WalletNamesMigrationRepository import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.domain.wallets.usecase.* @@ -126,10 +129,12 @@ internal object WalletsDomainModule { @Singleton fun providesValidateWalletAddressUseCase( walletAddressServiceRepository: WalletAddressServiceRepository, + walletManagersFacade: WalletManagersFacade, dispatchers: CoroutineDispatcherProvider, ): ValidateWalletAddressUseCase { return ValidateWalletAddressUseCase( walletAddressServiceRepository = walletAddressServiceRepository, + walletManagersFacade = walletManagersFacade, dispatchers = dispatchers, ) } diff --git a/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletAddressServiceRepository.kt b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultWalletAddressServiceRepository.kt similarity index 97% rename from data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletAddressServiceRepository.kt rename to data/transaction/src/main/java/com/tangem/data/transaction/DefaultWalletAddressServiceRepository.kt index 6831cc8ae4..00dc557554 100644 --- a/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletAddressServiceRepository.kt +++ b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultWalletAddressServiceRepository.kt @@ -1,15 +1,15 @@ -package com.tangem.data.wallets +package com.tangem.data.transaction import android.net.Uri import androidx.core.text.isDigitsOnly import com.tangem.blockchain.blockchains.near.NearWalletManager import com.tangem.blockchain.common.Blockchain import com.tangem.domain.tokens.model.Network +import com.tangem.domain.transaction.WalletAddressServiceRepository import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.models.ParsedQrCode import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.models.errors.ParsedQrCodeErrors -import com.tangem.domain.wallets.repository.WalletAddressServiceRepository import java.math.BigInteger class DefaultWalletAddressServiceRepository( diff --git a/data/wallets/src/main/java/com/tangem/data/wallets/di/WalletsDataModule.kt b/data/wallets/src/main/java/com/tangem/data/wallets/di/WalletsDataModule.kt index 9748691618..c41bb99652 100644 --- a/data/wallets/src/main/java/com/tangem/data/wallets/di/WalletsDataModule.kt +++ b/data/wallets/src/main/java/com/tangem/data/wallets/di/WalletsDataModule.kt @@ -1,12 +1,9 @@ package com.tangem.data.wallets.di import com.tangem.data.wallets.DefaultWalletNamesMigrationRepository -import com.tangem.data.wallets.DefaultWalletAddressServiceRepository import com.tangem.data.wallets.DefaultWalletsRepository import com.tangem.datasource.local.preferences.AppPreferencesStore -import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.repository.WalletNamesMigrationRepository -import com.tangem.domain.wallets.repository.WalletAddressServiceRepository import com.tangem.domain.wallets.repository.WalletsRepository import dagger.Module import dagger.Provides @@ -24,14 +21,6 @@ internal object WalletsDataModule { return DefaultWalletsRepository(appPreferencesStore = appPreferencesStore) } - @Provides - @Singleton - fun providesWalletAddressServiceRepository( - walletManagersFacade: WalletManagersFacade, - ): WalletAddressServiceRepository { - return DefaultWalletAddressServiceRepository(walletManagersFacade) - } - @Provides @Singleton fun provideMigrateNamesRepository(appPreferencesStore: AppPreferencesStore): WalletNamesMigrationRepository { diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt index c15246cb89..205ea91ce0 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt @@ -611,6 +611,17 @@ class DefaultWalletManagersFacade( return walletManager.fulfillRequirements(currencyType, signer) } + override suspend fun checkUtxoConsolidationAvailability(userWalletId: UserWalletId, network: Network): Boolean { + val blockchain = Blockchain.fromId(network.id.value) + val walletManager = getOrCreateWalletManager( + userWalletId = userWalletId, + blockchain = blockchain, + derivationPath = network.derivationPath.value, + ) ?: return false + + return walletManager.allowUtxoConsolidation + } + private fun updateWalletManagerTokensIfNeeded(walletManager: WalletManager, tokens: Set) { if (tokens.isEmpty()) return diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt index 5c80c0db3d..14f5fa53fa 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt @@ -253,4 +253,7 @@ interface WalletManagersFacade { currency: CryptoCurrency, signer: CommonSigner, ): SimpleResult + + /** Indicates UTXO consolidation availability */ + suspend fun checkUtxoConsolidationAvailability(userWalletId: UserWalletId, network: Network): Boolean } \ No newline at end of file diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletAddressServiceRepository.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/WalletAddressServiceRepository.kt similarity index 91% rename from domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletAddressServiceRepository.kt rename to domain/transaction/src/main/java/com/tangem/domain/transaction/WalletAddressServiceRepository.kt index 624ad44419..8d09e3b248 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletAddressServiceRepository.kt +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/WalletAddressServiceRepository.kt @@ -1,4 +1,4 @@ -package com.tangem.domain.wallets.repository +package com.tangem.domain.transaction import com.tangem.domain.tokens.model.Network import com.tangem.domain.wallets.models.ParsedQrCode diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/error/ValidateAddressError.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/error/ValidateAddressError.kt new file mode 100644 index 0000000000..75262c24e2 --- /dev/null +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/error/ValidateAddressError.kt @@ -0,0 +1,7 @@ +package com.tangem.domain.transaction.error + +sealed class ValidateAddressError { + data object AddressInWallet : ValidateAddressError() + data object InvalidAddress : ValidateAddressError() + data class DataError(val throwable: Throwable) : ValidateAddressError() +} \ No newline at end of file diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ParseSharedAddressUseCase.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/ParseSharedAddressUseCase.kt similarity index 85% rename from domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ParseSharedAddressUseCase.kt rename to domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/ParseSharedAddressUseCase.kt index 8d03b6ab6d..eb5f1e1c89 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ParseSharedAddressUseCase.kt +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/ParseSharedAddressUseCase.kt @@ -1,9 +1,9 @@ -package com.tangem.domain.wallets.usecase +package com.tangem.domain.transaction.usecase import arrow.core.Either import com.tangem.domain.tokens.model.Network +import com.tangem.domain.transaction.WalletAddressServiceRepository import com.tangem.domain.wallets.models.ParsedQrCode -import com.tangem.domain.wallets.repository.WalletAddressServiceRepository import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.withContext diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/ValidateWalletAddressUseCase.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/ValidateWalletAddressUseCase.kt new file mode 100644 index 0000000000..9d9de200e8 --- /dev/null +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/ValidateWalletAddressUseCase.kt @@ -0,0 +1,43 @@ +package com.tangem.domain.transaction.usecase + +import arrow.core.Either +import arrow.core.left +import arrow.core.right +import com.tangem.domain.tokens.model.Network +import com.tangem.domain.tokens.model.NetworkAddress +import com.tangem.domain.transaction.WalletAddressServiceRepository +import com.tangem.domain.transaction.error.ValidateAddressError +import com.tangem.domain.walletmanager.WalletManagersFacade +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.withContext + +/** + * Use case for validating wallet address. + */ +class ValidateWalletAddressUseCase( + private val walletAddressServiceRepository: WalletAddressServiceRepository, + private val walletManagersFacade: WalletManagersFacade, + private val dispatchers: CoroutineDispatcherProvider, +) { + + suspend operator fun invoke( + userWalletId: UserWalletId, + network: Network, + address: String, + currencyAddress: Set?, + ): Either = withContext(dispatchers.io) { + val isUtxoConsolidationAvailable = + walletManagersFacade.checkUtxoConsolidationAvailability(userWalletId, network) + val isCurrentAddress = currencyAddress?.any { it.value == address } ?: true + + val isForbidSelfSend = isCurrentAddress && !isUtxoConsolidationAvailable + val isValidAddress = walletAddressServiceRepository.validateAddress(userWalletId, network, address) + + when { + !isValidAddress -> ValidateAddressError.InvalidAddress.left() + isForbidSelfSend -> ValidateAddressError.AddressInWallet.left() + else -> Unit.right() + } + } +} \ No newline at end of file diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ValidateWalletMemoUseCase.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/ValidateWalletMemoUseCase.kt similarity index 77% rename from domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ValidateWalletMemoUseCase.kt rename to domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/ValidateWalletMemoUseCase.kt index 754819852b..d4d5ab22e8 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ValidateWalletMemoUseCase.kt +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/ValidateWalletMemoUseCase.kt @@ -1,8 +1,8 @@ -package com.tangem.domain.wallets.usecase +package com.tangem.domain.transaction.usecase import arrow.core.Either import com.tangem.domain.tokens.model.Network -import com.tangem.domain.wallets.repository.WalletAddressServiceRepository +import com.tangem.domain.transaction.WalletAddressServiceRepository /** * Use case for validating wallet memo. diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ValidateWalletAddressUseCase.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ValidateWalletAddressUseCase.kt deleted file mode 100644 index 149b12030d..0000000000 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ValidateWalletAddressUseCase.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.tangem.domain.wallets.usecase - -import arrow.core.Either -import com.tangem.domain.tokens.model.Network -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.domain.wallets.repository.WalletAddressServiceRepository -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.withContext - -/** - * Use case for validating wallet address. - */ -class ValidateWalletAddressUseCase( - private val walletAddressServiceRepository: WalletAddressServiceRepository, - private val dispatchers: CoroutineDispatcherProvider, -) { - - suspend operator fun invoke( - userWalletId: UserWalletId, - network: Network, - address: String, - ): Either = withContext(dispatchers.io) { - Either.catch { - walletAddressServiceRepository.validateAddress(userWalletId, network, address) - } - } -} \ No newline at end of file From f94370c41c67fd0d72e1db923e8774d6198d0cf9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 3 Jun 2024 15:03:36 +0500 Subject: [PATCH 3/5] Updated on 2026-08-14 --- .../tap/di/domain/TransactionDomainModule.kt | 26 ++++--- .../transaction/di/TransactionDataModule.kt | 10 +++ .../DefaultWalletManagersFacade.kt | 4 +- .../IsUtxoConsolidationAvailableUseCase.kt | 19 +++++ .../state/recipient/RecipientSendFactory.kt | 47 +++++++----- .../SendRecipientWalletListConverter.kt | 20 ++++- .../presentation/viewmodel/SendViewModel.kt | 75 +++++++++++-------- 7 files changed, 139 insertions(+), 62 deletions(-) create mode 100644 domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/IsUtxoConsolidationAvailableUseCase.kt 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 bb6a62fc78..9635e67a29 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 @@ -11,15 +11,15 @@ import com.tangem.domain.walletmanager.WalletManagersFacade import dagger.Module import dagger.Provides import dagger.hilt.InstallIn -import dagger.hilt.android.components.ViewModelComponent -import dagger.hilt.android.scopes.ViewModelScoped +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton @Module -@InstallIn(ViewModelComponent::class) +@InstallIn(SingletonComponent::class) internal object TransactionDomainModule { @Provides - @ViewModelScoped + @Singleton fun provideGetFeeUseCase(walletManagersFacade: WalletManagersFacade): GetFeeUseCase { return GetFeeUseCase( walletManagersFacade = walletManagersFacade, @@ -28,7 +28,7 @@ internal object TransactionDomainModule { } @Provides - @ViewModelScoped + @Singleton fun provideSendTransactionUseCase( cardSdkConfigRepository: CardSdkConfigRepository, transactionRepository: TransactionRepository, @@ -43,7 +43,7 @@ internal object TransactionDomainModule { } @Provides - @ViewModelScoped + @Singleton fun provideAssociateAssetUseCase( cardSdkConfigRepository: CardSdkConfigRepository, walletManagersFacade: WalletManagersFacade, @@ -59,20 +59,28 @@ internal object TransactionDomainModule { } @Provides - @ViewModelScoped + @Singleton fun provideCreateTransactionUseCase(transactionRepository: TransactionRepository): CreateTransactionUseCase { return CreateTransactionUseCase(transactionRepository) } @Provides - @ViewModelScoped + @Singleton fun provideIsFeeApproximateUseCase(feeRepository: FeeRepository): IsFeeApproximateUseCase { return IsFeeApproximateUseCase(feeRepository) } @Provides - @ViewModelScoped + @Singleton fun provideValidateTransactionUseCase(transactionRepository: TransactionRepository): ValidateTransactionUseCase { return ValidateTransactionUseCase(transactionRepository) } + + @Provides + @Singleton + fun provideIsUtxoConsolidationAvailableUseCase( + walletManagersFacade: WalletManagersFacade, + ): IsUtxoConsolidationAvailableUseCase { + return IsUtxoConsolidationAvailableUseCase(walletManagersFacade) + } } \ No newline at end of file diff --git a/data/transaction/src/main/java/com/tangem/data/transaction/di/TransactionDataModule.kt b/data/transaction/src/main/java/com/tangem/data/transaction/di/TransactionDataModule.kt index a6d7043d91..7a1b7db59c 100644 --- a/data/transaction/src/main/java/com/tangem/data/transaction/di/TransactionDataModule.kt +++ b/data/transaction/src/main/java/com/tangem/data/transaction/di/TransactionDataModule.kt @@ -2,9 +2,11 @@ package com.tangem.data.transaction.di import com.tangem.data.transaction.DefaultFeeRepository import com.tangem.data.transaction.DefaultTransactionRepository +import com.tangem.data.transaction.DefaultWalletAddressServiceRepository import com.tangem.datasource.local.walletmanager.WalletManagersStore import com.tangem.domain.transaction.FeeRepository import com.tangem.domain.transaction.TransactionRepository +import com.tangem.domain.transaction.WalletAddressServiceRepository import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module @@ -36,4 +38,12 @@ internal object TransactionDataModule { fun providesFeeRepository(): FeeRepository { return DefaultFeeRepository() } + + @Provides + @Singleton + fun providesWalletAddressServiceRepository( + walletManagersFacade: WalletManagersFacade, + ): WalletAddressServiceRepository { + return DefaultWalletAddressServiceRepository(walletManagersFacade) + } } \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt index 205ea91ce0..6c5f931d5e 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt @@ -13,9 +13,9 @@ import com.tangem.blockchain.common.pagination.Page import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.blockchain.common.trustlines.AssetRequirementsManager -import com.tangem.blockchain.common.txhistory.TransactionHistoryRequest import com.tangem.blockchain.extensions.Result import com.tangem.blockchain.extensions.SimpleResult +import com.tangem.blockchain.transactionhistory.models.TransactionHistoryRequest import com.tangem.blockchainsdk.BlockchainSDKFactory import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.datasource.asset.loader.AssetLoader @@ -619,7 +619,7 @@ class DefaultWalletManagersFacade( derivationPath = network.derivationPath.value, ) ?: return false - return walletManager.allowUtxoConsolidation + return (walletManager as? UtxoBlockchainManager)?.allowConsolidation == true } private fun updateWalletManagerTokensIfNeeded(walletManager: WalletManager, tokens: Set) { diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/IsUtxoConsolidationAvailableUseCase.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/IsUtxoConsolidationAvailableUseCase.kt new file mode 100644 index 0000000000..90839203b0 --- /dev/null +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/IsUtxoConsolidationAvailableUseCase.kt @@ -0,0 +1,19 @@ +package com.tangem.domain.transaction.usecase + +import com.tangem.domain.tokens.model.Network +import com.tangem.domain.walletmanager.WalletManagersFacade +import com.tangem.domain.wallets.models.UserWalletId + +/** + * Gets UTXO consolidation availability + */ +class IsUtxoConsolidationAvailableUseCase( + private val walletManagersFacade: WalletManagersFacade, +) { + + suspend fun invokeSync(userWalletId: UserWalletId, network: Network) = + walletManagersFacade.checkUtxoConsolidationAvailability( + userWalletId = userWalletId, + network = network, + ) +} \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/RecipientSendFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/RecipientSendFactory.kt index 5fcb985783..c3fdbfd71b 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/RecipientSendFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/RecipientSendFactory.kt @@ -1,11 +1,13 @@ package com.tangem.features.send.impl.presentation.state.recipient +import arrow.core.Either import arrow.core.getOrElse import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.txhistory.models.TxHistoryItem -import com.tangem.domain.wallets.usecase.ValidateWalletMemoUseCase +import com.tangem.domain.transaction.error.ValidateAddressError +import com.tangem.domain.transaction.usecase.ValidateWalletMemoUseCase import com.tangem.features.send.impl.R import com.tangem.features.send.impl.presentation.domain.AvailableWallet import com.tangem.features.send.impl.presentation.state.SendUiState @@ -18,10 +20,14 @@ internal class RecipientSendFactory( private val stateRouterProvider: Provider, private val currentStateProvider: Provider, private val cryptoCurrencyStatusProvider: Provider, + private val isUtxoConsolidationAvailableProvider: Provider, private val validateWalletMemoUseCase: ValidateWalletMemoUseCase, ) { private val recipientWalletListStateConverter by lazy(LazyThreadSafetyMode.NONE) { - SendRecipientWalletListConverter() + SendRecipientWalletListConverter( + cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider, + isUtxoConsolidationAvailableProvider = isUtxoConsolidationAvailableProvider, + ) } private val recipientHistoryListStateConverter by lazy(LazyThreadSafetyMode.NONE) { SendRecipientHistoryListConverter( @@ -60,7 +66,10 @@ internal class RecipientSendFactory( ) } - fun getOnRecipientAddressValidState(value: String, isValidAddress: Boolean): SendUiState { + fun getOnRecipientAddressValidState( + value: String, + maybeValidAddress: Either, + ): SendUiState { val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() val state = currentStateProvider() val isEditState = stateRouterProvider().isEditState @@ -73,21 +82,28 @@ internal class RecipientSendFactory( Timber.e("Failed to validateWalletMemoUseCase: $it") false } - val isAddressInWallet = cryptoCurrencyStatus.value.networkAddress?.availableAddresses - ?.any { it.value == value } ?: true return state.copyWrapped( isEditState = isEditState, recipientState = recipientState.copy( - isPrimaryButtonEnabled = isValidMemo && isValidAddress && !isAddressInWallet, + isPrimaryButtonEnabled = isValidMemo && maybeValidAddress.isRight(), isValidating = false, addressTextField = recipientState.addressTextField.copy( - error = when { - !isValidAddress -> resourceReference(R.string.send_recipient_address_error) - isAddressInWallet -> resourceReference(R.string.send_error_address_same_as_wallet) - else -> null - }, - isError = value.isNotEmpty() && !isValidAddress || isAddressInWallet, + error = maybeValidAddress.fold( + ifLeft = { + when (it) { + ValidateAddressError.InvalidAddress -> resourceReference( + R.string.send_recipient_address_error, + ) + ValidateAddressError.AddressInWallet -> resourceReference( + R.string.send_error_address_same_as_wallet, + ) + else -> null + } + }, + ifRight = { null }, + ), + isError = value.isNotEmpty() && maybeValidAddress.isLeft(), ), ), ) @@ -131,13 +147,11 @@ internal class RecipientSendFactory( Timber.e("Failed to validateWalletMemoUseCase: $it") false } - val isAddressInWallet = cryptoCurrencyStatus.value.networkAddress?.availableAddresses - ?.any { it.value == value } ?: true return state.copyWrapped( isEditState = isEditState, recipientState = recipientState.copy( - isPrimaryButtonEnabled = isValidMemo && isValidAddress && !isAddressInWallet, + isPrimaryButtonEnabled = isValidMemo && isValidAddress, isValidating = false, memoTextField = recipientState.memoTextField?.copy( isError = value.isNotEmpty() && !isValidMemo, @@ -162,11 +176,10 @@ internal class RecipientSendFactory( ) } - fun getHiddenRecentListState(isAddressInWallet: Boolean, isValidAddress: Boolean): SendUiState { + fun getHiddenRecentListState(isNotValid: Boolean): SendUiState { val state = currentStateProvider() val isEditState = stateRouterProvider().isEditState val recipientState = state.getRecipientState(isEditState) ?: return state - val isNotValid = isAddressInWallet || !isValidAddress return state.copyWrapped( isEditState = isEditState, recipientState = recipientState.copy( diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientWalletListConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientWalletListConverter.kt index 35d56f9846..6ac1a69d3f 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientWalletListConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/recipient/SendRecipientWalletListConverter.kt @@ -1,16 +1,22 @@ package com.tangem.features.send.impl.presentation.state.recipient import com.tangem.core.ui.extensions.TextReference +import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.features.send.impl.presentation.domain.AvailableWallet import com.tangem.features.send.impl.presentation.domain.SendRecipientListContent import com.tangem.features.send.impl.presentation.state.recipient.utils.WALLET_DEFAULT_COUNT import com.tangem.features.send.impl.presentation.state.recipient.utils.WALLET_KEY_TAG import com.tangem.features.send.impl.presentation.state.recipient.utils.emptyListState +import com.tangem.utils.Provider import com.tangem.utils.converter.Converter import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.toPersistentList -internal class SendRecipientWalletListConverter : +internal class SendRecipientWalletListConverter( + private val cryptoCurrencyStatusProvider: Provider, + private val isUtxoConsolidationAvailableProvider: Provider, +) : Converter, PersistentList> { override fun convert(value: List): PersistentList { return value.filterWallets().ifEmpty { @@ -20,8 +26,18 @@ internal class SendRecipientWalletListConverter : private fun List.filterWallets(): PersistentList { var walletsCounter = 0 + val currentAddress: String = runCatching { + cryptoCurrencyStatusProvider().value.networkAddress?.defaultAddress?.value + }.getOrNull().orEmpty() + return this.filterNotNull() - .filter { it.address.isNotBlank() } + .filter { + val isCoin = it.cryptoCurrency is CryptoCurrency.Coin + val isNotSameAddress = it.address != currentAddress + val isNotBlankAddress = it.address.isNotBlank() + + isNotBlankAddress && isCoin && (isNotSameAddress || isUtxoConsolidationAvailableProvider()) + } .groupBy { item -> item.name } .values.map { wallets -> val groupedByWallet = wallets.groupBy { it.userWalletId } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt index d07fa04c51..bcf4954db8 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt @@ -7,6 +7,7 @@ import androidx.compose.runtime.setValue import androidx.lifecycle.* import arrow.core.Either import arrow.core.getOrElse +import arrow.core.left import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.core.analytics.api.AnalyticsEventHandler @@ -34,10 +35,11 @@ import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase import com.tangem.domain.txhistory.usecase.GetFixedTxHistoryItemsUseCase import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.domain.transaction.error.ValidateAddressError import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.GetWalletsUseCase -import com.tangem.domain.wallets.usecase.ValidateWalletAddressUseCase -import com.tangem.domain.wallets.usecase.ValidateWalletMemoUseCase +import com.tangem.domain.transaction.usecase.ValidateWalletAddressUseCase +import com.tangem.domain.transaction.usecase.ValidateWalletMemoUseCase import com.tangem.features.send.api.navigation.SendRouter import com.tangem.features.send.impl.navigation.InnerSendRouter import com.tangem.features.send.impl.presentation.analytics.EnterAddressSource @@ -93,6 +95,7 @@ internal class SendViewModel @Inject constructor( private val addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase, private val updateDelayedCurrencyStatusUseCase: UpdateDelayedNetworkStatusUseCase, private val fetchPendingTransactionsUseCase: FetchPendingTransactionsUseCase, + private val isUtxoConsolidationAvailableUseCase: IsUtxoConsolidationAvailableUseCase, @DelayedWork private val coroutineScope: CoroutineScope, validateTransactionUseCase: ValidateTransactionUseCase, currencyChecksRepository: CurrencyChecksRepository, @@ -135,6 +138,7 @@ internal class SendViewModel @Inject constructor( stateRouterProvider = Provider { stateRouter }, currentStateProvider = Provider { uiState }, cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus }, + isUtxoConsolidationAvailableProvider = Provider { isUtxoConsolidationAvailable }, validateWalletMemoUseCase = validateWalletMemoUseCase, ) @@ -198,6 +202,7 @@ internal class SendViewModel @Inject constructor( private var userWallet: UserWallet by Delegates.notNull() private var userWallets: List = emptyList() private var isAmountSubtractAvailable: Boolean = false + private var isUtxoConsolidationAvailable: Boolean = false private var isTapHelpPreviewEnabled: Boolean = false private var coinCryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull() private var cryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull() @@ -248,6 +253,7 @@ internal class SendViewModel @Inject constructor( ifRight = { wallet -> userWallet = wallet checkIfSubtractAvailable() + checkIfUtxoConsolidationAvailable() val isSingleWalletWithToken = wallet.scanResponse.cardTypesResolver.isSingleWalletWithToken() val isMultiCurrency = wallet.isMultiCurrency @@ -424,9 +430,6 @@ internal class SendViewModel @Inject constructor( } private suspend fun List.toAvailableWallets(): List { - val currentAddress: String = kotlin.runCatching { - cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value - }.getOrNull().orEmpty() return filterNot { it.isLocked } .mapNotNull { wallet -> val addresses = if (!wallet.isMultiCurrency) { @@ -440,16 +443,14 @@ internal class SendViewModel @Inject constructor( } else { getNetworkAddressesUseCase.invokeSync(wallet.walletId, cryptoCurrency.network) } - addresses - ?.filter { it.address != currentAddress && it.cryptoCurrency is CryptoCurrency.Coin } - ?.map { (cryptoCurrency, address) -> - AvailableWallet( - name = wallet.name, - address = address, - cryptoCurrency = cryptoCurrency, - userWalletId = wallet.walletId, - ) - } + addresses?.map { (cryptoCurrency, address) -> + AvailableWallet( + name = wallet.name, + address = address, + cryptoCurrency = cryptoCurrency, + userWalletId = wallet.walletId, + ) + } }.flatten() } @@ -624,8 +625,15 @@ internal class SendViewModel @Inject constructor( uiState = recipientStateFactory.getOnRecipientAddressValidationStarted() val isValidAddress = validateAddress(value) uiState = recipientStateFactory.getOnRecipientAddressValidState(value, isValidAddress) - type?.let { analyticsEventHandler.send(SendAnalyticEvents.AddressEntered(it, isValidAddress)) } - autoNextFromRecipient(type, isValidAddress) + type?.let { + analyticsEventHandler.send( + SendAnalyticEvents.AddressEntered( + it, + isValidAddress.isRight(), + ), + ) + } + autoNextFromRecipient(type, isValidAddress.isRight()) } }.saveIn(addressValidationJobHolder) } @@ -636,23 +644,22 @@ internal class SendViewModel @Inject constructor( uiState = recipientStateFactory.getOnRecipientMemoValueChange(value, isValuePasted) uiState = recipientStateFactory.getOnRecipientAddressValidationStarted() val recipientState = uiState.getRecipientState(stateRouter.isEditState) - val isValidAddress = validateAddress(recipientState?.addressTextField?.value.orEmpty()) - uiState = recipientStateFactory.getOnRecipientMemoValidState(value, isValidAddress) + val maybeValidAddress = validateAddress(recipientState?.addressTextField?.value.orEmpty()) + uiState = recipientStateFactory.getOnRecipientMemoValidState(value, maybeValidAddress.isRight()) } }.saveIn(memoValidationJobHolder) } - private suspend fun validateAddress(value: String): Boolean = runCatching { - val isValidAddress = validateWalletAddressUseCase( + private suspend fun validateAddress(value: String): Either = runCatching { + val maybeValidAddress = validateWalletAddressUseCase( userWalletId = userWalletId, network = cryptoCurrency.network, address = value, - ).getOrElse { false } - val isAddressInWallet = cryptoCurrencyStatus.value.networkAddress?.availableAddresses - ?.any { it.value == value } ?: true - onEnteredValidAddress(isValidAddress, isAddressInWallet) - return isValidAddress - }.getOrElse { false } + currencyAddress = cryptoCurrencyStatus.value.networkAddress?.availableAddresses, + ) + onEnteredValidAddress(maybeValidAddress.isLeft()) + maybeValidAddress + }.getOrElse { ValidateAddressError.DataError(it).left() } private suspend fun checkIfXrpAddressValue(value: String): Boolean { return BlockchainUtils.decodeRippleXAddress(value, cryptoCurrency.network.id.value)?.let { decodedAddress -> @@ -665,11 +672,8 @@ internal class SendViewModel @Inject constructor( } ?: false } - private fun onEnteredValidAddress(isValidAddress: Boolean, isAddressInWallet: Boolean) { - uiState = recipientStateFactory.getHiddenRecentListState( - isAddressInWallet = isAddressInWallet, - isValidAddress = isValidAddress, - ) + private fun onEnteredValidAddress(isNotValid: Boolean) { + uiState = recipientStateFactory.getHiddenRecentListState(isNotValid = isNotValid) } private fun autoNextFromRecipient(type: EnterAddressSource?, isValidAddress: Boolean) { @@ -739,6 +743,13 @@ internal class SendViewModel @Inject constructor( ) } + private suspend fun checkIfUtxoConsolidationAvailable() { + isUtxoConsolidationAvailable = isUtxoConsolidationAvailableUseCase.invokeSync( + userWalletId = userWalletId, + network = cryptoCurrency.network, + ) + } + private suspend fun callFeeUseCase(): Either? { val isFromConfirmation = stateRouter.currentState.value.isFromConfirmation val amountState = uiState.getAmountState(isFromConfirmation) ?: return null From f9d98374b1bf9afe1b08fff38e294c53386959da Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 3 Jun 2024 15:03:54 +0500 Subject: [PATCH 4/5] Updated on 2026-08-14 --- .../walletmanager/utils/SdkTransactionHistoryItemConverter.kt | 4 ++-- .../utils/SdkTransactionHistoryStateConverter.kt | 4 ++-- .../domain/walletmanager/utils/SdkTransactionTypeConverter.kt | 2 +- gradle/dependencies.toml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionHistoryItemConverter.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionHistoryItemConverter.kt index 5999db6ea0..a429859de4 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionHistoryItemConverter.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionHistoryItemConverter.kt @@ -1,10 +1,10 @@ package com.tangem.domain.walletmanager.utils -import com.tangem.blockchain.common.txhistory.TransactionHistoryItem +import com.tangem.blockchain.transactionhistory.models.TransactionHistoryItem import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.walletmanager.model.SmartContractMethod import com.tangem.utils.converter.Converter -import com.tangem.blockchain.common.txhistory.TransactionHistoryItem as SdkTransactionHistoryItem +import com.tangem.blockchain.transactionhistory.models.TransactionHistoryItem as SdkTransactionHistoryItem internal class SdkTransactionHistoryItemConverter( smartContractMethods: Map, diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionHistoryStateConverter.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionHistoryStateConverter.kt index 6750e05410..6532b6338f 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionHistoryStateConverter.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionHistoryStateConverter.kt @@ -1,9 +1,9 @@ package com.tangem.domain.walletmanager.utils -import com.tangem.blockchain.common.txhistory.TransactionHistoryState +import com.tangem.blockchain.transactionhistory.TransactionHistoryState import com.tangem.domain.txhistory.models.TxHistoryState import com.tangem.utils.converter.Converter -import com.tangem.blockchain.common.txhistory.TransactionHistoryState as SdkTransactionHistoryState +import com.tangem.blockchain.transactionhistory.TransactionHistoryState as SdkTransactionHistoryState internal class SdkTransactionHistoryStateConverter : Converter { diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionTypeConverter.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionTypeConverter.kt index a416bf6085..c0ebfa9a66 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionTypeConverter.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionTypeConverter.kt @@ -1,6 +1,6 @@ package com.tangem.domain.walletmanager.utils -import com.tangem.blockchain.common.txhistory.TransactionHistoryItem +import com.tangem.blockchain.transactionhistory.models.TransactionHistoryItem import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.walletmanager.model.SmartContractMethod import com.tangem.utils.converter.Converter diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 78995e86ad..b39d711a8d 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -87,7 +87,7 @@ markdown = "0.7.2" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-652" +tangemBlockchainSdk = "develop-654" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-359" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ From e0dd5081c2700264e8271d6bd454ffc7ec4ef85a Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 3 Jun 2024 15:23:34 +0500 Subject: [PATCH 5/5] Updated on 2026-08-14 --- .../tap/di/domain/WalletsDomainModule.kt | 2 -- .../DefaultWalletAddressServiceRepository.kt | 28 +++++++++++-------- .../transaction/di/TransactionDataModule.kt | 6 +++- .../walletmanager/WalletManagersFacade.kt | 7 ++++- .../usecase/ValidateWalletAddressUseCase.kt | 7 ++--- 5 files changed, 29 insertions(+), 21 deletions(-) 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 c539df5afc..3dfc085c10 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 @@ -130,12 +130,10 @@ internal object WalletsDomainModule { fun providesValidateWalletAddressUseCase( walletAddressServiceRepository: WalletAddressServiceRepository, walletManagersFacade: WalletManagersFacade, - dispatchers: CoroutineDispatcherProvider, ): ValidateWalletAddressUseCase { return ValidateWalletAddressUseCase( walletAddressServiceRepository = walletAddressServiceRepository, walletManagersFacade = walletManagersFacade, - dispatchers = dispatchers, ) } diff --git a/data/transaction/src/main/java/com/tangem/data/transaction/DefaultWalletAddressServiceRepository.kt b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultWalletAddressServiceRepository.kt index 00dc557554..4d0bd6ac5e 100644 --- a/data/transaction/src/main/java/com/tangem/data/transaction/DefaultWalletAddressServiceRepository.kt +++ b/data/transaction/src/main/java/com/tangem/data/transaction/DefaultWalletAddressServiceRepository.kt @@ -10,26 +10,30 @@ import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.models.ParsedQrCode import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.models.errors.ParsedQrCodeErrors +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.withContext import java.math.BigInteger class DefaultWalletAddressServiceRepository( private val walletManagersFacade: WalletManagersFacade, + private val dispatchers: CoroutineDispatcherProvider, ) : WalletAddressServiceRepository { - override suspend fun validateAddress(userWalletId: UserWalletId, network: Network, address: String): Boolean { - val blockchain = Blockchain.fromId(network.id.value) + override suspend fun validateAddress(userWalletId: UserWalletId, network: Network, address: String): Boolean = + withContext(dispatchers.io) { + val blockchain = Blockchain.fromId(network.id.value) - return if (blockchain.isNear()) { - val walletManager = walletManagersFacade.getOrCreateWalletManager( - userWalletId = userWalletId, - blockchain = blockchain, - derivationPath = network.derivationPath.value, - ) ?: return false - (walletManager as? NearWalletManager)?.validateAddress(address) ?: false - } else { - blockchain.validateAddress(address) + if (blockchain.isNear()) { + val walletManager = walletManagersFacade.getOrCreateWalletManager( + userWalletId = userWalletId, + blockchain = blockchain, + derivationPath = network.derivationPath.value, + ) ?: return@withContext false + (walletManager as? NearWalletManager)?.validateAddress(address) ?: false + } else { + blockchain.validateAddress(address) + } } - } override fun validateMemo(network: Network, memo: String): Boolean { if (memo.isEmpty()) return true diff --git a/data/transaction/src/main/java/com/tangem/data/transaction/di/TransactionDataModule.kt b/data/transaction/src/main/java/com/tangem/data/transaction/di/TransactionDataModule.kt index 7a1b7db59c..d5fffe95ed 100644 --- a/data/transaction/src/main/java/com/tangem/data/transaction/di/TransactionDataModule.kt +++ b/data/transaction/src/main/java/com/tangem/data/transaction/di/TransactionDataModule.kt @@ -43,7 +43,11 @@ internal object TransactionDataModule { @Singleton fun providesWalletAddressServiceRepository( walletManagersFacade: WalletManagersFacade, + coroutineDispatcherProvider: CoroutineDispatcherProvider, ): WalletAddressServiceRepository { - return DefaultWalletAddressServiceRepository(walletManagersFacade) + return DefaultWalletAddressServiceRepository( + walletManagersFacade = walletManagersFacade, + dispatchers = coroutineDispatcherProvider, + ) } } \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt index 14f5fa53fa..9e1c59ea3d 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt @@ -254,6 +254,11 @@ interface WalletManagersFacade { signer: CommonSigner, ): SimpleResult - /** Indicates UTXO consolidation availability */ + /** + * Indicates UTXO consolidation availability + * + * @param userWalletId selected user wallet + * @param network availability for network + */ suspend fun checkUtxoConsolidationAvailability(userWalletId: UserWalletId, network: Network): Boolean } \ No newline at end of file diff --git a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/ValidateWalletAddressUseCase.kt b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/ValidateWalletAddressUseCase.kt index 9d9de200e8..7db3a825f7 100644 --- a/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/ValidateWalletAddressUseCase.kt +++ b/domain/transaction/src/main/java/com/tangem/domain/transaction/usecase/ValidateWalletAddressUseCase.kt @@ -9,8 +9,6 @@ import com.tangem.domain.transaction.WalletAddressServiceRepository import com.tangem.domain.transaction.error.ValidateAddressError import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.withContext /** * Use case for validating wallet address. @@ -18,7 +16,6 @@ import kotlinx.coroutines.withContext class ValidateWalletAddressUseCase( private val walletAddressServiceRepository: WalletAddressServiceRepository, private val walletManagersFacade: WalletManagersFacade, - private val dispatchers: CoroutineDispatcherProvider, ) { suspend operator fun invoke( @@ -26,7 +23,7 @@ class ValidateWalletAddressUseCase( network: Network, address: String, currencyAddress: Set?, - ): Either = withContext(dispatchers.io) { + ): Either { val isUtxoConsolidationAvailable = walletManagersFacade.checkUtxoConsolidationAvailability(userWalletId, network) val isCurrentAddress = currencyAddress?.any { it.value == address } ?: true @@ -34,7 +31,7 @@ class ValidateWalletAddressUseCase( val isForbidSelfSend = isCurrentAddress && !isUtxoConsolidationAvailable val isValidAddress = walletAddressServiceRepository.validateAddress(userWalletId, network, address) - when { + return when { !isValidAddress -> ValidateAddressError.InvalidAddress.left() isForbidSelfSend -> ValidateAddressError.AddressInWallet.left() else -> Unit.right()