From b738d864195dcb0bee0411cc8e047fbdd6787c3b Mon Sep 17 00:00:00 2001 From: Sergei Date: Mon, 19 Jan 2026 22:43:12 -0800 Subject: [PATCH] Update navigation to pass selected devices array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - add-sensor.tsx now passes devices array with mac address via JSON - setup-wifi.tsx parses devices from navigation params - Support batch mode display (shows count and device names) - Disconnect all devices when navigating back 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- app/(tabs)/beneficiaries/[id]/add-sensor.tsx | 217 +++++++++++++++---- app/(tabs)/beneficiaries/[id]/setup-wifi.tsx | 54 ++++- 2 files changed, 215 insertions(+), 56 deletions(-) diff --git a/app/(tabs)/beneficiaries/[id]/add-sensor.tsx b/app/(tabs)/beneficiaries/[id]/add-sensor.tsx index a0f5d4e..eb425f2 100644 --- a/app/(tabs)/beneficiaries/[id]/add-sensor.tsx +++ b/app/(tabs)/beneficiaries/[id]/add-sensor.tsx @@ -37,11 +37,40 @@ export default function AddSensorScreen() { connectDevice, } = useBLE(); - const [selectedDevice, setSelectedDevice] = useState(null); + const [selectedDevices, setSelectedDevices] = useState>(new Set()); const [isConnecting, setIsConnecting] = useState(false); const beneficiaryName = currentBeneficiary?.name || 'this person'; + // Select all devices by default when scan completes + React.useEffect(() => { + if (foundDevices.length > 0 && !isScanning) { + setSelectedDevices(new Set(foundDevices.map(d => d.id))); + } + }, [foundDevices, isScanning]); + + 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(); @@ -51,36 +80,26 @@ export default function AddSensorScreen() { } }; - const handleConnect = async (device: WPDevice) => { - setIsConnecting(true); - setSelectedDevice(device); - - try { - const success = await connectDevice(device.id); - - if (success) { - // Navigate to Setup WiFi screen - router.push({ - pathname: `/(tabs)/beneficiaries/${id}/setup-wifi` as any, - params: { - deviceId: device.id, - deviceName: device.name, - wellId: device.wellId?.toString() || '', - }, - }); - } else { - throw new Error('Connection failed'); - } - } catch (error: any) { - console.error('[AddSensor] Connection failed:', error); - Alert.alert( - 'Connection Failed', - `Failed to connect to ${device.name}. Please make sure the sensor is powered on and nearby.` - ); - } finally { - setIsConnecting(false); - setSelectedDevice(null); + 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) => { @@ -172,28 +191,52 @@ export default function AddSensorScreen() { <> Found Sensors ({foundDevices.length}) - - - Rescan - + + + + + {selectedDevices.size === foundDevices.length ? 'Deselect All' : 'Select All'} + + + + + Rescan + + {foundDevices.map((device) => { const isConnected = connectedDevices.has(device.id); - const isConnectingThis = isConnecting && selectedDevice?.id === device.id; + const isSelected = selectedDevices.has(device.id); return ( handleConnect(device)} - disabled={isConnectingThis || isConnected} + onPress={() => toggleDeviceSelection(device.id)} + disabled={isConnected} activeOpacity={0.7} > + + + {isSelected && ( + + )} + + @@ -216,19 +259,30 @@ export default function AddSensorScreen() { - {isConnectingThis ? ( - - ) : isConnected ? ( - - + {isConnected && ( + + Added - ) : ( - )} ); })} + + {/* Add Selected Button */} + + + + Add Selected ({selectedCount}) + + )} @@ -406,6 +460,21 @@ const styles = StyleSheet.create({ 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', @@ -427,12 +496,37 @@ const styles = StyleSheet.create({ padding: Spacing.md, flexDirection: 'row', alignItems: 'center', - justifyContent: 'space-between', ...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, @@ -471,8 +565,37 @@ const styles = StyleSheet.create({ fontSize: FontSizes.xs, fontWeight: FontWeights.medium, }, - connectedBadge: { - padding: Spacing.xs, + 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: { diff --git a/app/(tabs)/beneficiaries/[id]/setup-wifi.tsx b/app/(tabs)/beneficiaries/[id]/setup-wifi.tsx index d2d65d1..daa15d7 100644 --- a/app/(tabs)/beneficiaries/[id]/setup-wifi.tsx +++ b/app/(tabs)/beneficiaries/[id]/setup-wifi.tsx @@ -25,16 +25,39 @@ import { Shadows, } from '@/constants/theme'; +// Type for device passed via navigation params +interface DeviceParam { + id: string; + name: string; + mac: string; + wellId?: number; +} + export default function SetupWiFiScreen() { - const { id, deviceId, deviceName, wellId } = useLocalSearchParams<{ + const { id, devices: devicesParam } = useLocalSearchParams<{ id: string; - deviceId: string; - deviceName: string; - wellId: string; + devices: string; // JSON string of DeviceParam[] }>(); const { getWiFiList, setWiFi, disconnectDevice } = useBLE(); + // Parse devices from navigation params + const selectedDevices: DeviceParam[] = React.useMemo(() => { + if (!devicesParam) return []; + try { + return JSON.parse(devicesParam); + } catch (e) { + console.error('[SetupWiFi] Failed to parse devices param:', e); + return []; + } + }, [devicesParam]); + + // Use first device for WiFi scanning (all devices will use same WiFi) + const firstDevice = selectedDevices[0]; + const deviceId = firstDevice?.id; + const deviceName = firstDevice?.name; + const wellId = firstDevice?.wellId?.toString(); + const [networks, setNetworks] = useState([]); const [isLoadingNetworks, setIsLoadingNetworks] = useState(false); const [selectedNetwork, setSelectedNetwork] = useState(null); @@ -160,8 +183,8 @@ export default function SetupWiFiScreen() { { - // Disconnect BLE before going back - disconnectDevice(deviceId!); + // Disconnect all BLE devices before going back + selectedDevices.forEach(d => disconnectDevice(d.id)); router.back(); }} > @@ -178,15 +201,28 @@ export default function SetupWiFiScreen() { - {deviceName} - Well ID: {wellId} + {selectedDevices.length === 1 ? ( + <> + {deviceName} + Well ID: {wellId} + + ) : ( + <> + {selectedDevices.length} Sensors Selected + + {selectedDevices.map(d => d.name).join(', ')} + + + )} {/* Instructions */} - Select the WiFi network your sensor should connect to. Make sure the network has internet access. + {selectedDevices.length === 1 + ? 'Select the WiFi network your sensor should connect to. Make sure the network has internet access.' + : `Select the WiFi network for all ${selectedDevices.length} sensors. They will all be configured with the same WiFi credentials.`}