From 3e64bfbc2a8ad97aac267d2e1a7d0dd6a99dc846 Mon Sep 17 00:00:00 2001 From: Kasra Ghaffari <18647702+Kasra-G@users.noreply.github.com> Date: Fri, 9 Feb 2024 21:15:02 -0500 Subject: [PATCH 1/6] Hopefully fixed scanner (rewrite) --- projects/mybyte/context/AuthContext.tsx | 105 ++++++++++++++++++- projects/mybyte/package.json | 1 + projects/mybyte/pages/qrRead.tsx | 133 +++++++++++++----------- yarn.lock | 70 +++++++++++++ 4 files changed, 245 insertions(+), 64 deletions(-) diff --git a/projects/mybyte/context/AuthContext.tsx b/projects/mybyte/context/AuthContext.tsx index f60df09..d9e01b7 100644 --- a/projects/mybyte/context/AuthContext.tsx +++ b/projects/mybyte/context/AuthContext.tsx @@ -527,8 +527,7 @@ export const AuthContextProvider = ({ */ const checkinUser = async (userid: string) => { try { - const docRef = doc(registerRef, userid); - await updateDoc(docRef, { + await updateDoc(doc(registerRef, userid ? userid : ""), { checkedIn: true, }); setUserInformation(userid); @@ -553,6 +552,40 @@ export const AuthContextProvider = ({ } }; + /** + * checks if a user is checked in + * @param userid uuid of the user + * @return boolean true if the user is checked in + */ + const isUserCheckedIn = async (userid: string) => { + try { + const docRef = doc(registerRef, userid); + const docSnap = await getDoc(docRef); + + return docSnap.exists() && docSnap.data().checkedIn; + } catch (err: any) { + console.log(err); + } + }; + + /** + * gets a user's tshirt size by userid + * @param userid uuid of the user + * @return string size of the tshirt + */ + const getTShirtSizeOfUser = async (userid: string) => { + try { + const docRef = doc(registerRef, userid); + const docSnap = await getDoc(docRef); + if (!docSnap.exists()) { + return null; + } + return docSnap.data().shirtSize; + } catch (err: any) { + console.log(err); + } + }; + /** * Accepts a user by userid. * @param userid uuid of a user @@ -642,6 +675,38 @@ export const AuthContextProvider = ({ return docSnap.data().first_name; }; + /** + * Get's user's full name from userid + * @param userid user's uuid + * @returns string of their full name + */ + const getNameOfUser = async (userid: string) => { + const docRef = doc(userRef, userid ? userid : ""); + const docSnap = await getDoc(docRef); + + if (!docSnap.exists()) { + return null; + } + + return docSnap.data().name; + }; + + /** + * Get's a user's registered events + * @param userid user's id + * @returns an array of registered events + */ + const getRegisteredEventsForUser = async (userid: string) => { + const docRef = doc(userRef, userid ? userid : ""); + const docSnap = await getDoc(docRef); + + if (!docSnap.exists()) { + return null; + } + + return docSnap.data().registered; + }; + /** * Get's a user's registered events * @param first_name user's first_name @@ -799,6 +864,35 @@ export const AuthContextProvider = ({ return false; }; + const removePoints = async (uid: string, number: number) => { + if ( + user_type == null || + user_type == undefined || + user_type != "service_writer" + ) + throw new Error("Unauthorized"); + const docRef = doc(userRef, uid); + const docSnap = await getDoc(docRef); + + if (!docSnap.exists()) throw "User does not exist"; + const points = docSnap.data().points; + if (!points || points < number) { + throw `${docSnap.data().name} does not have enough points!`; + } + + try { + updateDoc(docRef, { + points: increment(-1 * number), + }); + return true; + } catch (error) { + if (error instanceof FirebaseError) handleError(error); + if (error instanceof Error) throw error; + if (typeof error === "string") throw new Error(error); + } + return false; + }; + const checkIn = async (uid: string) => { if ( user_type == null || @@ -809,7 +903,7 @@ export const AuthContextProvider = ({ const docRef = doc(userRef, uid); try { updateDoc(docRef, { - checkIn: true, + checkedIn: true, }); return true; } catch (error) { @@ -949,7 +1043,10 @@ export const AuthContextProvider = ({ hasFirstAndLastName, validUser, getFirstName, + getNameOfUser, getRegisteredEvents, + getRegisteredEventsForUser, + isUserCheckedIn, storeUserRegistrationInformation, setUserInformation, currEvent, @@ -964,6 +1061,7 @@ export const AuthContextProvider = ({ denyTeams, user_type, givePoints, + removePoints, checkIn, confirmEmails, confirmedOnTeam, @@ -971,6 +1069,7 @@ export const AuthContextProvider = ({ giveTeamPoints, checkinUser, checkoutUser, + getTShirtSizeOfUser, triggerRegistrationEmail, triggerESportsRegistrationEmail, }} diff --git a/projects/mybyte/package.json b/projects/mybyte/package.json index fa50cb2..355048e 100644 --- a/projects/mybyte/package.json +++ b/projects/mybyte/package.json @@ -20,6 +20,7 @@ "react-dom": "18.2.0", "react-hook-form": "^7.41.5", "react-phone-number-input": "^3.2.15", + "react-qr-reader": "^3.0.0-beta-1", "react-select": "^5.7.0", "react-select-country-list": "^2.2.3", "react-tsparticles": "2", diff --git a/projects/mybyte/pages/qrRead.tsx b/projects/mybyte/pages/qrRead.tsx index 3f8fe09..04cc5be 100644 --- a/projects/mybyte/pages/qrRead.tsx +++ b/projects/mybyte/pages/qrRead.tsx @@ -1,8 +1,13 @@ import React, {useState} from "react"; -import Html5QrcodePlugin from "../components/Html5QrcodePlugin"; import OrganizerRoute from "../components/OrganizerRoute"; import {useAuth} from "../context/AuthContext"; -import {Html5QrcodeScannerState} from "html5-qrcode"; +import {QrReader} from 'react-qr-reader'; + + +const initialUser = { + name: "N/A", + shirtSize: "N/A" +} export default function QrRead(props: any) { const { @@ -13,14 +18,15 @@ export default function QrRead(props: any) { givePoints, getNameOfUser, getRegisteredEventsForUser, - getTShirtSizeOfUser + getTShirtSizeOfUser, + user_type } = useAuth(); - const [data, setData] = useState("No result"); + const [scannedUID, setScannedUID] = useState(""); + const [user, setUser] = useState(initialUser); const [status, setStatus] = useState("Waiting for scan"); - const ref = React.useRef(null); const selectWhich: JSX.Element = ( - @@ -41,54 +47,49 @@ export default function QrRead(props: any) { ); - function pauseScanner() { - if (ref.current == null || ref.current?.html5QrcodeScanner == null) return - if (ref.current.html5QrcodeScanner.getState() == Html5QrcodeScannerState.SCANNING) { - ref.current.html5QrcodeScanner.pause(); - } - } - - function resumeScanner() { - if (ref.current == null || ref.current?.html5QrcodeScanner == null) return - if (ref.current.html5QrcodeScanner.getState() == Html5QrcodeScannerState.PAUSED) { - ref.current.html5QrcodeScanner.resume(); - } - } - const determineAction = async (uid: string) => { - // action state kept resetting to its default state for some reason - const val = document - .getElementsByTagName("select") - .namedItem("what-for")?.value; - let outcomeMessage = ""; + console.log(user_type) try { + if (!uid) throw "No QR Code has been scanned!"; + // action state kept resetting to its default state for some reason + if (uid.includes("/")) { + window.alert("Not valid User QR-Code"); + return; + } // https://stackoverflow.com/questions/52850099/what-is-the-reg-expression-for-firestore-constraints-on-document-ids + + const selectedOption = document + .getElementsByTagName("select") + .namedItem("what-for")?.value; + const name = await getNameOfUser(uid) if (!name) { - throw new Error("User not found!"); + throw "User not found!"; } - outcomeMessage = `Successfully completed ${val} for ${name}`; - switch (val) { + const tShirtSize = await getTShirtSizeOfUser(uid) + setUser({name: name, shirtSize: tShirtSize}) + outcomeMessage = `Successfully completed ${selectedOption} for ${name}`; + switch (selectedOption) { case "checkin-first-day": if (!("HACKS9" in await getRegisteredEventsForUser(uid))) { - throw new Error(`${name} is not registered for UGAHacks 9!`); + throw `${name} is not registered for UGAHacks 9!`; } if (await isUserCheckedIn(uid)) { - throw new Error(`${name} is already checked in!`); + throw `${name} is already checked in!`; } await checkinUser(uid); givePoints(uid, 100); - outcomeMessage = `Checked in ${name}!\nT-Shirt size: ${await getTShirtSizeOfUser(uid)}`; + outcomeMessage = `Checked in ${name}!`; break; case "checkin-other": if (!("HACKS9" in await getRegisteredEventsForUser(uid))) { - throw new Error(`${name} is not registered for UGAHacks 9!`); + throw `${name} is not registered for UGAHacks 9!`; } if (await isUserCheckedIn(uid)) { - throw new Error(`${name} is already checked in!`); + throw `${name} is already checked in!`; } checkinUser(uid); - outcomeMessage = `Checked in ${name}!\nT-Shirt size: ${await getTShirtSizeOfUser(uid)}`; + outcomeMessage = `Checked in ${name}!`; break; case "checkout": checkoutUser(uid); @@ -154,41 +155,51 @@ export default function QrRead(props: any) { break; } } catch (error) { - if (error instanceof Error) { - outcomeMessage = error.message; + if (typeof error === "string") { + outcomeMessage = error; + } else { + throw error; } } finally { setStatus(outcomeMessage); + setScannedUID("") window.alert(outcomeMessage); } }; - let lock = false return ( - { - if (ref.current == null || ref.current?.html5QrcodeScanner == null) return - if (data === decodedText) return; - if (decodedText.includes("/")) { - window.alert("Not valid User QR-Code"); - return; - } // https://stackoverflow.com/questions/52850099/what-is-the-reg-expression-for-firestore-constraints-on-document-ids - if (lock) return; - lock = true; - setData(decodedText); - if (ref.current?.html5QrcodeScanner?.getState() !== Html5QrcodeScannerState.PAUSED) { - await determineAction(decodedText); - } - lock = false; - }} - /> - Status: {status} -
Scanner Options:
- {selectWhich} +
+ { + if (!result) return; + setScannedUID(result.getText()) + }} + /> +
+
+ Scanned UID: {scannedUID}

+ + Name: {user.name}
+ Shirt Size: {user.shirtSize}
+
+
Previous Status: {status}
+ + {selectWhich} +
+ +
+
+
); } diff --git a/yarn.lock b/yarn.lock index 16e7527..15c0e27 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1591,6 +1591,40 @@ __metadata: languageName: node linkType: hard +"@zxing/browser@npm:0.0.7": + version: 0.0.7 + resolution: "@zxing/browser@npm:0.0.7" + dependencies: + "@zxing/text-encoding": "npm:^0.9.0" + peerDependencies: + "@zxing/library": ^0.18.3 + dependenciesMeta: + "@zxing/text-encoding": + optional: true + checksum: 853b391802c154ded7cf921af4346c67a1030d87551741a5aab0d13f38d9396a7fbbe8263fac4e00a722523f2e7e8edaf2cac15b1476e8106053bc33d2a58830 + languageName: node + linkType: hard + +"@zxing/library@npm:^0.18.3": + version: 0.18.6 + resolution: "@zxing/library@npm:0.18.6" + dependencies: + "@zxing/text-encoding": "npm:~0.9.0" + ts-custom-error: "npm:^3.0.0" + dependenciesMeta: + "@zxing/text-encoding": + optional: true + checksum: e8eb88812227e610920cfd0c1bc64e2444d93e57e7b5243b332135a4b7d8c16e6b7cbe2452e5278b478b78e882d541cfdf5dc483877a755806a512cbf83349fa + languageName: node + linkType: hard + +"@zxing/text-encoding@npm:^0.9.0, @zxing/text-encoding@npm:~0.9.0": + version: 0.9.0 + resolution: "@zxing/text-encoding@npm:0.9.0" + checksum: 268e4ef64b8eaa32b990240bdfd1f7b3e2b501a6ed866a565f7c9747f04ac884fbe0537fe12bb05d9241b98fb111270c0fd0023ef0a02d23a6619b4589e98f6b + languageName: node + linkType: hard + "abbrev@npm:^2.0.0": version: 2.0.0 resolution: "abbrev@npm:2.0.0" @@ -4335,6 +4369,7 @@ __metadata: react-dom: "npm:18.2.0" react-hook-form: "npm:^7.41.5" react-phone-number-input: "npm:^3.2.15" + react-qr-reader: "npm:^3.0.0-beta-1" react-select: "npm:^5.7.0" react-select-country-list: "npm:^2.2.3" react-tsparticles: "npm:2" @@ -5152,6 +5187,20 @@ __metadata: languageName: node linkType: hard +"react-qr-reader@npm:^3.0.0-beta-1": + version: 3.0.0-beta-1 + resolution: "react-qr-reader@npm:3.0.0-beta-1" + dependencies: + "@zxing/browser": "npm:0.0.7" + "@zxing/library": "npm:^0.18.3" + rollup: "npm:^2.67.2" + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + checksum: ed16730e1187ce57e764e0f4c5010d237128abe941bd174fce27785939cc835cc08b09e8e4ce7f9ecaf67fa0e87bfaf6fc7e76993c8ec2e640b3454e6a11953e + languageName: node + linkType: hard + "react-select-country-list@npm:^2.2.3": version: 2.2.3 resolution: "react-select-country-list@npm:2.2.3" @@ -5380,6 +5429,20 @@ __metadata: languageName: node linkType: hard +"rollup@npm:^2.67.2": + version: 2.79.1 + resolution: "rollup@npm:2.79.1" + dependencies: + fsevents: "npm:~2.3.2" + dependenciesMeta: + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: df087b701304432f30922bbee5f534ab189aa6938bd383b5686c03147e0d00cd1789ea10a462361326ce6b6ebe448ce272ad3f3cc40b82eeb3157df12f33663c + languageName: node + linkType: hard + "run-parallel@npm:^1.1.9": version: 1.2.0 resolution: "run-parallel@npm:1.2.0" @@ -5967,6 +6030,13 @@ __metadata: languageName: node linkType: hard +"ts-custom-error@npm:^3.0.0": + version: 3.3.1 + resolution: "ts-custom-error@npm:3.3.1" + checksum: 92e3a2c426bf6049579aeb889b6f9787e0cfb6bb715a1457e2571708be7fe739662ca9eb2a8c61b72a2d32189645f4fbcf1a370087e030d922e9e2a7b7c1c994 + languageName: node + linkType: hard + "ts-interface-checker@npm:^0.1.9": version: 0.1.13 resolution: "ts-interface-checker@npm:0.1.13" From 367c3e5fac12712b780dab3fb21d5e9cf1da56e8 Mon Sep 17 00:00:00 2001 From: Kasra Ghaffari <18647702+Kasra-G@users.noreply.github.com> Date: Fri, 9 Feb 2024 21:34:21 -0500 Subject: [PATCH 2/6] Attempt to fix the scanner only scanning once on mobile --- projects/mybyte/pages/qrRead.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/projects/mybyte/pages/qrRead.tsx b/projects/mybyte/pages/qrRead.tsx index 04cc5be..95fed4a 100644 --- a/projects/mybyte/pages/qrRead.tsx +++ b/projects/mybyte/pages/qrRead.tsx @@ -163,16 +163,15 @@ export default function QrRead(props: any) { } finally { setStatus(outcomeMessage); setScannedUID("") - window.alert(outcomeMessage); } }; return ( -
+
{ if (!result) return; From 7d3cd4bf269cd0648fc5c00a08c28f83709048ad Mon Sep 17 00:00:00 2001 From: Kasra Ghaffari <18647702+Kasra-G@users.noreply.github.com> Date: Fri, 9 Feb 2024 21:40:48 -0500 Subject: [PATCH 3/6] :( --- projects/mybyte/pages/qrRead.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/mybyte/pages/qrRead.tsx b/projects/mybyte/pages/qrRead.tsx index 95fed4a..f82f7d3 100644 --- a/projects/mybyte/pages/qrRead.tsx +++ b/projects/mybyte/pages/qrRead.tsx @@ -171,7 +171,7 @@ export default function QrRead(props: any) { { if (!result) return; From 518819049e2d6b9f36c9171b616ca4edd816737a Mon Sep 17 00:00:00 2001 From: Kasra Ghaffari <18647702+Kasra-G@users.noreply.github.com> Date: Fri, 9 Feb 2024 21:52:12 -0500 Subject: [PATCH 4/6] styling changes --- projects/mybyte/pages/qrRead.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/mybyte/pages/qrRead.tsx b/projects/mybyte/pages/qrRead.tsx index f82f7d3..7a453c1 100644 --- a/projects/mybyte/pages/qrRead.tsx +++ b/projects/mybyte/pages/qrRead.tsx @@ -178,7 +178,7 @@ export default function QrRead(props: any) { setScannedUID(result.getText()) }} /> -
+
Scanned UID: {scannedUID}

From 170c7c10ef2b2dd53c2b29db99c2dbd21d4c5319 Mon Sep 17 00:00:00 2001 From: Kasra Ghaffari <18647702+Kasra-G@users.noreply.github.com> Date: Fri, 9 Feb 2024 22:08:14 -0500 Subject: [PATCH 5/6] styling changes AGAIN --- projects/mybyte/pages/qrRead.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/mybyte/pages/qrRead.tsx b/projects/mybyte/pages/qrRead.tsx index 7a453c1..90408f0 100644 --- a/projects/mybyte/pages/qrRead.tsx +++ b/projects/mybyte/pages/qrRead.tsx @@ -167,7 +167,7 @@ export default function QrRead(props: any) { }; return ( -
+
-
+
Scanned UID: {scannedUID}

From fc48b0fee2ed1c63a1ef9c583742ef07e193b950 Mon Sep 17 00:00:00 2001 From: Shawn Pradeep Date: Sat, 10 Feb 2024 08:41:01 -0500 Subject: [PATCH 6/6] styling --- projects/mybyte/pages/qrRead.tsx | 68 ++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/projects/mybyte/pages/qrRead.tsx b/projects/mybyte/pages/qrRead.tsx index 90408f0..72e7ffe 100644 --- a/projects/mybyte/pages/qrRead.tsx +++ b/projects/mybyte/pages/qrRead.tsx @@ -1,13 +1,12 @@ -import React, {useState} from "react"; +import React, { useState } from "react"; import OrganizerRoute from "../components/OrganizerRoute"; -import {useAuth} from "../context/AuthContext"; -import {QrReader} from 'react-qr-reader'; - +import { useAuth } from "../context/AuthContext"; +import { QrReader } from "react-qr-reader"; const initialUser = { name: "N/A", - shirtSize: "N/A" -} + shirtSize: "N/A", +}; export default function QrRead(props: any) { const { @@ -19,14 +18,19 @@ export default function QrRead(props: any) { getNameOfUser, getRegisteredEventsForUser, getTShirtSizeOfUser, - user_type + user_type, } = useAuth(); const [scannedUID, setScannedUID] = useState(""); const [user, setUser] = useState(initialUser); const [status, setStatus] = useState("Waiting for scan"); const selectWhich: JSX.Element = ( - @@ -49,7 +53,7 @@ export default function QrRead(props: any) { const determineAction = async (uid: string) => { let outcomeMessage = ""; - console.log(user_type) + console.log(user_type); try { if (!uid) throw "No QR Code has been scanned!"; // action state kept resetting to its default state for some reason @@ -62,16 +66,16 @@ export default function QrRead(props: any) { .getElementsByTagName("select") .namedItem("what-for")?.value; - const name = await getNameOfUser(uid) + const name = await getNameOfUser(uid); if (!name) { throw "User not found!"; } - const tShirtSize = await getTShirtSizeOfUser(uid) - setUser({name: name, shirtSize: tShirtSize}) + const tShirtSize = await getTShirtSizeOfUser(uid); + setUser({ name: name, shirtSize: tShirtSize }); outcomeMessage = `Successfully completed ${selectedOption} for ${name}`; switch (selectedOption) { case "checkin-first-day": - if (!("HACKS9" in await getRegisteredEventsForUser(uid))) { + if (!("HACKS9" in (await getRegisteredEventsForUser(uid)))) { throw `${name} is not registered for UGAHacks 9!`; } if (await isUserCheckedIn(uid)) { @@ -82,7 +86,7 @@ export default function QrRead(props: any) { outcomeMessage = `Checked in ${name}!`; break; case "checkin-other": - if (!("HACKS9" in await getRegisteredEventsForUser(uid))) { + if (!("HACKS9" in (await getRegisteredEventsForUser(uid)))) { throw `${name} is not registered for UGAHacks 9!`; } if (await isUserCheckedIn(uid)) { @@ -162,39 +166,51 @@ export default function QrRead(props: any) { } } finally { setStatus(outcomeMessage); - setScannedUID("") + setScannedUID(""); } }; return ( -
+
{ if (!result) return; - setScannedUID(result.getText()) + setScannedUID(result.getText()); }} />
- Scanned UID: {scannedUID}

+ Scanned UID: {scannedUID}

- Name: {user.name}
- Shirt Size: {user.shirtSize}
+ Name: {user.name}
+ Shirt Size: {user.shirtSize}
Previous Status: {status}
- + {selectWhich}