add some jsdoc

This commit is contained in:
Vendicated 2023-04-10 01:16:44 +02:00
parent c2eaa9d35a
commit edfeca15ce
No known key found for this signature in database
GPG key ID: A1DC0CFB5615D905
5 changed files with 61 additions and 5 deletions

View file

@ -7,7 +7,7 @@
import { readFileSync, writeFileSync } from "fs"; import { readFileSync, writeFileSync } from "fs";
import { join } from "path"; import { join } from "path";
import type { Settings as TSettings } from "shared/settings"; 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"; import { DATA_DIR, VENCORD_SETTINGS_FILE } from "./constants";

View file

@ -5,7 +5,7 @@
*/ */
import type { Settings as TSettings } from "shared/settings"; import type { Settings as TSettings } from "shared/settings";
import { SettingsStore } from "shared/utils/makeChangeListenerProxy"; import { SettingsStore } from "shared/utils/SettingsStore";
import { Common } from "./vencord"; import { Common } from "./vencord";

View file

@ -4,12 +4,25 @@
* Copyright (c) 2023 Vendicated and Vencord contributors * 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<T extends object> { export class SettingsStore<T extends object> {
public declare store: T;
private globalListeners = new Set<(newData: T) => void>();
private pathListeners = new Map<string, Set<(newData: unknown) => void>>(); private pathListeners = new Map<string, Set<(newData: unknown) => 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); this.store = this.makeProxy(plain);
} }
@ -39,6 +52,13 @@ export class SettingsStore<T extends object> {
}); });
} }
/**
* 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) { public setData(value: T) {
this.plain = value; this.plain = value;
this.store = this.makeProxy(value); this.store = this.makeProxy(value);
@ -46,20 +66,45 @@ export class SettingsStore<T extends object> {
this.globalListeners.forEach(cb => cb(value)); 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) { public addGlobalChangeListener(cb: (store: T) => void) {
this.globalListeners.add(cb); 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) { public addChangeListener(path: string, cb: (data: any) => void) {
const listeners = this.pathListeners.get(path) ?? new Set(); const listeners = this.pathListeners.get(path) ?? new Set();
listeners.add(cb); listeners.add(cb);
this.pathListeners.set(path, listeners); this.pathListeners.set(path, listeners);
} }
/**
* Remove a global listener
* @see {@link addGlobalChangeListener}
*/
public removeGlobalChangeListener(cb: (store: T) => void) { public removeGlobalChangeListener(cb: (store: T) => void) {
this.globalListeners.delete(cb); this.globalListeners.delete(cb);
} }
/**
* Remove a scoped listener
* @see {@link addChangeListener}
*/
public removeChangeListener(path: string, cb: (data: any) => void) { public removeChangeListener(path: string, cb: (data: any) => void) {
const listeners = this.pathListeners.get(path); const listeners = this.pathListeners.get(path);
if (!listeners) return; if (!listeners) return;

View file

@ -4,6 +4,12 @@
* Copyright (c) 2023 Vendicated and Vencord contributors * 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<T extends Function>(func: T, delay = 300): T { export function debounce<T extends Function>(func: T, delay = 300): T {
let timeout: NodeJS.Timeout; let timeout: NodeJS.Timeout;
return function (...args: any[]) { return function (...args: any[]) {

View file

@ -4,6 +4,11 @@
* Copyright (c) 2023 Vendicated and Vencord contributors * 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<T extends Function>(fn: T): T { export function once<T extends Function>(fn: T): T {
let called = false; let called = false;
return function (this: any, ...args: any[]) { return function (this: any, ...args: any[]) {