import React, { useState, useEffect } from 'react'; import { View, Text, StyleSheet, TouchableOpacity, Image, Dimensions, Modal, Animated } from 'react-native'; import { Camera, useCameraDevice, useCameraPermission, useCodeScanner } from 'react-native-vision-camera'; import { Ionicons } from '@expo/vector-icons'; import * as ImagePicker from 'expo-image-picker'; import RNQRGenerator from 'rn-qr-generator'; import ScannedDataBox from '../components/ScannedDataBox'; import { scanQRCode, getQRTips } from '../api/qrCodeAPI'; import SettingsScreen from './SettingsScreen'; import NetInfo from '@react-native-community/netinfo'; const { width: screenWidth, height: screenHeight } = Dimensions.get('window'); const QRScannerScreen: React.FC = () => { // State management const [isSettingsModalVisible, setIsSettingsModalVisible] = useState(false); const [enableTorch, setEnableTorch] = useState(false); const [scanned, setScanned] = useState(false); const [qrCodeId, setQRCodeId] = useState(null); const [isScannedDataBoxVisible, setIsScannedDataBoxVisible] = useState(false); const [bannerOpacity] = useState(new Animated.Value(0)); const [isConnected, setIsConnected] = useState(true); const [qrTip, setQrTip] = useState('Always scan QR codes from trusted sources'); // Camera permissions and device management const { hasPermission, requestPermission } = useCameraPermission(); const device = useCameraDevice('back'); // Fetch QR Tips and manage polling useEffect(() => { const fetchTips = async () => { try { const response = await getQRTips(); setQrTip(response.tips); // Set the qrTip state to the value of the tips property } catch (error) { console.error('Error fetching QR tips:', error); } }; requestPermission(); // Request camera permission on component mount // Initial fetch for QR tips fetchTips(); // Set interval for fetching QR tips every 5 seconds const intervalId = setInterval(fetchTips, 10000); // Subscribe to network state updates const unsubscribe = NetInfo.addEventListener(state => { setIsConnected(state.isConnected); if (!state.isConnected) { showBanner(); // Show banner if the device goes offline } }); // Cleanup on component unmount return () => { clearInterval(intervalId); // Clear interval unsubscribe(); // Unsubscribe from network state updates }; }, []); // Show an offline banner const showBanner = () => { Animated.timing(bannerOpacity, { toValue: 1, duration: 500, useNativeDriver: true, }).start(() => { setTimeout(() => { Animated.timing(bannerOpacity, { toValue: 0, duration: 500, useNativeDriver: true, }).start(); }, 3000); }); }; // Handle payload after scanning QR code const handlePayload = async (payload: string) => { setScanned(true); console.info("Decoded QR Code, Payload is: ", payload); try { const response = await scanQRCode(payload); const qrCodeId = response.qrcode.data.id; setQRCodeId(qrCodeId); // Store QR code ID setIsScannedDataBoxVisible(true); // Show the ScannedDataBox pop-up } catch (error) { console.error("Error scanning QR code:", error); } }; // Use the camera to scan QR codes const codeScanner = useCodeScanner({ codeTypes: ['qr'], // Only scan QR codes onCodeScanned: (codes) => { if (!scanned && codes[0]?.value) { handlePayload(codes[0].value); // Handle the QR code value } } }); // Read QR code from an image const readQRFromImage = async () => { console.log("Reading QR code from image"); const result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images, allowsEditing: false, quality: 1, }); if (result && result.assets && result.assets.length > 0 && result.assets[0].uri) { try { const detectionResult = await RNQRGenerator.detect({ uri: result.assets[0].uri, }); const { values } = detectionResult; if (values.length > 0) { handlePayload(values[0]); // Handle the first detected QR code value } else { console.log("No QR code found in the selected image"); } } catch (error) { console.error('Error scanning QR code from image:', error); } } }; // Check for camera permissions if (!hasPermission) { return Requesting camera permission...; } // Wait for the device to be ready if (!device) { return Loading camera...; } return ( {/* Banner for network connectivity */} No Internet Connection Welcome to Please point the camera at the QR Code {device && ( )} {/* Torch Button */} device.hasFlash && setEnableTorch((prev) => !prev)} style={styles.flashButton} disabled={!device.hasFlash} > {/* Gallery Button */} {/* QR Code Tips Below Camera Container */} {qrTip} {/* Scanned Data Box as a pop-up */} {isScannedDataBoxVisible && ( { setScanned(false); setIsScannedDataBoxVisible(false); }} /> )} {/* Settings Icon */} setIsSettingsModalVisible(true)} style={styles.settingsButton}> {/* Settings Modal */} setIsSettingsModalVisible(false)} style={styles.settingsModal} > setIsSettingsModalVisible(false)} style={styles.closeButton}> Close ); }; // Stylesheet const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#f8f0fc', padding: 20, }, titleText: { textAlign: 'center', fontSize: 20, marginTop: screenHeight * 0.05, color: 'black', }, logo: { alignSelf: 'center', width: screenWidth * 0.5, height: screenWidth * 0.2, resizeMode: 'contain', marginVertical: 10, }, welcomeText: { textAlign: 'center', fontSize: 15, marginVertical: 10, color: 'black', }, cameraContainer: { width: '100%', // Adjust the width as needed height: '45%', // Adjust the height as needed alignItems: 'center', justifyContent: 'center', borderRadius: 10, overflow: 'hidden', alignSelf: 'center', // Center the container horizontally marginTop: '10%', // Adjust the top margin to position it vertically in the middle }, settingsButton: { position: 'absolute', top: screenHeight * 0.05, right: 20, }, flashButton: { position: 'absolute', bottom: screenHeight * 0.025, left: screenWidth * 0.2, width: screenWidth * 0.125, height: screenWidth * 0.125, justifyContent: 'center', alignItems: 'center', backgroundColor: '#000', borderRadius: screenWidth * 0.0625, }, galleryButton: { position: 'absolute', bottom: screenHeight * 0.025, right: screenWidth * 0.2, width: screenWidth * 0.125, height: screenWidth * 0.125, justifyContent: 'center', alignItems: 'center', backgroundColor: '#000', borderRadius: screenWidth * 0.0625, }, scannedDataBoxPopup: { position: 'absolute', top: '20%', left: '5%', right: '5%', zIndex: 2, backgroundColor: 'white', borderRadius: screenWidth * 0.025, padding: screenWidth * 0.025, elevation: 5, }, settingsModal: { backgroundColor: 'rgba(0, 0, 0, 0.5)', }, settingsModalContainer: { flex: 1, justifyContent: 'center', backgroundColor: '#f8f0fc', // Full pink background to match the app's theme }, settingsModalContent: { flex: 1, backgroundColor: '#f8f0fc', // Full pink background for the content as well padding: screenWidth * 0.05, borderTopLeftRadius: 0, // Remove border radius to avoid white edges borderTopRightRadius: 0, // Remove border radius to avoid white edges alignItems: 'center', }, closeButton: { marginTop: screenHeight * 0.01, padding: screenWidth * 0.025, backgroundColor: '#ff69b4', borderRadius: screenWidth * 0.0125, }, closeButtonText: { color: 'white', fontWeight: 'bold', }, banner: { position: 'absolute', top: screenHeight * 0.4, left: screenWidth * 0.1, right: screenWidth * 0.1, backgroundColor: '#ff69b4', paddingVertical: screenHeight * 0.02, paddingHorizontal: screenWidth * 0.05, borderRadius: screenWidth * 0.05, alignItems: 'center', justifyContent: 'center', zIndex: 10, }, bannerText: { color: '#fff', fontWeight: 'bold', textAlign: 'center', fontSize: screenWidth * 0.04, }, tipsContainer: { backgroundColor: '#fff', // Make sure the container background matches the white box padding: 10, borderRadius: 10, alignItems: 'center', marginTop: 10, // Space from the camera container }, iconTextRow: { flexDirection: 'row', alignItems: 'center', // This will align the icon and text vertically }, tipsText: { color: '#f41c87', // Adjusted color to match the pink theme fontSize: 16, textAlign: 'center', marginLeft: 5, // Add some spacing between the icon and the text paddingHorizontal: 10, // Add horizontal padding to ensure text isn't too close to the edges }, }); export default QRScannerScreen;