168 lines
6.4 KiB
TypeScript
168 lines
6.4 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, { Ndef, NfcTech } from 'react-native-nfc-manager';
|
|
|
|
interface NfcScanModalProps {
|
|
visible: boolean;
|
|
onClose: () => void;
|
|
onScan: (data: string) => void;
|
|
}
|
|
|
|
// TODO: Needs to be tested on a real device
|
|
export default function NfcScanModal({ visible, onClose, onScan }: NfcScanModalProps) {
|
|
// Breathing animation effect
|
|
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) {
|
|
const record = tag.ndefMessage?.[0];
|
|
const payloadBytes = new Uint8Array(record.payload);
|
|
const text = Ndef.text.decodePayload(payloadBytes);
|
|
console.log('NFC text:', text || 'No NDEF payload');
|
|
Vibration.vibrate();
|
|
if (!text) {
|
|
Alert.alert('Errore', 'Tag NFC non contiene dati validi.');
|
|
throw new Error('Empty NFC tag');
|
|
}
|
|
onScan(text);
|
|
onClose();
|
|
} else {
|
|
console.warn('Tag NFC vuoto o non formattato NDEF');
|
|
}
|
|
} catch (error) {
|
|
console.warn('Error reading NFC tag', error);
|
|
} finally {
|
|
NfcManager.cancelTechnologyRequest();
|
|
}
|
|
};
|
|
|
|
// Animation loop for the pulsing effect
|
|
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">
|
|
<View className="bg-white w-full max-w-sm rounded-[2.5rem] p-8 items-center shadow-2xl relative overflow-hidden">
|
|
|
|
{/* Close Button (Top Right) */}
|
|
<TouchableOpacity
|
|
onPress={onClose}
|
|
className="absolute top-6 right-6 z-10 p-2 bg-gray-50 rounded-full"
|
|
>
|
|
<X size={20} color="#9ca3af" pointerEvents="none" />
|
|
</TouchableOpacity>
|
|
|
|
{/* NFC Animated Area */}
|
|
<View className="mt-6 mb-10 items-center justify-center h-40 w-40">
|
|
{/* Pulsing Circle (Background) */}
|
|
<Animated.View
|
|
style={{
|
|
position: 'absolute',
|
|
width: 140,
|
|
height: 140,
|
|
borderRadius: 70,
|
|
backgroundColor: '#099499',
|
|
opacity: opacityAnim,
|
|
transform: [{ scale: scaleAnim }]
|
|
}}
|
|
/>
|
|
|
|
{/* Fixed Circle (Foreground) */}
|
|
<View className="bg-[#E6F4F4] p-6 rounded-full border-4 border-white shadow-sm z-10">
|
|
<SmartphoneNfc size={64} color="#099499" pointerEvents="none" />
|
|
</View>
|
|
</View>
|
|
|
|
{/* Texts */}
|
|
<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>
|
|
|
|
{/* Indicator (Wave Simulation) */}
|
|
<View className="flex-row gap-2 mt-8 items-center opacity-60">
|
|
<Radio size={20} color="#099499" pointerEvents="none" />
|
|
<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>
|
|
);
|
|
} |