@adventurelabs/scout-core 1.0.96 → 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,6 +12,21 @@ 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;
@@ -27,6 +42,10 @@ export function useScoutRealtimeConnectivity(scoutSupabase) {
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 herdId = activeHerdId; // Type narrowing
64
- const activeHerdModule = herdModules.find((hm) => hm.herd.id.toString() === herdId);
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 ${gpsDevices.length} GPS trackers`);
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(gpsDevices.map(async (device) => {
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(device.id, timestampFilter);
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[device.id] = trackerData
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.error(`[Connectivity] API error for device ${device.id}:`, response.msg || "Unknown error");
105
+ console.warn(`[Connectivity] API error for device ${deviceId}:`, response.msg || "Unknown error");
91
106
  }
92
107
  }
93
108
  catch (error) {
94
- console.error(`[Connectivity] Failed to fetch data for device ${device.id}:`, error);
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, herdModules]);
114
+ }, [activeHerdId, gpsDeviceIds, dispatch]);
100
115
  useEffect(() => {
101
- if (!scoutSupabase || !activeHerdId || herdModules.length == 0)
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.error(`[Connectivity] Failed to connect to herd ${activeHerdId}`);
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
- }, [activeHerdId, herdModules]);
140
+ }, [gpsDeviceIds]);
126
141
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adventurelabs/scout-core",
3
- "version": "1.0.96",
3
+ "version": "1.0.97",
4
4
  "description": "Core utilities and helpers for Adventure Labs Scout applications",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",