From 70b7fdbe1b5dcfedaa04bf19e5baad13901e37b5 Mon Sep 17 00:00:00 2001 From: Samu Date: Sat, 29 Aug 2026 22:11:01 +0200 Subject: [PATCH] feat: barcode scanner hook (BarcodeDetector + zxing fallback) and camera component --- package-lock.json | 32 ++++++++ package.json | 1 + vitest.config.ts | 2 +- web/src/components/Scanner.tsx | 39 ++++++++++ web/src/hooks/useBarcodeScanner.ts | 121 +++++++++++++++++++++++++++++ web/test/scanner.test.ts | 88 +++++++++++++++++++++ 6 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 web/src/components/Scanner.tsx create mode 100644 web/src/hooks/useBarcodeScanner.ts create mode 100644 web/test/scanner.test.ts diff --git a/package-lock.json b/package-lock.json index 3ebcdef..eb7886d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@fastify/cookie": "^11.0.0", "@fastify/static": "^8.0.0", + "@zxing/library": "^0.23.0", "argon2": "^0.41.1", "better-sqlite3": "^13.0.3", "fastify": "^5.0.0", @@ -2322,6 +2323,28 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@zxing/library": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@zxing/library/-/library-0.23.0.tgz", + "integrity": "sha512-6fkkoFwP8CHxl6ugnPsj74PLJgX2iRv5zczGAyt5OBzQgxFhuhF0NCEc4t4OvSr8xAv2MRLlI0Iu9ZGDZQ2urA==", + "license": "Apache-2.0", + "dependencies": { + "ts-custom-error": "^3.3.1" + }, + "engines": { + "node": ">= 24.0.0" + }, + "optionalDependencies": { + "@zxing/text-encoding": "~0.9.0" + } + }, + "node_modules/@zxing/text-encoding": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz", + "integrity": "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==", + "license": "(Unlicense OR Apache-2.0)", + "optional": true + }, "node_modules/abstract-logging": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", @@ -4705,6 +4728,15 @@ "tree-kill": "cli.js" } }, + "node_modules/ts-custom-error": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/ts-custom-error/-/ts-custom-error-3.3.1.tgz", + "integrity": "sha512-5OX1tzOjxWEgsr/YEUWSuPrQ00deKLh6D7OTWcvNHm12/7QPyRh8SYpyWvA4IZv8H/+GQWQEh/kwo95Q9OVW1A==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", diff --git a/package.json b/package.json index 6b1bf9e..d61b41b 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "dependencies": { "@fastify/cookie": "^11.0.0", "@fastify/static": "^8.0.0", + "@zxing/library": "^0.23.0", "argon2": "^0.41.1", "better-sqlite3": "^13.0.3", "fastify": "^5.0.0", diff --git a/vitest.config.ts b/vitest.config.ts index 4dbb933..e5fd4b2 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -13,7 +13,7 @@ export default defineConfig({ { test: { name: 'web', - include: ['web/test/**/*.test.tsx'], + include: ['web/test/**/*.test.{ts,tsx}'], environment: 'jsdom', setupFiles: ['web/test/setup.ts'], }, diff --git a/web/src/components/Scanner.tsx b/web/src/components/Scanner.tsx new file mode 100644 index 0000000..5102441 --- /dev/null +++ b/web/src/components/Scanner.tsx @@ -0,0 +1,39 @@ +import { useRef } from 'react' +import { useBarcodeScanner } from '../hooks/useBarcodeScanner.js' + +export default function Scanner({ + enabled, + onDetect, +}: { + enabled: boolean + onDetect: (code: string) => void +}) { + const videoRef = useRef(null) + const { status } = useBarcodeScanner(videoRef, onDetect, enabled) + + return ( +
+