Equicord/src/plugins/betterSettings/index.tsx

214 lines
7.2 KiB
TypeScript
Raw Normal View History

/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { definePluginSettings } from "@api/Settings";
import { disableStyle, enableStyle } from "@api/Styles";
import { buildPluginMenuEntries, buildThemeMenuEntries } from "@equicordplugins/equicordToolbox/menu";
import { Devs } from "@utils/constants";
import { classNameFactory } from "@utils/css";
import { Logger } from "@utils/Logger";
feat(i18n): Add translation keys and implement localization for 80+ p… (#772) * feat(i18n): Add translation keys and implement localization for 80+ plugins - Add comprehensive translation keys to i18n/en-US/vencord.json - Update all plugins to use t() function from @utils/translation - Replace hardcoded strings with translation function calls - Support plural forms with _one/_other suffixes - Use Translate component for strings with embedded JSX Plugins translated include: - Core: reviewDB, translate, voiceMessages, messageLogger - UI: showHiddenChannels, customRPC, clientTheme, permissionsViewer - Utility: betterSessions, betterSettings, memberCount, betterFolders - Social: relationshipNotifier, messageLatency, whoReacted - Media: imageZoom, viewIcons, viewRaw, decor, fakeNitro - And 60+ more plugins All changes pass TypeScript compilation. * Update i18n/en-US/vencord.json Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Update i18n/en-US/vencord.json Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Update i18n/en-US/vencord.json Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Update i18n/en-US/vencord.json Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Update i18n/en-US/vencord.json Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Update i18n/en-US/vencord.json Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * feat(i18n): Add equicord.json for equicordplugins localization - Create i18n/en-US/equicord.json with messageLoggerEnhanced translation keys - Create empty equicord.json files for all other locales (Crowdin will handle translations) - Build system automatically picks up all JSON files in i18n/{locale}/ * Update i18n/en-US/vencord.json Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * feat(i18n): Add translations to 200 equicordplugins - Translate all user-facing strings in equicordplugins to use t() function - Add translation keys to i18n/en-US/equicord.json - Plugin names remain static strings for build system compatibility - Settings descriptions, tooltips, context menus, and UI strings now translatable * FileUpload: add sharex file support (#771) * FileUpload: add sharex file support Quick note, the settings seem to split pacal case with spaces for some reason, so `ShareX` renders as `Share X`. This is stupid and im upset and I dont think theres anything I can do about it * FileUpload: use Headers object to construct HTTP headers * Update vencord.json --------- Co-authored-by: Mishal <91066601+mishalhossin@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Key <keircn@proton.me> Co-authored-by: thororen1234 <78185467+thororen1234@users.noreply.github.com>
2026-02-15 20:39:58 +06:00
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
2026-01-23 12:59:32 -05:00
import { findCssClassesLazy } from "@webpack";
import { ComponentDispatch, FocusLock, Menu, useEffect, useRef } from "@webpack/common";
import type { HTMLAttributes, ReactNode } from "react";
import fullHeightStyle from "./fullHeightContext.css?managed";
const cl = classNameFactory("");
2026-01-23 12:59:32 -05:00
const Classes = findCssClassesLazy("animating", "baseLayer", "bg", "layer", "layers");
const settings = definePluginSettings({
disableFade: {
2026-03-03 12:14:28 -05:00
description: t("vencord.betterSettings.settings.disableFade"),
type: OptionType.BOOLEAN,
default: true,
restartNeeded: true
},
organizeMenu: {
2026-03-03 12:14:28 -05:00
description: t("vencord.betterSettings.settings.organizeMenu"),
type: OptionType.BOOLEAN,
default: true,
restartNeeded: true
},
eagerLoad: {
2026-03-03 12:14:28 -05:00
description: t("vencord.betterSettings.settings.eagerLoad"),
type: OptionType.BOOLEAN,
default: true,
restartNeeded: true
}
});
interface LayerProps extends HTMLAttributes<HTMLDivElement> {
mode: "SHOWN" | "HIDDEN";
baseLayer?: boolean;
}
function Layer({ mode, baseLayer = false, ...props }: LayerProps) {
const hidden = mode === "HIDDEN";
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => () => {
ComponentDispatch.dispatch("LAYER_POP_START");
ComponentDispatch.dispatch("LAYER_POP_COMPLETE");
}, []);
const node = (
<div
ref={containerRef}
aria-hidden={hidden}
className={cl({
[Classes.layer]: true,
[Classes.baseLayer]: baseLayer,
"stop-animations": hidden
})}
style={{ opacity: hidden ? 0 : undefined }}
{...props}
/>
);
return baseLayer
? node
: <FocusLock containerRef={containerRef}>{node}</FocusLock>;
}
export default definePlugin({
name: "BetterSettings",
description: "Enhances your settings-menu-opening experience",
authors: [Devs.Kyuuhachi],
settings,
start() {
if (settings.store.organizeMenu)
enableStyle(fullHeightStyle);
},
stop() {
disableStyle(fullHeightStyle);
},
patches: [
{
find: "this.renderArtisanalHack()",
replacement: [
{
match: /class (\i)(?= extends \i\.PureComponent.+?static contextType=.+?jsx\)\(\1,\{mode:)/,
replace: "var $1=$self.Layer;class VencordPatchedOldFadeLayer",
predicate: () => settings.store.disableFade
},
{ // Lazy-load contents
match: /createPromise:\(\)=>([^:}]*?),webpackId:"?\d+"?,name:(?!="CollectiblesShop")"[^"]+"/g,
replace: "$&,_:$1",
predicate: () => settings.store.eagerLoad
}
]
},
{ // For some reason standardSidebarView also has a small fade-in
find: 'minimal:"contentColumnMinimal"',
replacement: [
{
match: /(?=\(0,\i\.\i\)\((\i),\{from:\{position:"absolute")/,
replace: "(_cb=>_cb(void 0,$1))||"
},
{
match: /\i\.animated\.div/,
replace: '"div"'
}
],
predicate: () => settings.store.disableFade
},
{ // Disable fade animations for settings menu
2026-02-26 04:29:47 +01:00
find: '"data-mana-component":"layer-modal"',
replacement: [
{
match: /(\i)\.animated\.div(?=,\{"data-mana-component":"layer-modal")/,
replace: '"div"'
},
{
match: /(?<="data-mana-component":"layer-modal"[^}]*?)style:\i,/,
replace: "style:{},"
}
],
predicate: () => settings.store.disableFade
},
{ // Disable initial and exit delay for settings menu
find: "headerId:void 0,headerIdIsManaged:!1",
replacement: {
match: /let (\i)=300/,
replace: "let $1=0"
},
predicate: () => settings.store.disableFade
},
2024-03-28 10:17:05 -03:00
{ // Load menu TOC eagerly
find: "handleOpenSettingsContextMenu=",
replacement: {
2026-02-04 18:25:48 -05:00
match: /(?=handleOpenSettingsContextMenu=.{0,100}?null!=\i&&.{0,100}?(await [^};]*?\)\)))/,
replace: "_vencordBetterSettingsEagerLoad=(async ()=>$1)();"
},
predicate: () => settings.store.eagerLoad
},
{ // Settings cog context menu
find: "#{intl::USER_SETTINGS_ACTIONS_MENU_LABEL}",
predicate: () => settings.store.organizeMenu,
replacement: [
{
match: /children:\[(\i),(?<=\1=.{0,30}\.openUserSettings.+?)/,
replace: "children:[$self.transformSettingsEntries($1),",
},
]
},
],
2025-11-21 13:04:09 +01:00
buildPluginMenuEntries,
buildThemeMenuEntries,
// This is the very outer layer of the entire ui, so we can't wrap this in an ErrorBoundary
// without possibly also catching unrelated errors of children.
//
// Thus, we sanity check webpack modules
Layer(props: LayerProps) {
try {
2026-01-23 17:52:36 -03:00
[FocusLock.$$vencordGetWrappedComponent(), ComponentDispatch, Classes.layer].forEach(e => e.test);
} catch {
new Logger("BetterSettings").error("Failed to find some components");
2024-05-09 03:12:11 +02:00
return props.children;
}
return <Layer {...props} />;
},
transformSettingsEntries(list) {
const items: ReactNode[] = [];
2026-02-25 09:30:12 -05:00
2026-03-03 20:38:40 -05:00
for (const item of list) {
const { key, props } = item;
if (!props) continue;
if (key === "equicord_plugins" || key === "equicord_themes") {
const children = key === "equicord_plugins"
? buildPluginMenuEntries()
: buildThemeMenuEntries();
items.push(
<Menu.MenuItem key={key} label={props.label} id={props.label} {...props}>
{children}
</Menu.MenuItem>
);
} else if (key.endsWith("_section") && props.label) {
items.push(
<Menu.MenuItem key={key} label={props.label} id={props.label}>
{this.transformSettingsEntries(props.children)}
2026-03-03 20:38:40 -05:00
</Menu.MenuItem>
);
} else {
items.push(item);
}
}
return items;
}
});