diff --git a/src/main/settings.ts b/src/main/settings.ts index 370d9ea..3061509 100644 --- a/src/main/settings.ts +++ b/src/main/settings.ts @@ -7,7 +7,7 @@ import { readFileSync, writeFileSync } from "fs"; import { join } from "path"; import type { Settings as TSettings } from "shared/settings"; -import { SettingsStore } from "shared/utils/makeChangeListenerProxy"; +import { SettingsStore } from "shared/utils/SettingsStore"; import { DATA_DIR, VENCORD_SETTINGS_FILE } from "./constants"; diff --git a/src/renderer/settings.ts b/src/renderer/settings.ts index 9e68525..cae36d4 100644 --- a/src/renderer/settings.ts +++ b/src/renderer/settings.ts @@ -5,7 +5,7 @@ */ import type { Settings as TSettings } from "shared/settings"; -import { SettingsStore } from "shared/utils/makeChangeListenerProxy"; +import { SettingsStore } from "shared/utils/SettingsStore"; import { Common } from "./vencord"; diff --git a/src/shared/utils/makeChangeListenerProxy.ts b/src/shared/utils/SettingsStore.ts similarity index 61% rename from src/shared/utils/makeChangeListenerProxy.ts rename to src/shared/utils/SettingsStore.ts index 53739f2..1daae2d 100644 --- a/src/shared/utils/makeChangeListenerProxy.ts +++ b/src/shared/utils/SettingsStore.ts @@ -4,12 +4,25 @@ * Copyright (c) 2023 Vendicated and Vencord contributors */ +/** + * The SettingsStore allows you to easily create a mutable store that + * has support for global and path-based change listeners. + */ export class SettingsStore { - public declare store: T; - private globalListeners = new Set<(newData: T) => void>(); private pathListeners = new Map void>>(); + private globalListeners = new Set<(newData: T) => void>(); - public constructor(public plain: T) { + /** + * The store object. Making changes to this object will trigger the applicable change listeners + */ + public declare store: T; + /** + * The plain data. Changes to this object will not trigger any change listeners + */ + public declare plain: T; + + public constructor(plain: T) { + this.plain = plain; this.store = this.makeProxy(plain); } @@ -39,6 +52,13 @@ export class SettingsStore { }); } + /** + * Set the data of the store. + * This will update this.store and this.plain (and old references to them will be stale! Avoid storing them in variables) + * + * Additionally, all global listeners will be called with the new data + * @param value + */ public setData(value: T) { this.plain = value; this.store = this.makeProxy(value); @@ -46,20 +66,45 @@ export class SettingsStore { this.globalListeners.forEach(cb => cb(value)); } + /** + * Add a global change listener, that will fire whenever any setting is changed + */ public addGlobalChangeListener(cb: (store: T) => void) { this.globalListeners.add(cb); } + /** + * Add a scoped change listener that will fire whenever a setting matching the specified path is changed. + * + * For example if path is `"foo.bar"`, the listener will fire on + * ```js + * Setting.store.foo.bar = "hi" + * ``` + * but not on + * ```js + * Setting.store.foo.baz = "hi" + * ``` + * @param path + * @param cb + */ public addChangeListener(path: string, cb: (data: any) => void) { const listeners = this.pathListeners.get(path) ?? new Set(); listeners.add(cb); this.pathListeners.set(path, listeners); } + /** + * Remove a global listener + * @see {@link addGlobalChangeListener} + */ public removeGlobalChangeListener(cb: (store: T) => void) { this.globalListeners.delete(cb); } + /** + * Remove a scoped listener + * @see {@link addChangeListener} + */ public removeChangeListener(path: string, cb: (data: any) => void) { const listeners = this.pathListeners.get(path); if (!listeners) return; diff --git a/src/shared/utils/debounce.ts b/src/shared/utils/debounce.ts index 90be8b3..9773258 100644 --- a/src/shared/utils/debounce.ts +++ b/src/shared/utils/debounce.ts @@ -4,6 +4,12 @@ * Copyright (c) 2023 Vendicated and Vencord contributors */ +/** + * Returns a new function that will only be called after the given delay. + * Subsequent calls will cancel the previous timeout and start a new one from 0 + * + * Useful for grouping multiple calls into one + */ export function debounce(func: T, delay = 300): T { let timeout: NodeJS.Timeout; return function (...args: any[]) { diff --git a/src/shared/utils/once.ts b/src/shared/utils/once.ts index 594718d..ef744f7 100644 --- a/src/shared/utils/once.ts +++ b/src/shared/utils/once.ts @@ -4,6 +4,11 @@ * Copyright (c) 2023 Vendicated and Vencord contributors */ +/** + * Wraps the given function so that it can only be called once + * @param fn Function to wrap + * @returns New function that can only be called once + */ export function once(fn: T): T { let called = false; return function (this: any, ...args: any[]) {