From 2cf7f5dc7b0cb2aa877b3dee69a45f548dfc8579 Mon Sep 17 00:00:00 2001 From: David Katrinka Date: Fri, 9 Jan 2026 21:10:18 +0100 Subject: [PATCH] added qr scaning and finishing touches --- app.json | 19 ++++- app/(tabs)/tests.tsx | 2 +- app/qr.tsx | 104 ++++++++++++++++++++++++++ app/user-tests/doing/[id].tsx | 2 +- components/ui/answer.tsx | 2 +- components/ui/custom-select/index.tsx | 3 +- package-lock.json | 25 +++++++ package.json | 5 +- 8 files changed, 153 insertions(+), 9 deletions(-) create mode 100644 app/qr.tsx diff --git a/app.json b/app.json index 42340dd..27fca11 100644 --- a/app.json +++ b/app.json @@ -9,7 +9,11 @@ "userInterfaceStyle": "automatic", "newArchEnabled": true, "ios": { - "supportsTablet": true + "supportsTablet": true, + "infoPlist": { + "NSCameraUsageDescription": "$(PRODUCT_NAME) needs access to your Camera." + }, + "bundleIdentifier": "com.anonymous.ai-questions-app" }, "android": { "adaptiveIcon": { @@ -19,7 +23,9 @@ "monochromeImage": "./assets/images/android-icon-monochrome.png" }, "edgeToEdgeEnabled": true, - "predictiveBackGestureEnabled": false + "predictiveBackGestureEnabled": false, + "permissions": ["android.permission.CAMERA"], + "package": "com.anonymous.ai_questions_app" }, "web": { "output": "static", @@ -39,7 +45,14 @@ } } ], - "expo-secure-store" + "expo-secure-store", + [ + "react-native-vision-camera", + { + "cameraPermissionText": "$(PRODUCT_NAME) needs access to your Camera.", + "enableCodeScanner": true + } + ] ], "experiments": { "typedRoutes": true, diff --git a/app/(tabs)/tests.tsx b/app/(tabs)/tests.tsx index 1da2c00..7665138 100644 --- a/app/(tabs)/tests.tsx +++ b/app/(tabs)/tests.tsx @@ -49,7 +49,7 @@ export default function TestsScreen() { selectedValue={category ? `${category}` : undefined} onValueChange={handleChangeCategory} placeholder="Select category" /> - diff --git a/app/qr.tsx b/app/qr.tsx new file mode 100644 index 0000000..9f14573 --- /dev/null +++ b/app/qr.tsx @@ -0,0 +1,104 @@ +import { useStartTestMutation } from "@/api/userTests"; +import { Button, ButtonText } from "@/components/ui/button"; +import { ThemedText } from "@/components/ui/themed-text"; +import getErrorAxiosMessage from "@/utils/get-error-axios-message"; +import { router, Stack, useFocusEffect } from "expo-router"; +import { useCallback, useState } from "react"; +import { StyleSheet, Text, View } from "react-native"; +import { useToast } from "react-native-toast-notifications"; +import { + Camera, + useCameraDevice, + useCameraPermission, + useCodeScanner, +} from "react-native-vision-camera"; + +const QrScreen = () => { + const device = useCameraDevice("back"); + const { hasPermission, requestPermission } = useCameraPermission(); + const toast = useToast(); + const [scanned, setScanned] = useState(false); + + const { mutate, isPending } = useStartTestMutation(); + + useFocusEffect( + useCallback(() => { + setScanned(false); + }, []) + ); + + const handleStartTest = (id: number) => { + mutate( + { + test_id: id, + }, + { + onSuccess: (data) => { + router.push(`/user-tests/doing/${data.id}`); + }, + onError: (error) => { + toast.show(getErrorAxiosMessage(error), { type: "danger" }); + }, + } + ); + }; + + const codeScanner = useCodeScanner({ + codeTypes: ["qr", "ean-13"], + onCodeScanned: (codes) => { + if (scanned) return; + if (!codes || codes.length === 0) return; + + const scannedValue = codes[0].value; + if (scannedValue) { + const id = parseInt(scannedValue, 10); + setScanned(true); + toast.show("Going to test", { type: "normal" }); + router.push(`/tests/${id}`); + // handleStartTest(id); + } else { + toast.show("Invalid QR code", { type: "warning" }); + } + }, + }); + + if (!hasPermission) { + return ( + + + No camera permission + + + ); + } + if (!device) { + return ( + + No camera device found + + ); + } + return ( + <> + + + + ); +}; + +const styles = StyleSheet.create({ + center: { + flex: 1, + alignItems: "center", + justifyContent: "center", + }, +}); + +export default QrScreen; diff --git a/app/user-tests/doing/[id].tsx b/app/user-tests/doing/[id].tsx index 41e0a33..8c2803d 100644 --- a/app/user-tests/doing/[id].tsx +++ b/app/user-tests/doing/[id].tsx @@ -252,7 +252,7 @@ const DoingUserTestScreen = () => { const styles = StyleSheet.create({ container: { flex: 1, - alignItems: "flex-start", + width: "100%" }, top: { width: "100%", diff --git a/components/ui/answer.tsx b/components/ui/answer.tsx index 01f51e7..b1a83fd 100644 --- a/components/ui/answer.tsx +++ b/components/ui/answer.tsx @@ -106,7 +106,7 @@ const Answer = ({ {isTextType ? ( ) : ( - + {answer.id} )} diff --git a/components/ui/custom-select/index.tsx b/components/ui/custom-select/index.tsx index 811e104..09eaf63 100644 --- a/components/ui/custom-select/index.tsx +++ b/components/ui/custom-select/index.tsx @@ -33,12 +33,13 @@ const CustomSelect = ({ return options; }, [options, noneOption]); const backgroundColor = useThemeColor({ }, "background"); + const metaColor = useThemeColor({}, "meta"); return (