Equicord/src/equicordplugins/declutter/index.tsx
2026-03-02 17:24:23 -05:00

335 lines
11 KiB
TypeScript

/*
* Vencord, a Discord client mod
* Copyright (c) 2026 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { definePluginSettings, migratePluginSetting, migratePluginSettings } from "@api/Settings";
import { HeadingSecondary } from "@components/Heading";
import { Devs, EquicordDevs } from "@utils/index";
import { t } from "@utils/translation";
import definePlugin, { OptionType } from "@utils/types";
migratePluginSettings("Declutter", "BetterUserArea", "Anammox");
const migrationsAnammox = [
["dms", "removeShopAboveDM"],
["quests", "removeQuestsAboveDM"],
["serverBoost", "removeServerBoostInfo"],
["billing", "removeBillingSettings"],
["gift", "removeGiftButton"],
["emojiList", "removeUnavailableEmojiPicker"],
];
for (const [oldKey, newKey] of migrationsAnammox) {
migratePluginSetting("Declutter", newKey, oldKey);
}
export const settings = definePluginSettings({
userProfileHeader: {
type: OptionType.COMPONENT,
component: () => SectionSeparator(t("declutter.headers.userProfile")),
},
removeAvatarDecoration: {
type: OptionType.BOOLEAN,
description: t("declutter.settings.removeAvatarDecoration"),
default: true,
restartNeeded: true,
},
removeNameplate: {
type: OptionType.BOOLEAN,
description: t("declutter.settings.removeNameplate"),
default: true,
restartNeeded: true,
},
removeProfileEffect: {
type: OptionType.BOOLEAN,
description: t("declutter.settings.removeProfileEffect"),
default: true,
restartNeeded: true,
},
removeClanTag: {
type: OptionType.BOOLEAN,
description: t("declutter.settings.removeClanTag"),
default: true,
restartNeeded: true,
},
alwaysShowUsername: {
type: OptionType.BOOLEAN,
description: t("declutter.settings.alwaysShowUsername"),
default: true,
restartNeeded: true
},
removeUsernameStyles: {
type: OptionType.BOOLEAN,
description: t("declutter.settings.removeUsernameStyles"),
default: true,
restartNeeded: true
},
friendsListHeader: {
type: OptionType.COMPONENT,
component: () => SectionSeparator(t("declutter.headers.friendsList")),
},
removeShopAboveDM: {
type: OptionType.BOOLEAN,
description: t("declutter.settings.removeShopAboveDM"),
default: true,
restartNeeded: true,
},
removeQuestsAboveDM: {
type: OptionType.BOOLEAN,
description: t("declutter.settings.removeQuestsAboveDM"),
default: false,
restartNeeded: true,
},
miscHeader: {
type: OptionType.COMPONENT,
component: () => SectionSeparator(t("declutter.headers.misc")),
},
removeServerBoostInfo: {
type: OptionType.BOOLEAN,
description: t("declutter.settings.removeServerBoostInfo"),
default: true,
restartNeeded: true,
},
removeBillingSettings: {
type: OptionType.BOOLEAN,
description: t("declutter.settings.removeBillingSettings"),
default: true,
restartNeeded: true,
},
removeGiftButton: {
type: OptionType.BOOLEAN,
description: t("declutter.settings.removeGiftButton"),
default: true,
restartNeeded: true,
},
removeUnavailableEmojiPicker: {
type: OptionType.BOOLEAN,
description: t("declutter.settings.removeUnavailableEmojiPicker"),
default: true,
restartNeeded: true,
},
removeAudioMenus: {
type: OptionType.BOOLEAN,
description: t("declutter.settings.removeAudioMenus"),
default: true,
restartNeeded: true
},
removeButtonTooltips: {
type: OptionType.BOOLEAN,
description: t("declutter.settings.removeButtonTooltips"),
default: false,
restartNeeded: true
},
});
function SectionSeparator(title: string) {
return (
<>
<hr style={{ width: "100%" }} />
<HeadingSecondary>{title}</HeadingSecondary>
<hr style={{ width: "100%" }} />
</>
);
}
export default definePlugin({
name: "Declutter",
description: t("declutter.description"),
authors: [EquicordDevs.Leon135, Devs.prism, Devs.Kyuuhachi],
settings,
patches: [
{
// Avatar decoration
find: "getAvatarDecorationURL:",
replacement: {
match: /(?<=function \i\((\i)\){)(?=.{0,150}let{avatarDecoration)/,
replace: "$&return null;"
},
predicate: () => settings.store.removeAvatarDecoration,
},
{
// Nameplate
find: "#{intl::AVATAR_MALLOW}",
replacement: {
match: /function \i\(\i\)\{(?=.{0,25}skuId:)/,
replace: "$&return null;"
},
predicate: () => settings.store.removeNameplate,
},
{
// Profile banner animation effect
find: "bannerAdjustment,noBorderRadius",
replacement: {
match: /\i=\i=>\{(?=.{0,50}\.useReducedMotion\))/,
replace: "$&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,
},
{
// Username styles and allways show username
find: ".NITRO_PRIVACY_PERK_BETA_COACHMARK));",
replacement: [
{
match: /displayNameStyles:(\i),/,
replace: "displayNameStyles:void 0,",
predicate: () => settings.store.removeUsernameStyles
},
{
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,
}
],
});