diff --git a/app/src/main/java/com/tangem/tap/common/DialogManager.kt b/app/src/main/java/com/tangem/tap/common/DialogManager.kt index c69530d3be..533cdac6a9 100644 --- a/app/src/main/java/com/tangem/tap/common/DialogManager.kt +++ b/app/src/main/java/com/tangem/tap/common/DialogManager.kt @@ -91,8 +91,6 @@ class DialogManager : StoreSubscriber { context = context, ) } - is WalletConnectDialog.ClipboardOrScanQr -> - ClipboardOrScanQrDialog.create(state.dialog.clipboardUri, context) is WalletConnectDialog.RequestTransaction -> TransactionDialog.create(state.dialog.data, context) is WalletConnectDialog.PersonalSign -> PersonalSignDialog.create(state.dialog.data, context) is WalletConnectDialog.BnbTransactionDialog -> diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt index 2ba6a15344..3d11b576d0 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt @@ -368,10 +368,6 @@ class WalletConnectInteractor( } } - fun isWalletConnectUri(uri: String): Boolean { - return uri.lowercase().startsWith(WC_SCHEME) - } - /** * Handles Wallet Connect deep links. * If wallet connect is able to handle the deeplink, session is started with deeplink. @@ -436,7 +432,6 @@ class WalletConnectInteractor( } private companion object { - const val WC_SCHEME = "wc" const val WC_TOPIC_QUERY_NAME = "sessionTopic" const val WC_PARAM_REGEX = "([a-zA-Z\\d-]+)=([a-zA-Z\\d]+)" } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt index 3383203b41..89437fadf4 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectAction.kt @@ -9,9 +9,7 @@ import org.rekotlin.Action sealed class WalletConnectAction : Action { data class HandleDeepLink(val wcUri: String?) : WalletConnectAction() - data class StartWalletConnect( - val copiedUri: String?, - ) : WalletConnectAction() + data object StartWalletConnect : WalletConnectAction() data class OpenSession( val wcUri: String, @@ -24,7 +22,6 @@ sealed class WalletConnectAction : Action { data class RejectRequest(val topic: String, val id: Long) : WalletConnectAction() - data class ShowClipboardOrScanQrDialog(val wcUri: String) : WalletConnectAction() //region WalletConnect 2.0 data class ApproveProposal(val proposal: WalletConnectEvents.SessionProposal) : WalletConnectAction() data object RejectProposal : WalletConnectAction() diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt index 5239a2cbd9..6bbd548de4 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt @@ -56,26 +56,10 @@ class WalletConnectMiddleware { walletConnectInteractor.disconnectSession(action.topic) } is WalletConnectAction.StartWalletConnect -> { - val uri = action.copiedUri - if (uri != null && isWalletConnectUri(uri)) { - store.dispatchOnMain(WalletConnectAction.ShowClipboardOrScanQrDialog(uri)) - } else { - store.dispatchNavigationAction { - push( - AppRoute.QrScanning(AppRoute.QrScanning.Source.WALLET_CONNECT), - ) - } + store.dispatchNavigationAction { + push(AppRoute.QrScanning(AppRoute.QrScanning.Source.WalletConnect)) } } - is WalletConnectAction.ShowClipboardOrScanQrDialog -> { - store.dispatchOnMain( - GlobalAction.ShowDialog( - WalletConnectDialog.ClipboardOrScanQr( - action.wcUri, - ), - ), - ) - } is WalletConnectAction.OpenSession -> { val index = action.wcUri.indexOf("@") when (action.wcUri[index + 1]) { @@ -200,8 +184,4 @@ class WalletConnectMiddleware { return walletManagerFacade.getStoredWalletManagers(userWallet.walletId) } - - private fun isWalletConnectUri(uri: String): Boolean { - return walletConnectInteractor.isWalletConnectUri(uri) - } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt index 9eb02ddd35..ac28ca0b95 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt @@ -83,7 +83,6 @@ data class WalletForSession( sealed class WalletConnectDialog : StateDialog { data object UnsupportedWcVersion : WalletConnectDialog() - data class ClipboardOrScanQr(val clipboardUri: String) : WalletConnectDialog() data object UnsupportedCard : WalletConnectDialog() data class UnsupportedNetwork(val networks: List? = null) : WalletConnectDialog() data object UnsupportedDapp : WalletConnectDialog() diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectModel.kt index b940ddcc29..21deae59a7 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectModel.kt @@ -4,7 +4,6 @@ import androidx.compose.runtime.Stable import arrow.core.getOrElse import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model -import com.tangem.core.ui.clipboard.ClipboardManager import com.tangem.domain.qrscanning.models.SourceType import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction @@ -23,7 +22,6 @@ import javax.inject.Inject internal class WalletConnectModel @Inject constructor( override val dispatchers: CoroutineDispatcherProvider, private val listenToQrScanningUseCase: ListenToQrScanningUseCase, - private val clipboardManager: ClipboardManager, ) : Model() { init { @@ -43,9 +41,7 @@ internal class WalletConnectModel @Inject constructor( isLoading = state.loading, onRemoveSession = { sessionUri -> onRemoveSession(sessionUri, sessions) }, onAddSession = { - store.dispatch( - WalletConnectAction.StartWalletConnect(copiedUri = clipboardManager.getText()), - ) + store.dispatch(WalletConnectAction.StartWalletConnect) }, ) } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/ClipboardOrScanQrDialog.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/ClipboardOrScanQrDialog.kt deleted file mode 100644 index 3a1c1dd7ea..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/ClipboardOrScanQrDialog.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.tangem.tap.features.details.ui.walletconnect.dialogs - -import android.content.Context -import androidx.appcompat.app.AlertDialog -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.tangem.common.routing.AppRoute -import com.tangem.domain.qrscanning.models.SourceType -import com.tangem.tap.common.extensions.dispatchNavigationAction -import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction -import com.tangem.tap.store -import com.tangem.wallet.R - -object ClipboardOrScanQrDialog { - fun create(wcUri: String, context: Context): AlertDialog { - return MaterialAlertDialogBuilder(context, R.style.CustomMaterialDialog).apply { - setTitle(context.getString(R.string.common_select_action)) - setMessage(context.getText(R.string.wallet_connect_clipboard_alert)) - setPositiveButton(context.getText(R.string.wallet_connect_paste_from_clipboard)) { _, _ -> - store.dispatch(WalletConnectAction.OpenSession(wcUri, WalletConnectAction.OpenSession.SourceType.ETC)) - } - setNegativeButton(context.getText(R.string.wallet_connect_scan_new_code)) { _, _ -> - store.dispatchNavigationAction { - push(AppRoute.QrScanning(source = AppRoute.QrScanning.Source.WALLET_CONNECT)) - } - } - setOnDismissListener { - store.dispatch(GlobalAction.HideDialog) - } - }.create() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt index 56711d9800..732fe9d4d0 100644 --- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt +++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt @@ -318,14 +318,14 @@ internal class ChildFactory @Inject constructor( } is AppRoute.QrScanning -> { val source = when (route.source) { - AppRoute.QrScanning.Source.SEND -> SourceType.SEND - AppRoute.QrScanning.Source.WALLET_CONNECT -> SourceType.WALLET_CONNECT + is AppRoute.QrScanning.Source.Send -> SourceType.SEND + is AppRoute.QrScanning.Source.WalletConnect -> SourceType.WALLET_CONNECT } createComponentChild( contextProvider = contextProvider(route, contextFactory), params = QrScanningComponent.Params( source = source, - networkName = route.networkName, + networkName = (route.source as? AppRoute.QrScanning.Source.Send)?.networkName, ), componentFactory = qrScanningComponentFactory, ) @@ -536,14 +536,14 @@ internal class ChildFactory @Inject constructor( } is AppRoute.QrScanning -> { val source = when (route.source) { - AppRoute.QrScanning.Source.SEND -> SourceType.SEND - AppRoute.QrScanning.Source.WALLET_CONNECT -> SourceType.WALLET_CONNECT + is AppRoute.QrScanning.Source.Send -> SourceType.SEND + is AppRoute.QrScanning.Source.WalletConnect -> SourceType.WALLET_CONNECT } route.asComponentChild( contextProvider = contextProvider(route, contextFactory), params = QrScanningComponent.Params( source = source, - networkName = route.networkName, + networkName = (route.source as? AppRoute.QrScanning.Source.Send)?.networkName, ), componentFactory = qrScanningComponentFactory, ) diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt index 1238ad4305..6619d29662 100644 --- a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt @@ -145,14 +145,19 @@ sealed class AppRoute(val path: String) : Route { data object WalletConnectSessions : AppRoute(path = "/wallet_connect_sessions") @Serializable - data class QrScanning( - val source: Source, - val networkName: String? = null, - ) : AppRoute(path = "/$source/qr_scanning${if (networkName != null) "/$networkName" else ""}") { + data class QrScanning(val source: Source) : AppRoute(path = "/$source/qr_scanning${source.path}") { - enum class Source { - WALLET_CONNECT, - SEND, + @Serializable + sealed class Source { + val path: String + get() = when (this) { + is Send -> "/$networkName" + WalletConnect -> "" + } + + data class Send(val networkName: String) : Source() + + data object WalletConnect : Source() } } diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/model/QrScanningModel.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/model/QrScanningModel.kt index b120a8bd59..505a42e7ec 100644 --- a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/model/QrScanningModel.kt +++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/model/QrScanningModel.kt @@ -8,6 +8,7 @@ import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.navigation.settings.SettingsManager +import com.tangem.core.ui.clipboard.ClipboardManager import com.tangem.data.card.sdk.CardSdkProvider import com.tangem.domain.qrscanning.usecases.EmitQrScannedEventUseCase import com.tangem.feature.qrscanning.QrScanningComponent @@ -34,6 +35,7 @@ internal class QrScanningModel @Inject constructor( private val emitQrScannedEventUseCase: EmitQrScannedEventUseCase, private val settingsManager: SettingsManager, private val appRouter: AppRouter, + private val clipboardManager: ClipboardManager, ) : Model(), QrScanningClickIntents { private val params = paramsContainer.require() @@ -49,7 +51,14 @@ internal class QrScanningModel @Inject constructor( // samsung for some reason disables reader mode, and then it works unstable // to prevent this disable ir manually before scan QR cardSdkProvider.sdk.forceDisableReaderMode() - stateHolder.update(InitializeQrScanningStateTransformer(this, params.source, params.networkName)) + stateHolder.update( + InitializeQrScanningStateTransformer( + clickIntents = this, + clipboardManager = clipboardManager, + source = params.source, + network = params.networkName, + ), + ) } fun onCameraDeniedState() { diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningContent.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningContent.kt index 019b7b6134..da9c43577f 100644 --- a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningContent.kt +++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningContent.kt @@ -6,9 +6,7 @@ import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.togetherWith import androidx.compose.foundation.Canvas -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.layout.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset @@ -22,11 +20,16 @@ import androidx.compose.ui.text.drawText import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.IntOffset import com.tangem.core.ui.components.appbar.TangemTopAppBar import com.tangem.core.ui.components.appbar.TopAppBarButton import com.tangem.core.ui.components.appbar.models.TopAppBarButtonUM +import com.tangem.core.ui.components.buttons.common.TangemButton +import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition +import com.tangem.core.ui.components.buttons.common.TangemButtonsDefaults import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resolveReference +import com.tangem.core.ui.extensions.stringResourceSafe import com.tangem.core.ui.res.TangemColorPalette import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.qrscanning.impl.R @@ -57,6 +60,7 @@ internal fun QrScanningContent( ) Overlay( message = uiState.message, + pasteAction = uiState.pasteAction, ) TangemTopAppBar( @@ -97,69 +101,100 @@ internal fun QrScanningContent( } } +@Suppress("LongMethod") @Composable -private fun Overlay(message: TextReference?, overlayColor: Color = TangemColorPalette.Black.copy(alpha = 0.7f)) { +private fun Overlay( + message: TextReference?, + pasteAction: PasteAction, + overlayColor: Color = TangemColorPalette.Black.copy(alpha = 0.7f), +) { val whiteColor = TangemTheme.colors.text.constantWhite val borderWidth = with(LocalDensity.current) { TangemTheme.dimens.spacing4.toPx() } val borderLength = with(LocalDensity.current) { TangemTheme.dimens.size20.toPx() } val textMeasurer = rememberTextMeasurer() - val textPadding = with(LocalDensity.current) { TangemTheme.dimens.spacing24.toPx() } - val style = TangemTheme.typography.caption2.copy(textAlign = TextAlign.Center) + val textPadding = with(LocalDensity.current) { TangemTheme.dimens.spacing48.toPx() } + val style = TangemTheme.typography.body1.copy(textAlign = TextAlign.Center) val text = message?.resolveReference() - Canvas(modifier = Modifier.fillMaxSize()) { - val squareSize = size.minDimension * QR_CODE_SIZE - val verticalOffset = (size.height - squareSize) / 2f - val horizontalOffset = (size.width - squareSize) / 2f - drawRect( - color = overlayColor, - topLeft = Offset.Zero, - size = Size(size.width, verticalOffset), - ) - drawRect( - color = overlayColor, - topLeft = Offset(0f, verticalOffset), - size = Size(horizontalOffset, squareSize), - ) - drawRect( - color = overlayColor, - topLeft = Offset(horizontalOffset + squareSize, verticalOffset), - size = Size(horizontalOffset, squareSize), - ) - drawRect( - color = overlayColor, - topLeft = Offset(0f, verticalOffset + squareSize), - size = Size(size.width, verticalOffset), - ) - drawRect( - color = whiteColor, - topLeft = Offset(horizontalOffset, verticalOffset), - size = Size(squareSize, squareSize), - style = Stroke( - width = borderWidth, - join = StrokeJoin.Round, - pathEffect = PathEffect.dashPathEffect( - intervals = floatArrayOf(2 * borderLength, squareSize - 2 * borderLength), - phase = borderLength, + BoxWithConstraints(modifier = Modifier.fillMaxSize()) { + val minDimension = minOf(this.constraints.minWidth, this.constraints.minHeight) + val squareSize = minDimension * QR_CODE_SIZE + val squareSizeInDp = with(LocalDensity.current) { squareSize.toDp() } + val verticalOffset = (constraints.minHeight - squareSize) / 2f + val horizontalOffset = (constraints.minWidth - squareSize) / 2f + + Canvas(modifier = Modifier.fillMaxSize()) { + drawRect( + color = overlayColor, + topLeft = Offset.Zero, + size = Size(size.width, verticalOffset), + ) + drawRect( + color = overlayColor, + topLeft = Offset(0f, verticalOffset), + size = Size(horizontalOffset, squareSize), + ) + drawRect( + color = overlayColor, + topLeft = Offset(horizontalOffset + squareSize, verticalOffset), + size = Size(horizontalOffset, squareSize), + ) + drawRect( + color = overlayColor, + topLeft = Offset(0f, verticalOffset + squareSize), + size = Size(size.width, verticalOffset), + ) + drawRect( + color = whiteColor, + topLeft = Offset(horizontalOffset, verticalOffset), + size = Size(squareSize, squareSize), + style = Stroke( + width = borderWidth, + join = StrokeJoin.Round, + pathEffect = PathEffect.dashPathEffect( + intervals = floatArrayOf(2 * borderLength, squareSize - 2 * borderLength), + phase = borderLength, + ), ), - ), - ) - text?.let { - val textLayoutResult = text.let { - textMeasurer.measure( - text = it, - style = style, - constraints = Constraints.fixedWidth(squareSize.roundToInt()), + ) + text?.let { + val textLayoutResult = text.let { + textMeasurer.measure( + text = it, + style = style, + constraints = Constraints.fixedWidth(squareSize.roundToInt()), + ) + } + drawText( + textLayoutResult = textLayoutResult, + color = whiteColor, + topLeft = Offset( + x = horizontalOffset, + y = verticalOffset - textPadding - textLayoutResult.size.height, + ), ) } - drawText( - textLayoutResult = textLayoutResult, - color = whiteColor, - topLeft = Offset( - x = horizontalOffset, - y = verticalOffset + squareSize + textPadding, - ), + } + if (pasteAction is PasteAction.Perform) { + TangemButton( + modifier = Modifier + .offset( + offset = { + IntOffset( + x = horizontalOffset.roundToInt(), + y = (verticalOffset + squareSize + textPadding).roundToInt(), + ) + }, + ) + .width(squareSizeInDp), + text = stringResourceSafe(id = R.string.wallet_connect_paste_from_clipboard), + colors = TangemButtonsDefaults.defaultTextButtonColors.copy(contentColor = whiteColor), + onClick = pasteAction.action, + textStyle = TangemTheme.typography.subtitle1, + icon = TangemButtonIconPosition.End(iconResId = R.drawable.ic_copy_24), + showProgress = false, + enabled = true, ) } } diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningState.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningState.kt index e9ca30f4e1..b18cf507af 100644 --- a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningState.kt +++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/QrScanningState.kt @@ -5,10 +5,17 @@ import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig import com.tangem.core.ui.extensions.TextReference @Immutable -data class QrScanningState( +internal data class QrScanningState( val message: TextReference?, val onQrScanned: (String) -> Unit, val onBackClick: () -> Unit, val onGalleryClick: () -> Unit, + val pasteAction: PasteAction = PasteAction.None, val bottomSheetConfig: TangemBottomSheetConfig? = null, -) \ No newline at end of file +) + +@Immutable +internal sealed interface PasteAction { + data object None : PasteAction + class Perform(val action: () -> Unit) : PasteAction +} \ No newline at end of file diff --git a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/transformers/InitializeQrScanningStateTransformer.kt b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/transformers/InitializeQrScanningStateTransformer.kt index 7c6d08b333..f4124119a7 100644 --- a/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/transformers/InitializeQrScanningStateTransformer.kt +++ b/features/qr-scanning/impl/src/main/java/com/tangem/feature/qrscanning/presentation/transformers/InitializeQrScanningStateTransformer.kt @@ -1,14 +1,20 @@ package com.tangem.feature.qrscanning.presentation.transformers +import com.tangem.core.ui.clipboard.ClipboardManager import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.stringReference import com.tangem.core.ui.extensions.wrappedList import com.tangem.domain.qrscanning.models.SourceType import com.tangem.feature.qrscanning.impl.R -import com.tangem.feature.qrscanning.presentation.QrScanningState import com.tangem.feature.qrscanning.model.QrScanningClickIntents +import com.tangem.feature.qrscanning.presentation.PasteAction +import com.tangem.feature.qrscanning.presentation.QrScanningState + +private const val WC_SCHEME = "wc" internal class InitializeQrScanningStateTransformer( private val clickIntents: QrScanningClickIntents, + private val clipboardManager: ClipboardManager, private val source: SourceType, private val network: String?, ) : QrScanningTransformer { @@ -16,7 +22,7 @@ internal class InitializeQrScanningStateTransformer( override fun transform(prevState: QrScanningState): QrScanningState { val message = when (source) { SourceType.SEND -> network?.let { resourceReference(R.string.send_qrcode_scan_info, wrappedList(it)) } - else -> null + SourceType.WALLET_CONNECT -> stringReference("Open Web3 app and chose WalletConnect option") } return QrScanningState( @@ -24,6 +30,20 @@ internal class InitializeQrScanningStateTransformer( onBackClick = clickIntents::onBackClick, onQrScanned = clickIntents::onQrScanned, onGalleryClick = clickIntents::onGalleryClicked, + pasteAction = constructPasteAction(), ) } + + private fun constructPasteAction(): PasteAction { + val uri = clipboardManager.getText() + return if (source == SourceType.WALLET_CONNECT && uri != null && isWalletConnectUri(uri)) { + PasteAction.Perform { clickIntents.onQrScanned(uri) } + } else { + PasteAction.None + } + } + + private fun isWalletConnectUri(uri: String): Boolean { + return uri.lowercase().startsWith(WC_SCHEME) + } } \ No newline at end of file diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/SendDestinationModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/SendDestinationModel.kt index 51c0bc7f63..4deb3cfe46 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/SendDestinationModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/subcomponents/destination/model/SendDestinationModel.kt @@ -117,10 +117,7 @@ internal class SendDestinationModel @Inject constructor( override fun onQrCodeScanClick() { analyticsEventHandler.send(SendDestinationAnalyticEvents.QrCodeButtonClicked(analyticsCategoryName)) router.push( - AppRoute.QrScanning( - source = AppRoute.QrScanning.Source.SEND, - networkName = cryptoCurrency.network.name, - ), + AppRoute.QrScanning(source = AppRoute.QrScanning.Source.Send(cryptoCurrency.network.name)), ) } 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 0a1397271b..0eaedec9ff 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 @@ -33,10 +33,7 @@ internal class DefaultSendRouter @Inject constructor( override fun openQrCodeScanner(network: String) { router.push( - AppRoute.QrScanning( - source = AppRoute.QrScanning.Source.SEND, - networkName = network, - ), + AppRoute.QrScanning(source = AppRoute.QrScanning.Source.Send(network)), ) } } \ No newline at end of file diff --git a/features/walletconnect/impl/build.gradle.kts b/features/walletconnect/impl/build.gradle.kts index f3822c54db..0fb71c954e 100644 --- a/features/walletconnect/impl/build.gradle.kts +++ b/features/walletconnect/impl/build.gradle.kts @@ -18,11 +18,16 @@ dependencies { implementation(projects.core.configToggles) implementation(projects.core.decompose) implementation(projects.core.ui) + implementation(projects.common.routing) /** Domain models */ + implementation(projects.domain.qrScanning.models) implementation(projects.domain.tokens.models) implementation(projects.domain.wallets.models) + /** Domain */ + implementation(projects.domain.qrScanning) + /** DI */ implementation(deps.hilt.android) kapt(deps.hilt.kapt) @@ -39,6 +44,7 @@ dependencies { implementation(deps.compose.ui.tooling) /** Other */ + implementation(deps.arrow.core) implementation(deps.decompose.ext.compose) implementation(deps.kotlin.immutable.collections) implementation(deps.timber) diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectionsModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectionsModel.kt index 0d6c65c6d2..ada41bf7d8 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectionsModel.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectionsModel.kt @@ -1,5 +1,7 @@ package com.tangem.features.walletconnect.connections.model +import arrow.core.getOrElse +import com.tangem.common.routing.AppRoute import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.navigation.Router @@ -7,19 +9,21 @@ import com.tangem.core.decompose.ui.UiMessageSender import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.message.DialogMessage import com.tangem.core.ui.message.EventMessageAction +import com.tangem.domain.qrscanning.models.SourceType +import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase import com.tangem.features.walletconnect.connections.entity.WcConnectionsState import com.tangem.features.walletconnect.connections.ui.preview.WcConnectionsPreviewData import com.tangem.features.walletconnect.impl.R import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.* +import timber.log.Timber import javax.inject.Inject @ModelScoped internal class WcConnectionsModel @Inject constructor( private val router: Router, private val uiMessageSender: UiMessageSender, + private val listenToQrScanningUseCase: ListenToQrScanningUseCase, override val dispatchers: CoroutineDispatcherProvider, ) : Model() { @@ -31,8 +35,22 @@ internal class WcConnectionsModel @Inject constructor( ), ).asStateFlow() + init { + listenQrUpdates() + } + + private fun listenQrUpdates() { + listenToQrScanningUseCase(SourceType.WALLET_CONNECT) + .getOrElse { emptyFlow() } + .onEach { + // TODO: [REDACTED_JIRA] + Timber.d(it) + } + .launchIn(modelScope) + } + private fun openQrScanScreen() { - // TODO("[REDACTED_JIRA]") + router.push(AppRoute.QrScanning(source = AppRoute.QrScanning.Source.WalletConnect)) } private fun disconnectAll() {