feat(qrcode-scanner): add an onScanSuccess callback to handle successful scans and exit the camera UI (#6580)

* fix(demos): set authentication token for mock API client in DesktopMode-basic

* feat(qrcode-scanner): add onScanSuccess callback to handle successful scans

* fix: include onScanSuccess in dependency array of useScanner hook

* fix: refactor onScanSuccess handling in QRCodeScanner and useScanner
This commit is contained in:
Sheldon Guo 2025-03-28 13:28:03 +08:00 committed by GitHub
parent 6bb754612c
commit d94c365e30
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 24 additions and 6 deletions

View File

@ -9,13 +9,13 @@
import { FileImageOutlined, LeftOutlined } from '@ant-design/icons'; import { FileImageOutlined, LeftOutlined } from '@ant-design/icons';
import { useActionContext } from '@nocobase/client'; import { useActionContext } from '@nocobase/client';
import { Html5Qrcode } from 'html5-qrcode'; import { Html5Qrcode } from 'html5-qrcode';
import React, { useEffect, useRef, useState } from 'react'; import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { ScanBox } from './ScanBox'; import { ScanBox } from './ScanBox';
import { useScanner } from './useScanner'; import { useScanner } from './useScanner';
const qrcodeEleId = 'qrcode'; const qrcodeEleId = 'qrcode';
export const QRCodeScannerInner = (props) => { export const QRCodeScannerInner = ({ setVisible }) => {
const containerRef = useRef<HTMLDivElement>(); const containerRef = useRef<HTMLDivElement>();
const imgUploaderRef = useRef<HTMLInputElement>(); const imgUploaderRef = useRef<HTMLInputElement>();
const { t } = useTranslation('block-workbench'); const { t } = useTranslation('block-workbench');
@ -23,9 +23,17 @@ export const QRCodeScannerInner = (props) => {
const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0); const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0); const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
const onScanSuccess = useCallback(
(text) => {
setVisible(false);
},
[setVisible],
);
const { startScanFile } = useScanner({ const { startScanFile } = useScanner({
onScannerSizeChanged: setOriginVideoSize, onScannerSizeChanged: setOriginVideoSize,
elementId: qrcodeEleId, elementId: qrcodeEleId,
onScanSuccess,
}); });
const getBoxStyle = (): React.CSSProperties => { const getBoxStyle = (): React.CSSProperties => {
@ -174,7 +182,7 @@ export const QRCodeScanner = (props) => {
return visible && cameraAvaliable ? ( return visible && cameraAvaliable ? (
<div style={style}> <div style={style}>
<QRCodeScannerInner /> <QRCodeScannerInner setVisible={setVisible} />
<LeftOutlined style={backIconStyle} onClick={() => setVisible(false)} /> <LeftOutlined style={backIconStyle} onClick={() => setVisible(false)} />
<div style={titleStyle}>{t('Scan QR code')}</div> <div style={titleStyle}>{t('Scan QR code')}</div>
</div> </div>

View File

@ -20,7 +20,7 @@ function removeStringIfStartsWith(text: string, prefix: string): string {
return text; return text;
} }
export function useScanner({ onScannerSizeChanged, elementId }) { export function useScanner({ onScannerSizeChanged, elementId, onScanSuccess }) {
const app = useApp(); const app = useApp();
const mobileManager = app.pm.get(MobileManager); const mobileManager = app.pm.get(MobileManager);
const basename = mobileManager.mobileRouter.basename.replace(/\/+$/, ''); const basename = mobileManager.mobileRouter.basename.replace(/\/+$/, '');
@ -50,12 +50,17 @@ export function useScanner({ onScannerSizeChanged, elementId }) {
}, },
}, },
(text) => { (text) => {
if (text?.startsWith('http')) {
window.location.href = text;
return;
}
navigate(removeStringIfStartsWith(text, basename)); navigate(removeStringIfStartsWith(text, basename));
onScanSuccess && onScanSuccess(text);
}, },
undefined, undefined,
); );
}, },
[navigate, onScannerSizeChanged, viewPoint, basename], [navigate, onScannerSizeChanged, viewPoint, basename, onScanSuccess],
); );
const stopScanner = useCallback(async (scanner: Html5Qrcode) => { const stopScanner = useCallback(async (scanner: Html5Qrcode) => {
const state = scanner.getState(); const state = scanner.getState();
@ -69,13 +74,18 @@ export function useScanner({ onScannerSizeChanged, elementId }) {
await stopScanner(scanner); await stopScanner(scanner);
try { try {
const { decodedText } = await scanner.scanFileV2(file, false); const { decodedText } = await scanner.scanFileV2(file, false);
if (decodedText?.startsWith('http')) {
window.location.href = decodedText;
return;
}
navigate(removeStringIfStartsWith(decodedText, basename)); navigate(removeStringIfStartsWith(decodedText, basename));
onScanSuccess && onScanSuccess(decodedText);
} catch (error) { } catch (error) {
alert(t('QR code recognition failed, please scan again')); alert(t('QR code recognition failed, please scan again'));
startScanCamera(scanner); startScanCamera(scanner);
} }
}, },
[stopScanner, scanner, navigate, basename, t, startScanCamera], [stopScanner, scanner, navigate, basename, t, startScanCamera, onScanSuccess],
); );
useEffect(() => { useEffect(() => {