feat: non-exact settings subscriptions for live recompile

This commit is contained in:
Lewis Crichton 2023-09-09 10:17:21 +01:00
parent 64848b2fbf
commit 51059c29e7
No known key found for this signature in database
3 changed files with 18 additions and 9 deletions

View file

@ -117,7 +117,7 @@ const saveSettingsOnFrequentAction = debounce(async () => {
} }
}, 60_000); }, 60_000);
type SubscriptionCallback = ((newValue: any, path: string) => void) & { _paths?: Array<string>; }; type SubscriptionCallback = ((newValue: any, path: string) => void) & { _paths?: Array<string>; _exact?: boolean; };
const subscriptions = new Set<SubscriptionCallback>(); const subscriptions = new Set<SubscriptionCallback>();
const proxyCache = {} as Record<string, any>; const proxyCache = {} as Record<string, any>;
@ -174,7 +174,12 @@ function makeProxy(settings: any, root = settings, path = ""): Settings {
const setPath = `${path}${path && "."}${p}`; const setPath = `${path}${path && "."}${p}`;
delete proxyCache[setPath]; delete proxyCache[setPath];
for (const subscription of subscriptions) { for (const subscription of subscriptions) {
if (!subscription._paths || subscription._paths.includes(setPath)) { if (
!subscription._paths ||
(subscription._exact
? subscription._paths.includes(setPath)
: subscription._paths.some(p => setPath.startsWith(p)))
) {
subscription(v, setPath); subscription(v, setPath);
} }
} }
@ -212,11 +217,14 @@ export const Settings = makeProxy(settings);
* @returns Settings * @returns Settings
*/ */
// TODO: Representing paths as essentially "string[].join('.')" wont allow dots in paths, change to "paths?: string[][]" later // TODO: Representing paths as essentially "string[].join('.')" wont allow dots in paths, change to "paths?: string[][]" later
export function useSettings(paths?: UseSettings<Settings>[]) { export function useSettings(paths?: UseSettings<Settings>[], exact = true) {
const [, forceUpdate] = React.useReducer(() => ({}), {}); const [, forceUpdate] = React.useReducer(() => ({}), {});
const onUpdate: SubscriptionCallback = paths const onUpdate: SubscriptionCallback = paths
? (value, path) => paths.includes(path as UseSettings<Settings>) && forceUpdate() ? (value, path) =>
(exact
? paths.includes(path as UseSettings<Settings>)
: paths.some(p => path.startsWith(p))) && forceUpdate()
: forceUpdate; : forceUpdate;
React.useEffect(() => { React.useEffect(() => {
@ -242,10 +250,11 @@ type ResolvePropDeep<T, P> = P extends "" ? T :
* @example addSettingsListener("", (newValue, path) => console.log(`${path} is now ${newValue}`)) * @example addSettingsListener("", (newValue, path) => console.log(`${path} is now ${newValue}`))
* addSettingsListener("plugins.Unindent.enabled", v => console.log("Unindent is now", v ? "enabled" : "disabled")) * addSettingsListener("plugins.Unindent.enabled", v => console.log("Unindent is now", v ? "enabled" : "disabled"))
*/ */
export function addSettingsListener<Path extends keyof Settings>(path: Path, onUpdate: (newValue: Settings[Path], path: Path) => void): void; export function addSettingsListener<Path extends keyof Settings>(path: Path, onUpdate: (newValue: Settings[Path], path: Path) => void, exact?: boolean): void;
export function addSettingsListener<Path extends string>(path: Path, onUpdate: (newValue: Path extends "" ? any : ResolvePropDeep<Settings, Path>, path: Path extends "" ? string : Path) => void): void; export function addSettingsListener<Path extends string>(path: Path, onUpdate: (newValue: Path extends "" ? any : ResolvePropDeep<Settings, Path>, path: Path extends "" ? string : Path) => void, exact?: boolean): void;
export function addSettingsListener(path: string, onUpdate: (newValue: any, path: string) => void) { export function addSettingsListener(path: string, onUpdate: (newValue: any, path: string) => void, exact = true) {
((onUpdate as SubscriptionCallback)._paths ??= []).push(path); ((onUpdate as SubscriptionCallback)._paths ??= []).push(path);
(onUpdate as SubscriptionCallback)._exact = exact;
subscriptions.add(onUpdate); subscriptions.add(onUpdate);
} }

View file

@ -141,7 +141,7 @@ interface UserCSSSettingsModalProps {
function UserCSSSettingsModal({ modalProps, theme }: UserCSSSettingsModalProps) { function UserCSSSettingsModal({ modalProps, theme }: UserCSSSettingsModalProps) {
// @ts-expect-error UseSettings<> can't determine this is a valid key // @ts-expect-error UseSettings<> can't determine this is a valid key
const themeSettings = useSettings(["userCssVars"]).userCssVars[theme.id]; const themeSettings = useSettings(["userCssVars"], false).userCssVars[theme.id];
const controls: ReactNode[] = []; const controls: ReactNode[] = [];

View file

@ -99,7 +99,7 @@ document.addEventListener("DOMContentLoaded", () => {
addSettingsListener("themeLinks", initThemes); addSettingsListener("themeLinks", initThemes);
addSettingsListener("enabledThemes", initThemes); addSettingsListener("enabledThemes", initThemes);
addSettingsListener("userCssVars", initThemes); addSettingsListener("userCssVars", initThemes, false);
if (!IS_WEB) if (!IS_WEB)
VencordNative.quickCss.addThemeChangeListener(initThemes); VencordNative.quickCss.addThemeChangeListener(initThemes);