fix comms

This commit is contained in:
superdinmc
2025-11-11 09:36:55 +07:00 Unverified
parent ef25566b65
commit 7b4e6b46ef
5 changed files with 68 additions and 41 deletions
+61 -38
View File
@@ -2,7 +2,7 @@
import { createContext, use, useEffect, useRef, useState } from "react";
type WebsiteComms = Partial<{ manual: boolean, debug: boolean }>;
type WebsiteComms = Partial<{ manual: boolean; debug: boolean }>;
type WebsiteSignals = { beforeSubmit: undefined } & WebsiteComms;
@@ -18,14 +18,19 @@ type WebsiteCommsMutator = {
listener: (value: WebsiteSignals[K]) => void,
): void;
emit<K extends keyof WebsiteSignals>(key: K, value: WebsiteSignals[K]): void;
active: boolean;
};
export const CommsContext = createContext<WebsiteCommsMutator>({
get: () => undefined,
get: () => {
console.error("Inter-component communication used outside of provider.");
return undefined;
},
set: () => undefined,
on: () => () => undefined,
off: () => undefined,
emit: () => undefined,
active: false,
});
export default function CommsProvider({
@@ -33,63 +38,81 @@ export default function CommsProvider({
}: {
children: React.ReactNode;
}) {
const [store, setStore] = useState<WebsiteComms>({});
const storeRef = useRef<WebsiteComms>({});
const [, forceUpdate] = useState({});
const listeners = useRef<{
[K in keyof WebsiteSignals]?: Array<(value: WebsiteSignals[K]) => void>;
}>({});
return (
<CommsContext
value={{
get: (k) => store[k],
set: (k, v) => {
setStore((x) => ({ ...x, [k]: v }));
(listeners.current[k] || []).map((l) => l(v));
},
on: (k, listener) => {
// @ts-expect-error
listeners.current[k] = [...(listeners.current[k] || []), listener];
return () => {
// @ts-expect-error
listeners.current[k] = listeners.current[k]?.filter(
(l) => l !== listener,
);
};
},
off: (k, listener) => {
// @ts-expect-error
listeners.current[k] = (listeners.current[k] || []).filter(
(l) => l !== listener,
);
},
emit: (k, value) => {
(listeners.current[k] || []).map((l) => l(value));
},
}}
>
{children}
</CommsContext>
);
const contextValue = useRef<WebsiteCommsMutator>({
get: (k) => storeRef.current[k],
set: (k, v) => {
storeRef.current = { ...storeRef.current, [k]: v };
(listeners.current[k] || []).map((l) => l(v));
forceUpdate({});
},
on: (k, listener) => {
// @ts-expect-error
listeners.current[k] = [...(listeners.current[k] || []), listener];
return () => {
// @ts-expect-error
listeners.current[k] = listeners.current[k]?.filter(
(l) => l !== listener,
);
};
},
off: (k, listener) => {
// @ts-expect-error
listeners.current[k] = (listeners.current[k] || []).filter(
(l) => l !== listener,
);
},
emit: (k, value) => {
(listeners.current[k] || []).map((l) => l(value));
},
active: true,
});
return <CommsContext value={contextValue.current}>{children}</CommsContext>;
}
export const comms = {
var(name: keyof WebsiteComms) {
const comms = use(CommsContext);
const [state, setState] = useState(comms.get(name));
if(!comms.active) {
console.warn("Inter-component communication used outside of provider. Using local state instead.");
}
const [state, setState] = useState(comms.active?comms.get(name): undefined);
useEffect(() => {
function listener(value: WebsiteComms[typeof name]) {
setState(value);
}
return comms.on(name, listener);
}, [comms, name]);
}, [name]);
return [state, comms.set.bind(comms, name)] as const;
function mutator(
value:
| WebsiteComms[typeof name]
| ((prev: WebsiteComms[typeof name]) => WebsiteComms[typeof name]),
) {
const v = typeof value === "function" ? value(comms.get(name)) : value;
if(comms.active) comms.set(name, v);
else setState(v);
}
return [state, mutator] as const;
},
event<K extends keyof WebsiteSignals>(
key: K,
listener: (ev: WebsiteSignals[K]) => void,
) {
const comms = use(CommsContext);
if (!comms.active) {
console.warn(
"Inter-component communication used outside of provider. No events will be emitted.",
);
}
useEffect(() => {
return comms.on(key, listener);
}, [comms, key, listener]);