Initial Settings UI work
This commit is contained in:
parent
8b68eef9a7
commit
7e0532444d
16 changed files with 169 additions and 37 deletions
|
@ -46,6 +46,8 @@ pnpm start
|
|||
|
||||
# Or package
|
||||
pnpm package
|
||||
# Or only build the pacman target
|
||||
pnpm package --linux pacman
|
||||
# Or package to a directory only
|
||||
pnpm package:dir
|
||||
```
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"author": "Vendicated <vendicated@riseup.net>",
|
||||
"main": "dist/js/main.js",
|
||||
"scripts": {
|
||||
"build": "tsx scripts/build.mts",
|
||||
"build": "tsx scripts/build/build.mts",
|
||||
"package": "pnpm build && electron-builder",
|
||||
"package:dir": "pnpm build && electron-builder --dir",
|
||||
"start": "pnpm build && electron .",
|
||||
|
|
|
@ -36,6 +36,11 @@ await Promise.all([
|
|||
entryPoints: ["src/renderer/index.ts"],
|
||||
outfile: "dist/js/renderer.js",
|
||||
format: "iife",
|
||||
inject: ["./scripts/build/injectReact.mjs"],
|
||||
jsxFactory: "VencordCreateElement",
|
||||
jsxFragment: "VencordFragment",
|
||||
// Work around https://github.com/evanw/esbuild/issues/2460
|
||||
tsconfig: "./scripts/build/tsconfig.esbuild.json"
|
||||
})
|
||||
]);
|
||||
|
3
scripts/build/injectReact.mjs
Normal file
3
scripts/build/injectReact.mjs
Normal file
|
@ -0,0 +1,3 @@
|
|||
export const VencordFragment = /* #__PURE__*/ Symbol.for("react.fragment");
|
||||
export let VencordCreateElement =
|
||||
(...args) => (VencordCreateElement = Vencord.Webpack.Common.React.createElement)(...args);
|
7
scripts/build/tsconfig.esbuild.json
Normal file
7
scripts/build/tsconfig.esbuild.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
// Work around https://github.com/evanw/esbuild/issues/2460
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "react"
|
||||
}
|
||||
}
|
|
@ -11,5 +11,5 @@ function spawn(bin: string, args: string[]) {
|
|||
cpSpawn(join("node_modules", ".bin", bin + EXT), args, OPTS);
|
||||
}
|
||||
|
||||
spawn("tsx", ["scripts/build.mts", "--", "--watch"]);
|
||||
spawn("tsx", ["scripts/build/build.mts", "--", "--watch"]);
|
||||
spawn("electron", ["."]);
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
import { app, BrowserWindow } from 'electron';
|
||||
import { createMainWindow } from "./mainWindow";
|
||||
import { createSplashWindow } from "./splash";
|
||||
|
||||
import { join } from "path";
|
||||
|
||||
import { DATA_DIR, VENCORD_FILES_DIR } from "./constants";
|
||||
|
||||
import { once } from "../shared/utils/once";
|
||||
import { ensureVencordFiles } from "./utils/vencordLoader";
|
||||
|
||||
import { ICON_PATH } from "../shared/paths";
|
||||
import { once } from "../shared/utils/once";
|
||||
import { DATA_DIR, VENCORD_FILES_DIR } from "./constants";
|
||||
import "./ipc";
|
||||
import { createMainWindow } from "./mainWindow";
|
||||
import { Settings } from "./settings";
|
||||
import { createSplashWindow } from "./splash";
|
||||
import { ensureVencordFiles } from "./utils/vencordLoader";
|
||||
|
||||
// Make the Vencord files use our DATA_DIR
|
||||
process.env.VENCORD_USER_DATA_DIR = DATA_DIR;
|
||||
|
|
|
@ -1,23 +1,12 @@
|
|||
import { readFileSync, writeFileSync } from "fs";
|
||||
import { join } from "path";
|
||||
import type { Settings as TSettings } from "shared/settings";
|
||||
import { makeChangeListenerProxy } from "shared/utils/makeChangeListenerProxy";
|
||||
import { DATA_DIR } from "./constants";
|
||||
|
||||
const SETTINGS_FILE = join(DATA_DIR, "settings.json");
|
||||
|
||||
interface Settings {
|
||||
maximized?: boolean;
|
||||
minimized?: boolean;
|
||||
windowBounds?: {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
discordBranch?: "stable" | "canary" | "ptb";
|
||||
openLinksWithElectron?: boolean;
|
||||
}
|
||||
|
||||
export let PlainSettings = {} as Settings;
|
||||
export let PlainSettings = {} as TSettings;
|
||||
try {
|
||||
const content = readFileSync(SETTINGS_FILE, "utf8");
|
||||
try {
|
||||
|
@ -27,21 +16,14 @@ try {
|
|||
}
|
||||
} catch { }
|
||||
|
||||
function makeSettingsProxy(settings: Settings) {
|
||||
return new Proxy(settings, {
|
||||
set(target, prop, value) {
|
||||
Reflect.set(target, prop, value);
|
||||
|
||||
writeFileSync(SETTINGS_FILE, JSON.stringify(target, null, 4));
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
const makeSettingsProxy = (settings: TSettings) => makeChangeListenerProxy(
|
||||
settings,
|
||||
target => writeFileSync(SETTINGS_FILE, JSON.stringify(target, null, 4))
|
||||
);
|
||||
|
||||
export let Settings = makeSettingsProxy(PlainSettings);
|
||||
|
||||
export function setSettings(settings: Settings) {
|
||||
export function setSettings(settings: TSettings) {
|
||||
writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 4));
|
||||
PlainSettings = settings;
|
||||
Settings = makeSettingsProxy(settings);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import "./fixes";
|
||||
import "./ui/patchSettings";
|
||||
|
||||
console.log("read if cute :3");
|
||||
|
|
28
src/renderer/settings.ts
Normal file
28
src/renderer/settings.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import type { Settings as TSettings } from "shared/settings";
|
||||
import { makeChangeListenerProxy } from "shared/utils/makeChangeListenerProxy";
|
||||
import { Common } from "./vencord";
|
||||
|
||||
const signals = new Set<() => void>();
|
||||
|
||||
export const PlainSettings = VencordDesktop.settings.get() as TSettings;
|
||||
export const Settings = makeChangeListenerProxy(PlainSettings, s => {
|
||||
VencordDesktop.settings.set(s);
|
||||
signals.forEach(fn => fn());
|
||||
});
|
||||
|
||||
export function useSettings() {
|
||||
const [, update] = Common.React.useReducer(x => x + 1, 0);
|
||||
Common.React.useEffect(() => {
|
||||
signals.add(update);
|
||||
return () => signals.delete(update);
|
||||
}, []);
|
||||
|
||||
return Settings;
|
||||
}
|
||||
|
||||
export function getValueAndOnChange(key: keyof TSettings) {
|
||||
return {
|
||||
value: Settings[key] as any,
|
||||
onChange: (value: any) => Settings[key] = value
|
||||
};
|
||||
}
|
36
src/renderer/ui/Settings.tsx
Normal file
36
src/renderer/ui/Settings.tsx
Normal file
|
@ -0,0 +1,36 @@
|
|||
import { getValueAndOnChange, useSettings } from "renderer/settings";
|
||||
import { Common } from "../vencord";
|
||||
|
||||
export default function SettingsUi() {
|
||||
const Settings = useSettings();
|
||||
const { Forms: { FormSection, FormText, FormDivider, FormSwitch, FormTitle }, Text, Select } = Common;
|
||||
|
||||
return (
|
||||
<FormSection>
|
||||
<Text variant="heading-lg/semibold" style={{ color: "var(--header-primary)" }} tag="h2">
|
||||
Vencord Desktop Settings
|
||||
</Text>
|
||||
|
||||
<FormTitle>Discord Branch</FormTitle>
|
||||
<Select
|
||||
placeholder="Stable"
|
||||
options={[
|
||||
{ label: "Stable", value: "stable", default: true },
|
||||
{ label: "Canary", value: "canary" },
|
||||
{ label: "PTB", value: "ptb" },
|
||||
]}
|
||||
closeOnSelect={true}
|
||||
select={v => Settings.discordBranch = v}
|
||||
isSelected={v => v === Settings.discordBranch}
|
||||
serialize={s => s}
|
||||
/>
|
||||
|
||||
<FormSwitch
|
||||
{...getValueAndOnChange("openLinksWithElectron")}
|
||||
note={"This will open links in a new window instead of your WebBrowser"}
|
||||
>
|
||||
Open Links in app
|
||||
</FormSwitch>
|
||||
</FormSection>
|
||||
);
|
||||
}
|
15
src/renderer/ui/patchSettings.ts
Normal file
15
src/renderer/ui/patchSettings.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { monkeyPatch } from "../../shared/utils/monkeyPatch";
|
||||
import { Common, plugins } from "../vencord";
|
||||
import Settings from "./Settings";
|
||||
|
||||
monkeyPatch(plugins.Settings, "makeSettingsCategories", function (this: unknown, original, { ID }: { ID: Record<string, unknown>; }) {
|
||||
const cats = original.call(this, { ID });
|
||||
cats.splice(1, 0, {
|
||||
section: "VencordDesktop",
|
||||
label: "Desktop Settings",
|
||||
element: Settings,
|
||||
onClick: () => Common.SettingsRouter.open("VencordDesktop")
|
||||
});
|
||||
|
||||
return cats;
|
||||
});
|
12
src/renderer/vencord.ts
Normal file
12
src/renderer/vencord.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
// TODO: Taaaips when?
|
||||
|
||||
const { Webpack, Plugins } = Vencord;
|
||||
const { Common } = Webpack;
|
||||
const { plugins } = Plugins;
|
||||
|
||||
export {
|
||||
Webpack,
|
||||
Common,
|
||||
Plugins,
|
||||
plugins
|
||||
};
|
12
src/shared/settings.d.ts
vendored
Normal file
12
src/shared/settings.d.ts
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
export interface Settings {
|
||||
maximized?: boolean;
|
||||
minimized?: boolean;
|
||||
windowBounds?: {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
discordBranch?: "stable" | "canary" | "ptb";
|
||||
openLinksWithElectron?: boolean;
|
||||
}
|
20
src/shared/utils/makeChangeListenerProxy.ts
Normal file
20
src/shared/utils/makeChangeListenerProxy.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
export function makeChangeListenerProxy<T extends object>(object: T, onChange: (object: T) => void, _root = object): T {
|
||||
return new Proxy(object, {
|
||||
get(target, key) {
|
||||
const v = target[key];
|
||||
if (typeof v === "object" && !Array.isArray(v) && v !== null)
|
||||
return makeChangeListenerProxy(v, onChange, _root);
|
||||
|
||||
return v;
|
||||
},
|
||||
|
||||
set(target, key, value) {
|
||||
if (target[key] === value) return true;
|
||||
|
||||
Reflect.set(target, key, value);
|
||||
onChange(_root);
|
||||
|
||||
return true;
|
||||
},
|
||||
});
|
||||
}
|
13
src/shared/utils/monkeyPatch.ts
Normal file
13
src/shared/utils/monkeyPatch.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
type Func = (...args: any[]) => any;
|
||||
|
||||
export function monkeyPatch<O extends object>(object: O, key: keyof O, replacement: (original: Func, ...args: any[]) => any): void {
|
||||
const original = object[key] as Func;
|
||||
|
||||
const replacer = object[key] = function (this: unknown, ...args: any[]) {
|
||||
return replacement.call(this, original, ...args);
|
||||
} as any;
|
||||
|
||||
Object.defineProperties(replacer, Object.getOwnPropertyDescriptors(original));
|
||||
replacer.toString = () => original.toString();
|
||||
replacer.$$original = original;
|
||||
}
|
Loading…
Reference in a new issue