aboutsummaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/components')
-rw-r--r--src/components/ErrorBoundary.tsx8
-rw-r--r--src/components/Flex.tsx1
-rw-r--r--src/components/Link.tsx19
-rw-r--r--src/components/Settings.tsx43
-rw-r--r--src/components/Updater.tsx128
5 files changed, 189 insertions, 10 deletions
diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx
index 2b754b2..5946cb1 100644
--- a/src/components/ErrorBoundary.tsx
+++ b/src/components/ErrorBoundary.tsx
@@ -1,5 +1,5 @@
import Logger from "../utils/logger";
-import { React } from "../webpack/common";
+import { Card, React } from "../webpack/common";
interface Props {
fallback?: React.ComponentType<React.PropsWithChildren<{ error: any; }>>;
@@ -16,7 +16,7 @@ export default class ErrorBoundary extends React.Component<React.PropsWithChildr
static wrap<T = any>(Component: React.ComponentType<T>): (props: T) => React.ReactElement {
return (props) => (
<ErrorBoundary>
- <Component {...props} />
+ <Component {...props as any/* I hate react typings ??? */} />
</ErrorBoundary>
);
}
@@ -49,7 +49,7 @@ export default class ErrorBoundary extends React.Component<React.PropsWithChildr
/>;
return (
- <div style={{
+ <Card style={{
overflow: "hidden",
padding: "2em",
backgroundColor: color + "30",
@@ -65,7 +65,7 @@ export default class ErrorBoundary extends React.Component<React.PropsWithChildr
<pre>{this.state.error}
</pre>
</code>
- </div>
+ </Card>
);
}
}
diff --git a/src/components/Flex.tsx b/src/components/Flex.tsx
index c369767..881c7c2 100644
--- a/src/components/Flex.tsx
+++ b/src/components/Flex.tsx
@@ -4,6 +4,7 @@ import type { React } from '../webpack/common';
export function Flex(props: React.PropsWithChildren<{
flexDirection?: React.CSSProperties["flexDirection"];
style?: React.CSSProperties;
+ className?: string;
}>) {
props.style ??= {};
props.style.flexDirection ||= props.flexDirection;
diff --git a/src/components/Link.tsx b/src/components/Link.tsx
new file mode 100644
index 0000000..ef342d1
--- /dev/null
+++ b/src/components/Link.tsx
@@ -0,0 +1,19 @@
+import { React } from "../webpack/common";
+
+interface Props {
+ href: string;
+ disabled?: boolean;
+ style?: React.CSSProperties;
+}
+
+export function Link(props: React.PropsWithChildren<Props>) {
+ if (props.disabled) {
+ props.style ??= {};
+ props.style.pointerEvents = "none";
+ }
+ return (
+ <a href={props.href} target="_blank" style={props.style}>
+ {props.children}
+ </a>
+ );
+}
diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx
index 1950d7a..dd23b73 100644
--- a/src/components/Settings.tsx
+++ b/src/components/Settings.tsx
@@ -1,16 +1,19 @@
-import { humanFriendlyJoin, useAwaiter } from "../utils/misc";
+import { classes, humanFriendlyJoin, lazy, useAwaiter } from "../utils/misc";
import Plugins from 'plugins';
import { useSettings } from "../api/settings";
import IpcEvents from "../utils/IpcEvents";
-import { Button, Switch, Forms, React } from "../webpack/common";
+import { Button, Switch, Forms, React, Margins } from "../webpack/common";
import ErrorBoundary from "./ErrorBoundary";
import { startPlugin } from "../plugins";
import { stopPlugin } from '../plugins/index';
import { Flex } from './Flex';
+import { isOutdated } from "../utils/updater";
+import { Updater } from "./Updater";
export default ErrorBoundary.wrap(function Settings(props) {
const [settingsDir, , settingsDirPending] = useAwaiter(() => VencordNative.ipc.invoke<string>(IpcEvents.GET_SETTINGS_DIR), "Loading...");
+ const [outdated, setOutdated] = React.useState(isOutdated);
const settings = useSettings();
const depMap = React.useMemo(() => {
@@ -31,8 +34,24 @@ export default ErrorBoundary.wrap(function Settings(props) {
return (
<Forms.FormSection tag="h1" title="Vencord">
- <Forms.FormText>SettingsDir: {settingsDir}</Forms.FormText>
- <Flex style={{ marginTop: "8px", marginBottom: "8px" }}>
+ {outdated && (
+ <>
+ <Forms.FormTitle tag="h5">Updater</Forms.FormTitle>
+ <Updater setIsOutdated={setOutdated} />
+ </>
+ )}
+
+ <Forms.FormDivider />
+
+ <Forms.FormTitle tag="h5" className={outdated ? `${Margins.marginTop20} ${Margins.marginBottom8}` : ""}>
+ Settings
+ </Forms.FormTitle>
+
+ <Forms.FormText>
+ SettingsDir: {settingsDir}
+ </Forms.FormText>
+
+ <Flex className={classes(Margins.marginBottom20)}>
<Button
onClick={() => VencordNative.ipc.invoke(IpcEvents.OPEN_PATH, settingsDir)}
size={Button.Sizes.SMALL}
@@ -48,7 +67,7 @@ export default ErrorBoundary.wrap(function Settings(props) {
Open QuickCSS File
</Button>
</Flex>
- <Forms.FormTitle tag="h5">Settings</Forms.FormTitle>
+
<Switch
value={settings.useQuickCss}
onChange={v => settings.useQuickCss = v}
@@ -57,14 +76,26 @@ export default ErrorBoundary.wrap(function Settings(props) {
Use QuickCss
</Switch>
<Switch
+ value={settings.notifyAboutUpdates}
+ onChange={v => settings.notifyAboutUpdates = v}
+ note="Shows a Toast on StartUp"
+ >
+ Get notified about new Updates
+ </Switch>
+ <Switch
value={settings.unsafeRequire}
onChange={v => settings.unsafeRequire = v}
note="Enables VencordNative.require. Useful for testing, very bad for security. Leave this off unless you need it."
>
Enable Unsafe Require
</Switch>
+
<Forms.FormDivider />
- <Forms.FormTitle tag="h5">Plugins</Forms.FormTitle>
+
+ <Forms.FormTitle tag="h5" className={classes(Margins.marginTop20, Margins.marginBottom8)}>
+ Plugins
+ </Forms.FormTitle>
+
{sortedPlugins.map(p => {
const enabledDependants = depMap[p.name]?.filter(d => settings.plugins[d].enabled);
const dependency = enabledDependants?.length;
diff --git a/src/components/Updater.tsx b/src/components/Updater.tsx
new file mode 100644
index 0000000..e7b6d54
--- /dev/null
+++ b/src/components/Updater.tsx
@@ -0,0 +1,128 @@
+import gitHash from "git-hash";
+import { changes, checkForUpdates, getRepo, rebuild, update, UpdateLogger } from "../utils/updater";
+import { React, Forms, Button, Margins, Alerts, Card, Parser } from '../webpack/common';
+import { Flex } from "./Flex";
+import { useAwaiter } from '../utils/misc';
+import { Link } from "./Link";
+
+interface Props {
+ setIsOutdated(b: boolean): void;
+}
+
+function withDispatcher(dispatcher: React.Dispatch<React.SetStateAction<boolean>>, action: () => any) {
+ return async () => {
+ dispatcher(true);
+ try {
+ await action();
+ } catch (e: any) {
+ UpdateLogger.error("Failed to update", e);
+ if (!e) {
+ var err = "An unknown error occurred (error is undefined).\nPlease try again.";
+ } else if (e.code && e.cmd) {
+ const { code, path, cmd, stderr } = e;
+
+ if (code === "ENOENT")
+ var err = `Command \`${path}\` not found.\nPlease install it and try again`;
+ else {
+ var err = `An error occured while running \`${cmd}\`:\n`;
+ err += stderr || `Code \`${code}\`. See the console for more info`;
+ }
+
+ } else {
+ var err = "An unknown error occurred. See the console for more info.";
+ }
+ Alerts.show({
+ title: "Oops!",
+ body: err.split("\n").map(line => <div>{Parser.parse(line)}</div>)
+ });
+ }
+ finally {
+ dispatcher(false);
+ }
+ };
+};
+
+export function Updater(p: Props) {
+ const [repo, err, repoPending] = useAwaiter(getRepo, "Loading...");
+ const [isChecking, setIsChecking] = React.useState(false);
+ const [isUpdating, setIsUpdating] = React.useState(false);
+ const [updates, setUpdates] = React.useState(changes);
+
+ React.useEffect(() => {
+ if (err)
+ UpdateLogger.error("Failed to retrieve repo", err);
+ }, [err]);
+
+ return (
+ <>
+ <Forms.FormText>Repo: {repoPending ? repo : err ? "Failed to retrieve - check console" : (
+ <Link href={repo}>
+ {repo.split("/").slice(-2).join("/")}
+ </Link>
+ )} ({gitHash})</Forms.FormText>
+
+ <Forms.FormText className={Margins.marginBottom8}>
+ There are {updates.length} Updates
+ </Forms.FormText>
+
+ <Card style={{ padding: ".5em" }}>
+ {updates.map(({ hash, author, message }) => (
+ <div>
+ <Link href={`${repo}/commit/${hash}`} disabled={repoPending}>
+ <code>{hash}</code>
+ </Link>
+ <span style={{
+ marginLeft: "0.5em",
+ color: "var(--text-normal)"
+ }}>{message} - {author}</span>
+ </div>
+ ))}
+ </Card>
+
+ <Flex className={`${Margins.marginBottom8} ${Margins.marginTop8}`}>
+ <Button
+ size={Button.Sizes.SMALL}
+ disabled={isUpdating || isChecking}
+ onClick={withDispatcher(setIsUpdating, async () => {
+ if (await update()) {
+ p.setIsOutdated(false);
+ const needFullRestart = await rebuild();
+ await new Promise<void>(r => {
+ Alerts.show({
+ title: "Update Success!",
+ body: "Successfully updated. Restart now to apply the changes?",
+ confirmText: "Restart",
+ cancelText: "Not now!",
+ onConfirm() {
+ if (needFullRestart)
+ window.DiscordNative.app.relaunch();
+ else
+ location.reload();
+ r();
+ },
+ onCancel: r
+ });
+ });
+ }
+ })}
+ >
+ Update
+ </Button>
+ <Button
+ size={Button.Sizes.SMALL}
+ disabled={isUpdating || isChecking}
+ onClick={withDispatcher(setIsChecking, async () => {
+ const res = await checkForUpdates();
+ if (res) {
+ setUpdates(changes);
+ } else {
+ p.setIsOutdated(false);
+ }
+ })}
+ >
+ Refresh
+ </Button>
+ </Flex>
+ </>
+ );
+}