@adventurelabs/scout-core 1.4.53 → 1.4.55
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.
|
@@ -6,26 +6,24 @@ export function useScoutRealtimePlans(scoutSupabase) {
|
|
|
6
6
|
const channels = useRef([]);
|
|
7
7
|
const [latestPlanUpdate, setLatestPlanUpdate] = useState(null);
|
|
8
8
|
const activeHerdId = useSelector((state) => state.scout.active_herd_id);
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
const handlePlanBroadcast = useCallback((message) => {
|
|
10
|
+
const { payload: data } = message;
|
|
11
|
+
if (data.table !== "plans" || data.schema !== "public") {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const planData = data.record ?? data.old_record;
|
|
14
15
|
if (!planData)
|
|
15
16
|
return;
|
|
16
17
|
let operation;
|
|
17
18
|
switch (data.operation) {
|
|
18
19
|
case "INSERT":
|
|
19
20
|
operation = EnumRealtimeOperation.INSERT;
|
|
20
|
-
console.log("[Plans] New plan received:", data.record);
|
|
21
21
|
break;
|
|
22
22
|
case "UPDATE":
|
|
23
23
|
operation = EnumRealtimeOperation.UPDATE;
|
|
24
|
-
console.log("[Plans] Plan updated:", data.record);
|
|
25
24
|
break;
|
|
26
25
|
case "DELETE":
|
|
27
26
|
operation = EnumRealtimeOperation.DELETE;
|
|
28
|
-
console.log("[Plans] Plan deleted:", data.old_record);
|
|
29
27
|
break;
|
|
30
28
|
default:
|
|
31
29
|
return;
|
|
@@ -34,40 +32,35 @@ export function useScoutRealtimePlans(scoutSupabase) {
|
|
|
34
32
|
data: planData,
|
|
35
33
|
operation,
|
|
36
34
|
};
|
|
37
|
-
console.log(`[scout-core realtime] PLAN ${data.operation} received:`, JSON.stringify(realtimeData));
|
|
38
35
|
setLatestPlanUpdate(realtimeData);
|
|
39
36
|
}, []);
|
|
40
|
-
// Clear latest update
|
|
41
37
|
const clearLatestUpdate = useCallback(() => {
|
|
42
38
|
setLatestPlanUpdate(null);
|
|
43
39
|
}, []);
|
|
44
|
-
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (!scoutSupabase)
|
|
42
|
+
return;
|
|
45
43
|
channels.current.forEach((channel) => scoutSupabase.removeChannel(channel));
|
|
46
44
|
channels.current = [];
|
|
47
|
-
};
|
|
48
|
-
const createPlansChannel = (herdId) => {
|
|
49
|
-
return scoutSupabase
|
|
50
|
-
.channel(`${herdId}-plans`, { config: { private: true } })
|
|
51
|
-
.on("broadcast", { event: "*" }, handlePlanBroadcast)
|
|
52
|
-
.subscribe((status) => {
|
|
53
|
-
if (status === "SUBSCRIBED") {
|
|
54
|
-
console.log(`[Plans] ✅ Connected to herd ${herdId}`);
|
|
55
|
-
}
|
|
56
|
-
else if (status === "CHANNEL_ERROR") {
|
|
57
|
-
console.warn(`[Plans] 🟡 Failed to connect to herd ${herdId}`);
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
};
|
|
61
|
-
useEffect(() => {
|
|
62
|
-
cleanupChannels();
|
|
63
|
-
// Clear previous update when switching herds
|
|
64
45
|
clearLatestUpdate();
|
|
65
|
-
// Create plans channel for active herd
|
|
66
46
|
if (activeHerdId) {
|
|
67
|
-
const channel =
|
|
47
|
+
const channel = scoutSupabase
|
|
48
|
+
.channel(`${activeHerdId}-plans`, { config: { private: true } })
|
|
49
|
+
.on("broadcast", { event: "*" }, handlePlanBroadcast)
|
|
50
|
+
.subscribe((status) => {
|
|
51
|
+
if (status === "SUBSCRIBED") {
|
|
52
|
+
console.log(`[Plans] ✅ Connected to herd ${activeHerdId}`);
|
|
53
|
+
}
|
|
54
|
+
else if (status === "CHANNEL_ERROR") {
|
|
55
|
+
console.warn(`[Plans] 🟡 Failed to connect to herd ${activeHerdId}`);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
68
58
|
channels.current.push(channel);
|
|
69
59
|
}
|
|
70
|
-
return
|
|
71
|
-
|
|
60
|
+
return () => {
|
|
61
|
+
channels.current.forEach((ch) => scoutSupabase.removeChannel(ch));
|
|
62
|
+
channels.current = [];
|
|
63
|
+
};
|
|
64
|
+
}, [scoutSupabase, activeHerdId, handlePlanBroadcast, clearLatestUpdate]);
|
|
72
65
|
return [latestPlanUpdate, clearLatestUpdate];
|
|
73
66
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
+
import { Database } from "../types/supabase";
|
|
1
3
|
export interface UseScoutRefreshOptions {
|
|
4
|
+
supabase?: SupabaseClient<Database>;
|
|
2
5
|
autoRefresh?: boolean;
|
|
3
6
|
onRefreshComplete?: () => void;
|
|
4
7
|
cacheFirst?: boolean;
|
|
@@ -14,6 +17,7 @@ export interface UseScoutRefreshOptions {
|
|
|
14
17
|
* @param options.cacheFirst - Whether to load from cache first, then refresh (default: true)
|
|
15
18
|
* @param options.cacheTtlMs - Cache time-to-live in milliseconds (default: 24 hours)
|
|
16
19
|
* @param options.onlineRefetchMinIntervalMs - Min ms since last refresh before refetch on "online" (default: 15s)
|
|
20
|
+
* @param options.supabase - Optional shared browser client; when omitted, the hook creates its own via `createBrowserClient`
|
|
17
21
|
*
|
|
18
22
|
* @returns Object containing:
|
|
19
23
|
* - handleRefresh: Function to manually trigger a refresh
|
|
@@ -16,6 +16,7 @@ import { createBrowserClient } from "@supabase/ssr";
|
|
|
16
16
|
* @param options.cacheFirst - Whether to load from cache first, then refresh (default: true)
|
|
17
17
|
* @param options.cacheTtlMs - Cache time-to-live in milliseconds (default: 24 hours)
|
|
18
18
|
* @param options.onlineRefetchMinIntervalMs - Min ms since last refresh before refetch on "online" (default: 15s)
|
|
19
|
+
* @param options.supabase - Optional shared browser client; when omitted, the hook creates its own via `createBrowserClient`
|
|
19
20
|
*
|
|
20
21
|
* @returns Object containing:
|
|
21
22
|
* - handleRefresh: Function to manually trigger a refresh
|
|
@@ -32,18 +33,19 @@ import { createBrowserClient } from "@supabase/ssr";
|
|
|
32
33
|
* ```
|
|
33
34
|
*/
|
|
34
35
|
export function useScoutRefresh(options = {}) {
|
|
35
|
-
const { autoRefresh = true, onRefreshComplete, cacheFirst = true, cacheTtlMs = 24 * 60 * 60 * 1000, // 24 hours default (1 day)
|
|
36
|
+
const { supabase: supabaseOption, autoRefresh = true, onRefreshComplete, cacheFirst = true, cacheTtlMs = 24 * 60 * 60 * 1000, // 24 hours default (1 day)
|
|
36
37
|
onlineRefetchMinIntervalMs = 15 * 1000, // 15 seconds
|
|
37
38
|
} = options;
|
|
38
39
|
const dispatch = useAppDispatch();
|
|
39
40
|
const store = useStore();
|
|
40
41
|
const refreshInProgressRef = useRef(false);
|
|
41
42
|
const lastQueryAtRef = useRef(0);
|
|
42
|
-
// Create Supabase client directly to avoid circular dependency
|
|
43
|
-
// Assumes Next.js environment variables (NEXT_PUBLIC_*)
|
|
44
43
|
const supabase = useMemo(() => {
|
|
44
|
+
if (supabaseOption) {
|
|
45
|
+
return supabaseOption;
|
|
46
|
+
}
|
|
45
47
|
return createBrowserClient(process.env.NEXT_PUBLIC_SUPABASE_URL || "", process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || "");
|
|
46
|
-
}, []);
|
|
48
|
+
}, [supabaseOption]);
|
|
47
49
|
// Refs to store timing measurements
|
|
48
50
|
const timingRefs = useRef({
|
|
49
51
|
startTime: 0,
|
|
@@ -32,7 +32,7 @@ export function ScoutRefreshProvider({ children, ...refreshOptions }) {
|
|
|
32
32
|
console.log("[ScoutRefreshProvider] Creating Supabase client");
|
|
33
33
|
return createBrowserClient(urlRef.current, anonKeyRef.current);
|
|
34
34
|
}, []); // Empty dependency array ensures this only runs once
|
|
35
|
-
useScoutRefresh(refreshOptions);
|
|
35
|
+
useScoutRefresh({ ...refreshOptions, supabase: supabaseClient });
|
|
36
36
|
// // Log connection status changes for debugging
|
|
37
37
|
// if (connectionStatus.lastError) {
|
|
38
38
|
// console.warn(
|