This commit is contained in:
superdinmc
2025-11-07 09:19:08 +07:00 Unverified
parent f067a70124
commit 13977d2cd1
4 changed files with 122 additions and 3 deletions
+66 -2
View File
@@ -1,17 +1,31 @@
"use client";
import { createContext, useState } from "react";
import { createContext, use, useEffect, useRef, useState } from "react";
type WebsiteComms = Partial<{ manual: boolean }>;
type WebsiteSignals = { beforeSubmit: undefined } & WebsiteComms;
type WebsiteCommsMutator = {
get<K extends keyof WebsiteComms>(key: K): WebsiteComms[K];
set<K extends keyof WebsiteComms>(key: K, value: WebsiteComms[K]): void;
on<K extends keyof WebsiteSignals>(
key: K,
listener: (value: WebsiteSignals[K]) => void,
): () => void;
off<K extends keyof WebsiteSignals>(
key: K,
listener: (value: WebsiteSignals[K]) => void,
): void;
emit<K extends keyof WebsiteSignals>(key: K, value: WebsiteSignals[K]): void;
};
export const CommsContext = createContext<WebsiteCommsMutator>({
get: () => undefined,
set: () => undefined,
on: () => () => undefined,
off: () => undefined,
emit: () => undefined,
});
export default function CommsProvider({
@@ -20,14 +34,64 @@ export default function CommsProvider({
children: React.ReactNode;
}) {
const [store, setStore] = useState<WebsiteComms>({});
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 })),
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>
);
}
export const comms = {
var(name: keyof WebsiteComms) {
const comms = use(CommsContext);
const [state, setState] = useState(comms.get(name));
useEffect(() => {
function listener(value: WebsiteComms[typeof name]) {
setState(value);
}
return comms.on(name, listener);
}, [comms, name]);
return [state, comms.set.bind(comms, name)] as const;
},
event<K extends keyof WebsiteSignals>(
key: K,
listener: (ev: WebsiteSignals[K]) => void,
) {
const comms = use(CommsContext);
useEffect(() => {
return comms.on(key, listener);
}, [comms, key, listener]);
},
};