Skip to content

Commit 01ba3d7

Browse files
committed
Add usePlatformState
1 parent 3dafbf2 commit 01ba3d7

File tree

3 files changed

+117
-6
lines changed

3 files changed

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

34
export function View1() {
45
const raiseIntent = useRaiseIntent();
6+
const [, setMyState] = usePlatformState<string>("demo", "");
57

68
const handleViewContact = () => {
7-
raiseIntent('ViewContact', { type: 'fdc3.contact' })
8-
}
9+
raiseIntent("ViewContact", { type: "fdc3.contact" });
10+
};
911

1012
const handleViewQuote = () => {
11-
raiseIntent('ViewQuote', { type: 'custom.instrument' })
12-
}
13+
raiseIntent("ViewQuote", { type: "custom.instrument" });
14+
};
1315

16+
const handleSetGlobalState = () => {
17+
setMyState("Hello World!");
18+
};
1419

1520
return (
1621
<div className="flex-col">
@@ -20,6 +25,9 @@ export function View1() {
2025
<button type="button" onClick={handleViewQuote}>
2126
View Quote
2227
</button>
28+
<button type="button" onClick={handleSetGlobalState}>
29+
Set global state
30+
</button>
2331
</div>
2432
);
2533
}
Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
import { usePlatformState } from "../hooks/usePlatformState";
2+
13
export function View2() {
2-
return <div>View 2</div>;
4+
const [myState] = usePlatformState<string>("demo");
5+
6+
return (
7+
<>
8+
<div>View 2</div>
9+
<h1>{myState}</h1>
10+
</>
11+
);
312
}

0 commit comments

Comments
 (0)