Skip to content

Commit

Permalink
add zoom, removed invalid qrcode check, remove auto navigate, and add…
Browse files Browse the repository at this point in the history
… button
  • Loading branch information
JasonIsAzn committed Sep 13, 2024
1 parent f6a8018 commit 1889ed4
Showing 1 changed file with 123 additions and 51 deletions.
174 changes: 123 additions & 51 deletions src/screens/events/QRCodeScanningScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import { View, Text, Button, StyleSheet, TouchableHighlight, TouchableOpacity, Alert, Animated, Easing } from 'react-native';
import { View, Text, TouchableOpacity, Alert, Animated, Easing } from 'react-native';
import React, { useEffect, useRef, useState } from 'react';
import { CameraView, Camera } from 'expo-camera';
import { SafeAreaView } from 'react-native-safe-area-context';
import { GestureHandlerRootView, PinchGestureHandler, PinchGestureHandlerGestureEvent, State } from 'react-native-gesture-handler';
import { MainStackParams } from '../../types/navigation';
import { Octicons } from '@expo/vector-icons';
import { NativeStackScreenProps } from '@react-navigation/native-stack';

const QRCodeScanningScreen = ({ navigation }: NativeStackScreenProps<MainStackParams>) => {
const [hasCameraPermissions, setHasCameraPermissions] = useState<boolean | null>(null);
const [scanned, setScanned] = useState(false);
const pulseAnim = useRef(new Animated.Value(1)).current;
const [zoom, setZoom] = useState(0);
const [qrData, setQrData] = useState<{ id: string; mode: string } | null>(null);
const cameraRef = useRef<CameraView>(null);
const lastScale = useRef(1);
const maxZoom = 0.05;
const timeoutRef = useRef<NodeJS.Timeout | null>(null);

useEffect(() => {
const getBarCodeScannerPermissions = async () => {
Expand Down Expand Up @@ -42,29 +48,60 @@ const QRCodeScanningScreen = ({ navigation }: NativeStackScreenProps<MainStackPa
}, [pulseAnim]);

const handleBarCodeScanned = ({ type, data }: { type: string, data: string }) => {
console.log('Data Received', `Bar code with type ${type} and data ${data} has been scanned!`);
setScanned(true);
const dataRegex: RegExp = /^tamu-shpe:\/\/event\?id=[a-zA-z0-9]+&mode=(sign-in|sign-out)$/i;
if (!dataRegex.test(data)) {
Alert.alert("Invalid QR Code", "Either this QR Code is invalid or was misscanned. Please try again.", [
{
text: 'ok',
onPress: () => {
setScanned(false);
}
}
]);
}
else {

if (dataRegex.test(data)) {
const linkVariables = data.split('?')[1].split('&');
const id = linkVariables[0].split('=')[1];
const mode = linkVariables[1].split('=')[1];
if (id && mode === 'sign-in' || mode === 'sign-out') {
navigation.navigate("EventVerificationScreen", { id, mode });
if (id && (mode === 'sign-in' || mode === 'sign-out')) {
setQrData({ id, mode });

// Clear any existing timeout when valid data is found
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}

// Set a timeout to clear qrData after 3 seconds if no new valid QR codes are detected
timeoutRef.current = setTimeout(() => {
setQrData(null);
}, 5000);
}
}
};

useEffect(() => {
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, []);

const handleConfirm = () => {
if (qrData) {
navigation.navigate("EventVerificationScreen", { id: qrData.id, mode: qrData.mode as "sign-in" | "sign-out" });
setQrData(null);
}
};

const handlePinchGestureEvent = ({ nativeEvent }: PinchGestureHandlerGestureEvent) => {
if (nativeEvent.scale !== 0) {
const baseZoomFactor = 0.0004;
const zoomSensitivity = 0.1;
const dynamicFactor = zoom * zoomSensitivity + baseZoomFactor;
const scaleChange = (nativeEvent.scale - 1) * dynamicFactor * (nativeEvent.scale > 1 ? 1 : 2);
const newZoom = Math.min(Math.max(zoom + scaleChange, 0), maxZoom);
setZoom(newZoom);
}
};

const handlePinchStateChange = ({ nativeEvent }: PinchGestureHandlerGestureEvent) => {
if (nativeEvent.state === State.END || nativeEvent.state === State.CANCELLED) {
lastScale.current = 1;
}
};

if (hasCameraPermissions === null) {
return <Text>Requesting for camera permission</Text>;
}
Expand All @@ -73,43 +110,78 @@ const QRCodeScanningScreen = ({ navigation }: NativeStackScreenProps<MainStackPa
}

return (
<SafeAreaView className='flex flex-col h-full w-screen bg-primary-blue'>
{/* Header */}
<View className={`flex-row items-center mb-4 bg-primary-blue`}>
<View className='w-screen absolute'>
<Text className={`text-2xl font-bold justify-center text-center text-white`}>Scanner</Text>
<GestureHandlerRootView>
<SafeAreaView className='flex flex-col h-full w-screen bg-primary-blue'>
{/* Header */}
<View className={`flex-row items-center mb-4 bg-primary-blue`}>
<View className='w-screen absolute'>
<Text className={`text-2xl font-bold justify-center text-center text-white`}>Scanner</Text>
</View>
<TouchableOpacity className='px-6' onPress={() => navigation.goBack()}>
<Octicons name="x" size={24} color="white" />
</TouchableOpacity>
</View>
<TouchableOpacity className='px-6' onPress={() => navigation.goBack()} >
<Octicons name="x" size={24} color="white" />
</TouchableOpacity>
</View>

<CameraView
onBarcodeScanned={scanned ? undefined : handleBarCodeScanned}
barcodeScannerSettings={{
barcodeTypes: ["qr", "pdf417"],
}}
className='flex-1'
>
{/* Pulsing Effect */}
<View className="flex justify-center items-center h-full">
<Animated.View className="flex justify-center items-center" style={{ transform: [{ scale: pulseAnim }] }}>
<View className='w-60 h-60'>
<View className='absolute top-0 left-0 w-11 h-11 border-t-4 border-l-4 border-white rounded-tl-lg' />
<View className='absolute top-0 right-0 w-11 h-11 border-t-4 border-r-4 border-white rounded-tr-lg' />
<View className='absolute bottom-0 left-0 w-11 h-11 border-b-4 border-l-4 border-white rounded-bl-lg' />
<View className='absolute bottom-0 right-0 w-11 h-11 border-b-4 border-r-4 border-white rounded-br-lg' />

<PinchGestureHandler
onGestureEvent={handlePinchGestureEvent}
onHandlerStateChange={handlePinchStateChange}
>
<CameraView
onBarcodeScanned={handleBarCodeScanned}
ref={cameraRef}
barcodeScannerSettings={{
barcodeTypes: ["qr", "pdf417"],
}}
zoom={zoom}
className='flex-1'
>
{/* Pulsing Effect */}
<View className="flex justify-center items-center h-full">
<Animated.View className="flex justify-center items-center" style={{ transform: [{ scale: pulseAnim }] }}>
<View className='w-60 h-60'>
<View
className={`absolute top-0 left-0 w-11 h-11 border-t-4 border-l-4 rounded-tl-lg ${qrData ? 'border-primary-orange' : 'border-white'}`}
/>
<View
className={`absolute top-0 right-0 w-11 h-11 border-t-4 border-r-4 rounded-tr-lg ${qrData ? 'border-primary-orange' : 'border-white'}`}
/>
<View
className={`absolute bottom-0 left-0 w-11 h-11 border-b-4 border-l-4 rounded-bl-lg ${qrData ? 'border-primary-orange' : 'border-white'}`}
/>
<View
className={`absolute bottom-0 right-0 w-11 h-11 border-b-4 border-r-4 rounded-br-lg ${qrData ? 'border-primary-orange' : 'border-white'}`}
/>
</View>
</Animated.View>
</View>
</Animated.View>
</View>
</CameraView>

<View className='my-2'>
<Text className='text-white text-center font-bold text-xl'>Using Scanner</Text>
<Text className='text-white text-center text-lg'>Scan the QRCode provided by the event host.</Text>
</View>
</SafeAreaView>
<View className="absolute top-2 left-2 bg-black/50 rounded px-2 py-1">
<Text className="text-white font-bold">{(1 + zoom * 100).toFixed(1)}x</Text>
</View>

{/* Button for Sign In/Sign Out */}
{qrData && (
<View className='absolute w-full bottom-0 mb-5 z-50 justify-center items-center'>
<TouchableOpacity
onPress={handleConfirm}
className="px-4 py-1 items-center justify-center rounded-lg mx-4 bg-primary-orange"
>
<Text className='text-center text-white text-xl'>
{qrData.mode === 'sign-in' ? 'Sign in to the event' : 'Sign out of the event'}
</Text>
</TouchableOpacity>
</View>
)}
</CameraView>
</PinchGestureHandler>

<View className='my-2'>
<Text className='text-white text-center font-bold text-xl'>Using Scanner</Text>
<Text className='text-white text-center text-lg'>Scan the QRCode provided by the event host.</Text>
</View>
</SafeAreaView>
</GestureHandlerRootView>
);
};

export default QRCodeScanningScreen;
export default QRCodeScanningScreen;

0 comments on commit 1889ed4

Please sign in to comment.