diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1750b51ff..3089267aa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -122,7 +122,7 @@ importers: version: 17.0.29 '@typescript-eslint/eslint-plugin': specifier: ^6.8.0 - version: 6.8.0(@typescript-eslint/parser@6.8.0)(eslint@8.51.0)(typescript@5.2.2) + version: 6.8.0(@typescript-eslint/parser@6.8.0(eslint@8.51.0)(typescript@5.2.2))(eslint@8.51.0)(typescript@5.2.2) '@typescript-eslint/parser': specifier: ^6.8.0 version: 6.8.0(eslint@8.51.0)(typescript@5.2.2) @@ -137,7 +137,7 @@ importers: version: 8.51.0 eslint-config-dmitmel: specifier: github:dmitmel/eslint-config-dmitmel - version: eslint-config-dmitmel@https://codeload.github.com/dmitmel/eslint-config-dmitmel/tar.gz/d97129ec35235415c6ae6a42299f55cdbb5d75fd(eslint@8.51.0) + version: https://codeload.github.com/dmitmel/eslint-config-dmitmel/tar.gz/d97129ec35235415c6ae6a42299f55cdbb5d75fd(eslint@8.51.0) eslint-plugin-node: specifier: ^11.1.0 version: 11.1.0(eslint@8.51.0) @@ -1518,7 +1518,6 @@ packages: eslint-config-dmitmel@https://codeload.github.com/dmitmel/eslint-config-dmitmel/tar.gz/d97129ec35235415c6ae6a42299f55cdbb5d75fd: resolution: {tarball: https://codeload.github.com/dmitmel/eslint-config-dmitmel/tar.gz/d97129ec35235415c6ae6a42299f55cdbb5d75fd} - name: eslint-config-dmitmel version: 8.3.0 engines: {node: '>=8.10.0'} peerDependencies: @@ -3768,6 +3767,7 @@ snapshots: '@samverschueren/stream-to-observable@0.3.1(rxjs@6.6.7)': dependencies: any-observable: 0.3.0(rxjs@6.6.7) + optionalDependencies: rxjs: 6.6.7 transitivePeerDependencies: - zenObservable @@ -3869,7 +3869,7 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.2 - '@typescript-eslint/eslint-plugin@6.8.0(@typescript-eslint/parser@6.8.0)(eslint@8.51.0)(typescript@5.2.2)': + '@typescript-eslint/eslint-plugin@6.8.0(@typescript-eslint/parser@6.8.0(eslint@8.51.0)(typescript@5.2.2))(eslint@8.51.0)(typescript@5.2.2)': dependencies: '@eslint-community/regexpp': 4.9.1 '@typescript-eslint/parser': 6.8.0(eslint@8.51.0)(typescript@5.2.2) @@ -3884,6 +3884,7 @@ snapshots: natural-compare: 1.4.0 semver: 7.5.4 ts-api-utils: 1.0.3(typescript@5.2.2) + optionalDependencies: typescript: 5.2.2 transitivePeerDependencies: - supports-color @@ -3896,6 +3897,7 @@ snapshots: '@typescript-eslint/visitor-keys': 6.8.0 debug: 4.3.4 eslint: 8.51.0 + optionalDependencies: typescript: 5.2.2 transitivePeerDependencies: - supports-color @@ -3912,6 +3914,7 @@ snapshots: debug: 4.3.4 eslint: 8.51.0 ts-api-utils: 1.0.3(typescript@5.2.2) + optionalDependencies: typescript: 5.2.2 transitivePeerDependencies: - supports-color @@ -3927,6 +3930,7 @@ snapshots: is-glob: 4.0.3 semver: 7.5.4 ts-api-utils: 1.0.3(typescript@5.2.2) + optionalDependencies: typescript: 5.2.2 transitivePeerDependencies: - supports-color @@ -4017,7 +4021,7 @@ snapshots: ansi-styles@6.2.1: {} any-observable@0.3.0(rxjs@6.6.7): - dependencies: + optionalDependencies: rxjs: 6.6.7 anymatch@3.1.3: @@ -4332,6 +4336,7 @@ snapshots: js-yaml: 4.1.0 parse-json: 5.2.0 path-type: 4.0.0 + optionalDependencies: typescript: 5.2.2 crelt@1.0.6: {} @@ -4677,7 +4682,6 @@ snapshots: escape-string-regexp@5.0.0: {} eslint-config-dmitmel@https://codeload.github.com/dmitmel/eslint-config-dmitmel/tar.gz/d97129ec35235415c6ae6a42299f55cdbb5d75fd(eslint@8.51.0): - id: eslint-config-dmitmel@https://codeload.github.com/dmitmel/eslint-config-dmitmel/tar.gz/d97129ec35235415c6ae6a42299f55cdbb5d75fd dependencies: eslint: 8.51.0 diff --git a/src/renderer/util.ts b/src/renderer/util.ts index e3bd99c2b..3d8833758 100644 --- a/src/renderer/util.ts +++ b/src/renderer/util.ts @@ -1,4 +1,4 @@ -import { React, channels, fluxDispatcher, guilds } from "@common"; +import { React, channels, fluxDispatcher, guilds, lodash } from "@common"; import type { Fiber } from "react-reconciler"; import type { Jsonifiable } from "type-fest"; import type { ObjectExports } from "../types"; @@ -14,7 +14,7 @@ export const loadStyleSheet = (path: string): HTMLLinkElement => { const el = document.createElement("link"); el.rel = "stylesheet"; el.href = `${path}?t=${Date.now()}`; - document.body.appendChild(el); + document.head.appendChild(el); return el; }; @@ -84,7 +84,6 @@ export function forceUpdateElement(selector: string, all = false): void { const elements = ( all ? [...document.querySelectorAll(selector)] : [document.querySelector(selector)] ).filter(Boolean) as Element[]; - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- May not actually have forceUpdate elements.forEach((element) => getOwnerInstance(element)?.forceUpdate()); } @@ -172,7 +171,7 @@ export async function goToOrJoinServer(invite: string): Promise { } export async function openExternal(url: string): Promise { - const mod = getBySource<(url: string) => Promise>(/href=\w,\w\.target="_blank"/); + const mod = getBySource<(url: string) => Promise>('.target="_blank"'); if (!mod) { throw new Error("Could not find openExternal"); } @@ -184,29 +183,44 @@ type ValType = | React.ChangeEvent | (Record & { value?: T; checked?: T }); +type NestedType = P extends + | `${infer K}.${infer NestedKey}` + | `${infer K}-${infer NestedKey}` + | `${infer K}/${infer NestedKey}` + ? K extends keyof T + ? NestedType, NestedKey> + : undefined + : P extends keyof T + ? NonNullable + : undefined; + export function useSetting< T extends Record, D extends keyof T, K extends Extract, - F extends T[K] | undefined, + F extends NestedType | T[K] | undefined, + P extends `${K}.${string}` | `${K}-${string}` | `${K}/${string}` | K, + V extends P extends `${K}.${string}` | `${K}-${string}` | `${K}/${string}` + ? NonNullable> + : P extends D + ? NonNullable + : F extends null | undefined + ? T[P] | undefined + : NonNullable | F, >( settings: SettingsManager, - key: K, + key: P, fallback?: F, ): { - value: K extends D - ? NonNullable - : F extends null | undefined - ? T[K] | undefined - : NonNullable | F; - onChange: (newValue: ValType) => void; + value: V; + onChange: (newValue: ValType> | ValType) => void; } { - const initial = settings.get(key, fallback); - const [value, setValue] = React.useState(initial); + const initial = settings.get(key as K) ?? lodash.get(settings.all(), key) ?? fallback; + const [value, setValue] = React.useState(initial as V); return { value, - onChange: (newValue: ValType) => { + onChange: (newValue: ValType> | ValType) => { const isObj = newValue && typeof newValue === "object"; const value = isObj && "value" in newValue ? newValue.value : newValue; const checked = isObj && "checked" in newValue ? newValue.checked : undefined; @@ -218,32 +232,43 @@ export function useSetting< const targetChecked = target && "checked" in target ? target.checked : undefined; const finalValue = (checked ?? targetChecked ?? targetValue ?? value ?? newValue) as T[K]; - // @ts-expect-error dumb - setValue(finalValue); - settings.set(key, finalValue); + // Update local state + setValue(finalValue as V); + + // Update settings + if (settings.get(key as K)) { + settings.set(key as K, finalValue); + } else { + const [rootKey] = key.split(/[./-]/); + // without cloning this changes property in default settings + const setting = lodash.set(lodash.cloneDeep(settings.all()), key, finalValue)[rootKey as K]; + settings.set(rootKey as K, setting); + } }, }; } + export function useSettingArray< T extends Record, D extends keyof T, K extends Extract, - F extends T[K] | undefined, + F extends NestedType | T[K] | undefined, + P extends `${K}.${string}` | `${K}-${string}` | `${K}/${string}` | K, + V extends P extends `${K}.${string}` | `${K}-${string}` | `${K}/${string}` + ? NonNullable> + : P extends D + ? NonNullable + : F extends null | undefined + ? T[P] | undefined + : NonNullable | F, >( settings: SettingsManager, - key: K, + key: P, fallback?: F, -): [ - K extends D - ? NonNullable - : F extends null | undefined - ? T[K] | undefined - : NonNullable | F, - (newValue: ValType) => void, -] { +): [V, (newValue: ValType> | ValType) => void] { const { value, onChange } = useSetting(settings, key, fallback); - return [value, onChange]; + return [value as V, onChange]; } // Credit to @Vendicated - https://github.com/Vendicated/virtual-merge @@ -301,7 +326,7 @@ export function virtualMerge(...objects: O): ExtractObje return new Proxy(fallback, handler) as ExtractObjectType; } -export type Tree = Record | null; +export type Tree = Record; type TreeFilter = string | ((tree: Tree) => boolean); /** @@ -321,8 +346,7 @@ export function findInTree( if (maxRecursion <= 0) return undefined; if (typeof searchFilter === "string") { - if (Object.prototype.hasOwnProperty.call(tree, searchFilter)) - return tree?.[searchFilter] as Tree; + if (Object.prototype.hasOwnProperty.call(tree, searchFilter)) return tree[searchFilter] as Tree; } else if (searchFilter(tree)) { return tree; }