import React, { useRef } from 'react'; import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'; import { useRouter } from 'expo-router'; import { Ionicons } from '@expo/vector-icons'; import { SafeAreaView } from 'react-native-safe-area-context'; import { CameraView, useCameraPermissions } from 'expo-camera'; import { colors, typography, spacing } from '@shared/theme'; /** * Tela de scanner de QR Code * Lê deeplinks meowspool://filament/ e navega para o filamento. */ export default function ScannerScreen(): React.ReactElement { const router = useRouter(); const [permission, requestPermission] = useCameraPermissions(); const handledRef = useRef(false); function handleBarCodeScanned({ data }: { data: string }): void { if (handledRef.current) return; handledRef.current = true; const match = data.match(/meowspool:\/\/filament\/([^/]+)/); if (match) { router.replace(`/(app)/inventory/${match[1]}` as never); } else { // QR não reconhecido — libera para próximo scan handledRef.current = false; } } if (!permission) { return ( Carregando câmera… ); } if (!permission.granted) { return ( Permissão de câmera necessária Conceder permissão ); } return ( {/* Header */} router.back()}> Escanear QR Code {/* Camera */} {/* Viewfinder overlay */} Aponte para o QR Code do filamento ); } const CORNER = 24; const CORNER_THICKNESS = 3; const VIEWFINDER_SIZE = 240; const styles = StyleSheet.create({ safe: { flex: 1, backgroundColor: colors.bgBase }, center: { flex: 1, alignItems: 'center', justifyContent: 'center', gap: spacing[4], paddingHorizontal: spacing[6] }, message: { fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize.base, color: colors.textSecondary, textAlign: 'center', }, permBtn: { backgroundColor: colors.accent, paddingHorizontal: spacing[5], paddingVertical: spacing[3], borderRadius: 8, }, permBtnText: { fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize.base, fontWeight: '600', color: colors.bgBase, }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: spacing[5], paddingVertical: spacing[4], }, headerTitle: { fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize.md, fontWeight: '600', color: colors.textPrimary, }, cameraWrapper: { flex: 1, }, overlay: { ...StyleSheet.absoluteFillObject, alignItems: 'center', justifyContent: 'center', backgroundColor: 'rgba(0,0,0,0.55)', }, viewfinder: { width: VIEWFINDER_SIZE, height: VIEWFINDER_SIZE, backgroundColor: 'transparent', }, corner: { position: 'absolute', width: CORNER, height: CORNER, borderColor: colors.accent, }, cornerTL: { top: 0, left: 0, borderTopWidth: CORNER_THICKNESS, borderLeftWidth: CORNER_THICKNESS }, cornerTR: { top: 0, right: 0, borderTopWidth: CORNER_THICKNESS, borderRightWidth: CORNER_THICKNESS }, cornerBL: { bottom: 0, left: 0, borderBottomWidth: CORNER_THICKNESS, borderLeftWidth: CORNER_THICKNESS }, cornerBR: { bottom: 0, right: 0, borderBottomWidth: CORNER_THICKNESS, borderRightWidth: CORNER_THICKNESS }, hint: { paddingVertical: spacing[5], paddingHorizontal: spacing[5], alignItems: 'center', borderTopWidth: 1, borderTopColor: colors.border, }, hintText: { fontFamily: typography.fontFamily.ui, fontSize: typography.fontSize.sm, color: colors.textSecondary, }, });