Updates
This commit is contained in:
parent
804bdb32c9
commit
1c2d328f5a
30 changed files with 754 additions and 296 deletions
|
|
@ -24,10 +24,7 @@ import { ErrorCard } from "@components/ErrorCard";
|
|||
import { Flex } from "@components/Flex";
|
||||
import { CogWheel, DeleteIcon, FolderIcon, PaintbrushIcon, PencilIcon, PluginIcon, PlusIcon, RestartIcon } from "@components/Icons";
|
||||
import { Link } from "@components/Link";
|
||||
import { openPluginModal } from "@components/PluginSettings/PluginModal";
|
||||
import { AddonCard } from "@components/VencordSettings/AddonCard";
|
||||
import { QuickAction, QuickActionCard } from "@components/VencordSettings/quickActions";
|
||||
import { SettingsTab, wrapTab } from "@components/VencordSettings/shared";
|
||||
import { AddonCard, openPluginModal, QuickAction, QuickActionCard, SettingsTab, wrapTab } from "@components/settings";
|
||||
import { CspBlockedUrls, useCspErrors } from "@utils/cspViolations";
|
||||
import { openInviteModal } from "@utils/discord";
|
||||
import { Margins } from "@utils/margins";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2025 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Margins } from "@utils/margins";
|
||||
import { wordsFromCamel, wordsToTitle } from "@utils/text";
|
||||
import { DefinedSettings, PluginOptionSlider } from "@utils/types";
|
||||
import { Forms, Slider, useEffect, useState } from "@webpack/common";
|
||||
|
||||
interface Props<T> {
|
||||
option: T;
|
||||
onChange(newValue: any): void;
|
||||
pluginSettings: {
|
||||
[setting: string]: any;
|
||||
enabled: boolean;
|
||||
};
|
||||
id: string;
|
||||
onError(hasError: boolean): void;
|
||||
definedSettings?: DefinedSettings;
|
||||
}
|
||||
|
||||
export function SettingSliderComponent({ option, pluginSettings, definedSettings, id, onChange, onError }: Props<PluginOptionSlider>) {
|
||||
const def = pluginSettings[id] ?? option.default;
|
||||
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
onError(error !== null);
|
||||
}, [error]);
|
||||
|
||||
function handleChange(newValue: number): void {
|
||||
const isValid = option.isValid?.call(definedSettings, newValue) ?? true;
|
||||
if (typeof isValid === "string") setError(isValid);
|
||||
else if (!isValid) setError("Invalid input provided.");
|
||||
else {
|
||||
setError(null);
|
||||
onChange(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Forms.FormSection>
|
||||
<Forms.FormTitle>{wordsToTitle(wordsFromCamel(id))}</Forms.FormTitle>
|
||||
<Forms.FormText className={Margins.bottom20} type="description">{option.description}</Forms.FormText>
|
||||
<Slider
|
||||
disabled={option.disabled?.call(definedSettings) ?? false}
|
||||
markers={option.markers}
|
||||
minValue={option.markers[0]}
|
||||
maxValue={option.markers[option.markers.length - 1]}
|
||||
initialValue={def}
|
||||
onValueChange={handleChange}
|
||||
onValueRender={(v: number) => String(v.toFixed(2))}
|
||||
stickToMarkers={option.stickToMarkers ?? true}
|
||||
{...option.componentProps}
|
||||
/>
|
||||
</Forms.FormSection>
|
||||
);
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
import "./styles.css";
|
||||
|
||||
export { default as ThemesTab } from "../../ThemeSettings/ThemesTab";
|
||||
export * from "./BaseTab";
|
||||
export { default as PatchHelperTab } from "./patchHelper";
|
||||
export { default as PluginsTab } from "./plugins";
|
||||
|
|
@ -13,6 +14,5 @@ export { openContributorModal } from "./plugins/ContributorModal";
|
|||
export { openPluginModal } from "./plugins/PluginModal";
|
||||
export { default as BackupAndRestoreTab } from "./sync/BackupAndRestoreTab";
|
||||
export { default as CloudTab } from "./sync/CloudTab";
|
||||
export { default as ThemesTab } from "./themes";
|
||||
export { openUpdaterModal, default as UpdaterTab } from "./updater";
|
||||
export { default as VencordTab } from "./vencord";
|
||||
|
|
|
|||
|
|
@ -5,18 +5,22 @@
|
|||
*/
|
||||
|
||||
import { showNotice } from "@api/Notices";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { CogWheel, InfoIcon } from "@components/Icons";
|
||||
import { AddonCard } from "@components/settings/AddonCard";
|
||||
import { proxyLazy } from "@utils/lazy";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { classes, isObjectEmpty } from "@utils/misc";
|
||||
import { Plugin } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { React, showToast, Toasts } from "@webpack/common";
|
||||
import { Settings } from "Vencord";
|
||||
|
||||
import { cl, logger } from ".";
|
||||
import { openPluginModal } from "./PluginModal";
|
||||
|
||||
const logger = new Logger("PluginCard");
|
||||
const cl = classNameFactory("vc-plugins-");
|
||||
|
||||
// Avoid circular dependency
|
||||
const { startDependenciesRecursive, startPlugin, stopPlugin, isPluginEnabled } = proxyLazy(() => require("plugins") as typeof import("plugins"));
|
||||
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti
|
|||
<Forms.FormTitle tag="h3">Settings</Forms.FormTitle>
|
||||
{renderSettings()}
|
||||
</Forms.FormSection>
|
||||
</ModalContent>;
|
||||
</ModalContent>
|
||||
{
|
||||
hasSettings && <ModalFooter>
|
||||
<Flex flexDirection="column" style={{ width: "100%" }}>
|
||||
|
|
|
|||
|
|
@ -19,29 +19,36 @@
|
|||
import "./styles.css";
|
||||
|
||||
import * as DataStore from "@api/DataStore";
|
||||
import { showNotice } from "@api/Notices";
|
||||
import { Settings, useSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { SettingsTab, wrapTab } from "@components/settings/tabs/BaseTab";
|
||||
import { stopPlugin } from "@plugins/index";
|
||||
import { CogWheel, InfoIcon } from "@components/Icons";
|
||||
import { openPluginModal, SettingsTab } from "@components/settings";
|
||||
import { AddonCard } from "@components/settings/AddonCard";
|
||||
import { ChangeList } from "@utils/ChangeList";
|
||||
import { proxyLazy } from "@utils/lazy";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { classes } from "@utils/misc";
|
||||
import { classes, isObjectEmpty } from "@utils/misc";
|
||||
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { useAwaiter, useCleanupEffect } from "@utils/react";
|
||||
import { useAwaiter } from "@utils/react";
|
||||
import { Plugin } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { Alerts, Button, Card, Flex, Forms, lodash, Parser, React, Select, Text, TextInput, Toasts, Tooltip, useMemo, useState } from "@webpack/common";
|
||||
import { Alerts, Button, Card, Flex, Forms, lodash, Parser, React, Select, Text, TextInput, Toasts, Tooltip, useMemo } from "@webpack/common";
|
||||
import { JSX } from "react";
|
||||
|
||||
import Plugins, { ExcludedPlugins, PluginMeta } from "~plugins";
|
||||
|
||||
import { PluginCard } from "./PluginCard";
|
||||
import { StockPluginsCard, UserPluginsCard } from "./PluginStatCards";
|
||||
|
||||
// Avoid circular dependency
|
||||
const { startDependenciesRecursive, startPlugin, stopPlugin } = proxyLazy(() => require("plugins"));
|
||||
|
||||
export const cl = classNameFactory("vc-plugins-");
|
||||
export const logger = new Logger("PluginSettings", "#a6d189");
|
||||
|
||||
const InputStyles = findByPropsLazy("inputWrapper", "inputError", "error");
|
||||
const ButtonClasses = findByPropsLazy("button", "disabled", "enabled");
|
||||
|
||||
function showErrorToast(message: string) {
|
||||
Toasts.show({
|
||||
|
|
@ -57,25 +64,23 @@ function showErrorToast(message: string) {
|
|||
function ReloadRequiredCard({ required, enabledPlugins, openDisablePluginsModal, resetCheckAndDo }) {
|
||||
return (
|
||||
<Card className={classes(cl("info-card"), required && "vc-warning-card")}>
|
||||
{required
|
||||
? (
|
||||
<>
|
||||
<Forms.FormTitle tag="h5">Restart required!</Forms.FormTitle>
|
||||
<Forms.FormText className={cl("dep-text")}>
|
||||
Restart now to apply new plugins and their settings
|
||||
</Forms.FormText>
|
||||
<Button onClick={() => location.reload()} className={cl("restart-button")}>
|
||||
Restart
|
||||
</Button>
|
||||
</>
|
||||
)
|
||||
: (
|
||||
<>
|
||||
<Forms.FormTitle tag="h5">Plugin Management</Forms.FormTitle>
|
||||
<Forms.FormText>Press the cog wheel or info icon to get more info on a plugin</Forms.FormText>
|
||||
<Forms.FormText>Plugins with a cog wheel have settings you can modify!</Forms.FormText>
|
||||
</>
|
||||
)}
|
||||
{required ? (
|
||||
<>
|
||||
<Forms.FormTitle tag="h5">Restart required!</Forms.FormTitle>
|
||||
<Forms.FormText className={cl("dep-text")}>
|
||||
Restart now to apply new plugins and their settings
|
||||
</Forms.FormText>
|
||||
<Button className={cl("restart-button")} onClick={() => location.reload()}>
|
||||
Restart
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Forms.FormTitle tag="h5">Plugin Management</Forms.FormTitle>
|
||||
<Forms.FormText>Press the cog wheel or info icon to get more info on a plugin</Forms.FormText>
|
||||
<Forms.FormText>Plugins with a cog wheel have settings you can modify!</Forms.FormText>
|
||||
</>
|
||||
)}
|
||||
{enabledPlugins.length > 0 && !required && (
|
||||
<Button
|
||||
size={Button.Sizes.SMALL}
|
||||
|
|
@ -93,6 +98,88 @@ function ReloadRequiredCard({ required, enabledPlugins, openDisablePluginsModal,
|
|||
);
|
||||
}
|
||||
|
||||
interface PluginCardProps extends React.HTMLProps<HTMLDivElement> {
|
||||
plugin: Plugin;
|
||||
disabled: boolean;
|
||||
onRestartNeeded(name: string): void;
|
||||
isNew?: boolean;
|
||||
}
|
||||
|
||||
export function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, onMouseLeave, isNew }: PluginCardProps) {
|
||||
const settings = Settings.plugins[plugin.name];
|
||||
|
||||
const isEnabled = () => Vencord.Plugins.isPluginEnabled(plugin.name);
|
||||
|
||||
function toggleEnabled() {
|
||||
const wasEnabled = isEnabled();
|
||||
|
||||
// If we're enabling a plugin, make sure all deps are enabled recursively.
|
||||
if (!wasEnabled) {
|
||||
const { restartNeeded, failures } = startDependenciesRecursive(plugin);
|
||||
if (failures.length) {
|
||||
logger.error(`Failed to start dependencies for ${plugin.name}: ${failures.join(", ")}`);
|
||||
showNotice("Failed to start dependencies: " + failures.join(", "), "Close", () => null);
|
||||
return;
|
||||
} else if (restartNeeded) {
|
||||
// If any dependencies have patches, don't start the plugin yet.
|
||||
settings.enabled = true;
|
||||
onRestartNeeded(plugin.name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if the plugin has patches, dont use stopPlugin/startPlugin. Wait for restart to apply changes.
|
||||
if (plugin.patches?.length) {
|
||||
settings.enabled = !wasEnabled;
|
||||
onRestartNeeded(plugin.name);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the plugin is enabled, but hasn't been started, then we can just toggle it off.
|
||||
if (wasEnabled && !plugin.started) {
|
||||
settings.enabled = !wasEnabled;
|
||||
return;
|
||||
}
|
||||
|
||||
const result = wasEnabled ? stopPlugin(plugin) : startPlugin(plugin);
|
||||
|
||||
if (!result) {
|
||||
settings.enabled = false;
|
||||
|
||||
const msg = `Error while ${wasEnabled ? "stopping" : "starting"} plugin ${plugin.name}`;
|
||||
logger.error(msg);
|
||||
showErrorToast(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
settings.enabled = !wasEnabled;
|
||||
}
|
||||
|
||||
return (
|
||||
<AddonCard
|
||||
name={plugin.name}
|
||||
description={plugin.description}
|
||||
isNew={isNew}
|
||||
enabled={isEnabled()}
|
||||
setEnabled={toggleEnabled}
|
||||
disabled={disabled}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
infoButton={
|
||||
<button
|
||||
role="switch"
|
||||
onClick={() => openPluginModal(plugin, onRestartNeeded)}
|
||||
className={classes(ButtonClasses.button, cl("info-button"))}
|
||||
>
|
||||
{plugin.options && !isObjectEmpty(plugin.options)
|
||||
? <CogWheel className={cl("info-icon")} />
|
||||
: <InfoIcon className={cl("info-icon")} />}
|
||||
</button>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const enum SearchStatus {
|
||||
ALL,
|
||||
ENABLED,
|
||||
|
|
@ -132,29 +219,28 @@ function ExcludedPluginsList({ search }: { search: string; }) {
|
|||
);
|
||||
}
|
||||
|
||||
function PluginSettings() {
|
||||
export default function PluginSettings() {
|
||||
const settings = useSettings();
|
||||
const changes = useMemo(() => new ChangeList<string>(), []);
|
||||
const changes = React.useMemo(() => new ChangeList<string>(), []);
|
||||
|
||||
useCleanupEffect(() => {
|
||||
if (changes.hasChanges)
|
||||
Alerts.show({
|
||||
title: "Restart required",
|
||||
body: (
|
||||
<>
|
||||
<p>The following plugins require a restart:</p>
|
||||
<div>{changes.map((s, i) => (
|
||||
<>
|
||||
{i > 0 && ", "}
|
||||
{Parser.parse("`" + s.split(".")[0] + "`")}
|
||||
</>
|
||||
))}</div>
|
||||
</>
|
||||
),
|
||||
confirmText: "Restart now",
|
||||
cancelText: "Later!",
|
||||
onConfirm: () => location.reload()
|
||||
});
|
||||
React.useEffect(() => {
|
||||
return () => void (changes.hasChanges && Alerts.show({
|
||||
title: "Restart required",
|
||||
body: (
|
||||
<>
|
||||
<p>The following plugins require a restart:</p>
|
||||
<div>{changes.map((s, i) => (
|
||||
<>
|
||||
{i > 0 && ", "}
|
||||
{Parser.parse("`" + s + "`")}
|
||||
</>
|
||||
))}</div>
|
||||
</>
|
||||
),
|
||||
confirmText: "Restart now",
|
||||
cancelText: "Later!",
|
||||
onConfirm: () => location.reload()
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const depMap = useMemo(() => {
|
||||
|
|
@ -171,12 +257,10 @@ function PluginSettings() {
|
|||
return o;
|
||||
}, []);
|
||||
|
||||
const sortedPlugins = useMemo(() =>
|
||||
Object.values(Plugins).sort((a, b) => a.name.localeCompare(b.name)),
|
||||
[]
|
||||
);
|
||||
const sortedPlugins = useMemo(() => Object.values(Plugins)
|
||||
.sort((a, b) => a.name.localeCompare(b.name)), []);
|
||||
|
||||
const [searchValue, setSearchValue] = useState({ value: "", status: SearchStatus.ALL });
|
||||
const [searchValue, setSearchValue] = React.useState({ value: "", status: SearchStatus.ALL });
|
||||
|
||||
const search = searchValue.value.toLowerCase();
|
||||
const onSearch = (query: string) => setSearchValue(prev => ({ ...prev, value: query }));
|
||||
|
|
@ -185,19 +269,9 @@ function PluginSettings() {
|
|||
const pluginFilter = (plugin: typeof Plugins[keyof typeof Plugins]) => {
|
||||
const { status } = searchValue;
|
||||
const enabled = Vencord.Plugins.isPluginEnabled(plugin.name);
|
||||
|
||||
switch (status) {
|
||||
case SearchStatus.DISABLED:
|
||||
if (enabled) return false;
|
||||
break;
|
||||
case SearchStatus.ENABLED:
|
||||
if (!enabled) return false;
|
||||
break;
|
||||
case SearchStatus.NEW:
|
||||
if (!newPlugins?.includes(plugin.name)) return false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (enabled && status === SearchStatus.DISABLED) return false;
|
||||
if (!enabled && status === SearchStatus.ENABLED) return false;
|
||||
if (status === SearchStatus.NEW && !newPlugins?.includes(plugin.name)) return false;
|
||||
if (!search.length) return true;
|
||||
|
||||
return (
|
||||
|
|
@ -247,7 +321,7 @@ function PluginSettings() {
|
|||
<PluginCard
|
||||
onMouseLeave={onMouseLeave}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onRestartNeeded={(name, key) => changes.handleChange(`${name}.${key}`)}
|
||||
onRestartNeeded={name => changes.handleChange(name)}
|
||||
disabled={true}
|
||||
plugin={p}
|
||||
key={p.name}
|
||||
|
|
@ -258,7 +332,7 @@ function PluginSettings() {
|
|||
} else {
|
||||
plugins.push(
|
||||
<PluginCard
|
||||
onRestartNeeded={(name, key) => changes.handleChange(`${name}.${key}`)}
|
||||
onRestartNeeded={name => changes.handleChange(name)}
|
||||
disabled={false}
|
||||
plugin={p}
|
||||
isNew={newPlugins?.includes(p.name)}
|
||||
|
|
@ -472,11 +546,9 @@ function PluginSettings() {
|
|||
|
||||
function makeDependencyList(deps: string[]) {
|
||||
return (
|
||||
<>
|
||||
<React.Fragment>
|
||||
<Forms.FormText>This plugin is required by:</Forms.FormText>
|
||||
{deps.map((dep: string) => <Forms.FormText key={dep} className={cl("dep-text")}>{dep}</Forms.FormText>)}
|
||||
</>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
export default wrapTab(PluginSettings, "Plugins");
|
||||
|
|
|
|||
|
|
@ -17,25 +17,184 @@
|
|||
*/
|
||||
|
||||
import { useSettings } from "@api/Settings";
|
||||
import { ErrorCard } from "@components/ErrorCard";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { Link } from "@components/Link";
|
||||
import { handleSettingsTabError, SettingsTab, wrapTab } from "@components/settings/tabs/BaseTab";
|
||||
import { handleSettingsTabError, SettingsTab, wrapTab } from "@components/settings";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { classes } from "@utils/misc";
|
||||
import { ModalCloseButton, ModalContent, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { relaunch } from "@utils/native";
|
||||
import { useAwaiter } from "@utils/react";
|
||||
import { getRepo, isNewer, UpdateLogger } from "@utils/updater";
|
||||
import { Forms, React, Switch } from "@webpack/common";
|
||||
import { changes, checkForUpdates, getRepo, isNewer, update, updateError, UpdateLogger } from "@utils/updater";
|
||||
import { Alerts, Button, Card, Forms, Parser, React, Switch, Toasts } from "@webpack/common";
|
||||
|
||||
import gitHash from "~git-hash";
|
||||
|
||||
import { CommonProps, HashLink, Newer, Updatable } from "./Components";
|
||||
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);
|
||||
|
||||
let err: string;
|
||||
if (!e) {
|
||||
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")
|
||||
err = `Command \`${path}\` not found.\nPlease install it and try again`;
|
||||
else {
|
||||
err = `An error occurred while running \`${cmd}\`:\n`;
|
||||
err += stderr || `Code \`${code}\`. See the console for more info`;
|
||||
}
|
||||
|
||||
} else {
|
||||
err = "An unknown error occurred. See the console for more info.";
|
||||
}
|
||||
|
||||
Alerts.show({
|
||||
title: "Oops!",
|
||||
body: (
|
||||
<ErrorCard>
|
||||
{err.split("\n").map((line, idx) => <div key={idx}>{Parser.parse(line)}</div>)}
|
||||
</ErrorCard>
|
||||
)
|
||||
});
|
||||
}
|
||||
finally {
|
||||
dispatcher(false);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
interface CommonProps {
|
||||
repo: string;
|
||||
repoPending: boolean;
|
||||
}
|
||||
|
||||
function HashLink({ repo, hash, disabled = false }: { repo: string, hash: string, disabled?: boolean; }) {
|
||||
return <Link href={`${repo}/commit/${hash}`} disabled={disabled}>
|
||||
{hash}
|
||||
</Link>;
|
||||
}
|
||||
|
||||
function Changes({ updates, repo, repoPending }: CommonProps & { updates: typeof changes; }) {
|
||||
return (
|
||||
<Card style={{ padding: "0 0.5em" }}>
|
||||
{updates.map(({ hash, author, message }) => (
|
||||
<div key={hash} style={{
|
||||
marginTop: "0.5em",
|
||||
marginBottom: "0.5em"
|
||||
}}>
|
||||
<code><HashLink {...{ repo, hash }} disabled={repoPending} /></code>
|
||||
<span style={{
|
||||
marginLeft: "0.5em",
|
||||
color: "var(--text-default)"
|
||||
}}>{message} - {author}</span>
|
||||
</div>
|
||||
))}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
function Updatable(props: CommonProps) {
|
||||
const [updates, setUpdates] = React.useState(changes);
|
||||
const [isChecking, setIsChecking] = React.useState(false);
|
||||
const [isUpdating, setIsUpdating] = React.useState(false);
|
||||
const isOutdated = (updates?.length ?? 0) > 0;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex className={classes(Margins.bottom8, Margins.top8)}>
|
||||
{isOutdated && <Button
|
||||
size={Button.Sizes.SMALL}
|
||||
disabled={isUpdating || isChecking}
|
||||
onClick={withDispatcher(setIsUpdating, async () => {
|
||||
if (await update()) {
|
||||
setUpdates([]);
|
||||
return 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() {
|
||||
relaunch();
|
||||
r();
|
||||
},
|
||||
onCancel: r
|
||||
});
|
||||
});
|
||||
}
|
||||
})}
|
||||
>
|
||||
Update Now
|
||||
</Button>}
|
||||
<Button
|
||||
size={Button.Sizes.SMALL}
|
||||
disabled={isUpdating || isChecking}
|
||||
onClick={withDispatcher(setIsChecking, async () => {
|
||||
const outdated = await checkForUpdates();
|
||||
if (outdated) {
|
||||
setUpdates(changes);
|
||||
} else {
|
||||
setUpdates([]);
|
||||
Toasts.show({
|
||||
message: "No updates found!",
|
||||
id: Toasts.genId(),
|
||||
type: Toasts.Type.MESSAGE,
|
||||
options: {
|
||||
position: Toasts.Position.BOTTOM
|
||||
}
|
||||
});
|
||||
}
|
||||
})}
|
||||
>
|
||||
Check for Updates
|
||||
</Button>
|
||||
</Flex>
|
||||
{!updates && updateError ? (
|
||||
<>
|
||||
<Forms.FormText>Failed to check updates. Check the console for more info</Forms.FormText>
|
||||
<ErrorCard style={{ padding: "1em" }}>
|
||||
<p>{updateError.stderr || updateError.stdout || "An unknown error occurred"}</p>
|
||||
</ErrorCard>
|
||||
</>
|
||||
) : (
|
||||
<Forms.FormText className={Margins.bottom8}>
|
||||
{isOutdated ? (updates.length === 1 ? "There is 1 Update" : `There are ${updates.length} Updates`) : "Up to Date!"}
|
||||
</Forms.FormText>
|
||||
)}
|
||||
|
||||
{isOutdated && <Changes updates={updates} {...props} />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Newer(props: CommonProps) {
|
||||
return (
|
||||
<>
|
||||
<Forms.FormText className={Margins.bottom8}>
|
||||
Your local copy has more recent commits. Please stash or reset them.
|
||||
</Forms.FormText>
|
||||
<Changes {...props} updates={changes} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Updater() {
|
||||
const settings = useSettings(["autoUpdate", "autoUpdateNotification"]);
|
||||
|
||||
const [repo, err, repoPending] = useAwaiter(getRepo, {
|
||||
fallbackValue: "Loading...",
|
||||
onError: e => UpdateLogger.error("Failed to retrieve repo", err)
|
||||
});
|
||||
const [repo, err, repoPending] = useAwaiter(getRepo, { fallbackValue: "Loading..." });
|
||||
|
||||
React.useEffect(() => {
|
||||
if (err)
|
||||
UpdateLogger.error("Failed to retrieve repo", err);
|
||||
}, [err]);
|
||||
|
||||
const commonProps: CommonProps = {
|
||||
repo,
|
||||
|
|
@ -43,20 +202,19 @@ function Updater() {
|
|||
};
|
||||
|
||||
return (
|
||||
<SettingsTab title="Vencord Updater">
|
||||
<SettingsTab title="Equicord Updater">
|
||||
<Forms.FormTitle tag="h5">Updater Settings</Forms.FormTitle>
|
||||
|
||||
<Switch
|
||||
value={settings.autoUpdate}
|
||||
onChange={(v: boolean) => settings.autoUpdate = v}
|
||||
note="Automatically update Vencord without confirmation prompt"
|
||||
note="Automatically update Equicord without confirmation prompt"
|
||||
>
|
||||
Automatically update
|
||||
</Switch>
|
||||
<Switch
|
||||
value={settings.autoUpdateNotification}
|
||||
onChange={(v: boolean) => settings.autoUpdateNotification = v}
|
||||
note="Show a notification when Vencord automatically updates"
|
||||
note="Shows a notification when Equicord automatically updates"
|
||||
disabled={!settings.autoUpdate}
|
||||
>
|
||||
Get notified when an automatic update completes
|
||||
|
|
@ -75,41 +233,33 @@ function Updater() {
|
|||
</Link>
|
||||
)
|
||||
}
|
||||
{" "}
|
||||
(<HashLink hash={gitHash} repo={repo} disabled={repoPending} />)
|
||||
{" "}(<HashLink hash={gitHash} repo={repo} disabled={repoPending} />)
|
||||
</Forms.FormText>
|
||||
|
||||
<Forms.FormDivider className={Margins.top8 + " " + Margins.bottom8} />
|
||||
|
||||
<Forms.FormTitle tag="h5">Updates</Forms.FormTitle>
|
||||
|
||||
{isNewer
|
||||
? <Newer {...commonProps} />
|
||||
: <Updatable {...commonProps} />
|
||||
}
|
||||
</SettingsTab>
|
||||
{isNewer ? <Newer {...commonProps} /> : <Updatable {...commonProps} />}
|
||||
</SettingsTab >
|
||||
);
|
||||
}
|
||||
|
||||
export default IS_UPDATER_DISABLED
|
||||
? null
|
||||
: wrapTab(Updater, "Updater");
|
||||
export default IS_UPDATER_DISABLED ? null : wrapTab(Updater, "Updater");
|
||||
|
||||
export const openUpdaterModal = IS_UPDATER_DISABLED
|
||||
? null
|
||||
: function () {
|
||||
const UpdaterTab = wrapTab(Updater, "Updater");
|
||||
export const openUpdaterModal = IS_UPDATER_DISABLED ? null : function () {
|
||||
const UpdaterTab = wrapTab(Updater, "Updater");
|
||||
|
||||
try {
|
||||
openModal(wrapTab((modalProps: ModalProps) => (
|
||||
<ModalRoot {...modalProps} size={ModalSize.MEDIUM}>
|
||||
<ModalContent className="vc-updater-modal">
|
||||
<ModalCloseButton onClick={modalProps.onClose} className="vc-updater-modal-close-button" />
|
||||
<UpdaterTab />
|
||||
</ModalContent>
|
||||
</ModalRoot>
|
||||
), "UpdaterModal"));
|
||||
} catch {
|
||||
handleSettingsTabError();
|
||||
}
|
||||
};
|
||||
try {
|
||||
openModal(wrapTab((modalProps: ModalProps) => (
|
||||
<ModalRoot {...modalProps} size={ModalSize.MEDIUM}>
|
||||
<ModalContent className="vc-updater-modal">
|
||||
<ModalCloseButton onClick={modalProps.onClose} className="vc-updater-modal-close-button" />
|
||||
<UpdaterTab />
|
||||
</ModalContent>
|
||||
</ModalRoot>
|
||||
), "UpdaterModal"));
|
||||
} catch {
|
||||
handleSettingsTabError();
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import DonateButton from "@components/settings/DonateButton";
|
||||
import { DONOR_ROLE_ID, VENCORD_GUILD_ID } from "@utils/constants";
|
||||
import { DonateButton } from "@components/settings";
|
||||
import { DONOR_ROLE_ID, VC_GUILD_ID } from "@utils/constants";
|
||||
import { Button, GuildMemberStore } from "@webpack/common";
|
||||
import BadgeAPI from "plugins/_api/badges";
|
||||
|
||||
export const isDonor = (userId: string) => !!(
|
||||
BadgeAPI.getDonorBadges(userId)?.length > 0
|
||||
|| GuildMemberStore.getMember(VENCORD_GUILD_ID, userId)?.roles.includes(DONOR_ROLE_ID)
|
||||
|| GuildMemberStore.getMember(VC_GUILD_ID, userId)?.roles.includes(DONOR_ROLE_ID)
|
||||
);
|
||||
|
||||
export function DonateButtonComponent() {
|
||||
|
|
|
|||
49
src/components/settings/tabs/vencord/VencordTab.css
Normal file
49
src/components/settings/tabs/vencord/VencordTab.css
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
:root {
|
||||
--vc-donatebutton: #ffc1cbc2;
|
||||
--vc-donatebutton-secondary: #f7536fe5;
|
||||
}
|
||||
|
||||
.vc-donatebutton {
|
||||
background-color: var(--background-color) !important;
|
||||
border-color: var(--vc-donatebutton) !important;
|
||||
filter: brightness(1);
|
||||
border-width: .1rem !important;
|
||||
border-style: solid !important;
|
||||
}
|
||||
|
||||
.vc-donatebutton:hover {
|
||||
background-color: var(--vc-donatebutton) !important;
|
||||
border-color: transparent !important;
|
||||
}
|
||||
|
||||
|
||||
.vc-joindiscordbutton {
|
||||
background-color: var(--button-secondary-background) !important;
|
||||
}
|
||||
|
||||
.vc-joindiscordbutton:hover {
|
||||
background-color: var(--button-secondary-background-hover) !important;
|
||||
}
|
||||
|
||||
.vc-settings-card-buttons {
|
||||
display: flex;
|
||||
gap: 1em !important;
|
||||
margin-left: .2rem;
|
||||
margin-bottom: .2rem;
|
||||
}
|
||||
|
||||
.vc-settingbuttons {
|
||||
border-radius: 5px !important;
|
||||
}
|
||||
|
||||
.form-switch-warning {
|
||||
font-size: 16px;
|
||||
background-color: rgb(240 71 71 / 10%);
|
||||
color: rgb(240 71 71);
|
||||
border: 1px solid rgb(240 71 71 / 60%);
|
||||
border-radius: 4px;
|
||||
font-weight: 500;
|
||||
padding: 6px 10px;
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
|
@ -1,44 +1,37 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2022 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2024 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import "./VencordTab.css";
|
||||
|
||||
import { openNotificationLogModal } from "@api/Notifications/notificationLog";
|
||||
import { useSettings } from "@api/Settings";
|
||||
import { FolderIcon, GithubIcon, LogIcon, PaintbrushIcon, RestartIcon } from "@components/index";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { FolderIcon, GithubIcon, LogIcon, PaintbrushIcon, RestartIcon } from "@components/Icons";
|
||||
import { openContributorModal, openPluginModal, SettingsTab, wrapTab } from "@components/settings";
|
||||
import { DonateButton, InviteButton } from "@components/settings/DonateButton";
|
||||
import { QuickAction, QuickActionCard } from "@components/settings/QuickAction";
|
||||
import { SpecialCard } from "@components/settings/SpecialCard";
|
||||
import { SettingsTab, wrapTab } from "@components/settings/tabs/BaseTab";
|
||||
import { openContributorModal } from "@components/settings/tabs/plugins/ContributorModal";
|
||||
import { openPluginModal } from "@components/settings/tabs/plugins/PluginModal";
|
||||
import { gitRemote } from "@shared/vencordUserAgent";
|
||||
import { IS_MAC, IS_WINDOWS } from "@utils/constants";
|
||||
import { DONOR_ROLE_ID, GUILD_ID, VC_DONOR_ROLE_ID, VC_GUILD_ID } from "@utils/constants";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { isPluginDev } from "@utils/misc";
|
||||
import { identity, isEquicordPluginDev, isPluginDev } from "@utils/misc";
|
||||
import { relaunch } from "@utils/native";
|
||||
import { Forms, React, Switch, useMemo, UserStore } from "@webpack/common";
|
||||
import { Button, Flex, Forms, GuildMemberStore, React, Select, Switch, UserStore } from "@webpack/common";
|
||||
import BadgeAPI from "plugins/_api/badges";
|
||||
|
||||
import { DonateButtonComponent, isDonor } from "./DonateButton";
|
||||
import { VibrancySettings } from "./MacVibrancySettings";
|
||||
import { NotificationSection } from "./NotificationSettings";
|
||||
import { openNotificationSettingsModal } from "./NotificationSettings";
|
||||
|
||||
const cl = classNameFactory("vc-settings-");
|
||||
|
||||
const DEFAULT_DONATE_IMAGE = "https://cdn.discordapp.com/emojis/1026533090627174460.png";
|
||||
const SHIGGY_DONATE_IMAGE = "https://media.discordapp.net/stickers/1039992459209490513.png";
|
||||
const SHIGGY_DONATE_IMAGE = "https://i.imgur.com/57ATLZu.png";
|
||||
|
||||
const VENNIE_DONATOR_IMAGE = "https://cdn.discordapp.com/emojis/1238120638020063377.png";
|
||||
const COZY_CONTRIB_IMAGE = "https://cdn.discordapp.com/emojis/1026533070955872337.png";
|
||||
|
||||
const DONOR_BACKGROUND_IMAGE = "https://media.discordapp.net/stickers/1311070116305436712.png?size=2048";
|
||||
const CONTRIB_BACKGROUND_IMAGE = "https://media.discordapp.net/stickers/1311070166481895484.png?size=2048";
|
||||
|
||||
|
|
@ -46,105 +39,114 @@ type KeysOfType<Object, Type> = {
|
|||
[K in keyof Object]: Object[K] extends Type ? K : never;
|
||||
}[keyof Object];
|
||||
|
||||
function Switches() {
|
||||
const settings = useSettings(["useQuickCss", "enableReactDevtools", "frameless", "winNativeTitleBar", "transparent", "winCtrlQ", "disableMinSize"]);
|
||||
function EquicordSettings() {
|
||||
const settings = useSettings();
|
||||
|
||||
const Switches = [
|
||||
{
|
||||
key: "useQuickCss",
|
||||
title: "Enable Custom CSS",
|
||||
note: "Loads your Custom CSS"
|
||||
},
|
||||
!IS_WEB && {
|
||||
key: "enableReactDevtools",
|
||||
title: "Enable React Developer Tools",
|
||||
note: "Requires a full restart"
|
||||
},
|
||||
!IS_WEB && (!IS_DISCORD_DESKTOP || !IS_WINDOWS ? {
|
||||
key: "frameless",
|
||||
title: "Disable the window frame",
|
||||
note: "Requires a full restart"
|
||||
} : {
|
||||
key: "winNativeTitleBar",
|
||||
title: "Use Windows' native title bar instead of Discord's custom one",
|
||||
note: "Requires a full restart"
|
||||
}),
|
||||
!IS_WEB && {
|
||||
key: "transparent",
|
||||
title: "Enable window transparency.",
|
||||
note: "You need a theme that supports transparency or this will do nothing. WILL STOP THE WINDOW FROM BEING RESIZABLE!! Requires a full restart"
|
||||
},
|
||||
!IS_WEB && IS_WINDOWS && {
|
||||
key: "winCtrlQ",
|
||||
title: "Register Ctrl+Q as shortcut to close Discord (Alternative to Alt+F4)",
|
||||
note: "Requires a full restart"
|
||||
},
|
||||
IS_DISCORD_DESKTOP && {
|
||||
key: "disableMinSize",
|
||||
title: "Disable minimum window size",
|
||||
note: "Requires a full restart"
|
||||
},
|
||||
] satisfies Array<false | {
|
||||
key: KeysOfType<typeof settings, boolean>;
|
||||
title: string;
|
||||
note: string;
|
||||
}>;
|
||||
|
||||
return Switches.map(s => s && (
|
||||
<Switch
|
||||
key={s.key}
|
||||
value={settings[s.key]}
|
||||
onChange={v => settings[s.key] = v}
|
||||
note={s.note}
|
||||
>
|
||||
{s.title}
|
||||
</Switch>
|
||||
));
|
||||
}
|
||||
|
||||
function VencordSettings() {
|
||||
const donateImage = useMemo(() =>
|
||||
Math.random() > 0.5 ? DEFAULT_DONATE_IMAGE : SHIGGY_DONATE_IMAGE,
|
||||
[]
|
||||
const donateImage = React.useMemo(
|
||||
() => (Math.random() > 0.5 ? DEFAULT_DONATE_IMAGE : SHIGGY_DONATE_IMAGE),
|
||||
[],
|
||||
);
|
||||
|
||||
const needsVibrancySettings = IS_DISCORD_DESKTOP && IS_MAC;
|
||||
const isWindows = navigator.platform.toLowerCase().startsWith("win");
|
||||
const isMac = navigator.platform.toLowerCase().startsWith("mac");
|
||||
const needsVibrancySettings = IS_DISCORD_DESKTOP && isMac;
|
||||
|
||||
const user = UserStore.getCurrentUser();
|
||||
|
||||
return (
|
||||
<SettingsTab title="Vencord Settings">
|
||||
{isDonor(user?.id)
|
||||
? (
|
||||
<SpecialCard
|
||||
title="Donations"
|
||||
subtitle="Thank you for donating!"
|
||||
description="You can manage your perks at any time by messaging @vending.machine."
|
||||
cardImage={VENNIE_DONATOR_IMAGE}
|
||||
backgroundImage={DONOR_BACKGROUND_IMAGE}
|
||||
backgroundColor="#ED87A9"
|
||||
>
|
||||
<DonateButtonComponent />
|
||||
</SpecialCard>
|
||||
)
|
||||
: (
|
||||
<SpecialCard
|
||||
title="Support the Project"
|
||||
description="Please consider supporting the development of Vencord by donating!"
|
||||
cardImage={donateImage}
|
||||
backgroundImage={DONOR_BACKGROUND_IMAGE}
|
||||
backgroundColor="#c3a3ce"
|
||||
>
|
||||
<DonateButtonComponent />
|
||||
</SpecialCard>
|
||||
)
|
||||
}
|
||||
const Switches: Array<false | {
|
||||
key: KeysOfType<typeof settings, boolean>;
|
||||
title: string;
|
||||
note: string;
|
||||
warning: { enabled: boolean; message?: string; };
|
||||
}
|
||||
> = [
|
||||
{
|
||||
key: "useQuickCss",
|
||||
title: "Enable Custom CSS",
|
||||
note: "Loads your Custom CSS",
|
||||
warning: { enabled: false },
|
||||
},
|
||||
!IS_WEB && {
|
||||
key: "enableReactDevtools",
|
||||
title: "Enable React Developer Tools",
|
||||
note: "Requires a full restart",
|
||||
warning: { enabled: false },
|
||||
},
|
||||
!IS_WEB &&
|
||||
(!IS_DISCORD_DESKTOP || !isWindows
|
||||
? {
|
||||
key: "frameless",
|
||||
title: "Disable the Window Frame",
|
||||
note: "Requires a full restart",
|
||||
warning: { enabled: false },
|
||||
}
|
||||
: {
|
||||
key: "winNativeTitleBar",
|
||||
title:
|
||||
"Use Windows' native title bar instead of Discord's custom one",
|
||||
note: "Requires a full restart",
|
||||
warning: { enabled: false },
|
||||
}),
|
||||
!IS_WEB && {
|
||||
key: "transparent",
|
||||
title: "Enable Window Transparency",
|
||||
note: "You need a theme that supports transparency or this will do nothing. Requires a full restart!",
|
||||
warning: {
|
||||
enabled: isWindows,
|
||||
message: "Enabling this will prevent you from snapping this window.",
|
||||
},
|
||||
},
|
||||
!IS_WEB &&
|
||||
isWindows && {
|
||||
key: "winCtrlQ",
|
||||
title:
|
||||
"Register Ctrl+Q as shortcut to close Discord (Alternative to Alt+F4)",
|
||||
note: "Requires a full restart",
|
||||
warning: { enabled: false },
|
||||
},
|
||||
IS_DISCORD_DESKTOP && {
|
||||
key: "disableMinSize",
|
||||
title: "Disable Minimum Window Size",
|
||||
note: "Requires a full restart",
|
||||
warning: { enabled: false },
|
||||
},
|
||||
];
|
||||
|
||||
{isPluginDev(user?.id) && (
|
||||
return (
|
||||
<SettingsTab title="Equicord Settings">
|
||||
{(isEquicordDonor(user?.id) || isVencordDonor(user?.id)) ? (
|
||||
<SpecialCard
|
||||
title="Donations"
|
||||
subtitle="Thank you for donating!"
|
||||
description={
|
||||
isEquicordDonor(user?.id) && isVencordDonor(user?.id)
|
||||
? "All Vencord users can see your Vencord donor badge, and Equicord users can see your Equicord donor badge. To change your Vencord donor badge, contact @vending.machine. For your Equicord donor badge, make a ticket in Equicord's server."
|
||||
: isVencordDonor(user?.id)
|
||||
? "All Vencord users can see your badge! You can manage your perks by messaging @vending.machine."
|
||||
: "All Equicord users can see your badge! You can manage your perks by making a ticket in Equicord's server."
|
||||
}
|
||||
cardImage={VENNIE_DONATOR_IMAGE}
|
||||
backgroundImage={DONOR_BACKGROUND_IMAGE}
|
||||
backgroundColor="#ED87A9"
|
||||
>
|
||||
<DonateButtonComponent />
|
||||
</SpecialCard>
|
||||
) : (
|
||||
<SpecialCard
|
||||
title="Support the Project"
|
||||
description="Please consider supporting the development of Equicord by donating!"
|
||||
cardImage={donateImage}
|
||||
backgroundImage={DONOR_BACKGROUND_IMAGE}
|
||||
backgroundColor="#c3a3ce"
|
||||
>
|
||||
<DonateButtonComponent />
|
||||
</SpecialCard>
|
||||
)}
|
||||
{isPluginDev(user?.id) || isEquicordPluginDev(user?.id) && (
|
||||
<SpecialCard
|
||||
title="Contributions"
|
||||
subtitle="Thank you for contributing!"
|
||||
description="Since you've contributed to Vencord you now have a cool new badge!"
|
||||
description="Since you've contributed to Equicord you now have a cool new badge!"
|
||||
cardImage={COZY_CONTRIB_IMAGE}
|
||||
backgroundImage={CONTRIB_BACKGROUND_IMAGE}
|
||||
backgroundColor="#EDCC87"
|
||||
|
|
@ -152,7 +154,6 @@ function VencordSettings() {
|
|||
buttonOnClick={() => openContributorModal(user)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Forms.FormSection title="Quick Actions">
|
||||
<QuickActionCard>
|
||||
<QuickAction
|
||||
|
|
@ -166,23 +167,27 @@ function VencordSettings() {
|
|||
action={() => VencordNative.quickCss.openEditor()}
|
||||
/>
|
||||
{!IS_WEB && (
|
||||
<>
|
||||
<QuickAction
|
||||
Icon={RestartIcon}
|
||||
text="Relaunch Discord"
|
||||
action={relaunch}
|
||||
/>
|
||||
<QuickAction
|
||||
Icon={FolderIcon}
|
||||
text="Open Settings Folder"
|
||||
action={() => VencordNative.settings.openFolder()}
|
||||
/>
|
||||
</>
|
||||
<QuickAction
|
||||
Icon={RestartIcon}
|
||||
text="Relaunch Discord"
|
||||
action={relaunch}
|
||||
/>
|
||||
)}
|
||||
{!IS_WEB && (
|
||||
<QuickAction
|
||||
Icon={FolderIcon}
|
||||
text="Open Settings Folder"
|
||||
action={() => VencordNative.settings.openFolder()}
|
||||
/>
|
||||
)}
|
||||
<QuickAction
|
||||
Icon={GithubIcon}
|
||||
text="View Source Code"
|
||||
action={() => VencordNative.native.openExternal("https://github.com/" + gitRemote)}
|
||||
action={() =>
|
||||
VencordNative.native.openExternal(
|
||||
"https://github.com/" + gitRemote,
|
||||
)
|
||||
}
|
||||
/>
|
||||
</QuickActionCard>
|
||||
</Forms.FormSection>
|
||||
|
|
@ -190,22 +195,158 @@ function VencordSettings() {
|
|||
<Forms.FormDivider />
|
||||
|
||||
<Forms.FormSection className={Margins.top16} title="Settings" tag="h5">
|
||||
<Forms.FormText className={Margins.bottom20} style={{ color: "var(--text-muted)" }}>
|
||||
<Forms.FormText
|
||||
className={Margins.bottom20}
|
||||
style={{ color: "var(--text-muted)" }}
|
||||
>
|
||||
Hint: You can change the position of this settings section in the{" "}
|
||||
<a onClick={() => openPluginModal(Vencord.Plugins.plugins.Settings)}>
|
||||
<Button
|
||||
look={Button.Looks.BLANK}
|
||||
style={{ color: "var(--text-link)", display: "inline-block" }}
|
||||
onClick={() => openPluginModal(Vencord.Plugins.plugins.Settings)}
|
||||
>
|
||||
settings of the Settings plugin
|
||||
</a>!
|
||||
</Button>
|
||||
!
|
||||
</Forms.FormText>
|
||||
|
||||
<Switches />
|
||||
{Switches.map(
|
||||
s =>
|
||||
s && (
|
||||
<Switch
|
||||
key={s.key}
|
||||
value={settings[s.key]}
|
||||
onChange={v => (settings[s.key] = v)}
|
||||
note={
|
||||
s.warning.enabled ? (
|
||||
<>
|
||||
{s.note}
|
||||
<div className="form-switch-warning">
|
||||
{s.warning.message}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
s.note
|
||||
)
|
||||
}
|
||||
>
|
||||
{s.title}
|
||||
</Switch>
|
||||
),
|
||||
)}
|
||||
</Forms.FormSection>
|
||||
|
||||
{needsVibrancySettings && (
|
||||
<>
|
||||
<Forms.FormTitle tag="h5">
|
||||
Window vibrancy style (requires restart)
|
||||
</Forms.FormTitle>
|
||||
<Select
|
||||
className={Margins.bottom20}
|
||||
placeholder="Window vibrancy style"
|
||||
options={[
|
||||
// Sorted from most opaque to most transparent
|
||||
{
|
||||
label: "No vibrancy",
|
||||
value: undefined,
|
||||
},
|
||||
{
|
||||
label: "Under Page (window tinting)",
|
||||
value: "under-page",
|
||||
},
|
||||
{
|
||||
label: "Content",
|
||||
value: "content",
|
||||
},
|
||||
{
|
||||
label: "Window",
|
||||
value: "window",
|
||||
},
|
||||
{
|
||||
label: "Selection",
|
||||
value: "selection",
|
||||
},
|
||||
{
|
||||
label: "Titlebar",
|
||||
value: "titlebar",
|
||||
},
|
||||
{
|
||||
label: "Header",
|
||||
value: "header",
|
||||
},
|
||||
{
|
||||
label: "Sidebar",
|
||||
value: "sidebar",
|
||||
},
|
||||
{
|
||||
label: "Tooltip",
|
||||
value: "tooltip",
|
||||
},
|
||||
{
|
||||
label: "Menu",
|
||||
value: "menu",
|
||||
},
|
||||
{
|
||||
label: "Popover",
|
||||
value: "popover",
|
||||
},
|
||||
{
|
||||
label: "Fullscreen UI (transparent but slightly muted)",
|
||||
value: "fullscreen-ui",
|
||||
},
|
||||
{
|
||||
label: "HUD (Most transparent)",
|
||||
value: "hud",
|
||||
},
|
||||
]}
|
||||
select={v => (settings.macosVibrancyStyle = v)}
|
||||
isSelected={v => settings.macosVibrancyStyle === v}
|
||||
serialize={identity}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{needsVibrancySettings && <VibrancySettings />}
|
||||
|
||||
<NotificationSection />
|
||||
<Forms.FormSection
|
||||
className={Margins.top16}
|
||||
title="Equicord Notifications"
|
||||
tag="h5"
|
||||
>
|
||||
<Flex>
|
||||
<Button onClick={openNotificationSettingsModal}>
|
||||
Notification Settings
|
||||
</Button>
|
||||
<Button onClick={openNotificationLogModal} style={{ marginLeft: 16 }}>
|
||||
View Notification Log
|
||||
</Button>
|
||||
</Flex>
|
||||
</Forms.FormSection>
|
||||
</SettingsTab>
|
||||
);
|
||||
}
|
||||
|
||||
export default wrapTab(VencordSettings, "Vencord Settings");
|
||||
function DonateButtonComponent() {
|
||||
return (
|
||||
<Flex>
|
||||
<DonateButton
|
||||
look={Button.Looks.FILLED}
|
||||
color={Button.Colors.TRANSPARENT}
|
||||
style={{ marginTop: "1em" }} />
|
||||
<InviteButton
|
||||
look={Button.Looks.FILLED}
|
||||
color={Button.Colors.TRANSPARENT}
|
||||
style={{ marginTop: "1em" }} />
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
function isVencordDonor(userId: string): boolean {
|
||||
const donorBadges = BadgeAPI.getDonorBadges(userId);
|
||||
return GuildMemberStore.getMember(VC_GUILD_ID, userId)?.roles.includes(VC_DONOR_ROLE_ID) || !!donorBadges;
|
||||
}
|
||||
|
||||
export function isEquicordDonor(userId: string): boolean {
|
||||
const donorBadges = BadgeAPI.getEquicordDonorBadges(userId);
|
||||
return GuildMemberStore.getMember(GUILD_ID, userId)?.roles.includes(DONOR_ROLE_ID) || !!donorBadges;
|
||||
}
|
||||
|
||||
export default wrapTab(EquicordSettings, "Equicord Settings");
|
||||
|
|
|
|||
|
|
@ -7,9 +7,8 @@
|
|||
import "./style.css";
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { makeRange } from "@components/PluginSettings/components";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import definePlugin, { makeRange, OptionType } from "@utils/types";
|
||||
|
||||
export const settings = definePluginSettings({
|
||||
frequentEmojis: {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { makeDummyUser } from "@components/PluginSettings/PluginModal";
|
||||
import { makeDummyUser } from "@components/settings/tabs/plugins/PluginModal";
|
||||
import { Channel, Message } from "@vencord/discord-types";
|
||||
import { ChannelStore, lodash, Toasts, UserStore } from "@webpack/common";
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { makeDummyUser } from "@components/PluginSettings/PluginModal";
|
||||
import { makeDummyUser } from "@components/settings/tabs/plugins/PluginModal";
|
||||
import { MessageType } from "@equicordplugins/holyNotes";
|
||||
import { copyToClipboard } from "@utils/clipboard";
|
||||
import { classes } from "@utils/misc";
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
import "./IconsTab.css";
|
||||
|
||||
import { SettingsTab, wrapTab } from "@components/VencordSettings/shared";
|
||||
import { SettingsTab, wrapTab } from "@components/settings";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { classes } from "@utils/misc";
|
||||
import * as t from "@vencord/discord-types";
|
||||
|
|
|
|||
|
|
@ -17,9 +17,8 @@
|
|||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { makeRange } from "@components/PluginSettings/components";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import definePlugin, { makeRange, OptionType } from "@utils/types";
|
||||
|
||||
|
||||
const settings = definePluginSettings({
|
||||
|
|
|
|||
|
|
@ -9,9 +9,8 @@ import "./styles.css";
|
|||
import { definePluginSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { makeRange } from "@components/PluginSettings/components";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import definePlugin, { makeRange, OptionType } from "@utils/types";
|
||||
import { ContextMenuApi, FluxDispatcher, Heading, Menu, React, Tooltip, useEffect } from "@webpack/common";
|
||||
import { RefObject } from "react";
|
||||
|
||||
|
|
|
|||
|
|
@ -17,10 +17,9 @@
|
|||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { makeRange } from "@components/PluginSettings/components/SettingSliderComponent";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { sleep } from "@utils/misc";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import definePlugin, { makeRange, OptionType } from "@utils/types";
|
||||
import { Message, ReactionEmoji } from "@vencord/discord-types";
|
||||
import { RelationshipStore, SelectedChannelStore, UserStore } from "@webpack/common";
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import { useSettings } from "@api/Settings";
|
||||
import { classNameFactory } from "@api/Styles";
|
||||
import { PluginCard } from "@components/PluginSettings";
|
||||
import { PluginCard } from "@components/settings/tabs/plugins/PluginCard";
|
||||
import { ChangeList } from "@utils/ChangeList";
|
||||
import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||
import { Alerts, Button, Flex, Forms, Parser, React, Text, Tooltip, useMemo } from "@webpack/common";
|
||||
|
|
|
|||
|
|
@ -13,9 +13,8 @@ import {
|
|||
definePluginSettings,
|
||||
Settings,
|
||||
} from "@api/Settings";
|
||||
import { makeRange } from "@components/PluginSettings/components";
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import definePlugin, { makeRange, OptionType } from "@utils/types";
|
||||
|
||||
const presendObject: MessageSendListener = (channelId, msg) => {
|
||||
msg.content = textProcessing(msg.content);
|
||||
|
|
|
|||
|
|
@ -7,10 +7,9 @@
|
|||
import { definePluginSettings } from "@api/Settings";
|
||||
import { disableStyle, enableStyle } from "@api/Styles";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { makeRange } from "@components/PluginSettings/components";
|
||||
import { debounce } from "@shared/debounce";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import definePlugin, { makeRange, OptionType } from "@utils/types";
|
||||
import { findByCode, findByProps, findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack";
|
||||
import { ChannelRouter, ChannelStore, ContextMenuApi, GuildStore, Menu, PermissionsBits, PermissionStore, React, SelectedChannelStore, Toasts, UserStore, VoiceStateStore } from "@webpack/common";
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Switch } from "@components/Switch";
|
||||
import { Switch } from "@components/settings";
|
||||
import { Button, Forms, Select, Slider, useEffect, useState } from "@webpack/common";
|
||||
|
||||
import { BrushTool } from "../tools/brush";
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@
|
|||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { makeRange, SettingSliderComponent } from "@components/PluginSettings/components";
|
||||
import { SettingSliderComponent } from "@components/ThemeSettings/components/SettingSliderComponent";
|
||||
import { useAwaiter } from "@utils/react";
|
||||
import { OptionType } from "@utils/types";
|
||||
import { makeRange, OptionType } from "@utils/types";
|
||||
import { Button, showToast, Text, Toasts, useMemo } from "@webpack/common";
|
||||
|
||||
import { clearLyricsCache, getLyricsCount, removeTranslations } from "./api";
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import * as DataStore from "@api/DataStore";
|
|||
import { Settings } from "@api/Settings";
|
||||
import { ErrorCard } from "@components/ErrorCard";
|
||||
import { OpenExternalIcon } from "@components/Icons";
|
||||
import { SettingsTab, wrapTab } from "@components/VencordSettings/shared";
|
||||
import { SettingsTab, wrapTab } from "@components/settings";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { classes } from "@utils/misc";
|
||||
|
|
|
|||
|
|
@ -17,9 +17,8 @@
|
|||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { makeRange } from "@components/PluginSettings/components";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import definePlugin, { makeRange, OptionType } from "@utils/types";
|
||||
import { Channel, Message, User } from "@vencord/discord-types";
|
||||
import { findByPropsLazy, findStore } from "@webpack";
|
||||
import { Button, ChannelStore, GuildRoleStore, NavigationRouter, RelationshipStore, SelectedChannelStore, UserStore } from "@webpack/common";
|
||||
|
|
|
|||
|
|
@ -214,21 +214,19 @@ function getTypeAndChannelId(
|
|||
return ["", ""];
|
||||
}
|
||||
|
||||
function playSample(tempSettings: any, type: string) {
|
||||
const s = Object.assign({}, settings.plain, tempSettings);
|
||||
function playSample(type: string) {
|
||||
const currentUser = UserStore.getCurrentUser();
|
||||
const myGuildId = SelectedGuildStore.getGuildId()!;
|
||||
|
||||
speak(
|
||||
formatText(
|
||||
s[type + "Message"],
|
||||
settings.store[type + "Message"],
|
||||
currentUser.username,
|
||||
"general",
|
||||
(currentUser as any).globalName ?? currentUser.username,
|
||||
GuildMemberStore.getNick(myGuildId, currentUser.id) ??
|
||||
currentUser.username
|
||||
),
|
||||
s
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -371,7 +369,7 @@ export default definePlugin({
|
|||
},
|
||||
},
|
||||
|
||||
settingsAboutComponent({ tempSettings: s }) {
|
||||
settingsAboutComponent() {
|
||||
const types = useMemo(
|
||||
() =>
|
||||
Object.keys(settings.def)
|
||||
|
|
@ -419,7 +417,7 @@ export default definePlugin({
|
|||
className={"vc-narrator-buttons"}
|
||||
>
|
||||
{types.map(t => (
|
||||
<Button key={t} onClick={() => playSample(s, t)}>
|
||||
<Button key={t} onClick={() => playSample(t)}>
|
||||
{wordsToTitle([t])}
|
||||
</Button>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,8 @@
|
|||
|
||||
import { NavContextMenuPatchCallback } from "@api/ContextMenu";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { makeRange } from "@components/PluginSettings/components";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import definePlugin, { makeRange, OptionType } from "@utils/types";
|
||||
import type { Channel } from "@vencord/discord-types";
|
||||
import { GuildChannelStore, Menu, React, RestAPI, UserStore, VoiceStateStore } from "@webpack/common";
|
||||
|
||||
|
|
|
|||
|
|
@ -5,9 +5,8 @@
|
|||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { makeRange } from "@components/PluginSettings/components";
|
||||
import { EquicordDevs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import definePlugin, { makeRange, OptionType } from "@utils/types";
|
||||
import { Text } from "@webpack/common";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import "./fixDiscordBadgePadding.css";
|
|||
import { _getBadges, BadgePosition, BadgeUserArgs, ProfileBadge } from "@api/Badges";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { openContributorModal } from "@components/settings/tabs";
|
||||
import { isEquicordDonor } from "@components/VencordSettings/VencordTab";
|
||||
import { isEquicordDonor } from "@components/settings/tabs/vencord";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { Logger } from "@utils/Logger";
|
||||
import { shouldShowContributorBadge, shouldShowEquicordContributorBadge } from "@utils/misc";
|
||||
|
|
|
|||
|
|
@ -4,10 +4,9 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { DonateButton, VCDonateButton } from "@components/DonateButton";
|
||||
import { Flex } from "@components/Flex";
|
||||
import { Heart } from "@components/Heart";
|
||||
import { ErrorBoundary } from "@components/index";
|
||||
import { DonateButton, ErrorBoundary, VCDonateButton } from "@components/index";
|
||||
import { Margins } from "@utils/margins";
|
||||
import { closeModal, ModalContent, ModalFooter, ModalHeader, ModalRoot, openModal } from "@utils/modal";
|
||||
import { Forms } from "@webpack/common";
|
||||
|
|
|
|||
|
|
@ -17,11 +17,8 @@
|
|||
*/
|
||||
|
||||
import { Settings } from "@api/Settings";
|
||||
import { BackupAndRestoreTab, CloudTab, PluginsTab } from "@components/settings";
|
||||
import { BackupAndRestoreTab, CloudTab, PatchHelperTab, PluginsTab, UpdaterTab, VencordTab } from "@components/settings";
|
||||
import ThemesTab from "@components/ThemeSettings/ThemesTab";
|
||||
import PatchHelperTab from "@components/VencordSettings/PatchHelperTab";
|
||||
import UpdaterTab from "@components/VencordSettings/UpdaterTab";
|
||||
import VencordTab from "@components/VencordSettings/VencordTab";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { getIntlMessage } from "@utils/discord";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue