diff --git a/android/build.gradle b/android/build.gradle index f1b3b0e51..859b2419e 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -9,7 +9,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:8.7.2' classpath 'com.google.gms:google-services:4.4.2' - + classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.22' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/src/ui/components/Scanner/Scanner.scss b/src/ui/components/Scanner/Scanner.scss index a5d0d485b..b331ccbce 100644 --- a/src/ui/components/Scanner/Scanner.scss +++ b/src/ui/components/Scanner/Scanner.scss @@ -76,7 +76,7 @@ ion-grid.qr-code-scanner { width: 100%; align-items: center; justify-content: center; - background: rgba(var(--ion-color-neutral-700-rgb), 0.2); + background: var(--ion-color-black-900); } .page-footer .secondary-button { diff --git a/src/ui/components/Scanner/Scanner.tsx b/src/ui/components/Scanner/Scanner.tsx index 813fba898..c92b453d1 100644 --- a/src/ui/components/Scanner/Scanner.tsx +++ b/src/ui/components/Scanner/Scanner.tsx @@ -104,6 +104,8 @@ const Scanner = forwardRef( const [resumeMultiSig, setResumeMultiSig] = useState(null); const isHandlingQR = useRef(false); + const [isTransitioning, setIsTransitioning] = useState(false); + const [isAlreadyLoaded, setIsAlreadyLoaded] = useState(false); useEffect(() => { if (platforms.includes("mobileweb")) { @@ -111,6 +113,119 @@ const Scanner = forwardRef( } }, [platforms]); + useImperativeHandle(ref, () => ({ + stopScan, + })); + + const initScan = async () => { + if (Capacitor.isNativePlatform()) { + const allowed = await checkPermission(); + setPermisson(!!allowed); + onCheckPermissionFinish?.(!!allowed); + + if (allowed) { + await BarcodeScanner.removeAllListeners(); + const listener = await BarcodeScanner.addListener( + "barcodesScanned", + async (result) => { + await listener.remove(); + if (isHandlingQR.current) return; + isHandlingQR.current = true; + if (!result.barcodes?.length) return; + await processValue(result.barcodes[0].rawValue); + } + ); + + try { + await BarcodeScanner.startScan({ + formats: [BarcodeFormat.QrCode], + lensFacing: cameraDirection, + }); + } catch (error) { + showError("Error starting barcode scan:", error); + setScanUnavailable(true); + stopScan(); + } + } + + document?.querySelector("body")?.classList.add("scanner-active"); + setScanning(true); + document?.querySelector("body")?.classList.add("scanner-active"); + document + ?.querySelector("body.scanner-active > div:last-child") + ?.classList.remove("hide"); + } + }; + + useEffect(() => { + const onLoad = async () => { + if ( + routePath === TabsRoutePath.SCAN && + (isShowConnectionsModal || createIdentifierModalIsOpen) + ) { + await stopScan(); + return; + } + + const isDuplicateConnectionToast = currentToastMsgs.some( + (item) => ToastMsgType.DUPLICATE_CONNECTION === item.message + ); + const isRequestPending = currentToastMsgs.some((item) => + [ + ToastMsgType.CONNECTION_REQUEST_PENDING, + ToastMsgType.CREDENTIAL_REQUEST_PENDING, + ].includes(item.message) + ); + + const isScanning = + routePath === TabsRoutePath.SCAN || + [ + OperationType.SCAN_CONNECTION, + OperationType.SCAN_WALLET_CONNECTION, + OperationType.SCAN_SSI_BOOT_URL, + OperationType.SCAN_SSI_CONNECT_URL, + ].includes(currentOperation); + + const isMultisignScan = + [ + OperationType.MULTI_SIG_INITIATOR_SCAN, + OperationType.MULTI_SIG_RECEIVER_SCAN, + ].includes(currentOperation) && !isDuplicateConnectionToast; + + if ((isScanning && !isRequestPending) || isMultisignScan) { + await initScan(); + } else { + await stopScan(); + } + setIsAlreadyLoaded(true); + }; + onLoad(); + }, [ + currentOperation, + routePath, + isShowConnectionsModal, + createIdentifierModalIsOpen, + currentToastMsgs, + ]); + + useEffect(() => { + const handleCameraChange = async () => { + setIsTransitioning(true); + await stopScan(); + await initScan(); + setIsTransitioning(false); + }; + + if (!isAlreadyLoaded) return; + handleCameraChange(); + }, [cameraDirection]); + + useEffect(() => { + return () => { + stopScan(); + }; + }, []); + const checkPermission = async () => { const status = await BarcodeScanner.checkPermissions(); if (status.camera === "granted") { @@ -135,10 +250,6 @@ const Scanner = forwardRef( setGroupId(""); }; - useImperativeHandle(ref, () => ({ - stopScan, - })); - const handleConnectWallet = (id: string) => { if (/^b[1-9A-HJ-NP-Za-km-z]{33}/.test(id)) { dispatch(setToastMsg(ToastMsgType.PEER_ID_SUCCESS)); @@ -417,108 +528,6 @@ const Scanner = forwardRef( handleResolveOobi(content); }; - const initScan = async () => { - if (Capacitor.isNativePlatform()) { - const allowed = await checkPermission(); - setPermisson(!!allowed); - onCheckPermissionFinish?.(!!allowed); - - if (allowed) { - await BarcodeScanner.removeAllListeners(); - const listener = await BarcodeScanner.addListener( - "barcodesScanned", - async (result) => { - await listener.remove(); - if (isHandlingQR.current) return; - isHandlingQR.current = true; - if (!result.barcodes?.length) return; - await processValue(result.barcodes[0].rawValue); - } - ); - const listenerError = await BarcodeScanner.addListener( - "scanError", - async (result) => { - await listenerError.remove(); - } - ); - try { - await BarcodeScanner.startScan({ - formats: [BarcodeFormat.QrCode], - lensFacing: cameraDirection, - }); - } catch (error) { - showError("Error starting barcode scan:", error); - setScanUnavailable(true); - stopScan(); - } - } - - document?.querySelector("body")?.classList.add("scanner-active"); - setScanning(true); - document?.querySelector("body")?.classList.add("scanner-active"); - document - ?.querySelector("body.scanner-active > div:last-child") - ?.classList.remove("hide"); - } - }; - - useEffect(() => { - const onLoad = async () => { - if ( - routePath === TabsRoutePath.SCAN && - (isShowConnectionsModal || createIdentifierModalIsOpen) - ) { - await stopScan(); - return; - } - - const isDuplicateConnectionToast = currentToastMsgs.some( - (item) => ToastMsgType.DUPLICATE_CONNECTION === item.message - ); - const isRequestPending = currentToastMsgs.some((item) => - [ - ToastMsgType.CONNECTION_REQUEST_PENDING, - ToastMsgType.CREDENTIAL_REQUEST_PENDING, - ].includes(item.message) - ); - - const isScanning = - routePath === TabsRoutePath.SCAN || - [ - OperationType.SCAN_CONNECTION, - OperationType.SCAN_WALLET_CONNECTION, - OperationType.SCAN_SSI_BOOT_URL, - OperationType.SCAN_SSI_CONNECT_URL, - ].includes(currentOperation); - - const isMultisignScan = - [ - OperationType.MULTI_SIG_INITIATOR_SCAN, - OperationType.MULTI_SIG_RECEIVER_SCAN, - ].includes(currentOperation) && !isDuplicateConnectionToast; - - if ((isScanning && !isRequestPending) || isMultisignScan) { - await initScan(); - } else { - await stopScan(); - } - }; - onLoad(); - }, [ - currentOperation, - routePath, - cameraDirection, - isShowConnectionsModal, - createIdentifierModalIsOpen, - currentToastMsgs, - ]); - - useEffect(() => { - return () => { - stopScan(); - }; - }, []); - const handlePrimaryButtonAction = () => { stopScan(); dispatch(setCurrentOperation(OperationType.MULTI_SIG_INITIATOR_INIT)); @@ -602,7 +611,12 @@ const Scanner = forwardRef( className={containerClass} data-testid="qr-code-scanner" > - {scanning || mobileweb || scanUnavailable ? ( + {isTransitioning ? ( +
+ ) : scanning || mobileweb || scanUnavailable ? ( <> diff --git a/src/ui/styles/colors.scss b/src/ui/styles/colors.scss index 7388e6f6a..37f39799d 100644 --- a/src/ui/styles/colors.scss +++ b/src/ui/styles/colors.scss @@ -48,6 +48,7 @@ --ion-color-error-800-rgb: 163, 0, 0; + --ion-color-black-900: #000000; --ion-color-archive-400: #531fad; --ion-color-primary: var(--ion-color-neutral-700);