@adventurelabs/scout-core 1.4.1 → 1.4.2

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.
@@ -7,5 +7,6 @@ export { useScoutRealtimeTags } from "./useScoutRealtimeTags";
7
7
  export { useScoutRealtimeSessions } from "./useScoutRealtimeSessions";
8
8
  export { useScoutRealtimePlans } from "./useScoutRealtimePlans";
9
9
  export { useScoutRealtimePins } from "./useScoutRealtimePins";
10
+ export { useScoutRealtimeParts } from "./useScoutRealtimeParts";
10
11
  export { useInfiniteSessionsByHerd, useInfiniteSessionsByDevice, useInfiniteEventsByHerd, useInfiniteEventsByDevice, useInfiniteArtifactsByHerd, useInfiniteArtifactsByDevice, useIntersectionObserver, } from "./useInfiniteQuery";
11
12
  export { useLoadingPerformance, useSessionSummariesByHerd, useHasSessionSummaries, } from "../store/hooks";
@@ -7,6 +7,7 @@ export { useScoutRealtimeTags } from "./useScoutRealtimeTags";
7
7
  export { useScoutRealtimeSessions } from "./useScoutRealtimeSessions";
8
8
  export { useScoutRealtimePlans } from "./useScoutRealtimePlans";
9
9
  export { useScoutRealtimePins } from "./useScoutRealtimePins";
10
+ export { useScoutRealtimeParts } from "./useScoutRealtimeParts";
10
11
  // RTK Query infinite scroll hooks
11
12
  export { useInfiniteSessionsByHerd, useInfiniteSessionsByDevice, useInfiniteEventsByHerd, useInfiniteEventsByDevice, useInfiniteArtifactsByHerd, useInfiniteArtifactsByDevice, useIntersectionObserver, } from "./useInfiniteQuery";
12
13
  // Session summaries and performance hooks
