@adventurelabs/scout-core 1.0.95 → 1.0.97
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useAppDispatch } from "../store/hooks";
|
|
3
3
|
import { useSelector } from "react-redux";
|
|
4
|
-
import { useEffect, useRef, useCallback } from "react";
|
|
4
|
+
import { useEffect, useRef, useCallback, useMemo } from "react";
|
|
5
5
|
import { setActiveHerdGpsTrackersConnectivity } from "../store/scout";
|
|
6
6
|
import { server_get_connectivity_by_device_id } from "../helpers/connectivity";
|
|
7
7
|
import { EnumWebResponse } from "../types/requests";
|
|
@@ -12,21 +12,40 @@ export function useScoutRealtimeConnectivity(scoutSupabase) {
|
|
|
12
12
|
const activeHerdId = useSelector((state) => state.scout.active_herd_id);
|
|
13
13
|
const connectivity = useSelector((state) => state.scout.active_herd_gps_trackers_connectivity);
|
|
14
14
|
const herdModules = useSelector((state) => state.scout.herd_modules);
|
|
15
|
+
// Create stable reference for GPS device IDs to prevent unnecessary refetching
|
|
16
|
+
const gpsDeviceIds = useMemo(() => {
|
|
17
|
+
if (!activeHerdId)
|
|
18
|
+
return "";
|
|
19
|
+
const activeHerdModule = herdModules.find((hm) => hm.herd.id.toString() === activeHerdId);
|
|
20
|
+
if (!activeHerdModule)
|
|
21
|
+
return "";
|
|
22
|
+
const gpsDevices = activeHerdModule.devices.filter((device) => device.device_type &&
|
|
23
|
+
["gps_tracker", "gps_tracker_vehicle", "gps_tracker_person"].includes(device.device_type));
|
|
24
|
+
return gpsDevices
|
|
25
|
+
.map((d) => d.id)
|
|
26
|
+
.filter(Boolean)
|
|
27
|
+
.sort()
|
|
28
|
+
.join(",");
|
|
29
|
+
}, [activeHerdId, herdModules]);
|
|
15
30
|
// Handle connectivity broadcasts
|
|
16
31
|
const handleConnectivityBroadcast = useCallback((payload) => {
|
|
17
32
|
const { event, payload: data } = payload;
|
|
18
|
-
const connectivityData = data.
|
|
33
|
+
const connectivityData = data.record || data.old_record;
|
|
19
34
|
// Only process GPS tracker data (no session_id)
|
|
20
35
|
if (!connectivityData?.device_id || connectivityData.session_id) {
|
|
21
36
|
return;
|
|
22
37
|
}
|
|
23
38
|
const deviceId = connectivityData.device_id;
|
|
24
39
|
const updatedConnectivity = { ...connectivity };
|
|
25
|
-
switch (
|
|
40
|
+
switch (data.operation) {
|
|
26
41
|
case "INSERT":
|
|
27
42
|
if (!updatedConnectivity[deviceId]) {
|
|
28
43
|
updatedConnectivity[deviceId] = [];
|
|
29
44
|
}
|
|
45
|
+
else {
|
|
46
|
+
// Create a copy of the existing array to avoid mutating immutable state
|
|
47
|
+
updatedConnectivity[deviceId] = [...updatedConnectivity[deviceId]];
|
|
48
|
+
}
|
|
30
49
|
updatedConnectivity[deviceId].push(connectivityData);
|
|
31
50
|
// Keep only recent 100 entries
|
|
32
51
|
if (updatedConnectivity[deviceId].length > 100) {
|
|
@@ -38,6 +57,8 @@ export function useScoutRealtimeConnectivity(scoutSupabase) {
|
|
|
38
57
|
break;
|
|
39
58
|
case "UPDATE":
|
|
40
59
|
if (updatedConnectivity[deviceId]) {
|
|
60
|
+
// Create a copy of the array before modifying
|
|
61
|
+
updatedConnectivity[deviceId] = [...updatedConnectivity[deviceId]];
|
|
41
62
|
const index = updatedConnectivity[deviceId].findIndex((c) => c.id === connectivityData.id);
|
|
42
63
|
if (index >= 0) {
|
|
43
64
|
updatedConnectivity[deviceId][index] = connectivityData;
|
|
@@ -46,6 +67,7 @@ export function useScoutRealtimeConnectivity(scoutSupabase) {
|
|
|
46
67
|
break;
|
|
47
68
|
case "DELETE":
|
|
48
69
|
if (updatedConnectivity[deviceId]) {
|
|
70
|
+
// Filter creates a new array, so this is safe
|
|
49
71
|
updatedConnectivity[deviceId] = updatedConnectivity[deviceId].filter((c) => c.id !== connectivityData.id);
|
|
50
72
|
if (updatedConnectivity[deviceId].length === 0) {
|
|
51
73
|
delete updatedConnectivity[deviceId];
|
|
@@ -55,50 +77,43 @@ export function useScoutRealtimeConnectivity(scoutSupabase) {
|
|
|
55
77
|
}
|
|
56
78
|
console.log("[Connectivity] updating tracker connectivity in response to broadcast");
|
|
57
79
|
dispatch(setActiveHerdGpsTrackersConnectivity(updatedConnectivity));
|
|
58
|
-
}, [connectivity]);
|
|
80
|
+
}, [connectivity, dispatch]);
|
|
59
81
|
// Fetch initial connectivity data
|
|
60
82
|
const fetchInitialData = useCallback(async () => {
|
|
61
|
-
if (!activeHerdId)
|
|
83
|
+
if (!activeHerdId || !gpsDeviceIds)
|
|
62
84
|
return;
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
if (!activeHerdModule)
|
|
66
|
-
return;
|
|
67
|
-
const gpsDevices = activeHerdModule.devices.filter((device) => device.device_type &&
|
|
68
|
-
["gps_tracker", "gps_tracker_vehicle", "gps_tracker_person"].includes(device.device_type));
|
|
69
|
-
if (gpsDevices.length === 0) {
|
|
85
|
+
const deviceIds = gpsDeviceIds.split(",").filter(Boolean).map(Number);
|
|
86
|
+
if (deviceIds.length === 0) {
|
|
70
87
|
return;
|
|
71
88
|
}
|
|
72
|
-
console.log(`[Connectivity] Loading data for ${
|
|
89
|
+
console.log(`[Connectivity] Loading data for ${deviceIds.length} GPS trackers`);
|
|
73
90
|
const timestampFilter = getDaysAgoTimestamp(1);
|
|
74
91
|
const connectivityData = {};
|
|
75
|
-
await Promise.all(
|
|
76
|
-
if (!device.id)
|
|
77
|
-
return;
|
|
92
|
+
await Promise.all(deviceIds.map(async (deviceId) => {
|
|
78
93
|
try {
|
|
79
|
-
const response = await server_get_connectivity_by_device_id(
|
|
94
|
+
const response = await server_get_connectivity_by_device_id(deviceId, timestampFilter);
|
|
80
95
|
if (response.status === EnumWebResponse.SUCCESS && response.data) {
|
|
81
96
|
const trackerData = response.data.filter((conn) => !conn.session_id);
|
|
82
97
|
if (trackerData.length > 0) {
|
|
83
|
-
connectivityData[
|
|
98
|
+
connectivityData[deviceId] = trackerData
|
|
84
99
|
.sort((a, b) => new Date(b.timestamp_start || 0).getTime() -
|
|
85
100
|
new Date(a.timestamp_start || 0).getTime())
|
|
86
101
|
.slice(0, 100);
|
|
87
102
|
}
|
|
88
103
|
}
|
|
89
104
|
else {
|
|
90
|
-
console.
|
|
105
|
+
console.warn(`[Connectivity] API error for device ${deviceId}:`, response.msg || "Unknown error");
|
|
91
106
|
}
|
|
92
107
|
}
|
|
93
108
|
catch (error) {
|
|
94
|
-
console.
|
|
109
|
+
console.warn(`[Connectivity] Failed to fetch data for device ${deviceId}:`, error);
|
|
95
110
|
}
|
|
96
111
|
}));
|
|
97
112
|
dispatch(setActiveHerdGpsTrackersConnectivity(connectivityData));
|
|
98
113
|
console.log(`[Connectivity] Loaded data for ${Object.keys(connectivityData).length} devices`);
|
|
99
|
-
}, [activeHerdId,
|
|
114
|
+
}, [activeHerdId, gpsDeviceIds, dispatch]);
|
|
100
115
|
useEffect(() => {
|
|
101
|
-
if (!scoutSupabase || !
|
|
116
|
+
if (!scoutSupabase || !gpsDeviceIds)
|
|
102
117
|
return;
|
|
103
118
|
// Clean up existing channels
|
|
104
119
|
channels.current.forEach((channel) => scoutSupabase.removeChannel(channel));
|
|
@@ -112,7 +127,7 @@ export function useScoutRealtimeConnectivity(scoutSupabase) {
|
|
|
112
127
|
console.log(`[Connectivity] ✅ Connected to herd ${activeHerdId}`);
|
|
113
128
|
}
|
|
114
129
|
else if (status === "CHANNEL_ERROR") {
|
|
115
|
-
console.
|
|
130
|
+
console.warn(`[Connectivity] 🟡 Failed to connect to herd ${activeHerdId}`);
|
|
116
131
|
}
|
|
117
132
|
});
|
|
118
133
|
channels.current.push(channel);
|
|
@@ -122,5 +137,5 @@ export function useScoutRealtimeConnectivity(scoutSupabase) {
|
|
|
122
137
|
channels.current.forEach((ch) => scoutSupabase.removeChannel(ch));
|
|
123
138
|
channels.current = [];
|
|
124
139
|
};
|
|
125
|
-
}, [
|
|
140
|
+
}, [gpsDeviceIds]);
|
|
126
141
|
}
|
|
@@ -9,21 +9,20 @@ export function useScoutRealtimeDevices(scoutSupabase) {
|
|
|
9
9
|
const activeHerdId = useSelector((state) => state.scout.active_herd_id);
|
|
10
10
|
// Device broadcast handler
|
|
11
11
|
const handleDeviceBroadcast = useCallback((payload) => {
|
|
12
|
-
console.log("[Devices] Broadcast received:", payload.payload.
|
|
13
|
-
const event = payload.payload.event;
|
|
12
|
+
console.log("[Devices] Broadcast received:", payload.payload.operation);
|
|
14
13
|
const data = payload.payload;
|
|
15
|
-
switch (
|
|
14
|
+
switch (data.operation) {
|
|
16
15
|
case "INSERT":
|
|
17
|
-
if (data.
|
|
18
|
-
dispatch(addDevice(data.
|
|
16
|
+
if (data.record)
|
|
17
|
+
dispatch(addDevice(data.record));
|
|
19
18
|
break;
|
|
20
19
|
case "UPDATE":
|
|
21
|
-
if (data.
|
|
22
|
-
dispatch(updateDevice(data.
|
|
20
|
+
if (data.record)
|
|
21
|
+
dispatch(updateDevice(data.record));
|
|
23
22
|
break;
|
|
24
23
|
case "DELETE":
|
|
25
|
-
if (data.
|
|
26
|
-
dispatch(deleteDevice(data.
|
|
24
|
+
if (data.old_record)
|
|
25
|
+
dispatch(deleteDevice(data.old_record));
|
|
27
26
|
break;
|
|
28
27
|
}
|
|
29
28
|
}, [dispatch]);
|