From 1c2d328f5a6b6b3910ee8727de4b1b8f67df106f Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Tue, 15 Jul 2025 14:56:05 -0400 Subject: [PATCH] Updates --- src/components/ThemeSettings/ThemesTab.tsx | 5 +- .../components/SettingSliderComponent.tsx | 60 +++ src/components/settings/tabs/index.ts | 2 +- .../settings/tabs/plugins/PluginCard.tsx | 6 +- .../settings/tabs/plugins/PluginModal.tsx | 2 +- .../settings/tabs/plugins/index.tsx | 212 ++++++--- .../settings/tabs/updater/index.tsx | 228 ++++++++-- .../settings/tabs/vencord/DonateButton.tsx | 6 +- .../settings/tabs/vencord/VencordTab.css | 49 ++ .../settings/tabs/vencord/index.tsx | 417 ++++++++++++------ .../betterQuickReact/index.tsx | 3 +- src/equicordplugins/holyNotes/NoteHandler.ts | 2 +- .../components/modals/RenderMessage.tsx | 2 +- src/equicordplugins/iconViewer/IconsTab.tsx | 2 +- .../limitMiddleClickPaste/index.ts | 3 +- .../mediaPlaybackSpeed/index.tsx | 3 +- src/equicordplugins/moyai/index.ts | 3 +- .../newPluginsManager/NewPluginsModal.tsx | 2 +- src/equicordplugins/polishWording/index.ts | 3 +- src/equicordplugins/randomVoice/index.tsx | 3 +- .../remix/editor/components/Toolbar.tsx | 2 +- .../spotifyLyrics/settings.tsx | 4 +- .../themeLibrary/components/ThemeTab.tsx | 2 +- .../toastNotifications/index.tsx | 3 +- .../vcNarratorCustom/index.tsx | 10 +- src/equicordplugins/voiceChatUtils/index.tsx | 3 +- src/equicordplugins/wigglyText/index.tsx | 3 +- src/plugins/_api/badges/index.tsx | 2 +- src/plugins/_api/badges/modals.tsx | 3 +- src/plugins/_core/settings.tsx | 5 +- 30 files changed, 754 insertions(+), 296 deletions(-) create mode 100644 src/components/ThemeSettings/components/SettingSliderComponent.tsx create mode 100644 src/components/settings/tabs/vencord/VencordTab.css diff --git a/src/components/ThemeSettings/ThemesTab.tsx b/src/components/ThemeSettings/ThemesTab.tsx index b30b08bf5..901a3e930 100644 --- a/src/components/ThemeSettings/ThemesTab.tsx +++ b/src/components/ThemeSettings/ThemesTab.tsx @@ -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"; diff --git a/src/components/ThemeSettings/components/SettingSliderComponent.tsx b/src/components/ThemeSettings/components/SettingSliderComponent.tsx new file mode 100644 index 000000000..2aac85fe4 --- /dev/null +++ b/src/components/ThemeSettings/components/SettingSliderComponent.tsx @@ -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 { + 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) { + const def = pluginSettings[id] ?? option.default; + + const [error, setError] = useState(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 ( + + {wordsToTitle(wordsFromCamel(id))} + {option.description} + String(v.toFixed(2))} + stickToMarkers={option.stickToMarkers ?? true} + {...option.componentProps} + /> + + ); +} diff --git a/src/components/settings/tabs/index.ts b/src/components/settings/tabs/index.ts index 9b4296c39..bad044984 100644 --- a/src/components/settings/tabs/index.ts +++ b/src/components/settings/tabs/index.ts @@ -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"; diff --git a/src/components/settings/tabs/plugins/PluginCard.tsx b/src/components/settings/tabs/plugins/PluginCard.tsx index b9da5edc9..05567ceab 100644 --- a/src/components/settings/tabs/plugins/PluginCard.tsx +++ b/src/components/settings/tabs/plugins/PluginCard.tsx @@ -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")); diff --git a/src/components/settings/tabs/plugins/PluginModal.tsx b/src/components/settings/tabs/plugins/PluginModal.tsx index d9ff3f37c..eda4c345a 100644 --- a/src/components/settings/tabs/plugins/PluginModal.tsx +++ b/src/components/settings/tabs/plugins/PluginModal.tsx @@ -201,7 +201,7 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti Settings {renderSettings()} - ; + { hasSettings && diff --git a/src/components/settings/tabs/plugins/index.tsx b/src/components/settings/tabs/plugins/index.tsx index 04f8193a7..4894b4eae 100644 --- a/src/components/settings/tabs/plugins/index.tsx +++ b/src/components/settings/tabs/plugins/index.tsx @@ -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 ( - {required - ? ( - <> - Restart required! - - Restart now to apply new plugins and their settings - - - - ) - : ( - <> - Plugin Management - Press the cog wheel or info icon to get more info on a plugin - Plugins with a cog wheel have settings you can modify! - - )} + {required ? ( + <> + Restart required! + + Restart now to apply new plugins and their settings + + + + ) : ( + <> + Plugin Management + Press the cog wheel or info icon to get more info on a plugin + Plugins with a cog wheel have settings you can modify! + + )} {enabledPlugins.length > 0 && !required && ( + } + /> + ); +} + 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(), []); + const changes = React.useMemo(() => new ChangeList(), []); - useCleanupEffect(() => { - if (changes.hasChanges) - Alerts.show({ - title: "Restart required", - body: ( - <> -

