import React, { useState, useCallback, useEffect } from 'react'; import { View, Text, StyleSheet, ScrollView, TouchableOpacity, Alert, ActivityIndicator, } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { SafeAreaView } from 'react-native-safe-area-context'; import { router, useLocalSearchParams, useFocusEffect } from 'expo-router'; import { useBLE } from '@/contexts/BLEContext'; import { useBeneficiary } from '@/contexts/BeneficiaryContext'; import { AppColors, BorderRadius, FontSizes, FontWeights, Spacing, Shadows, } from '@/constants/theme'; export default function AddSensorScreen() { const { id } = useLocalSearchParams<{ id: string }>(); const { currentBeneficiary } = useBeneficiary(); const { foundDevices, isScanning, connectedDevices, isBLEAvailable, scanDevices, stopScan, } = useBLE(); const [selectedDevices, setSelectedDevices] = useState>(new Set()); // Select all devices by default when scan completes useEffect(() => { if (foundDevices.length > 0 && !isScanning) { setSelectedDevices(new Set(foundDevices.map(d => d.id))); } }, [foundDevices, isScanning]); // Cleanup: Stop scan when screen loses focus or component unmounts useFocusEffect( useCallback(() => { // Cleanup function - called when screen loses focus return () => { if (isScanning) { stopScan(); } }; }, [isScanning, stopScan]) ); const toggleDeviceSelection = (deviceId: string) => { setSelectedDevices(prev => { const next = new Set(prev); if (next.has(deviceId)) { next.delete(deviceId); } else { next.add(deviceId); } return next; }); }; const toggleSelectAll = () => { if (selectedDevices.size === foundDevices.length) { setSelectedDevices(new Set()); } else { setSelectedDevices(new Set(foundDevices.map(d => d.id))); } }; const selectedCount = selectedDevices.size; const handleScan = async () => { try { await scanDevices(); } catch (error: any) { Alert.alert('Scan Failed', error.message || 'Failed to scan for sensors. Please try again.'); } }; const handleAddSelected = () => { if (selectedCount === 0) { Alert.alert('No Sensors Selected', 'Please select at least one sensor to add.'); return; } const devices = foundDevices.filter(d => selectedDevices.has(d.id)); // Navigate to Setup WiFi screen with selected devices router.push({ pathname: `/(tabs)/beneficiaries/${id}/setup-wifi` as any, params: { devices: JSON.stringify(devices.map(d => ({ id: d.id, name: d.name, mac: d.mac, wellId: d.wellId, }))), }, }); }; const getSignalIcon = (rssi: number) => { if (rssi >= -50) return 'cellular'; if (rssi >= -60) return 'cellular-outline'; if (rssi >= -70) return 'cellular'; return 'cellular-outline'; }; const getSignalColor = (rssi: number) => { if (rssi >= -50) return AppColors.success; if (rssi >= -60) return AppColors.info; if (rssi >= -70) return AppColors.warning; return AppColors.error; }; return ( {/* Header */} router.back()}> Add Sensor {/* Simulator Warning */} {!isBLEAvailable && ( Running in Simulator - showing mock sensors )} {/* Instructions */} How to Add a Sensor 1 Make sure the WP sensor is powered on and nearby 2 Tap "Scan for Sensors" to search for available devices 3 Select your sensor from the list to connect 4 Configure WiFi settings to complete setup {/* Scan Button */} {!isScanning && foundDevices.length === 0 && ( Scan for Sensors )} {/* Scanning Indicator */} {isScanning && ( Scanning for WP sensors... Stop Scan )} {/* Found Devices */} {foundDevices.length > 0 && !isScanning && ( <> Found Sensors ({foundDevices.length}) {selectedDevices.size === foundDevices.length ? 'Deselect All' : 'Select All'} Rescan {foundDevices.map((device) => { const isConnected = connectedDevices.has(device.id); const isSelected = selectedDevices.has(device.id); return ( toggleDeviceSelection(device.id)} disabled={isConnected} activeOpacity={0.7} > {isSelected && ( )} {device.name} {device.wellId && ( Well ID: {device.wellId} )} {device.rssi} dBm {isConnected && ( Added )} ); })} {/* Add Selected Button */} Add Selected ({selectedCount}) )} {/* Empty State (after scan completed, no devices found) */} {!isScanning && foundDevices.length === 0 && ( No Sensors Found Make sure your WP sensor is powered on and within range, then try scanning again. )} {/* Help Card */} Troubleshooting • Sensor not showing up? Make sure it's powered on and the LED is blinking{'\n'} • Weak signal? Move closer to the sensor{'\n'} • Connection fails? Try restarting the sensor{'\n'} • Still having issues? Contact support for assistance ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: AppColors.background, }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: Spacing.md, paddingVertical: Spacing.sm, borderBottomWidth: 1, borderBottomColor: AppColors.border, }, backButton: { padding: Spacing.xs, }, headerTitle: { fontSize: FontSizes.lg, fontWeight: FontWeights.semibold, color: AppColors.textPrimary, }, placeholder: { width: 32, }, simulatorWarning: { flexDirection: 'row', alignItems: 'center', backgroundColor: AppColors.warningLight, paddingVertical: Spacing.xs, paddingHorizontal: Spacing.md, gap: Spacing.xs, borderBottomWidth: 1, borderBottomColor: AppColors.warning, }, simulatorWarningText: { fontSize: FontSizes.xs, color: AppColors.warning, fontWeight: FontWeights.medium, flex: 1, }, content: { flex: 1, }, scrollContent: { padding: Spacing.lg, paddingBottom: Spacing.xxl, }, // Instructions instructionsCard: { backgroundColor: AppColors.surface, borderRadius: BorderRadius.xl, padding: Spacing.lg, marginBottom: Spacing.lg, ...Shadows.sm, }, instructionsTitle: { fontSize: FontSizes.base, fontWeight: FontWeights.semibold, color: AppColors.textPrimary, marginBottom: Spacing.md, }, step: { flexDirection: 'row', alignItems: 'flex-start', marginBottom: Spacing.sm, gap: Spacing.sm, }, stepNumber: { width: 24, height: 24, borderRadius: 12, backgroundColor: AppColors.primary, justifyContent: 'center', alignItems: 'center', }, stepNumberText: { fontSize: FontSizes.xs, fontWeight: FontWeights.bold, color: AppColors.white, }, stepText: { flex: 1, fontSize: FontSizes.sm, color: AppColors.textSecondary, lineHeight: 20, paddingTop: 2, }, // Scan Button scanButton: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', backgroundColor: AppColors.primary, paddingVertical: Spacing.md, borderRadius: BorderRadius.lg, marginBottom: Spacing.lg, gap: Spacing.sm, ...Shadows.md, }, scanButtonText: { fontSize: FontSizes.base, fontWeight: FontWeights.semibold, color: AppColors.white, }, // Scanning scanningCard: { backgroundColor: AppColors.surface, borderRadius: BorderRadius.xl, padding: Spacing.xl, alignItems: 'center', marginBottom: Spacing.lg, ...Shadows.sm, }, scanningText: { fontSize: FontSizes.base, color: AppColors.textSecondary, marginTop: Spacing.md, marginBottom: Spacing.md, }, stopScanButton: { paddingVertical: Spacing.sm, paddingHorizontal: Spacing.lg, }, stopScanText: { fontSize: FontSizes.sm, fontWeight: FontWeights.medium, color: AppColors.error, }, // Section Header sectionHeader: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginBottom: Spacing.md, }, sectionTitle: { fontSize: FontSizes.sm, fontWeight: FontWeights.semibold, color: AppColors.textSecondary, textTransform: 'uppercase', letterSpacing: 0.5, }, sectionActions: { flexDirection: 'row', alignItems: 'center', gap: Spacing.md, }, selectAllButton: { flexDirection: 'row', alignItems: 'center', gap: 4, }, selectAllText: { fontSize: FontSizes.sm, fontWeight: FontWeights.medium, color: AppColors.primary, }, rescanButton: { flexDirection: 'row', alignItems: 'center', gap: 4, }, rescanText: { fontSize: FontSizes.sm, fontWeight: FontWeights.medium, color: AppColors.primary, }, // Devices List devicesList: { gap: Spacing.md, marginBottom: Spacing.lg, }, deviceCard: { backgroundColor: AppColors.surface, borderRadius: BorderRadius.lg, padding: Spacing.md, flexDirection: 'row', alignItems: 'center', ...Shadows.xs, }, deviceCardSelected: { borderWidth: 2, borderColor: AppColors.primary, }, deviceCardConnected: { borderWidth: 2, borderColor: AppColors.success, opacity: 0.6, }, checkboxContainer: { marginRight: Spacing.sm, }, checkbox: { width: 24, height: 24, borderRadius: BorderRadius.sm, borderWidth: 2, borderColor: AppColors.border, justifyContent: 'center', alignItems: 'center', backgroundColor: AppColors.white, }, checkboxSelected: { backgroundColor: AppColors.primary, borderColor: AppColors.primary, }, checkboxDisabled: { backgroundColor: AppColors.surfaceSecondary, borderColor: AppColors.border, }, deviceInfo: { flex: 1, flexDirection: 'row', alignItems: 'center', gap: Spacing.md, }, deviceIcon: { width: 48, height: 48, borderRadius: BorderRadius.lg, backgroundColor: AppColors.primaryLighter, justifyContent: 'center', alignItems: 'center', }, deviceDetails: { flex: 1, }, deviceName: { fontSize: FontSizes.base, fontWeight: FontWeights.semibold, color: AppColors.textPrimary, marginBottom: 2, }, deviceMeta: { fontSize: FontSizes.xs, color: AppColors.textMuted, marginBottom: 4, }, signalRow: { flexDirection: 'row', alignItems: 'center', gap: 4, }, signalText: { fontSize: FontSizes.xs, fontWeight: FontWeights.medium, }, alreadyAddedBadge: { backgroundColor: AppColors.successLight, paddingVertical: Spacing.xs, paddingHorizontal: Spacing.sm, borderRadius: BorderRadius.sm, }, alreadyAddedText: { fontSize: FontSizes.xs, fontWeight: FontWeights.medium, color: AppColors.success, }, // Add Selected Button addSelectedButton: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', backgroundColor: AppColors.primary, paddingVertical: Spacing.md, borderRadius: BorderRadius.lg, marginTop: Spacing.md, marginBottom: Spacing.lg, gap: Spacing.sm, ...Shadows.md, }, addSelectedButtonDisabled: { backgroundColor: AppColors.textMuted, }, addSelectedButtonText: { fontSize: FontSizes.base, fontWeight: FontWeights.semibold, color: AppColors.white, }, // Empty State emptyState: { alignItems: 'center', padding: Spacing.xl, backgroundColor: AppColors.surface, borderRadius: BorderRadius.xl, marginBottom: Spacing.lg, ...Shadows.sm, }, emptyIconContainer: { width: 80, height: 80, borderRadius: 40, backgroundColor: AppColors.surfaceSecondary, justifyContent: 'center', alignItems: 'center', marginBottom: Spacing.md, }, emptyTitle: { fontSize: FontSizes.lg, fontWeight: FontWeights.semibold, color: AppColors.textPrimary, marginBottom: Spacing.xs, }, emptyText: { fontSize: FontSizes.sm, color: AppColors.textMuted, textAlign: 'center', lineHeight: 20, }, // Help Card helpCard: { backgroundColor: AppColors.infoLight, borderRadius: BorderRadius.lg, padding: Spacing.md, }, helpHeader: { flexDirection: 'row', alignItems: 'center', gap: Spacing.sm, marginBottom: Spacing.xs, }, helpTitle: { fontSize: FontSizes.sm, fontWeight: FontWeights.semibold, color: AppColors.info, }, helpText: { fontSize: FontSizes.sm, color: AppColors.info, lineHeight: 20, }, });