diff --git a/package-lock.json b/package-lock.json index c894898..6685ef1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "axios": "^1.7.2", "dotenv": "^16.4.5", "expo": "~51.0.24", + "expo-barcode-scanner": "~13.0.1", "expo-camera": "~15.0.14", "expo-dev-client": "~4.0.22", "expo-image-manipulator": "^12.0.5", @@ -33,11 +34,16 @@ "expo-linking": "~6.3.1", "expo-sharing": "~12.0.1", "expo-status-bar": "~1.12.1", + "jpeg-js": "^0.4.4", + "jsqr": "^1.4.0", "react": "18.2.0", "react-native": "0.74.5", + "react-native-canvas": "^0.1.40", "react-native-dotenv": "^3.4.11", + "react-native-fs": "^2.20.0", "react-native-gesture-handler": "~2.16.1", "react-native-get-random-values": "^1.11.0", + "react-native-image-picker": "^7.1.2", "react-native-qrcode-svg": "^6.3.1", "react-native-reanimated": "~3.10.1", "react-native-safe-area-context": "^4.10.5", @@ -10521,6 +10527,11 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, + "node_modules/ctx-polyfill": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/ctx-polyfill/-/ctx-polyfill-1.1.4.tgz", + "integrity": "sha512-tz65F3/zmZ2+CMtn4MhNhYi/yIN9dKnItMKzSkH2GE6dBpAIbUR0K5pSHWqUL3OuNBCA70DKlWZYUClHfydIXg==" + }, "node_modules/dag-map": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/dag-map/-/dag-map-1.0.2.tgz", @@ -11260,6 +11271,17 @@ "expo": "*" } }, + "node_modules/expo-barcode-scanner": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/expo-barcode-scanner/-/expo-barcode-scanner-13.0.1.tgz", + "integrity": "sha512-xBGLT1An2gpAMIQRTLU3oHydKohX8r8F9/ait1Fk9Vgd0GraFZbP4IiT7nHMlaw4H6E7Muucf7vXpGV6u7d4HQ==", + "dependencies": { + "expo-image-loader": "~4.7.0" + }, + "peerDependencies": { + "expo": "*" + } + }, "node_modules/expo-camera": { "version": "15.0.14", "resolved": "https://registry.npmjs.org/expo-camera/-/expo-camera-15.0.14.tgz", @@ -13300,6 +13322,11 @@ "resolved": "https://registry.npmjs.org/join-component/-/join-component-1.1.0.tgz", "integrity": "sha512-bF7vcQxbODoGK1imE2P9GS9aw4zD0Sd+Hni68IMZLj7zRnquH7dXUmMw9hDI5S/Jzt7q+IyTXN0rSg2GI0IKhQ==" }, + "node_modules/jpeg-js": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz", + "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==" + }, "node_modules/js-cookie": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", @@ -13510,6 +13537,11 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsqr": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsqr/-/jsqr-1.4.0.tgz", + "integrity": "sha512-dxLob7q65Xg2DvstYkRpkYtmKm2sPJ9oFhrhmudT1dZvNFFTlroai3AWSpLey/w5vMcLBXRgOJsbXpdN9HzU/A==" + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -16019,6 +16051,17 @@ } } }, + "node_modules/react-native-canvas": { + "version": "0.1.40", + "resolved": "https://registry.npmjs.org/react-native-canvas/-/react-native-canvas-0.1.40.tgz", + "integrity": "sha512-VVQreUUGBYZF4qjCP09MoK8Eh5wr1LuL2XNKvQLcbd/NJ93Y8bT2pwwrZJXANCTVzAY2o/B9g+f0B7fS/vfXOA==", + "dependencies": { + "ctx-polyfill": "^1.1.4" + }, + "peerDependencies": { + "react-native-webview": ">=5.10.0 || >=6.1.0" + } + }, "node_modules/react-native-dotenv": { "version": "3.4.11", "resolved": "https://registry.npmjs.org/react-native-dotenv/-/react-native-dotenv-3.4.11.tgz", @@ -16030,6 +16073,29 @@ "@babel/runtime": "^7.20.6" } }, + "node_modules/react-native-fs": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/react-native-fs/-/react-native-fs-2.20.0.tgz", + "integrity": "sha512-VkTBzs7fIDUiy/XajOSNk0XazFE9l+QlMAce7lGuebZcag5CnjszB+u4BdqzwaQOdcYb5wsJIsqq4kxInIRpJQ==", + "dependencies": { + "base-64": "^0.1.0", + "utf8": "^3.0.0" + }, + "peerDependencies": { + "react-native": "*", + "react-native-windows": "*" + }, + "peerDependenciesMeta": { + "react-native-windows": { + "optional": true + } + } + }, + "node_modules/react-native-fs/node_modules/base-64": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", + "integrity": "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==" + }, "node_modules/react-native-gesture-handler": { "version": "2.16.2", "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.16.2.tgz", @@ -16057,6 +16123,15 @@ "react-native": ">=0.56" } }, + "node_modules/react-native-image-picker": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/react-native-image-picker/-/react-native-image-picker-7.1.2.tgz", + "integrity": "sha512-b5y5nP60RIPxlAXlptn2QwlIuZWCUDWa/YPUVjgHc0Ih60mRiOg1PSzf0IjHSLeOZShCpirpvSPGnDExIpTRUg==", + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "node_modules/react-native-qrcode-svg": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/react-native-qrcode-svg/-/react-native-qrcode-svg-6.3.1.tgz", @@ -18268,6 +18343,11 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index d93e179..5dab797 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "axios": "^1.7.2", "dotenv": "^16.4.5", "expo": "~51.0.24", + "expo-barcode-scanner": "~13.0.1", "expo-camera": "~15.0.14", "expo-dev-client": "~4.0.22", "expo-image-manipulator": "^12.0.5", @@ -37,11 +38,16 @@ "expo-linking": "~6.3.1", "expo-sharing": "~12.0.1", "expo-status-bar": "~1.12.1", + "jpeg-js": "^0.4.4", + "jsqr": "^1.4.0", "react": "18.2.0", "react-native": "0.74.5", + "react-native-canvas": "^0.1.40", "react-native-dotenv": "^3.4.11", + "react-native-fs": "^2.20.0", "react-native-gesture-handler": "~2.16.1", "react-native-get-random-values": "^1.11.0", + "react-native-image-picker": "^7.1.2", "react-native-qrcode-svg": "^6.3.1", "react-native-reanimated": "~3.10.1", "react-native-safe-area-context": "^4.10.5", diff --git a/screens/QRScannerScreen.tsx b/screens/QRScannerScreen.tsx index 16dd05e..5341c88 100644 --- a/screens/QRScannerScreen.tsx +++ b/screens/QRScannerScreen.tsx @@ -6,9 +6,14 @@ import * as ImagePicker from 'expo-image-picker'; import ScannedDataBox from '../components/ScannedDataBox'; import { scanQRCode } from '../api/qrCodeAPI'; import SettingsScreen from './SettingsScreen'; +import {scanFromURLAsync } from 'expo-camera'; + const { width: screenWidth, height: screenHeight } = Dimensions.get('window'); + + + const QRScannerScreen: React.FC = () => { const [isSettingsModalVisible, setIsSettingsModalVisible] = useState(false); const [enableTorch, setEnableTorch] = useState(false); @@ -42,50 +47,36 @@ const QRScannerScreen: React.FC = () => { const codeScanner = useCodeScanner({ codeTypes: ['qr'], // Only scan QR codes onCodeScanned: (codes) => { - if (!scanned) { - handlePayload(codes[0]?.value); // Extract and handle the value only + if (!scanned && codes[0]?.value) { + handlePayload(codes[0].value); // Extract and handle the value only if it exists } } }); - const openImagePicker = async () => { + const readQRFromImage = async () => { + console.log("Reading QR code from image"); + const result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images, - allowsEditing: false, + allowsEditing: false, // Don't ask user to crop images quality: 1, }); - if (!result.canceled && result.assets.length > 0) { - const { uri } = result.assets[0]; + if (result && result.assets && result.assets.length > 0 && result.assets[0].uri) { // Ensure the uri is not empty try { - // If using expo-camera or similar packages: - const scannedResult = await scanQRCodeFromImage(uri); // Function to scan QR code from the selected image URL - if (scannedResult) { - handlePayload(scannedResult); + const scannedResult = await scanFromURLAsync(result.assets[0].uri); + if (scannedResult && scannedResult[0] && scannedResult[0].data) { + handlePayload(scannedResult[0].data); + console.log('QR code data from image:', scannedResult[0].data); } else { console.log("No QR code found in the selected image"); } } catch (error) { - console.error('Error scanning QR code from image:', error); + console.error('Error scanning QR code from image:', error); } } }; - const scanQRCodeFromImage = async (uri: string) => { - // This method can vary depending on the package used for scanning - // For example, using expo-camera or other available options to scan QR code from an image URL - // Implement scanFromURLAsync if using expo-camera - - // If using expo-camera - // const scannedResult = await scanFromURLAsync(uri); - // return scannedResult?.[0]?.data; - - // If using other methods, implement them accordingly - // Here we simulate scanning with a placeholder result - const placeholderResult = "SIMULATED_PAYLOAD"; // Replace with actual scanning logic - return placeholderResult; - }; - if (!hasPermission) { return Requesting camera permission...; } @@ -125,7 +116,7 @@ const QRScannerScreen: React.FC = () => { {/* Gallery Button */} - +