The following plugins require a restart:

-
{changes.map((s, i) => ( - <> - {i > 0 && ", "} - {Parser.parse("`" + s.split(".")[0] + "`")} - - ))}
- - ), - confirmText: "Restart now", - cancelText: "Later!", - onConfirm: () => location.reload() - }); + React.useEffect(() => { + return () => void (changes.hasChanges && Alerts.show({ + title: "Restart required", + body: ( + <> +

The following plugins require a restart:

+
{changes.map((s, i) => ( + <> + {i > 0 && ", "} + {Parser.parse("`" + s + "`")} + + ))}
+ + ), + 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() { 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( 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 ( - <> + This plugin is required by: {deps.map((dep: string) => {dep})} - + ); } - -export default wrapTab(PluginSettings, "Plugins"); diff --git a/src/components/settings/tabs/updater/index.tsx b/src/components/settings/tabs/updater/index.tsx index 81238617f..e243020ef 100644 --- a/src/components/settings/tabs/updater/index.tsx +++ b/src/components/settings/tabs/updater/index.tsx @@ -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>, 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: ( + + {err.split("\n").map((line, idx) =>
{Parser.parse(line)}
)} +
+ ) + }); + } + finally { + dispatcher(false); + } + }; +} + +interface CommonProps { + repo: string; + repoPending: boolean; +} + +function HashLink({ repo, hash, disabled = false }: { repo: string, hash: string, disabled?: boolean; }) { + return + {hash} + ; +} + +function Changes({ updates, repo, repoPending }: CommonProps & { updates: typeof changes; }) { + return ( + + {updates.map(({ hash, author, message }) => ( +
+ + {message} - {author} +
+ ))} +
+ ); +} + +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 ( + <> + + {isOutdated && } + + + {!updates && updateError ? ( + <> + Failed to check updates. Check the console for more info + +

{updateError.stderr || updateError.stdout || "An unknown error occurred"}

+
+ + ) : ( + + {isOutdated ? (updates.length === 1 ? "There is 1 Update" : `There are ${updates.length} Updates`) : "Up to Date!"} + + )} + + {isOutdated && } + + ); +} + +function Newer(props: CommonProps) { + return ( + <> + + Your local copy has more recent commits. Please stash or reset them. + + + + ); +} 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 ( - + Updater Settings - settings.autoUpdate = v} - note="Automatically update Vencord without confirmation prompt" + note="Automatically update Equicord without confirmation prompt" > Automatically update 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() { ) } - {" "} - () + {" "}() Updates - {isNewer - ? - : - } - + {isNewer ? : } + ); } -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) => ( - - - - - - - ), "UpdaterModal")); - } catch { - handleSettingsTabError(); - } - }; + try { + openModal(wrapTab((modalProps: ModalProps) => ( + + + + + + + ), "UpdaterModal")); + } catch { + handleSettingsTabError(); + } +}; diff --git a/src/components/settings/tabs/vencord/DonateButton.tsx b/src/components/settings/tabs/vencord/DonateButton.tsx index df0bf4088..8d2971f02 100644 --- a/src/components/settings/tabs/vencord/DonateButton.tsx +++ b/src/components/settings/tabs/vencord/DonateButton.tsx @@ -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() { diff --git a/src/components/settings/tabs/vencord/VencordTab.css b/src/components/settings/tabs/vencord/VencordTab.css new file mode 100644 index 000000000..2b2394c71 --- /dev/null +++ b/src/components/settings/tabs/vencord/VencordTab.css @@ -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; +} diff --git a/src/components/settings/tabs/vencord/index.tsx b/src/components/settings/tabs/vencord/index.tsx index 19b8fe4fe..6e9d29261 100644 --- a/src/components/settings/tabs/vencord/index.tsx +++ b/src/components/settings/tabs/vencord/index.tsx @@ -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 . -*/ + * 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 = { [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; - title: string; - note: string; - }>; - - return Switches.map(s => s && ( - settings[s.key] = v} - note={s.note} - > - {s.title} - - )); -} - -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 ( - - {isDonor(user?.id) - ? ( - - - - ) - : ( - - - - ) - } + const Switches: Array; + 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 ( + + {(isEquicordDonor(user?.id) || isVencordDonor(user?.id)) ? ( + + + + ) : ( + + + + )} + {isPluginDev(user?.id) || isEquicordPluginDev(user?.id) && ( openContributorModal(user)} /> )} - VencordNative.quickCss.openEditor()} /> {!IS_WEB && ( - <> - - VencordNative.settings.openFolder()} - /> - + + )} + {!IS_WEB && ( + VencordNative.settings.openFolder()} + /> )} VencordNative.native.openExternal("https://github.com/" + gitRemote)} + action={() => + VencordNative.native.openExternal( + "https://github.com/" + gitRemote, + ) + } /> @@ -190,22 +195,158 @@ function VencordSettings() { - + Hint: You can change the position of this settings section in the{" "} - openPluginModal(Vencord.Plugins.plugins.Settings)}> + + ! - + {Switches.map( + s => + s && ( + (settings[s.key] = v)} + note={ + s.warning.enabled ? ( + <> + {s.note} +
+ {s.warning.message} +
+ + ) : ( + s.note + ) + } + > + {s.title} +
+ ), + )}
+ {needsVibrancySettings && ( + <> + + Window vibrancy style (requires restart) + +