From 1301c6e093792754454aa4c0e041fa530230094b Mon Sep 17 00:00:00 2001 From: Sergei Date: Mon, 19 Jan 2026 23:17:00 -0800 Subject: [PATCH] Make sensor location tappable to navigate to Device Settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added TouchableOpacity wrapper around the location text in the equipment list so users can tap on a sensor's location to go directly to its Device Settings screen. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- app/(tabs)/beneficiaries/[id]/equipment.tsx | 153 ++++++++++++-------- 1 file changed, 93 insertions(+), 60 deletions(-) diff --git a/app/(tabs)/beneficiaries/[id]/equipment.tsx b/app/(tabs)/beneficiaries/[id]/equipment.tsx index d2186cc..995fc89 100644 --- a/app/(tabs)/beneficiaries/[id]/equipment.tsx +++ b/app/(tabs)/beneficiaries/[id]/equipment.tsx @@ -39,14 +39,13 @@ const sensorConfig = { export default function EquipmentScreen() { const { id } = useLocalSearchParams<{ id: string }>(); const { currentBeneficiary } = useBeneficiary(); - const { isBLEAvailable, scanForDevices, stopScan } = useBLE(); + const { isBLEAvailable, scanDevices, stopScan, foundDevices, isScanning: isBLEScanning } = useBLE(); // Separate state for API sensors (attached) and BLE sensors (nearby) const [apiSensors, setApiSensors] = useState([]); const [bleSensors, setBleSensors] = useState([]); const [isLoading, setIsLoading] = useState(true); const [isRefreshing, setIsRefreshing] = useState(false); - const [isScanning, setIsScanning] = useState(false); const [isDetaching, setIsDetaching] = useState(null); const beneficiaryName = currentBeneficiary?.name || 'this person'; @@ -72,7 +71,7 @@ export default function EquipmentScreen() { return; } - setApiSensors(response.data); + setApiSensors(response.data || []); } catch (error) { console.error('[Equipment] Failed to load sensors:', error); // Show empty state instead of Alert @@ -90,54 +89,56 @@ export default function EquipmentScreen() { // BLE Scan for nearby sensors const handleScanNearby = async () => { - if (isScanning) { + if (isBLEScanning) { // Stop scan stopScan(); - setIsScanning(false); return; } - setIsScanning(true); setBleSensors([]); // Clear previous results try { - const devices = await scanForDevices(10000); // 10 second scan - - // Convert BLE devices to WPSensor format - const nearbyWPSensors: WPSensor[] = devices - .filter(d => d.name?.startsWith('WP_')) // Only WP sensors - .map(d => { - // Parse WP__ format - const parts = d.name!.split('_'); - const wellId = parseInt(parts[1], 10) || 0; - const mac = parts[2] || d.id.slice(-6); - - return { - deviceId: d.id, - wellId: wellId, - mac: mac, - name: d.name!, - status: 'offline' as const, // Nearby but not attached - lastSeen: new Date(), - beneficiaryId: id!, - deploymentId: 0, // Not attached yet - source: 'ble' as const, // From BLE scan - }; - }); - - // Filter out sensors that are already in API list - const apiDeviceIds = new Set(apiSensors.map(s => s.mac)); - const uniqueBleSensors = nearbyWPSensors.filter(s => !apiDeviceIds.has(s.mac)); - - setBleSensors(uniqueBleSensors); + await scanDevices(); + // foundDevices will be updated by BLEContext } catch (error) { console.error('[Equipment] BLE scan failed:', error); Alert.alert('Scan Failed', 'Could not scan for nearby sensors. Make sure Bluetooth is enabled.'); - } finally { - setIsScanning(false); } }; + // Effect to convert BLE foundDevices to WPSensor format + useEffect(() => { + if (foundDevices.length === 0) return; + + // Convert BLE devices to WPSensor format + const nearbyWPSensors: WPSensor[] = foundDevices + .filter((d: { name?: string }) => d.name?.startsWith('WP_')) // Only WP sensors + .map((d: { id: string; name?: string }) => { + // Parse WP__ format + const parts = d.name!.split('_'); + const wellId = parseInt(parts[1], 10) || 0; + const mac = parts[2] || d.id.slice(-6); + + return { + deviceId: d.id, + wellId: wellId, + mac: mac, + name: d.name!, + status: 'offline' as const, // Nearby but not attached + lastSeen: new Date(), + beneficiaryId: id!, + deploymentId: 0, // Not attached yet + source: 'ble' as const, // From BLE scan + }; + }); + + // Filter out sensors that are already in API list + const apiDeviceIds = new Set(apiSensors.map(s => s.mac)); + const uniqueBleSensors = nearbyWPSensors.filter(s => !apiDeviceIds.has(s.mac)); + + setBleSensors(uniqueBleSensors); + }, [foundDevices, apiSensors, id]); + // Handle sensor click - show action sheet for offline, navigate to settings for online const handleSensorPress = (sensor: WPSensor) => { // For offline API sensors - show reconnect options @@ -451,29 +452,48 @@ export default function EquipmentScreen() { • {formatLastSeen(sensor.lastSeen)} - - {sensor.location || 'No location set'} - + { + e.stopPropagation(); + handleDeviceSettings(sensor); + }} + activeOpacity={0.7} + > + + {sensor.location || 'No location set'} + + - { - e.stopPropagation(); - handleDetachDevice(sensor); - }} - disabled={isDetachingThis} - > - {isDetachingThis ? ( - - ) : ( - - )} - + + { + e.stopPropagation(); + handleDeviceSettings(sensor); + }} + > + + + { + e.stopPropagation(); + handleDetachDevice(sensor); + }} + disabled={isDetachingThis} + > + {isDetachingThis ? ( + + ) : ( + + )} + + ); })} @@ -491,11 +511,11 @@ export default function EquipmentScreen() { {/* Scan Nearby Button */} - {isScanning ? ( + {isBLEScanning ? ( <> Scanning... ({bleSensors.length} found) @@ -720,6 +740,19 @@ const styles = StyleSheet.create({ fontSize: FontSizes.xs, color: AppColors.textMuted, }, + deviceActions: { + flexDirection: 'row', + alignItems: 'center', + gap: Spacing.xs, + }, + settingsButton: { + width: 40, + height: 40, + borderRadius: BorderRadius.md, + backgroundColor: AppColors.primaryLighter, + justifyContent: 'center', + alignItems: 'center', + }, detachButton: { width: 40, height: 40, @@ -852,7 +885,7 @@ const styles = StyleSheet.create({ ...Shadows.md, }, scanButtonActive: { - backgroundColor: AppColors.secondary, + backgroundColor: AppColors.primaryDark, }, scanButtonText: { fontSize: FontSizes.base,