Equicord/src/equicordplugins/declutter/index.tsx
2026-05-07 11:37:41 -04:00

358 lines
12 KiB
TypeScript

/*
* Vencord, a Discord client mod
* Copyright (c) 2026 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import "./style.css";
import { isPluginEnabled } from "@api/PluginManager";
import { definePluginSettings, migrateOldSettingToNewPlugin, migratePluginSetting, migratePluginSettings } from "@api/Settings";
import { Divider } from "@components/Divider";
import { HeadingSecondary } from "@components/Heading";
import { Notice } from "@components/Notice";
import decor from "@plugins/decor";
import { classNameFactory } from "@utils/css";
import { Devs, EquicordDevs } from "@utils/index";
import { t, Translate } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
import { Alerts } from "@webpack/common";
migratePluginSettings("Declutter", "BetterUserArea", "Anammox");
migrateOldSettingToNewPlugin("Declutter", "removeClanTag", "GuildTagSettings", "hideTags");
const migrationsAnammox = [
["dms", "removeShopAboveDM"],
["quests", "removeQuestsAboveDM"],
["serverBoost", "removeServerBoostInfo"],
["billing", "removeBillingSettings"],
["gift", "removeGiftButton"],
["emojiList", "removeUnavailableEmojiPicker"],
];
for (const [oldKey, newKey] of migrationsAnammox) {
migratePluginSetting("Declutter", newKey, oldKey);
}
const cl = classNameFactory("vc-declutter-");
export const settings = definePluginSettings({
userProfileHeader: {
type: OptionType.COMPONENT,
component: () => SectionSeparator(t("equicord.declutter.headers.userProfile")),
},
removeAvatarDecoration: {
type: OptionType.BOOLEAN,
description: "Remove avatar decorations.",
default: false,
disabled: () => isPluginEnabled("Decor"),
restartNeeded: true,
},
removeNameplate: {
type: OptionType.BOOLEAN,
description: t("equicord.declutter.settings.removeNameplate"),
default: true,
restartNeeded: true,
},
removeProfileEffect: {
type: OptionType.BOOLEAN,
description: t("equicord.declutter.settings.removeProfileEffect"),
default: true,
restartNeeded: true,
},
removeClanTag: {
type: OptionType.BOOLEAN,
description: t("equicord.declutter.settings.removeClanTag"),
default: true,
restartNeeded: true,
},
alwaysShowUsername: {
type: OptionType.BOOLEAN,
description: t("equicord.declutter.settings.alwaysShowUsername"),
default: true,
restartNeeded: true
},
accessibilityNotice: {
type: OptionType.COMPONENT,
component: () => (
<Notice.Info className={cl("accessibility-notice")}>
<Translate i18nKey="equicord.declutter.headers.accessibilityNotice">
Discord already has a built-in username style option in Accessibility settings.
</Translate>
</Notice.Info>
)
},
friendsListHeader: {
type: OptionType.COMPONENT,
component: () => SectionSeparator(t("equicord.declutter.headers.friendsList")),
},
removeShopAboveDM: {
type: OptionType.BOOLEAN,
description: t("equicord.declutter.settings.removeShopAboveDM"),
default: false,
restartNeeded: true,
},
removeQuestsAboveDM: {
type: OptionType.BOOLEAN,
description: t("equicord.declutter.settings.removeQuestsAboveDM"),
default: false,
restartNeeded: true,
},
miscHeader: {
type: OptionType.COMPONENT,
component: () => SectionSeparator(t("equicord.declutter.headers.misc")),
},
removeServerBoostInfo: {
type: OptionType.BOOLEAN,
description: t("equicord.declutter.settings.removeServerBoostInfo"),
default: true,
restartNeeded: true,
},
removeBillingSettings: {
type: OptionType.BOOLEAN,
description: t("equicord.declutter.settings.removeBillingSettings"),
default: true,
restartNeeded: true,
},
removeGiftButton: {
type: OptionType.BOOLEAN,
description: t("equicord.declutter.settings.removeGiftButton"),
default: true,
restartNeeded: true,
},
removeUnavailableEmojiPicker: {
type: OptionType.BOOLEAN,
description: t("equicord.declutter.settings.removeUnavailableEmojiPicker"),
default: true,
restartNeeded: true,
},
removeAudioMenus: {
type: OptionType.BOOLEAN,
description: t("equicord.declutter.settings.removeAudioMenus"),
default: true,
restartNeeded: true
},
removeButtonTooltips: {
type: OptionType.BOOLEAN,
description: t("equicord.declutter.settings.removeButtonTooltips"),
default: false,
restartNeeded: true
},
});
function SectionSeparator(title: string) {
return (
<div className={cl("section-separator")}>
<Divider />
<HeadingSecondary className={cl("section-title")}>
{title}
</HeadingSecondary>
</div>
);
}
export default definePlugin({
name: "Declutter",
description: t("equicord.declutter.description"),
tags: ["Appearance", "Customisation"],
authors: [EquicordDevs.Leon135, Devs.prism, Devs.Kyuuhachi],
start() {
if (isPluginEnabled("Decor") && settings.store.removeAvatarDecoration) {
settings.store.removeAvatarDecoration = false;
Alerts.show({
title: "Declutter",
body: "Avatar decoration removal has been disabled to prevent conflicts with Decor plugin.",
confirmText: "OK",
// @ts-expect-error not typed
confirmVariant: "critical-primary"
});
}
},
settings,
patches: [
{
// Avatar decoration
find: "isAvatarDecorationAnimating:",
replacement: {
match: /(?<=\{avatarDecoration:.{0,40}?)(void 0!==\i\?\i:)\i(?=\)?,canAnimate:)/,
replace: "$1null"
},
predicate: () => settings.store.removeAvatarDecoration && !isPluginEnabled(decor.name),
},
{
// Nameplate
find: ".MINI_PREVIEW,[",
replacement: {
match: /function \i\((\i)\)\{(?=let.{1,5}\{nameplate:\i,)/,
replace: '$&if($1.placement!=="preview"&&$1.placement!=="mini_preview")return null;'
},
predicate: () => settings.store.removeNameplate,
},
{
// Profile banner animation effect
find: "bannerAdjustment,isHovering",
replacement: {
match: /\i=(\i)=>\{(?=.{0,50}\.useReducedMotion\))/,
replace: "$&if(!$1.shopPreview)return null;"
},
predicate: () => settings.store.removeProfileEffect,
},
{
// Clan tag
find: ".GuildFeatures.GUILD_TAGS)",
replacement: {
match: /(?<=\.profile\?\.badge.{0,50}\i\)\{)/,
replace: "return false;"
},
predicate: () => settings.store.removeClanTag,
},
{
// Always show username
find: ".DISPLAY_NAME_STYLES_COACHMARK)",
replacement: {
match: /hoverText:(\i),forceHover:\i,children:/g,
replace: "hoverText:$1,forceHover:!0,children:"
},
predicate: () => settings.store.alwaysShowUsername
},
{
// Button tooltips in user area
find: '"MicrophoneButton"',
replacement: [
{
// Button tooltips
match: /:\{tooltipText:\i\};/,
replace: ":{tooltipText:void 0};",
predicate: () => settings.store.removeButtonTooltips
},
{
// Audio menus
match: /(?<=#{intl::MUTE}\),)className:\i\.\i,/,
replace: "",
predicate: () => settings.store.removeAudioMenus
},
{
// Audio menus
match: /,\(0,\i\.jsxs?\)\(\i\.\i,\{.{0,600}#{intl::ACCOUNT_INPUT_OPTIONS}\)\}\)(?=\])/,
replace: "",
predicate: () => settings.store.removeAudioMenus
},
],
},
{
// Button tooltips in right click audio settings
find: "#{intl::f+DDY/::raw},{outputDeviceName",
replacement: [
{
// Button tooltips
match: /(?<=role:"switch",)tooltipText:\i\}/,
replace: "tooltipText:void 0}",
predicate: () => settings.store.removeButtonTooltips
},
{
// Audio menus
match: /(?<=#{intl::DEAFEN}\),)className:\i\.\i,/,
replace: "",
predicate: () => settings.store.removeAudioMenus
},
{
// Audio menus
match: /,\(0,\i\.jsxs?\)\(\i\.\i,\{.{0,650}#{intl::ACCOUNT_OUTPUT_OPTIONS}\)\}\)(?=\])/,
replace: "",
predicate: () => settings.store.removeAudioMenus
}
],
},
{
// ? Another button tooltips
find: "#{intl::USER_SETTINGS_WITH_BUILD_OVERRIDE}",
replacement: {
match: /tooltipText:\i,tooltipPositionKey/,
replace: "tooltipText:void 0,tooltipPositionKey"
},
predicate: () => settings.store.removeButtonTooltips
},
{
// Above DMs section, shops and quests
find: 'tutorialId:"direct-messages"',
replacement: [
{
match: /"nitro-tab-group"\)/,
replace: "$&&&undefined",
predicate: () => settings.store.removeShopAboveDM,
},
{
match: /"discord-shop"\)/,
replace: "$&&&undefined",
predicate: () => settings.store.removeShopAboveDM
},
{
match: /"quests"\)/,
replace: "$&&&undefined",
predicate: () => settings.store.removeQuestsAboveDM
},
],
},
{
// Above DMs section, keyboard navigation
find: ".hasLibraryApplication()&&!",
replacement: [
{
match: /\i\.\i\.APPLICATION_STORE,/,
replace: "/*$&*/",
},
{
match: /\i\.\i\.COLLECTIBLES_SHOP,/,
replace: "/*$&*/",
},
],
predicate: () => settings.store.removeShopAboveDM,
},
{
// Channel list server boost progress bar
find: "useGuildActionRow",
replacement: {
match: /(GUILD_NEW_MEMBER_ACTIONS_PROGRESS_BAR\)):(\i(?:\.premiumProgressBarEnabled)?)/,
replace: "$1:null"
},
predicate: () => settings.store.removeServerBoostInfo,
},
{
// Billing settings
find: ".BILLING_SECTION,",
replacement: {
match: /(?<=buildLayout:\(\)=>)\[.+?\]/,
replace: "[]",
},
predicate: () => settings.store.removeBillingSettings,
},
{
// Gift button
find: '"sticker")',
replacement: {
match: /&&\i\.push\(\([^&]*?,"gift"\)\)/,
replace: "",
},
predicate: () => settings.store.removeGiftButton,
},
{
// Emoji list
find: "#{intl::EMOJI_PICKER_EXPAND_EMOJI_SECTION}),size:",
replacement: {
match: /(\i)=\i\|\|!\i&&\i.\i.isEmojiCategoryNitroLocked\(\{[^}]*\}\);/,
replace: "$&$1||"
},
predicate: () => settings.store.removeUnavailableEmojiPicker,
},
{
// Emoji category list
find: "#{intl::EMOJI_CATEGORY_TOP_GUILD_EMOJI},{guildName:",
replacement: {
match: /(?<=(\i)\.unshift\((\i)\):)(?=\1\.push\(\2\))/,
replace: "$2.isNitroLocked||"
},
predicate: () => settings.store.removeUnavailableEmojiPicker,
}
],
});