Skip to content

Commit 334c011

Browse files
committed
Add usePlatformState
1 parent 3dafbf2 commit 334c011

File tree

1 file changed

+88
-0
lines changed

1 file changed

+88
-0
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { type Channel, type Context, type DesktopAgent, type Listener } from "@finos/fdc3";
2+
import { type Dispatch, type SetStateAction, useCallback, useEffect, useRef, useState } from "react";
3+
4+
const CONTEXT_TYPE = "workspace.platformState";
5+
6+
export function usePlatformState<T>(topic: string, defaultState: T): [T, Dispatch<SetStateAction<T>>] {
7+
const [currentValue, setCurrentValue] = useState<T>(defaultState);
8+
const channelRef = useRef<Channel | null>(null);
9+
const listenerRef = useRef<Listener | null>(null);
10+
const mountedRef = useRef(true); // Flag to avoid state updates after unmount
11+
const fcd3: DesktopAgent = window.fdc3;
12+
13+
const setValue = useCallback(async (payload: T | ((prevValue: T) => T)) => {
14+
setCurrentValue((prev) => {
15+
const newValue = typeof payload === "function" ? (payload as (p: T) => T)(prev) : (payload as T);
16+
if (channelRef.current) {
17+
const context: Context = { type: CONTEXT_TYPE, payload: newValue };
18+
channelRef.current.broadcast(context).catch((err) => {
19+
console.error("Failed to broadcast platform state context:", err);
20+
});
21+
}
22+
return newValue;
23+
});
24+
}, []);
25+
26+
useEffect(() => {
27+
if (!window.fdc3) {
28+
console.warn("FDC3 is not available");
29+
return () => {};
30+
}
31+
console.info(`Creating channel ${topic}`);
32+
33+
const initChannel = async () => window.fdc3.getOrCreateChannel(topic);
34+
35+
const getChannelValue = async (channel: Channel) => {
36+
try {
37+
const current = await channel.getCurrentContext();
38+
if (current && current.payload !== undefined) {
39+
if (mountedRef.current) {
40+
setCurrentValue(current.payload as T);
41+
}
42+
}
43+
} catch (err) {
44+
console.error("Failed to get current context for platform state:", err);
45+
}
46+
};
47+
48+
const listenOnChannel = async (channel: Channel) => {
49+
if (!listenerRef.current) {
50+
listenerRef.current = await channel.addContextListener(CONTEXT_TYPE, (context) => {
51+
console.info(`Received context on channel ${topic}`, context);
52+
if (mountedRef.current) {
53+
setCurrentValue(context.payload as T);
54+
}
55+
});
56+
}
57+
};
58+
59+
(async () => {
60+
channelRef.current = await initChannel();
61+
await getChannelValue(channelRef.current);
62+
await listenOnChannel(channelRef.current);
63+
})().catch((error: Error | string) => {
64+
console.error(error.toString());
65+
});
66+
67+
return () => {
68+
mountedRef.current = false;
69+
if (listenerRef.current) {
70+
try {
71+
listenerRef.current.unsubscribe();
72+
} catch {
73+
// ignore unsubscribe errors
74+
}
75+
listenerRef.current = null;
76+
}
77+
};
78+
}, [topic, fcd3]);
79+
80+
useEffect(() => {
81+
mountedRef.current = true;
82+
return () => {
83+
mountedRef.current = false;
84+
};
85+
}, []);
86+
87+
return [currentValue, setValue];
88+
}

0 commit comments

Comments
 (0)