Files
mariani_mobile/components/NfcScanModal.tsx
leonardo 44d021891f feat: Add document download and upload. Add NFC support and enhance attendance and permits views
- Improved error message handling in LoginScreen for invalid credentials.
- Added new images: mariani-icon.png and mariani-splash.png.
- Updated AddDocumentModal to handle file extensions and improve UI.
- Enhanced CalendarWidget to support month change callbacks.
- Introduced NfcScanModal for NFC tag scanning with animations.
- Revamped QrScanModal to utilize camera for QR code scanning.
- Removed mock data from data.ts and streamlined Office data.
- Updated package dependencies for expo-camera and react-native-nfc-manager.
- Added utility function to parse seconds to time format.
- Refactored document upload logic to use FormData for server uploads.
2026-01-19 18:10:31 +01:00

162 lines
6.0 KiB
TypeScript

import React, { useEffect, useRef } from 'react';
import { Modal, Text, TouchableOpacity, View, Animated, Easing, Vibration, Alert } from 'react-native';
import { X, Radio, SmartphoneNfc } from 'lucide-react-native';
import NfcManager, { NfcTech } from 'react-native-nfc-manager';
interface NfcScanModalProps {
visible: boolean;
onClose: () => void;
onScan: (data: string) => void;
}
export default function NfcScanModal({ visible, onClose, onScan }: NfcScanModalProps) {
// Animazione per l'effetto "pulsante" (Breathing)
const scaleAnim = useRef(new Animated.Value(1)).current;
const opacityAnim = useRef(new Animated.Value(0.3)).current;
const readNfcTag = async () => {
const supported = await NfcManager.isSupported();
const nfcScanning = await NfcManager.isEnabled();
if (!supported || !nfcScanning) {
onClose();
return;
}
NfcManager.start();
try {
await NfcManager.requestTechnology(NfcTech.Ndef);
const tag = await NfcManager.getTag();
if (tag) {
console.log('NFC Tag Found:', tag);
Vibration.vibrate();
// onScan(tag.id || JSON.stringify(tag));
onClose();
} else {
console.warn('Tag NFC vuoto o non formattato NDEF');
}
} catch (error) {
console.warn('Error reading NFC tag', error);
} finally {
NfcManager.cancelTechnologyRequest();
}
};
// Loop infinito di espansione e contrazione
const animationLoop = () => {
Animated.loop(
Animated.parallel([
Animated.sequence([
Animated.timing(scaleAnim, {
toValue: 1.2,
duration: 1500,
easing: Easing.inOut(Easing.ease),
useNativeDriver: true,
}),
Animated.timing(scaleAnim, {
toValue: 1,
duration: 1500,
easing: Easing.inOut(Easing.ease),
useNativeDriver: true,
}),
]),
Animated.sequence([
Animated.timing(opacityAnim, {
toValue: 0.1,
duration: 1500,
useNativeDriver: true,
}),
Animated.timing(opacityAnim, {
toValue: 0.3,
duration: 1500,
useNativeDriver: true,
}),
])
])
).start();
}
useEffect(() => {
if (visible) {
animationLoop();
readNfcTag();
} else {
scaleAnim.setValue(1);
opacityAnim.setValue(0.3);
}
}, [visible]);
return (
<Modal
animationType="fade"
transparent={true}
visible={visible}
onRequestClose={onClose}
statusBarTranslucent
>
<View className="flex-1 bg-black/80 items-center justify-center p-6">
{/* Card Principale */}
<View className="bg-white w-full max-w-sm rounded-[2.5rem] p-8 items-center shadow-2xl relative overflow-hidden">
{/* Bottone Chiudi (Alto Destra) */}
<TouchableOpacity
onPress={onClose}
className="absolute top-6 right-6 z-10 p-2 bg-gray-50 rounded-full"
>
<X size={20} color="#9ca3af" />
</TouchableOpacity>
{/* Area Animata NFC */}
<View className="mt-6 mb-10 items-center justify-center h-40 w-40">
{/* Cerchio Pulsante (Sfondo) */}
<Animated.View
style={{
position: 'absolute',
width: 140,
height: 140,
borderRadius: 70,
backgroundColor: '#099499',
opacity: opacityAnim,
transform: [{ scale: scaleAnim }]
}}
/>
{/* Cerchio Fisso (Primo piano) */}
<View className="bg-[#E6F4F4] p-6 rounded-full border-4 border-white shadow-sm z-10">
<SmartphoneNfc size={64} color="#099499" />
</View>
</View>
{/* Testi */}
<Text className="text-2xl font-bold text-gray-800 text-center mb-2">
Pronto alla scansione
</Text>
<Text className="text-gray-500 text-center text-base px-2 leading-relaxed">
Avvicina il retro del tuo smartphone al <Text className="font-bold text-gray-700">Tag NFC</Text> per registrare la presenza.
</Text>
{/* Indicatore Visivo (Simulazione onde) */}
<View className="flex-row gap-2 mt-8 items-center opacity-60">
<Radio size={20} color="#099499" />
<Text className="text-[#099499] font-medium text-sm uppercase tracking-widest">
Ricerca in corso...
</Text>
</View>
{/* Footer Button */}
<TouchableOpacity
onPress={onClose}
className="mt-10 bg-gray-100 rounded-2xl px-10 py-4 w-full active:bg-gray-200"
>
<Text className="text-gray-600 font-bold text-lg text-center">Annulla</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
);
}