From b723940318f8803a1f814219f6a3e611c54b02ea Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 11 Jan 2024 20:50:18 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/di/domain/WalletsDomainModule.kt | 12 +++++++ .../DefaultWalletAddressServiceRepository.kt | 36 +++++++++++++++++++ .../domain/wallets/models/ParsedQrCode.kt | 9 +++++ .../models/errors/ParsedQrCodeErrors.kt | 6 ++++ .../WalletAddressServiceRepository.kt | 3 ++ .../usecase/ParseSharedAddressUseCase.kt | 22 ++++++++++++ .../feature/qrscanning/QrScanningFragment.kt | 5 ++- .../qrscanning/inner/MLKitBarcodeAnalyzer.kt | 2 +- .../viewmodel/QrScanningViewModel.kt | 7 +++- features/send/impl/build.gradle.kts | 3 +- .../send/impl/navigation/DefaultSendRouter.kt | 14 ++++++++ .../send/impl/navigation/InnerSendRouter.kt | 3 ++ .../send/impl/presentation/SendFragment.kt | 30 ++++++++++++++++ .../presentation/state/SendStateFactory.kt | 18 +++++----- .../fields/SendAmountFieldChangeConverter.kt | 4 +-- .../presentation/viewmodel/SendViewModel.kt | 30 ++++++++++++---- 16 files changed, 182 insertions(+), 22 deletions(-) create mode 100644 domain/wallets/models/src/main/java/com/tangem/domain/wallets/models/ParsedQrCode.kt create mode 100644 domain/wallets/models/src/main/java/com/tangem/domain/wallets/models/errors/ParsedQrCodeErrors.kt create mode 100644 domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ParseSharedAddressUseCase.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 809e962e12..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 @@ -113,4 +113,16 @@ internal object WalletsDomainModule { ): 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/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletAddressServiceRepository.kt b/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletAddressServiceRepository.kt index 09f2eb0835..6831cc8ae4 100644 --- a/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletAddressServiceRepository.kt +++ b/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletAddressServiceRepository.kt @@ -1,11 +1,14 @@ package com.tangem.data.wallets +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.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 @@ -42,6 +45,39 @@ class DefaultWalletAddressServiceRepository( } } + override suspend fun parseSharedAddress(input: String, network: Network): ParsedQrCode { + val blockchain = Blockchain.fromId(network.id.value) + val addressSchemeSplit = when (blockchain) { + Blockchain.BitcoinCash, Blockchain.Kaspa -> listOf(input) + else -> input.split(":") + } + + val noSchemeAddress = when (addressSchemeSplit.size) { + 1 -> { // no scheme + input + } + 2 -> { // scheme + if (blockchain.validateShareScheme(addressSchemeSplit[0])) { + addressSchemeSplit[1] + } else { + // to preserve old logic + return ParsedQrCode(address = input) + } + } + else -> { // invalid URI + throw ParsedQrCodeErrors.InvalidUriError + } + } + + val uri = Uri.parse(noSchemeAddress) + val address = uri.host ?: noSchemeAddress + val amount = uri.getQueryParameter("amount")?.toBigDecimalOrNull() + return ParsedQrCode( + address = address, + amount = amount, + ) + } + private fun Blockchain.isNear(): Boolean { return this == Blockchain.Near || this == Blockchain.NearTestnet } diff --git a/domain/wallets/models/src/main/java/com/tangem/domain/wallets/models/ParsedQrCode.kt b/domain/wallets/models/src/main/java/com/tangem/domain/wallets/models/ParsedQrCode.kt new file mode 100644 index 0000000000..809ec66d72 --- /dev/null +++ b/domain/wallets/models/src/main/java/com/tangem/domain/wallets/models/ParsedQrCode.kt @@ -0,0 +1,9 @@ +package com.tangem.domain.wallets.models + +import java.math.BigDecimal + +data class ParsedQrCode( + val address: String, + val memo: String? = null, + val amount: BigDecimal? = null, +) \ No newline at end of file diff --git a/domain/wallets/models/src/main/java/com/tangem/domain/wallets/models/errors/ParsedQrCodeErrors.kt b/domain/wallets/models/src/main/java/com/tangem/domain/wallets/models/errors/ParsedQrCodeErrors.kt new file mode 100644 index 0000000000..4cf5bc7683 --- /dev/null +++ b/domain/wallets/models/src/main/java/com/tangem/domain/wallets/models/errors/ParsedQrCodeErrors.kt @@ -0,0 +1,6 @@ +package com.tangem.domain.wallets.models.errors + +sealed class ParsedQrCodeErrors : Throwable() { + + object InvalidUriError : ParsedQrCodeErrors() +} \ No newline at end of file diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletAddressServiceRepository.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletAddressServiceRepository.kt index 1d36444caf..624ad44419 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletAddressServiceRepository.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/repository/WalletAddressServiceRepository.kt @@ -1,6 +1,7 @@ package com.tangem.domain.wallets.repository import com.tangem.domain.tokens.model.Network +import com.tangem.domain.wallets.models.ParsedQrCode import com.tangem.domain.wallets.models.UserWalletId /** @@ -11,4 +12,6 @@ interface WalletAddressServiceRepository { suspend fun validateAddress(userWalletId: UserWalletId, network: Network, address: String): Boolean fun validateMemo(network: Network, memo: String): Boolean + + suspend fun parseSharedAddress(input: String, network: Network): ParsedQrCode } \ No newline at end of file diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ParseSharedAddressUseCase.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ParseSharedAddressUseCase.kt new file mode 100644 index 0000000000..8d03b6ab6d --- /dev/null +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/ParseSharedAddressUseCase.kt @@ -0,0 +1,22 @@ +package com.tangem.domain.wallets.usecase + +import arrow.core.Either +import com.tangem.domain.tokens.model.Network +import com.tangem.domain.wallets.models.ParsedQrCode +import com.tangem.domain.wallets.repository.WalletAddressServiceRepository +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.withContext + +class ParseSharedAddressUseCase( + private val walletAddressServiceRepository: WalletAddressServiceRepository, + private val dispatchers: CoroutineDispatcherProvider, +) { + + suspend operator fun invoke(input: String, network: Network): Either { + return withContext(dispatchers.io) { + Either.catch { + walletAddressServiceRepository.parseSharedAddress(input, network) + } + } + } +} \ No newline at end of file diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/QrScanningFragment.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/QrScanningFragment.kt index 9120d00cd2..5805460bc2 100644 --- a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/QrScanningFragment.kt +++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/QrScanningFragment.kt @@ -40,6 +40,9 @@ internal class QrScanningFragment : ComposeFragment() { private val viewModel by viewModels() private var cameraExecutor: ExecutorService by Delegates.notNull() + private val analyzer: MLKitBarcodeAnalyzer by lazy(LazyThreadSafetyMode.NONE) { + MLKitBarcodeAnalyzer(viewModel::onQrScanned) + } private val cameraPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { if (!it) parentFragmentManager.popBackStack() @@ -66,7 +69,7 @@ internal class QrScanningFragment : ComposeFragment() { } QrScanningContent( executor = { cameraExecutor }, - analyzer = { MLKitBarcodeAnalyzer(viewModel::onQrScanned) }, + analyzer = { analyzer }, uiState = viewModel.uiState, ) setFitSystemWindows(fit = false) diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/inner/MLKitBarcodeAnalyzer.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/inner/MLKitBarcodeAnalyzer.kt index ddd9d09857..9a339e2d92 100644 --- a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/inner/MLKitBarcodeAnalyzer.kt +++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/inner/MLKitBarcodeAnalyzer.kt @@ -11,13 +11,13 @@ import javax.annotation.concurrent.Immutable class MLKitBarcodeAnalyzer(private val onScanned: (String) -> Unit) : ImageAnalysis.Analyzer { private var isScanning: Boolean = false + private val scanner = BarcodeScanning.getClient() @ExperimentalGetImage override fun analyze(imageProxy: ImageProxy) { val mediaImage = imageProxy.image if (mediaImage != null && !isScanning) { val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees) - val scanner = BarcodeScanning.getClient() isScanning = true scanner.process(image) diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/viewmodel/QrScanningViewModel.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/viewmodel/QrScanningViewModel.kt index a879b5f9fb..b0eef6d35c 100644 --- a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/viewmodel/QrScanningViewModel.kt +++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/viewmodel/QrScanningViewModel.kt @@ -38,11 +38,16 @@ internal class QrScanningViewModel @Inject constructor( var uiState: QrScanningState by mutableStateOf(factory.getInitialState(source, network)) private set + private var isScanned = false + override fun onBackClick() = router.popBackStack() override fun onQrScanned(qrCode: String) { if (qrCode.isNotBlank()) { - router.popBackStack() + if (!isScanned) { + router.popBackStack() + isScanned = true + } viewModelScope.launch(dispatcher.main) { emitQrScannedEventUseCase.invoke(source, qrCode) } diff --git a/features/send/impl/build.gradle.kts b/features/send/impl/build.gradle.kts index d0bf0f05b7..53a6d8a573 100644 --- a/features/send/impl/build.gradle.kts +++ b/features/send/impl/build.gradle.kts @@ -58,11 +58,12 @@ dependencies { implementation(projects.domain.txhistory.models) implementation(projects.domain.transaction) implementation(projects.domain.card) - implementation(projects.domain.demo) /** Feature modules */ implementation(projects.features.send.api) implementation(projects.features.tokendetails.api) + implementation(projects.features.qrScanning.api) + implementation(projects.features.qrScanning.impl) /** DI */ implementation(deps.hilt.android) diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/navigation/DefaultSendRouter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/navigation/DefaultSendRouter.kt index 41ddca52e6..360165f228 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/navigation/DefaultSendRouter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/navigation/DefaultSendRouter.kt @@ -7,6 +7,8 @@ import com.tangem.core.navigation.NavigationAction import com.tangem.core.navigation.ReduxNavController import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.feature.qrscanning.QrScanningRouter +import com.tangem.feature.qrscanning.SourceType import com.tangem.features.send.impl.presentation.SendFragment import com.tangem.features.tokendetails.navigation.TokenDetailsRouter @@ -31,4 +33,16 @@ internal class DefaultSendRouter( ), ) } + + override fun openQrCodeScanner(network: String) { + reduxNavController.navigate( + action = NavigationAction.NavigateTo( + screen = AppScreen.QrScanning, + bundle = bundleOf( + QrScanningRouter.SOURCE_KEY to SourceType.SEND, + QrScanningRouter.NETWORK_KEY to network, + ), + ), + ) + } } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/navigation/InnerSendRouter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/navigation/InnerSendRouter.kt index 1cba26003d..f061eaba01 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/navigation/InnerSendRouter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/navigation/InnerSendRouter.kt @@ -11,4 +11,7 @@ interface InnerSendRouter : SendRouter { /** Open token details screen by [userWalletId] and [currency] */ fun openTokenDetails(userWalletId: UserWalletId, currency: CryptoCurrency) + + /** Open QR code scanner screen */ + fun openQrCodeScanner(network: String) } \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/SendFragment.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/SendFragment.kt index cd47d6c8fe..7cdab91041 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/SendFragment.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/SendFragment.kt @@ -4,16 +4,25 @@ import android.os.Bundle import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.fragment.app.viewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import arrow.core.getOrElse import com.tangem.core.ui.components.SystemBarsEffect import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.screen.ComposeFragment import com.tangem.core.ui.theme.AppThemeModeHolder +import com.tangem.feature.qrscanning.SourceType +import com.tangem.feature.qrscanning.usecase.ListenToQrScanningUseCase import com.tangem.features.send.api.navigation.SendRouter import com.tangem.features.send.impl.navigation.InnerSendRouter import com.tangem.features.send.impl.presentation.state.StateRouter import com.tangem.features.send.impl.presentation.ui.SendScreen import com.tangem.features.send.impl.presentation.viewmodel.SendViewModel import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.launch import java.lang.ref.WeakReference import javax.inject.Inject @@ -29,6 +38,9 @@ internal class SendFragment : ComposeFragment() { @Inject lateinit var router: SendRouter + @Inject + lateinit var listenToQrScanningUseCase: ListenToQrScanningUseCase + private val viewModel by viewModels() private val innerSendRouter: InnerSendRouter get() = requireNotNull(router as? InnerSendRouter) { @@ -44,6 +56,7 @@ internal class SendFragment : ComposeFragment() { fragmentManager = WeakReference(parentFragmentManager), ), ) + listenToQrCode() } @Composable @@ -60,7 +73,24 @@ internal class SendFragment : ComposeFragment() { super.onDestroy() } + private fun listenToQrCode() { + lifecycleScope.launch { + listenToQrScanningUseCase(SourceType.SEND) + .getOrElse { emptyFlow() } + .flowWithLifecycle(this@SendFragment.lifecycle, minActiveState = Lifecycle.State.CREATED) + .collect { + delay(QR_SCAN_DELAY) + + // 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 + viewModel.onRecipientAddressScanned(it) + } + } + } + companion object { + private const val QR_SCAN_DELAY = 200L /** Create send fragment instance */ fun create(): SendFragment = SendFragment() diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt index 177b470fda..70fac6ad0d 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt @@ -94,11 +94,14 @@ internal class SendStateFactory( currentState = MutableStateFlow(SendUiStateType.Amount), ) - fun getReadyState(): SendUiState = currentStateProvider().copy( - amountState = amountStateConverter.convert(Unit), - recipientState = recipientStateConverter.convert(Unit), - feeState = feeStateConverter.convert(Unit), - ) + fun getReadyState(): SendUiState { + val state = currentStateProvider() + return state.copy( + amountState = state.amountState ?: amountStateConverter.convert(Unit), + recipientState = state.recipientState ?: recipientStateConverter.convert(Unit), + feeState = state.feeState ?: feeStateConverter.convert(Unit), + ) + } //endregion //region amount state clicks @@ -160,9 +163,8 @@ internal class SendStateFactory( isValidating = false, addressTextField = recipientState.addressTextField.copy( error = when { - !isValidAddress || isAddressInWallet -> resourceReference( - R.string.send_recipient_address_error, - ) + !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, diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldChangeConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldChangeConverter.kt index 90193bd7d5..66f31d9233 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldChangeConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldChangeConverter.kt @@ -6,7 +6,6 @@ import com.tangem.features.send.impl.presentation.state.SendUiState import com.tangem.utils.Provider import com.tangem.utils.converter.Converter import java.math.BigDecimal -import java.text.DecimalFormatSymbols import java.text.NumberFormat internal class SendAmountFieldChangeConverter( @@ -106,8 +105,7 @@ internal class SendAmountFieldChangeConverter( var trimmedValue = this if (length > 1 && firstOrNull() == '0' && get(1).isDigit()) trimmedValue = drop(1) - val separatorChar = DecimalFormatSymbols.getInstance().decimalSeparator.toString() - return trimmedValue.replace(TRIM_REGEX.toRegex(), separatorChar) + return trimmedValue.replace(TRIM_REGEX.toRegex(), ".") } companion object { 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 394c03f652..133e865e26 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 @@ -26,14 +26,15 @@ import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId -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.wallets.usecase.* import com.tangem.features.send.api.navigation.SendRouter import com.tangem.features.send.impl.navigation.InnerSendRouter import com.tangem.features.send.impl.presentation.domain.AvailableWallet import com.tangem.features.send.impl.presentation.state.* +import com.tangem.features.send.impl.presentation.state.SendNotificationFactory +import com.tangem.features.send.impl.presentation.state.SendStateFactory +import com.tangem.features.send.impl.presentation.state.SendUiState +import com.tangem.features.send.impl.presentation.state.StateRouter import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState import com.tangem.features.send.impl.presentation.state.fee.FeeType import com.tangem.features.send.impl.presentation.state.fee.getFee @@ -67,6 +68,7 @@ internal class SendViewModel @Inject constructor( private val sendTransactionUseCase: SendTransactionUseCase, private val createTransactionUseCase: CreateTransactionUseCase, private val validateWalletAddressUseCase: ValidateWalletAddressUseCase, + private val parseSharedAddressUseCase: ParseSharedAddressUseCase, private val walletManagersFacade: WalletManagersFacade, getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase, validateWalletMemoUseCase: ValidateWalletMemoUseCase, @@ -116,6 +118,7 @@ internal class SendViewModel @Inject constructor( private var feeJobHolder = JobHolder() private var addressValidationJobHolder = JobHolder() private var sendNotificationsJobHolder = JobHolder() + private var qrScannerJobHolder = JobHolder() override fun onCreate(owner: LifecycleOwner) { subscribeOnCurrencyStatusUpdates(owner) @@ -326,9 +329,7 @@ internal class SendViewModel @Inject constructor( override fun onNextClick() = stateRouter.onNextClick() override fun onPrevClick() = stateRouter.onPrevClick() - override fun onQrCodeScanClick() { - // TODO Add QR code scanning - } + override fun onQrCodeScanClick() = innerRouter.openQrCodeScanner(cryptoCurrency.network.name) override fun onTokenDetailsClick(userWalletId: UserWalletId, currency: CryptoCurrency) = innerRouter.openTokenDetails(userWalletId, currency) @@ -355,6 +356,21 @@ internal class SendViewModel @Inject constructor( // endregion // region recipient state clicks + fun onRecipientAddressScanned(address: String) { + viewModelScope.launch(dispatchers.main) { + parseSharedAddressUseCase(address, cryptoCurrency.network).fold( + ifRight = { parsedCode -> + onRecipientAddressValueChange(parsedCode.address) + parsedCode.amount?.let { onAmountValueChange(it.toPlainString()) } + parsedCode.memo?.let { onRecipientMemoValueChange(it) } + }, + ifLeft = { + Timber.w(it) + }, + ) + }.saveIn(qrScannerJobHolder) + } + override fun onRecipientAddressValueChange(value: String) { uiState = stateFactory.onRecipientAddressValueChange(value) viewModelScope.launch(dispatchers.main) {