@adventurelabs/scout-core 1.0.91 → 1.0.93

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,3 +1,4 @@
1
+ "use server";
1
2
  import { newServerClient } from "../supabase/server";
2
3
  import { EnumWebResponse, IWebResponse, } from "../types/requests";
3
4
  // Get connectivity by session id using RPC function with coordinates
@@ -9,149 +9,127 @@ import { getDaysAgoTimestamp } from "../helpers/time";
9
9
  export function useScoutRealtimeConnectivity(scoutSupabase) {
10
10
  const channels = useRef([]);
11
11
  const dispatch = useAppDispatch();
12
- const [isLoading, setIsLoading] = useState(false);
12
+ const [hasInitialized, setHasInitialized] = useState(null);
13
13
  const activeHerdId = useSelector((state) => state.scout.active_herd_id);
14
- const activeHerdGpsTrackersConnectivity = useSelector((state) => state.scout.active_herd_gps_trackers_connectivity);
14
+ const connectivity = useSelector((state) => state.scout.active_herd_gps_trackers_connectivity);
15
15
  const herdModules = useSelector((state) => state.scout.herd_modules);
16
- // Connectivity broadcast handler
16
+ // Handle connectivity broadcasts
17
17
  const handleConnectivityBroadcast = useCallback((payload) => {
18
- console.log("[Connectivity] Broadcast received:", payload.payload.event);
19
- const event = payload.payload.event;
20
- const data = payload.payload;
18
+ const { event, payload: data } = payload;
21
19
  const connectivityData = data.new || data.old;
22
- // Only process tracker connectivity data (no session_id)
23
- if (!connectivityData || connectivityData.session_id) {
20
+ // Only process GPS tracker data (no session_id)
21
+ if (!connectivityData?.device_id || connectivityData.session_id) {
24
22
  return;
25
23
  }
26
24
  const deviceId = connectivityData.device_id;
27
- if (!deviceId)
28
- return;
29
- const currentConnectivity = {
30
- ...activeHerdGpsTrackersConnectivity,
31
- };
32
- console.log("[Connectivity] Current connectivity:", currentConnectivity);
25
+ const updatedConnectivity = { ...connectivity };
33
26
  switch (event) {
34
27
  case "INSERT":
35
- if (!currentConnectivity[deviceId]) {
36
- currentConnectivity[deviceId] = [];
28
+ if (!updatedConnectivity[deviceId]) {
29
+ updatedConnectivity[deviceId] = [];
37
30
  }
38
- currentConnectivity[deviceId].push(connectivityData);
31
+ updatedConnectivity[deviceId].push(connectivityData);
39
32
  // Keep only recent 100 entries
40
- if (currentConnectivity[deviceId].length > 100) {
41
- currentConnectivity[deviceId] = currentConnectivity[deviceId]
33
+ if (updatedConnectivity[deviceId].length > 100) {
34
+ updatedConnectivity[deviceId] = updatedConnectivity[deviceId]
42
35
  .sort((a, b) => new Date(b.timestamp_start || 0).getTime() -
43
36
  new Date(a.timestamp_start || 0).getTime())
44
37
  .slice(0, 100);
45
38
  }
46
39
  break;
47
40
  case "UPDATE":
48
- if (currentConnectivity[deviceId]) {
49
- const index = currentConnectivity[deviceId].findIndex((c) => c.id === connectivityData.id);
41
+ if (updatedConnectivity[deviceId]) {
42
+ const index = updatedConnectivity[deviceId].findIndex((c) => c.id === connectivityData.id);
50
43
  if (index >= 0) {
51
- currentConnectivity[deviceId][index] = connectivityData;
44
+ updatedConnectivity[deviceId][index] = connectivityData;
52
45
  }
53
46
  }
54
47
  break;
55
48
  case "DELETE":
56
- if (currentConnectivity[deviceId]) {
57
- currentConnectivity[deviceId] = currentConnectivity[deviceId].filter((c) => c.id !== connectivityData.id);
58
- if (currentConnectivity[deviceId].length === 0) {
59
- delete currentConnectivity[deviceId];
49
+ if (updatedConnectivity[deviceId]) {
50
+ updatedConnectivity[deviceId] = updatedConnectivity[deviceId].filter((c) => c.id !== connectivityData.id);
51
+ if (updatedConnectivity[deviceId].length === 0) {
52
+ delete updatedConnectivity[deviceId];
60
53
  }
61
54
  }
62
55
  break;
63
56
  }
64
- dispatch(setActiveHerdGpsTrackersConnectivity(currentConnectivity));
65
- }, [activeHerdGpsTrackersConnectivity, dispatch]);
66
- const cleanupChannels = () => {
67
- channels.current.forEach((channel) => scoutSupabase.removeChannel(channel));
68
- channels.current = [];
69
- };
70
- const createConnectivityChannel = (herdId) => {
71
- return scoutSupabase
72
- .channel(`${herdId}-connectivity`, { config: { private: true } })
73
- .on("broadcast", { event: "*" }, handleConnectivityBroadcast)
74
- .subscribe((status) => {
75
- if (status === "SUBSCRIBED") {
76
- console.log(`[Connectivity] ✅ Connected to herd ${herdId}`);
77
- }
78
- else if (status === "CHANNEL_ERROR") {
79
- console.error(`[Connectivity] ❌ Failed to connect to herd ${herdId}`);
80
- }
81
- });
82
- };
83
- // Fetch initial connectivity data for GPS trackers
84
- const fetchInitialConnectivityData = useCallback(async () => {
85
- if (!activeHerdId || isLoading)
57
+ dispatch(setActiveHerdGpsTrackersConnectivity(updatedConnectivity));
58
+ }, [connectivity, dispatch]);
59
+ // Fetch initial connectivity data
60
+ const fetchInitialData = useCallback(async () => {
61
+ if (!activeHerdId || hasInitialized === activeHerdId)
86
62
  return;
87
- // Find the active herd module
88
- const activeHerdModule = herdModules.find((hm) => hm.herd.id.toString() === activeHerdId);
63
+ const herdId = activeHerdId; // Type narrowing
64
+ const activeHerdModule = herdModules.find((hm) => hm.herd.id.toString() === herdId);
89
65
  if (!activeHerdModule)
90
66
  return;
91
- // Get GPS tracker devices from the herd
92
- const gpsTrackerDevices = activeHerdModule.devices.filter((device) => device.device_type === "gps_tracker" ||
93
- device.device_type === "gps_tracker_vehicle" ||
94
- device.device_type === "gps_tracker_person");
95
- if (gpsTrackerDevices.length === 0) {
96
- console.log("[Connectivity] No GPS trackers found in herd");
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) {
70
+ setHasInitialized(herdId);
97
71
  return;
98
72
  }
99
- setIsLoading(true);
100
- console.log(`[Connectivity] Fetching last day connectivity for ${gpsTrackerDevices.length} GPS trackers`);
101
- // Calculate timestamp for last 24 hours
73
+ console.log(`[Connectivity] Loading data for ${gpsDevices.length} GPS trackers`);
102
74
  const timestampFilter = getDaysAgoTimestamp(1);
103
75
  const connectivityData = {};
104
- try {
105
- // Fetch connectivity for each GPS tracker
106
- await Promise.all(gpsTrackerDevices.map(async (device) => {
107
- try {
108
- if (!device.id)
109
- return;
110
- const response = await server_get_connectivity_by_device_id(device.id, timestampFilter);
111
- if (response.status === EnumWebResponse.SUCCESS && response.data) {
112
- // Filter out any data with session_id (only tracker data)
113
- const trackerConnectivity = response.data.filter((conn) => !conn.session_id);
114
- if (trackerConnectivity.length > 0 && device.id) {
115
- // Keep only most recent 100 entries per device
116
- connectivityData[device.id] = trackerConnectivity
117
- .sort((a, b) => new Date(b.timestamp_start || 0).getTime() -
118
- new Date(a.timestamp_start || 0).getTime())
119
- .slice(0, 100);
120
- console.log(`[Connectivity] Loaded ${connectivityData[device.id]?.length} records for device ${device.id}`);
121
- }
76
+ await Promise.all(gpsDevices.map(async (device) => {
77
+ if (!device.id)
78
+ return;
79
+ try {
80
+ const response = await server_get_connectivity_by_device_id(device.id, timestampFilter);
81
+ if (response.status === EnumWebResponse.SUCCESS && response.data) {
82
+ const trackerData = response.data.filter((conn) => !conn.session_id);
83
+ if (trackerData.length > 0) {
84
+ connectivityData[device.id] = trackerData
85
+ .sort((a, b) => new Date(b.timestamp_start || 0).getTime() -
86
+ new Date(a.timestamp_start || 0).getTime())
87
+ .slice(0, 100);
122
88
  }
123
89
  }
124
- catch (error) {
125
- console.warn(`[Connectivity] Failed to fetch data for device ${device.id}:`, error);
90
+ else {
91
+ console.error(`[Connectivity] API error for device ${device.id}:`, response.msg || "Unknown error");
126
92
  }
127
- }));
128
- // Update the store with initial connectivity data
129
- dispatch(setActiveHerdGpsTrackersConnectivity(connectivityData));
130
- console.log(`[Connectivity] Initial data loaded for ${Object.keys(connectivityData).length} devices`);
131
- }
132
- catch (error) {
133
- console.error("[Connectivity] Error fetching initial data:", error);
134
- }
135
- finally {
136
- setIsLoading(false);
137
- }
138
- }, [activeHerdId, herdModules, isLoading, dispatch]);
93
+ }
94
+ catch (error) {
95
+ console.error(`[Connectivity] Failed to fetch data for device ${device.id}:`, error);
96
+ }
97
+ }));
98
+ dispatch(setActiveHerdGpsTrackersConnectivity(connectivityData));
99
+ setHasInitialized(herdId);
100
+ console.log(`[Connectivity] Loaded data for ${Object.keys(connectivityData).length} devices`);
101
+ }, [activeHerdId, herdModules, hasInitialized, dispatch]);
139
102
  useEffect(() => {
140
- if (!scoutSupabase)
103
+ if (!scoutSupabase || !activeHerdId)
141
104
  return;
142
- cleanupChannels();
143
- // Create connectivity channel for active herd
144
- if (activeHerdId) {
145
- const channel = createConnectivityChannel(activeHerdId);
146
- channels.current.push(channel);
147
- // Fetch initial connectivity data
148
- fetchInitialConnectivityData();
149
- }
150
- return cleanupChannels;
105
+ // Clean up existing channels
106
+ channels.current.forEach((channel) => scoutSupabase.removeChannel(channel));
107
+ channels.current = [];
108
+ // Reset initialization when herd changes
109
+ setHasInitialized(null);
110
+ // Create connectivity channel
111
+ const channel = scoutSupabase
112
+ .channel(`${activeHerdId}-connectivity`, { config: { private: true } })
113
+ .on("broadcast", { event: "*" }, handleConnectivityBroadcast)
114
+ .subscribe((status) => {
115
+ if (status === "SUBSCRIBED") {
116
+ console.log(`[Connectivity] ✅ Connected to herd ${activeHerdId}`);
117
+ }
118
+ else if (status === "CHANNEL_ERROR") {
119
+ console.error(`[Connectivity] ❌ Failed to connect to herd ${activeHerdId}`);
120
+ }
121
+ });
122
+ channels.current.push(channel);
123
+ // Fetch initial data
124
+ fetchInitialData();
125
+ return () => {
126
+ channels.current.forEach((ch) => scoutSupabase.removeChannel(ch));
127
+ channels.current = [];
128
+ };
151
129
  }, [
152
130
  scoutSupabase,
153
131
  activeHerdId,
154
132
  handleConnectivityBroadcast,
155
- fetchInitialConnectivityData,
133
+ fetchInitialData,
156
134
  ]);
157
135
  }
@@ -45,8 +45,6 @@ export function useScoutRealtimeDevices(scoutSupabase) {
45
45
  });
46
46
  };
47
47
  useEffect(() => {
48
- if (!scoutSupabase)
49
- return;
50
48
  cleanupChannels();
51
49
  // Create devices channel for active herd
52
50
  if (activeHerdId) {
@@ -54,5 +52,5 @@ export function useScoutRealtimeDevices(scoutSupabase) {
54
52
  channels.current.push(channel);
55
53
  }
56
54
  return cleanupChannels;
57
- }, [scoutSupabase, activeHerdId, handleDeviceBroadcast]);
55
+ }, [activeHerdId]);
58
56
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adventurelabs/scout-core",
3
- "version": "1.0.91",
3
+ "version": "1.0.93",
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",