@@ -0,0 +1,5 @@
1
+ import { SupabaseClient } from "@supabase/supabase-js";
2
+ import { Database } from "../types/supabase";
3
+ import { IPart } from "../types/db";
4
+ import { RealtimeData } from "../types/realtime";
5
+ export declare function useScoutRealtimeParts(scoutSupabase: SupabaseClient<Database>): [RealtimeData<IPart> | null, () => void];
@@ -0,0 +1,113 @@
1
+ "use client";
2
+ import { useAppDispatch } from "../store/hooks";
3
+ import { useSelector } from "react-redux";
4
+ import { useEffect, useRef, useCallback, useState } from "react";
5
+ import { EnumRealtimeOperation } from "../types/realtime";
6
+ export function useScoutRealtimeParts(scoutSupabase) {
7
+ const channels = useRef([]);
8
+ const dispatch = useAppDispatch();
9
+ const [latestPartUpdate, setLatestPartUpdate] = useState(null);
10
+ const activeHerdId = useSelector((state) => state.scout.active_herd_id);
11
+ const herdModules = useSelector((state) => state.scout.herd_modules);
12
+ // Part broadcast handler
13
+ const handlePartBroadcast = useCallback((payload) => {
14
+ console.log("[Parts] Broadcast received:", payload.payload.operation);
15
+ const data = payload.payload;
16
+ const partData = data.record || data.old_record;
17
+ if (!partData)
18
+ return;
19
+ let operation;
20
+ // Find the target herd module and device
21
+ const herdModule = herdModules.find((hm) => hm.herd.id.toString() === activeHerdId);
22
+ if (!herdModule) {
23
+ console.warn("[Parts] No herd module found for active herd");
24
+ return;
25
+ }
26
+ const targetDevice = herdModule.devices.find((device) => device.id === partData.device_id);
27
+ if (!targetDevice) {
28
+ console.warn(`[Parts] No device found with ID: ${partData.device_id}`);
29
+ return;
30
+ }
31
+ // Ensure device has parts array
32
+ if (!targetDevice.parts) {
33
+ targetDevice.parts = [];
34
+ }
35
+ switch (data.operation) {
36
+ case "INSERT":
37
+ operation = EnumRealtimeOperation.INSERT;
38
+ if (data.record) {
39
+ console.log("[Parts] New part received:", data.record);
40
+ // Add part to device's parts array if not already present
41
+ const existingPartIndex = targetDevice.parts.findIndex((p) => p.id === data.record.id);
42
+ if (existingPartIndex === -1) {
43
+ targetDevice.parts.push(data.record);
44
+ }
45
+ }
46
+ break;
47
+ case "UPDATE":
48
+ operation = EnumRealtimeOperation.UPDATE;
49
+ if (data.record) {
50
+ console.log("[Parts] Part updated:", data.record);
51
+ // Update existing part in device's parts array
52
+ const partIndex = targetDevice.parts.findIndex((p) => p.id === data.record.id);
53
+ if (partIndex !== -1) {
54
+ targetDevice.parts[partIndex] = data.record;
55
+ }
56
+ else {
57
+ // Part not found, add it
58
+ targetDevice.parts.push(data.record);
59
+ }
60
+ }
61
+ break;
62
+ case "DELETE":
63
+ operation = EnumRealtimeOperation.DELETE;
64
+ if (data.old_record) {
65
+ console.log("[Parts] Part deleted:", data.old_record);
66
+ // Remove part from device's parts array
67
+ targetDevice.parts = targetDevice.parts.filter((p) => p.id !== data.old_record.id);
68
+ }
69
+ break;
70
+ default:
71
+ return;
72
+ }
73
+ const realtimeData = {
74
+ data: partData,
75
+ operation,
76
+ };
77
+ console.log(`[scout-core realtime] PART ${data.operation} received:`, JSON.stringify(realtimeData));
78
+ setLatestPartUpdate(realtimeData);
79
+ }, [dispatch, activeHerdId, herdModules]);
80
+ // Clear latest update
81
+ const clearLatestUpdate = useCallback(() => {
82
+ setLatestPartUpdate(null);
83
+ }, []);
84
+ const cleanupChannels = () => {
85
+ channels.current.forEach((channel) => scoutSupabase.removeChannel(channel));
86
+ channels.current = [];
87
+ };
88
+ const createPartsChannel = (herdId) => {
89
+ return scoutSupabase
90
+ .channel(`${herdId}-parts`, { config: { private: true } })
91
+ .on("broadcast", { event: "*" }, handlePartBroadcast)
92
+ .subscribe((status) => {
93
+ if (status === "SUBSCRIBED") {
94
+ console.log(`[Parts] ✅ Connected to herd ${herdId}`);
95
+ }
96
+ else if (status === "CHANNEL_ERROR") {
97
+ console.warn(`[Parts] 🟡 Failed to connect to herd ${herdId}`);
98
+ }
99
+ });
100
+ };
101
+ useEffect(() => {
102
+ cleanupChannels();
103
+ // Clear previous update when switching herds
104
+ clearLatestUpdate();
105
+ // Create parts channel for active herd
106
+ if (activeHerdId) {
107
+ const channel = createPartsChannel(activeHerdId);
108
+ channels.current.push(channel);
109
+ }
110
+ return cleanupChannels;
111
+ }, [activeHerdId, clearLatestUpdate]);
112
+ return [latestPartUpdate, clearLatestUpdate];
113
+ }
package/dist/index.d.ts CHANGED
@@ -45,6 +45,7 @@ export * from "./hooks/useScoutRealtimeVersionsSoftware";
45
45
  export * from "./hooks/useScoutRealtimeEvents";
46
46
  export * from "./hooks/useScoutRealtimeTags";
47
47
  export * from "./hooks/useScoutRealtimeSessions";
48
+ export * from "./hooks/useScoutRealtimeParts";
48
49
  export * from "./hooks/useScoutRealtimePlans";
49
50
  export * from "./hooks/useScoutRealtimePins";
50
51
  export * from "./hooks/useScoutRefresh";
package/dist/index.js CHANGED
@@ -49,6 +49,7 @@ export * from "./hooks/useScoutRealtimeVersionsSoftware";
49
49
  export * from "./hooks/useScoutRealtimeEvents";
50
50
  export * from "./hooks/useScoutRealtimeTags";
51
51
  export * from "./hooks/useScoutRealtimeSessions";
52
+ export * from "./hooks/useScoutRealtimeParts";
52
53
  export * from "./hooks/useScoutRealtimePlans";
53
54
  export * from "./hooks/useScoutRealtimePins";
54
55
  export * from "./hooks/useScoutRefresh";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adventurelabs/scout-core",
3
- "version": "1.4.1",
3
+ "version": "1.4.2",
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",