From a108448255261c9bffa0911386a35d6e2ba93117 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 7 Feb 2024 20:39:02 -0300 Subject: [PATCH 01/79] Improve DisableDMCallIdle --- src/plugins/disableDMCallIdle/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/disableDMCallIdle/index.ts b/src/plugins/disableDMCallIdle/index.ts index bce119d01..5738dc238 100644 --- a/src/plugins/disableDMCallIdle/index.ts +++ b/src/plugins/disableDMCallIdle/index.ts @@ -27,8 +27,8 @@ export default definePlugin({ { find: ".Messages.BOT_CALL_IDLE_DISCONNECT", replacement: { - match: /(?<=function \i\(\){)(?=.{1,120}\.Messages\.BOT_CALL_IDLE_DISCONNECT)/, - replace: "return;" + match: /,?(?=this\.idleTimeout=new \i\.Timeout)/, + replace: ";return;" } } ] From 935d0a0a03a7e713014fc1019b1b36d5f1989980 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 8 Feb 2024 15:33:49 -0300 Subject: [PATCH 02/79] Fix broken patches and finds --- src/plugins/showHiddenChannels/index.tsx | 4 ++-- src/plugins/typingIndicator/index.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/showHiddenChannels/index.tsx b/src/plugins/showHiddenChannels/index.tsx index 906bed504..919f3f3c5 100644 --- a/src/plugins/showHiddenChannels/index.tsx +++ b/src/plugins/showHiddenChannels/index.tsx @@ -29,7 +29,7 @@ import type { Channel, Role } from "discord-types/general"; import HiddenChannelLockScreen from "./components/HiddenChannelLockScreen"; -const ChannelListClasses = findByPropsLazy("channelEmoji", "unread", "icon"); +const ChannelListClasses = findByPropsLazy("modeMuted", "modeSelected", "unread", "icon"); const enum ShowMode { LockIcon, @@ -162,7 +162,7 @@ export default definePlugin({ }, // Add the hidden eye icon if the channel is hidden { - match: /\i\.children.+?:null(?<=,channel:(\i).+?)/, + match: /\.name\),.{0,120}\.children.+?:null(?<=,channel:(\i).+?)/, replace: (m, channel) => `${m},$self.isHiddenChannel(${channel})?$self.HiddenChannelIcon():null` }, // Make voice channels also appear as muted if they are muted diff --git a/src/plugins/typingIndicator/index.tsx b/src/plugins/typingIndicator/index.tsx index 171c560d8..280301480 100644 --- a/src/plugins/typingIndicator/index.tsx +++ b/src/plugins/typingIndicator/index.tsx @@ -133,7 +133,7 @@ export default definePlugin({ { find: "UNREAD_IMPORTANT:", replacement: { - match: /channel:(\i).{0,100}?channelEmoji,.{0,250}?\.children.{0,50}?:null/, + match: /\.name\),.{0,120}\.children.+?:null(?<=,channel:(\i).+?)/, replace: "$&,$self.TypingIndicator($1.id)" } }, From f1f0da4a9db09625a50a812285c4479d5e692036 Mon Sep 17 00:00:00 2001 From: Klen_list <49125075+klen-list@users.noreply.github.com> Date: Thu, 8 Feb 2024 21:55:48 +0300 Subject: [PATCH 03/79] fixYoutubeEmbeds: fix when youtube decides to use http without s (#2155) Co-authored-by: V --- src/plugins/fixYoutubeEmbeds.desktop/native.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/fixYoutubeEmbeds.desktop/native.ts b/src/plugins/fixYoutubeEmbeds.desktop/native.ts index 5419d4ad8..d5c2df363 100644 --- a/src/plugins/fixYoutubeEmbeds.desktop/native.ts +++ b/src/plugins/fixYoutubeEmbeds.desktop/native.ts @@ -17,7 +17,7 @@ app.on("browser-window-created", (_, win) => { frame.executeJavaScript(` new MutationObserver(() => { if( - document.querySelector('div.ytp-error-content-wrap-subreason a[href^="https://www.youtube.com/watch?v="]') + document.querySelector('div.ytp-error-content-wrap-subreason a[href*="www.youtube.com/watch?v="]') ) location.reload() }).observe(document.body, { childList: true, subtree:true }); `); From b2c047390e1e383758aa6e996b5c255a1e31d003 Mon Sep 17 00:00:00 2001 From: sappho Date: Fri, 9 Feb 2024 17:31:02 -0500 Subject: [PATCH 04/79] FakeNitro: Send fake links using Discord hyperlink markdown feature (#2158) --- src/plugins/fakeNitro/index.ts | 53 ++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/src/plugins/fakeNitro/index.ts b/src/plugins/fakeNitro/index.ts index 0b96412fd..2caecc45b 100644 --- a/src/plugins/fakeNitro/index.ts +++ b/src/plugins/fakeNitro/index.ts @@ -108,6 +108,7 @@ const enum FakeNoticeType { const fakeNitroEmojiRegex = /\/emojis\/(\d+?)\.(png|webp|gif)/; const fakeNitroStickerRegex = /\/stickers\/(\d+?)\./; const fakeNitroGifStickerRegex = /\/attachments\/\d+?\/\d+?\/(\d+?)\.gif/; +const hyperLinkRegex = /\[.+?\]\((https?:\/\/.+?)\)/; const settings = definePluginSettings({ enableEmojiBypass: { @@ -447,13 +448,23 @@ export default definePlugin({ trimContent(content: Array) { const firstContent = content[0]; - if (typeof firstContent === "string") content[0] = firstContent.trimStart(); - if (content[0] === "") content.shift(); + if (typeof firstContent === "string") { + content[0] = firstContent.trimStart(); + content[0] || content.shift(); + } else if (firstContent?.type === "span") { + firstContent.props.children = firstContent.props.children.trimStart(); + firstContent.props.children || content.shift(); + } const lastIndex = content.length - 1; const lastContent = content[lastIndex]; - if (typeof lastContent === "string") content[lastIndex] = lastContent.trimEnd(); - if (content[lastIndex] === "") content.pop(); + if (typeof lastContent === "string") { + content[lastIndex] = lastContent.trimEnd(); + content[lastIndex] || content.pop(); + } else if (lastContent?.type === "span") { + lastContent.props.children = lastContent.props.children.trimEnd(); + lastContent.props.children || content.pop(); + } }, clearEmptyArrayItems(array: Array) { @@ -465,7 +476,7 @@ export default definePlugin({ }, patchFakeNitroEmojisOrRemoveStickersLinks(content: Array, inline: boolean) { - // If content has more than one child or it's a single ReactElement like a header or list + // If content has more than one child or it's a single ReactElement like a header, list or span if ((content.length > 1 || typeof content[0]?.type === "string") && !settings.store.transformCompoundSentence) return content; let nextIndex = content.length; @@ -574,7 +585,7 @@ export default definePlugin({ itemsToMaybePush.push(...message.attachments.filter(attachment => attachment.content_type === "image/gif").map(attachment => attachment.url)); for (const item of itemsToMaybePush) { - if (!settings.store.transformCompoundSentence && !item.startsWith("http")) continue; + if (!settings.store.transformCompoundSentence && !item.startsWith("http") && !hyperLinkRegex.test(item)) continue; const imgMatch = item.match(fakeNitroStickerRegex); if (imgMatch) { @@ -619,8 +630,7 @@ export default definePlugin({ case "image": { if ( !settings.store.transformCompoundSentence - && !contentItems.includes(embed.url!) - && !contentItems.includes(embed.image?.proxyURL!) + && !contentItems.some(item => item === embed.url! || item.match(hyperLinkRegex)?.[1] === embed.url!) ) return false; if (settings.store.transformEmojis) { @@ -795,12 +805,16 @@ export default definePlugin({ if (sticker.format_type === StickerType.GIF && link.includes(".png")) { link = link.replace(".png", ".gif"); } + if (sticker.format_type === StickerType.APNG) { this.sendAnimatedSticker(link, sticker.id, channelId); return { cancel: true }; } else { + const url = new URL(link); + url.searchParams.set("name", sticker.name); + + messageObj.content += `${getWordBoundary(messageObj.content, messageObj.content.length - 1)}[${sticker.name}](${url})`; extra.stickers!.length = 0; - messageObj.content += ` ${link}&name=${encodeURIComponent(sticker.name)}`; } } @@ -813,12 +827,13 @@ export default definePlugin({ if (emoji.guildId === guildId && !emoji.animated) continue; const emojiString = `<${emoji.animated ? "a" : ""}:${emoji.originalName || emoji.name}:${emoji.id}>`; - const url = emoji.url.replace(/\?size=\d+/, "?" + new URLSearchParams({ - size: Settings.plugins.FakeNitro.emojiSize, - name: encodeURIComponent(emoji.name) - })); + + const url = new URL(emoji.url); + url.searchParams.set("size", settings.store.emojiSize.toString()); + url.searchParams.set("name", emoji.name); + messageObj.content = messageObj.content.replace(emojiString, (match, offset, origStr) => { - return `${getWordBoundary(origStr, offset - 1)}${url}${getWordBoundary(origStr, offset + match.length)}`; + return `${getWordBoundary(origStr, offset - 1)}[:${emoji.name}:](${url})${getWordBoundary(origStr, offset + match.length)}`; }); } } @@ -840,11 +855,11 @@ export default definePlugin({ if (emoji.available !== false && canUseEmotes) return emojiStr; if (emoji.guildId === guildId && !emoji.animated) return emojiStr; - const url = emoji.url.replace(/\?size=\d+/, "?" + new URLSearchParams({ - size: Settings.plugins.FakeNitro.emojiSize, - name: encodeURIComponent(emoji.name) - })); - return `${getWordBoundary(origStr, offset - 1)}${url}${getWordBoundary(origStr, offset + emojiStr.length)}`; + const url = new URL(emoji.url); + url.searchParams.set("size", settings.store.emojiSize.toString()); + url.searchParams.set("name", emoji.name); + + return `${getWordBoundary(origStr, offset - 1)}[:${emoji.name}:](${url})${getWordBoundary(origStr, offset + emojiStr.length)}`; }); }); }, From 237f0804717d0fe9bef1f33c0eea9f7682dba884 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 9 Feb 2024 17:59:40 -0300 Subject: [PATCH 05/79] DisableDMCallIdle -> DisableCallIdle; add more functionality --- .../{disableDMCallIdle => disableCallIdle}/index.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) rename src/plugins/{disableDMCallIdle => disableCallIdle}/index.ts (74%) diff --git a/src/plugins/disableDMCallIdle/index.ts b/src/plugins/disableCallIdle/index.ts similarity index 74% rename from src/plugins/disableDMCallIdle/index.ts rename to src/plugins/disableCallIdle/index.ts index 5738dc238..904d53b03 100644 --- a/src/plugins/disableDMCallIdle/index.ts +++ b/src/plugins/disableCallIdle/index.ts @@ -16,12 +16,14 @@ * along with this program. If not, see . */ +import { migratePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; +migratePluginSettings("DisableCallIdle", "DisableDMCallIdle"); export default definePlugin({ - name: "DisableDMCallIdle", - description: "Disables automatically getting kicked from a DM voice call after 3 minutes.", + name: "DisableCallIdle", + description: "Disables automatically getting kicked from a DM voice call after 3 minutes and being moved to an AFK voice channel.", authors: [Devs.Nuckyz], patches: [ { @@ -30,6 +32,13 @@ export default definePlugin({ match: /,?(?=this\.idleTimeout=new \i\.Timeout)/, replace: ";return;" } + }, + { + find: "handleIdleUpdate(){", + replacement: { + match: /(?<=_initialize\(\){)/, + replace: "return;" + } } ] }); From 558c2a0b2ef46cb19ecabef64e11f0b11ca9bd32 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 9 Feb 2024 19:46:10 -0300 Subject: [PATCH 06/79] Make PermissionsViewer and ShowHiddenChannels default dropdown change when clicked --- .../permissionsViewer/components/UserPermissions.tsx | 1 + .../components/HiddenChannelLockScreen.tsx | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/plugins/permissionsViewer/components/UserPermissions.tsx b/src/plugins/permissionsViewer/components/UserPermissions.tsx index 3c6767713..bcd6bdf07 100644 --- a/src/plugins/permissionsViewer/components/UserPermissions.tsx +++ b/src/plugins/permissionsViewer/components/UserPermissions.tsx @@ -104,6 +104,7 @@ function UserPermissionsComponent({ guild, guildMember, showBorder }: { guild: G guildMember.nick || UserStore.getUser(guildMember.userId).username ) } + onDropDownClick={state => settings.store.defaultPermissionsDropdownState = !state} defaultState={settings.store.defaultPermissionsDropdownState} buttons={[ ( diff --git a/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx b/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx index 87946110d..7904cd10b 100644 --- a/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx +++ b/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx @@ -120,7 +120,7 @@ const VideoQualityModesToNames = { const HiddenChannelLogo = "/assets/433e3ec4319a9d11b0cbe39342614982.svg"; function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) { - const [viewAllowedUsersAndRoles, setViewAllowedUsersAndRoles] = useState(settings.store.defaultAllowedUsersAndRolesDropdownState); + const { defaultAllowedUsersAndRolesDropdownState } = settings.use(["defaultAllowedUsersAndRolesDropdownState"]); const [permissions, setPermissions] = useState([]); const { @@ -301,19 +301,19 @@ function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) { )} Allowed users and roles: - + {({ onMouseLeave, onMouseEnter }) => ( + + + ); +} + +let allowedIdsPushID: ((id: string) => boolean) | null = null; + +function AllowedIdsComponent(props: { setValue: (value: string) => void; }) { + const [allowedIds, setAllowedIds] = useState(settings.store.allowedIds ?? ""); + + allowedIdsPushID = (id: string) => { + const currentIds = new Set(allowedIds.split(",").map(id => id.trim()).filter(Boolean)); + + const isAlreadyAdded = currentIds.has(id) || (currentIds.add(id), false); + + const ids = Array.from(currentIds).join(", "); + setAllowedIds(ids); + props.setValue(ids); + + return isAlreadyAdded; + }; + + useEffect(() => () => { + allowedIdsPushID = null; + }, []); + + function handleChange(newValue: string) { + setAllowedIds(newValue); + props.setValue(newValue); + } + + return ( + + Allowed List + Comma separated list of activity IDs to allow (Useful for allowing RPC activities and CustomRPC) + + + ); +} + +const settings = definePluginSettings({ + importCustomRPC: { + type: OptionType.COMPONENT, + description: "", + component: () => + }, + allowedIds: { + type: OptionType.COMPONENT, + description: "", + default: "", + onChange(newValue: string) { + const ids = new Set(newValue.split(",").map(id => id.trim()).filter(Boolean)); + settings.store.allowedIds = Array.from(ids).join(", "); + }, + component: props => + }, + ignorePlaying: { + type: OptionType.BOOLEAN, + description: "Ignore all playing activities (These are usually game and RPC activities)", + default: false + }, + ignoreStreaming: { + type: OptionType.BOOLEAN, + description: "Ignore all streaming activities", + default: false + }, + ignoreListening: { + type: OptionType.BOOLEAN, + description: "Ignore all listening activities (These are usually spotify activities)", + default: false + }, + ignoreWatching: { + type: OptionType.BOOLEAN, + description: "Ignore all watching activities", + default: false + }, + ignoreCompeting: { + type: OptionType.BOOLEAN, + description: "Ignore all competing activities (These are normally special game activities)", + default: false + } +}).withPrivateSettings<{ ignoredActivities: IgnoredActivity[]; }>(); @@ -77,10 +185,26 @@ function getIgnoredActivities() { return settings.store.ignoredActivities ??= []; } +function isActivityTypeIgnored(type: number, id?: string) { + if (id && settings.store.allowedIds.includes(id)) { + return false; + } + + switch (type) { + case 0: return settings.store.ignorePlaying; + case 1: return settings.store.ignoreStreaming; + case 2: return settings.store.ignoreListening; + case 3: return settings.store.ignoreWatching; + case 5: return settings.store.ignoreCompeting; + } + + return false; +} + export default definePlugin({ name: "IgnoreActivities", authors: [Devs.Nuckyz], - description: "Ignore activities from showing up on your status ONLY. You can configure which ones are ignored from the Registered Games and Activities tabs.", + description: "Ignore activities from showing up on your status ONLY. You can configure which ones are specifically ignored from the Registered Games and Activities tabs, or use the general settings below.", settings, @@ -141,13 +265,17 @@ export default definePlugin({ }, isActivityNotIgnored(props: { type: number; application_id?: string; name?: string; }) { - if (props.type === 0 || props.type === 3) { - if (props.application_id != null) return !getIgnoredActivities().some(activity => activity.id === props.application_id); - else { - const exePath = RunningGameStore.getRunningGames().find(game => game.name === props.name)?.exePath; - if (exePath) return !getIgnoredActivities().some(activity => activity.id === exePath); + if (isActivityTypeIgnored(props.type, props.application_id)) return false; + + if (props.application_id != null) { + return !getIgnoredActivities().some(activity => activity.id === props.application_id) || settings.store.allowedIds.includes(props.application_id); + } else { + const exePath = RunningGameStore.getRunningGames().find(game => game.name === props.name)?.exePath; + if (exePath) { + return !getIgnoredActivities().some(activity => activity.id === exePath); } } + return true; }, From 806960f1c640822e190e2c7f355bda38d596601b Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 29 Feb 2024 23:15:19 -0300 Subject: [PATCH 40/79] ClientTheme: do not use lodash on start method --- src/plugins/clientTheme/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/clientTheme/index.tsx b/src/plugins/clientTheme/index.tsx index 5d8cd4dc0..4e07daf42 100644 --- a/src/plugins/clientTheme/index.tsx +++ b/src/plugins/clientTheme/index.tsx @@ -12,7 +12,7 @@ import { Margins } from "@utils/margins"; import { classes } from "@utils/misc"; import definePlugin, { OptionType, StartAt } from "@utils/types"; import { findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack"; -import { Button, Forms, lodash as _, useStateFromStores } from "@webpack/common"; +import { Button, Forms, useStateFromStores } from "@webpack/common"; const ColorPicker = findComponentByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); @@ -200,8 +200,8 @@ function captureOne(str, regex) { return (result === null) ? null : result[1]; } -function mapReject(arr, mapFunc, rejectFunc = _.isNull) { - return _.reject(arr.map(mapFunc), rejectFunc); +function mapReject(arr, mapFunc) { + return arr.map(mapFunc).filter(Boolean); } function updateColorVars(color: string) { From 553a48b6ce50a107e36e16f9914e723717b82c96 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 1 Mar 2024 01:20:12 -0300 Subject: [PATCH 41/79] FakeNitro: Fix hyperlink text setting for stickers --- src/plugins/fakeNitro/index.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx index 1cf1e536f..5d662feed 100644 --- a/src/plugins/fakeNitro/index.tsx +++ b/src/plugins/fakeNitro/index.tsx @@ -162,7 +162,7 @@ const settings = definePluginSettings({ default: true }, hyperLinkText: { - description: "What text the hyperlink should use. {{NAME}} will be replaced with the emoji name.", + description: "What text the hyperlink should use. {{NAME}} will be replaced with the emoji/sticker name.", type: OptionType.STRING, default: "{{NAME}}" } @@ -864,7 +864,9 @@ export default definePlugin({ const url = new URL(link); url.searchParams.set("name", sticker.name); - messageObj.content += `${getWordBoundary(messageObj.content, messageObj.content.length - 1)}${s.useHyperLinks ? `[${sticker.name}](${url})` : url}`; + const linkText = s.hyperLinkText.replaceAll("{{NAME}}", sticker.name); + + messageObj.content += `${getWordBoundary(messageObj.content, messageObj.content.length - 1)}${s.useHyperLinks ? `[${linkText}](${url})` : url}`; extra.stickers!.length = 0; } } From 23ff82fa627807fcbcdea03e88b3f21f65d007ca Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 1 Mar 2024 05:33:20 -0300 Subject: [PATCH 42/79] FakeNitro: Remove extra space in modal --- src/plugins/fakeNitro/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx index 5d662feed..8cf2492a8 100644 --- a/src/plugins/fakeNitro/index.tsx +++ b/src/plugins/fakeNitro/index.tsx @@ -791,8 +791,8 @@ export default definePlugin({ title: "Hold on!", body:
- You are trying to send/edit a message that contains a FakeNitro emoji or sticker - , however you do not have permissions to embed links in the current channel. + You are trying to send/edit a message that contains a FakeNitro emoji or sticker, + however you do not have permissions to embed links in the current channel. Are you sure you want to send this message? Your FakeNitro items will appear as a link only. From 4f0c0a12dc1832fbff3a71e05b0eb503e6b9c9c2 Mon Sep 17 00:00:00 2001 From: dolfies Date: Tue, 5 Mar 2024 17:38:49 -0500 Subject: [PATCH 43/79] feat(plugin): ResurrectHome (#2232) --- src/plugins/resurrectHome/README.md | 5 ++ src/plugins/resurrectHome/index.tsx | 125 ++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 src/plugins/resurrectHome/README.md create mode 100644 src/plugins/resurrectHome/index.tsx diff --git a/src/plugins/resurrectHome/README.md b/src/plugins/resurrectHome/README.md new file mode 100644 index 000000000..215ef816c --- /dev/null +++ b/src/plugins/resurrectHome/README.md @@ -0,0 +1,5 @@ +# ResurrectHome + +Brings back the phased out [Server Home](https://support.discord.com/hc/en-us/articles/6156116949911-Server-Home-Beta) feature! + +![](https://private-user-images.githubusercontent.com/47677887/309572891-a9ee7354-9e5e-4b81-8faf-304d9c44f512.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MDk0OTE5MTIsIm5iZiI6MTcwOTQ5MTYxMiwicGF0aCI6Ii80NzY3Nzg4Ny8zMDk1NzI4OTEtYTllZTczNTQtOWU1ZS00YjgxLThmYWYtMzA0ZDljNDRmNTEyLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDAzMDMlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwMzAzVDE4NDY1MlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTBhYzUxMWY1MzQxNTA4NDE1MWU0YjAxNzM1NzI1YWJkMTNiZmNkNjRmYTRkZDg1ZDE5NzdkMjM0MGVjMDA0OWQmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.TPYWPRWHTJstfviT9HOaBWFkbBhokyxiDC-gOVL2dqs) diff --git a/src/plugins/resurrectHome/index.tsx b/src/plugins/resurrectHome/index.tsx new file mode 100644 index 000000000..91ed87a02 --- /dev/null +++ b/src/plugins/resurrectHome/index.tsx @@ -0,0 +1,125 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2023 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 . +*/ + +import { addContextMenuPatch, findGroupChildrenByChildId, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; +import { definePluginSettings } from "@api/Settings"; +import { Devs } from "@utils/constants"; +import definePlugin, { OptionType } from "@utils/types"; +import { ContextMenuApi, Menu } from "@webpack/common"; + +const settings = definePluginSettings({ + forceServerHome: { + type: OptionType.BOOLEAN, + description: "Force the Server Guide to be the Server Home tab when it is enabled.", + default: false + } +}); + +const contextMenuPatch: NavContextMenuPatchCallback = (children, props) => () => { + if (!props?.guild) return; + + const group = findGroupChildrenByChildId("hide-muted-channels", children); + + group?.unshift( + { + settings.store.forceServerHome = !settings.store.forceServerHome; + ContextMenuApi.closeContextMenu(); + }} + /> + + ); +}; + +export default definePlugin({ + name: "ResurrectHome", + description: "Re-enables the Server Home tab when there isn't a Server Guide. Also has an option to force the Server Home over the Server Guide, which is accessible through right-clicking the Server Guide.", + authors: [Devs.Dolfies, Devs.Nuckyz], + settings, + + patches: [ + // Force home deprecation override + { + find: "GuildFeatures.GUILD_HOME_DEPRECATION_OVERRIDE", + all: true, + replacement: [ + { + match: /\i\.hasFeature\(\i\.GuildFeatures\.GUILD_HOME_DEPRECATION_OVERRIDE\)/g, + replace: "true" + } + ], + }, + // Disable feedback prompts + { + find: "GuildHomeFeedbackExperiment.definition.id", + replacement: [ + { + match: /return{showFeedback:\i,setOnDismissedFeedback:(\i)}/, + replace: "return{showFeedback:false,setOnDismissedFeedback:$1}" + } + ] + }, + // This feature was never finished, so the patch is disabled + + // Enable guild feed render mode selector + // { + // find: "2022-01_home_feed_toggle", + // replacement: [ + // { + // match: /showSelector:!1/, + // replace: "showSelector:true" + // } + // ] + // }, + + // Fix focusMessage clearing previously cached messages and causing a loop when fetching messages around home messages + { + find: '"MessageActionCreators"', + replacement: { + match: /(?<=focusMessage\(\i\){.+?)(?=focus:{messageId:(\i)})/, + replace: "before:$1," + } + }, + // Force Server Home instead of Server Guide + { + find: "61eef9_2", + replacement: { + match: /(?<=getMutableGuildChannelsForGuild\(\i\)\);)(?=if\(null==\i\|\|)/, + replace: "if($self.useForceServerHome())return false;" + } + } + ], + + start() { + addContextMenuPatch("guild-context", contextMenuPatch); + }, + + stop() { + removeContextMenuPatch("guild-context", contextMenuPatch); + }, + + useForceServerHome() { + const { forceServerHome } = settings.use(["forceServerHome"]); + + return forceServerHome; + } +}); From 612fdf8952c8b3ad80e16552a146088deadecb53 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 6 Mar 2024 07:11:14 -0300 Subject: [PATCH 44/79] FakeNitro: Fix trimming wrong content --- src/plugins/fakeNitro/index.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx index 8cf2492a8..68560817c 100644 --- a/src/plugins/fakeNitro/index.tsx +++ b/src/plugins/fakeNitro/index.tsx @@ -585,13 +585,15 @@ export default definePlugin({ for (const [index, child] of children.entries()) children[index] = modifyChild(child); children = this.clearEmptyArrayItems(children); - this.trimContent(children); return children; }; try { - return modifyChildren(lodash.cloneDeep(content)); + const newContent = modifyChildren(lodash.cloneDeep(content)); + this.trimContent(newContent); + + return newContent; } catch (err) { new Logger("FakeNitro").error(err); return content; From 42a9fa2d47d2e0d0e4233436086e93f0b3dcb1af Mon Sep 17 00:00:00 2001 From: Kyuuhachi <1547062+Kyuuhachi@users.noreply.github.com> Date: Thu, 7 Mar 2024 11:06:24 +0100 Subject: [PATCH 45/79] Refactor ContextMenuAPI (#2236) --- src/api/ContextMenu.ts | 53 ++++++++++++++++------- src/plugins/_api/contextMenu.ts | 6 +-- src/plugins/_core/settings.tsx | 12 ++--- src/plugins/biggerStreamPreview/index.tsx | 14 +++--- src/plugins/copyUserURLs/index.tsx | 15 +++---- src/plugins/emoteCloner/index.tsx | 18 +++----- src/plugins/imageZoom/index.tsx | 23 +++++----- src/plugins/index.ts | 17 +++++++- src/plugins/messageLogger/index.tsx | 13 +++--- src/plugins/permissionsViewer/index.tsx | 27 ++++-------- src/plugins/pinDms/contextMenus.tsx | 19 +++----- src/plugins/pinDms/index.tsx | 6 +-- src/plugins/resurrectHome/index.tsx | 52 ++++++++++------------ src/plugins/reverseImageSearch/index.tsx | 18 +++----- src/plugins/reviewDB/index.tsx | 13 +++--- src/plugins/searchReply/index.tsx | 19 ++++---- src/plugins/serverProfile/index.tsx | 14 +++--- src/plugins/translate/index.tsx | 9 ++-- src/plugins/unsuppressEmbeds/index.tsx | 15 +++---- src/plugins/vencordToolbox/index.tsx | 9 ++-- src/plugins/viewIcons/index.tsx | 17 +++----- src/plugins/viewRaw/index.tsx | 22 ++++------ src/plugins/voiceMessages/index.tsx | 45 +++++++++---------- src/utils/constants.ts | 4 ++ src/utils/types.ts | 5 +++ 25 files changed, 220 insertions(+), 245 deletions(-) diff --git a/src/api/ContextMenu.ts b/src/api/ContextMenu.ts index d66d98c4f..fdd4facf4 100644 --- a/src/api/ContextMenu.ts +++ b/src/api/ContextMenu.ts @@ -17,22 +17,20 @@ */ import { Logger } from "@utils/Logger"; +import { Menu, React } from "@webpack/common"; import type { ReactElement } from "react"; -type ContextMenuPatchCallbackReturn = (() => void) | void; /** * @param children The rendered context menu elements * @param args Any arguments passed into making the context menu, like the guild, channel, user or message for example - * @returns A callback which is only ran once used to modify the context menu elements (Use to avoid duplicates) */ -export type NavContextMenuPatchCallback = (children: Array, ...args: Array) => ContextMenuPatchCallbackReturn; +export type NavContextMenuPatchCallback = (children: Array, ...args: Array) => void; /** * @param navId The navId of the context menu being patched * @param children The rendered context menu elements * @param args Any arguments passed into making the context menu, like the guild, channel, user or message for example - * @returns A callback which is only ran once used to modify the context menu elements (Use to avoid duplicates) */ -export type GlobalContextMenuPatchCallback = (navId: string, children: Array, ...args: Array) => ContextMenuPatchCallbackReturn; +export type GlobalContextMenuPatchCallback = (navId: string, children: Array, ...args: Array) => void; const ContextMenuLogger = new Logger("ContextMenu"); @@ -93,14 +91,19 @@ export function removeGlobalContextMenuPatch(patch: GlobalContextMenuPatchCallba * @param id The id of the child. If an array is specified, all ids will be tried * @param children The context menu children */ -export function findGroupChildrenByChildId(id: string | string[], children: Array, _itemsArray?: Array): Array | null { +export function findGroupChildrenByChildId(id: string | string[], children: Array): Array | null { for (const child of children) { if (child == null) continue; + if (Array.isArray(child)) { + const found = findGroupChildrenByChildId(id, child); + if (found !== null) return found; + } + if ( (Array.isArray(id) && id.some(id => child.props?.id === id)) || child.props?.id === id - ) return _itemsArray ?? null; + ) return children; let nextChildren = child.props?.children; if (nextChildren) { @@ -109,7 +112,7 @@ export function findGroupChildrenByChildId(id: string | string[], children: Arra child.props.children = nextChildren; } - const found = findGroupChildrenByChildId(id, nextChildren, nextChildren); + const found = findGroupChildrenByChildId(id, nextChildren); if (found !== null) return found; } } @@ -126,9 +129,12 @@ interface ContextMenuProps { onClose: (callback: (...args: Array) => any) => void; } -const patchedMenus = new WeakSet(); +export function _usePatchContextMenu(props: ContextMenuProps) { + props = { + ...props, + children: cloneMenuChildren(props.children), + }; -export function _patchContextMenu(props: ContextMenuProps) { props.contextMenuApiArguments ??= []; const contextMenuPatches = navPatches.get(props.navId); @@ -137,8 +143,7 @@ export function _patchContextMenu(props: ContextMenuProps) { if (contextMenuPatches) { for (const patch of contextMenuPatches) { try { - const callback = patch(props.children, ...props.contextMenuApiArguments); - if (!patchedMenus.has(props)) callback?.(); + patch(props.children, ...props.contextMenuApiArguments); } catch (err) { ContextMenuLogger.error(`Patch for ${props.navId} errored,`, err); } @@ -147,12 +152,30 @@ export function _patchContextMenu(props: ContextMenuProps) { for (const patch of globalPatches) { try { - const callback = patch(props.navId, props.children, ...props.contextMenuApiArguments); - if (!patchedMenus.has(props)) callback?.(); + patch(props.navId, props.children, ...props.contextMenuApiArguments); } catch (err) { ContextMenuLogger.error("Global patch errored,", err); } } - patchedMenus.add(props); + return props; +} + +function cloneMenuChildren(obj: ReactElement | Array | null) { + if (Array.isArray(obj)) { + return obj.map(cloneMenuChildren); + } + + if (React.isValidElement(obj)) { + obj = React.cloneElement(obj); + + if ( + obj?.props?.children && + (obj.type !== Menu.MenuControlItem || obj.type === Menu.MenuControlItem && obj.props.control != null) + ) { + obj.props.children = cloneMenuChildren(obj.props.children); + } + } + + return obj; } diff --git a/src/plugins/_api/contextMenu.ts b/src/plugins/_api/contextMenu.ts index 55fdf3eae..01619546d 100644 --- a/src/plugins/_api/contextMenu.ts +++ b/src/plugins/_api/contextMenu.ts @@ -22,15 +22,15 @@ import definePlugin from "@utils/types"; export default definePlugin({ name: "ContextMenuAPI", description: "API for adding/removing items to/from context menus.", - authors: [Devs.Nuckyz, Devs.Ven], + authors: [Devs.Nuckyz, Devs.Ven, Devs.Kyuuhachi], required: true, patches: [ { find: "♫ (つ。◕‿‿◕。)つ ♪", replacement: { - match: /let{navId:/, - replace: "Vencord.Api.ContextMenu._patchContextMenu(arguments[0]);$&" + match: /(?=let{navId:)(?<=function \i\((\i)\).+?)/, + replace: "$1=Vencord.Api.ContextMenu._usePatchContextMenu($1);" } }, { diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index 6f43b76a8..01220eb4e 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { addContextMenuPatch } from "@api/ContextMenu"; +import { findGroupChildrenByChildId } from "@api/ContextMenu"; import { Settings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; @@ -30,21 +30,21 @@ export default definePlugin({ authors: [Devs.Ven, Devs.Megu], required: true, - start() { + contextMenus: { // The settings shortcuts in the user settings cog context menu // read the elements from a hardcoded map which for obvious reason // doesn't contain our sections. This patches the actions of our // sections to manually use SettingsRouter (which only works on desktop // but the context menu is usually not available on mobile anyway) - addContextMenuPatch("user-settings-cog", children => () => { - const section = children.find(c => Array.isArray(c) && c.some(it => it?.props?.id === "VencordSettings")) as any; + "user-settings-cog"(children) { + const section = findGroupChildrenByChildId("VencordSettings", children); section?.forEach(c => { const id = c?.props?.id; if (id?.startsWith("Vencord") || id?.startsWith("Vesktop")) { - c.props.action = () => SettingsRouter.open(id); + c!.props.action = () => SettingsRouter.open(id); } }); - }); + } }, patches: [{ diff --git a/src/plugins/biggerStreamPreview/index.tsx b/src/plugins/biggerStreamPreview/index.tsx index 40bbe30a8..8cca912bc 100644 --- a/src/plugins/biggerStreamPreview/index.tsx +++ b/src/plugins/biggerStreamPreview/index.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; +import { NavContextMenuPatchCallback } from "@api/ContextMenu"; import { ScreenshareIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; import { openImageModal } from "@utils/discord"; @@ -60,7 +60,7 @@ export const handleViewPreview = async ({ guildId, channelId, ownerId }: Applica openImageModal(previewUrl); }; -export const addViewStreamContext: NavContextMenuPatchCallback = (children, { userId }: { userId: string | bigint; }) => () => { +export const addViewStreamContext: NavContextMenuPatchCallback = (children, { userId }: { userId: string | bigint; }) => { const stream = ApplicationStreamingStore.getAnyStreamForUser(userId); if (!stream) return; @@ -89,12 +89,8 @@ export default definePlugin({ name: "BiggerStreamPreview", description: "This plugin allows you to enlarge stream previews", authors: [Devs.phil], - start: () => { - addContextMenuPatch("user-context", userContextPatch); - addContextMenuPatch("stream-context", streamContextPatch); - }, - stop: () => { - removeContextMenuPatch("user-context", userContextPatch); - removeContextMenuPatch("stream-context", streamContextPatch); + contextMenus: { + "user-context": userContextPatch, + "stream-context": streamContextPatch } }); diff --git a/src/plugins/copyUserURLs/index.tsx b/src/plugins/copyUserURLs/index.tsx index 9f69674cf..7af8502db 100644 --- a/src/plugins/copyUserURLs/index.tsx +++ b/src/plugins/copyUserURLs/index.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; +import { NavContextMenuPatchCallback } from "@api/ContextMenu"; import { LinkIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; @@ -29,7 +29,7 @@ interface UserContextProps { user: User; } -const UserContextMenuPatch: NavContextMenuPatchCallback = (children, { user }: UserContextProps) => () => { +const UserContextMenuPatch: NavContextMenuPatchCallback = (children, { user }: UserContextProps) => { if (!user) return; children.push( @@ -46,12 +46,7 @@ export default definePlugin({ name: "CopyUserURLs", authors: [Devs.castdrian], description: "Adds a 'Copy User URL' option to the user context menu.", - - start() { - addContextMenuPatch("user-context", UserContextMenuPatch); - }, - - stop() { - removeContextMenuPatch("user-context", UserContextMenuPatch); - }, + contextMenus: { + "user-context": UserContextMenuPatch + } }); diff --git a/src/plugins/emoteCloner/index.tsx b/src/plugins/emoteCloner/index.tsx index 219ce435f..b25e1be27 100644 --- a/src/plugins/emoteCloner/index.tsx +++ b/src/plugins/emoteCloner/index.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { addContextMenuPatch, findGroupChildrenByChildId, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; +import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; import { CheckedTextInput } from "@components/CheckedTextInput"; import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; @@ -312,7 +312,7 @@ function isGifUrl(url: string) { return new URL(url).pathname.endsWith(".gif"); } -const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => () => { +const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => { const { favoriteableId, itemHref, itemSrc, favoriteableType } = props ?? {}; if (!favoriteableId) return; @@ -341,7 +341,7 @@ const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) = findGroupChildrenByChildId("copy-link", children)?.push(menuItem); }; -const expressionPickerPatch: NavContextMenuPatchCallback = (children, props: { target: HTMLElement; }) => () => { +const expressionPickerPatch: NavContextMenuPatchCallback = (children, props: { target: HTMLElement; }) => { const { id, name, type } = props?.target?.dataset ?? {}; if (!id) return; @@ -363,14 +363,8 @@ export default definePlugin({ description: "Allows you to clone Emotes & Stickers to your own server (right click them)", tags: ["StickerCloner"], authors: [Devs.Ven, Devs.Nuckyz], - - start() { - addContextMenuPatch("message", messageContextMenuPatch); - addContextMenuPatch("expression-picker", expressionPickerPatch); - }, - - stop() { - removeContextMenuPatch("message", messageContextMenuPatch); - removeContextMenuPatch("expression-picker", expressionPickerPatch); + contextMenus: { + "message": messageContextMenuPatch, + "expression-picker": expressionPickerPatch } }); diff --git a/src/plugins/imageZoom/index.tsx b/src/plugins/imageZoom/index.tsx index 8d8b6726d..048c0ed5b 100644 --- a/src/plugins/imageZoom/index.tsx +++ b/src/plugins/imageZoom/index.tsx @@ -16,14 +16,14 @@ * along with this program. If not, see . */ -import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; +import { NavContextMenuPatchCallback } from "@api/ContextMenu"; import { definePluginSettings } from "@api/Settings"; import { disableStyle, enableStyle } from "@api/Styles"; import { makeRange } from "@components/PluginSettings/components"; import { Devs } from "@utils/constants"; import { debounce } from "@utils/debounce"; import definePlugin, { OptionType } from "@utils/types"; -import { ContextMenuApi, Menu, React, ReactDOM } from "@webpack/common"; +import { Menu, React, ReactDOM } from "@webpack/common"; import type { Root } from "react-dom/client"; import { Magnifier, MagnifierProps } from "./components/Magnifier"; @@ -80,25 +80,25 @@ export const settings = definePluginSettings({ }); -const imageContextMenuPatch: NavContextMenuPatchCallback = children => () => { +const imageContextMenuPatch: NavContextMenuPatchCallback = children => { + const { square, nearestNeighbour } = settings.use(["square", "nearestNeighbour"]); + children.push( { - settings.store.square = !settings.store.square; - ContextMenuApi.closeContextMenu(); + settings.store.square = !square; }} /> { - settings.store.nearestNeighbour = !settings.store.nearestNeighbour; - ContextMenuApi.closeContextMenu(); + settings.store.nearestNeighbour = !nearestNeighbour; }} /> | null, @@ -245,7 +248,6 @@ export default definePlugin({ start() { enableStyle(styles); - addContextMenuPatch("image-context", imageContextMenuPatch); this.element = document.createElement("div"); this.element.classList.add("MagnifierContainer"); document.body.appendChild(this.element); @@ -256,6 +258,5 @@ export default definePlugin({ // so componenetWillUnMount gets called if Magnifier component is still alive this.root && this.root.unmount(); this.element?.remove(); - removeContextMenuPatch("image-context", imageContextMenuPatch); } }); diff --git a/src/plugins/index.ts b/src/plugins/index.ts index 234838606..7092001ee 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -17,6 +17,7 @@ */ import { registerCommand, unregisterCommand } from "@api/Commands"; +import { addContextMenuPatch, removeContextMenuPatch } from "@api/ContextMenu"; import { Settings } from "@api/Settings"; import { Logger } from "@utils/Logger"; import { Patch, Plugin, StartAt } from "@utils/types"; @@ -119,7 +120,7 @@ export function startDependenciesRecursive(p: Plugin) { } export const startPlugin = traceFunction("startPlugin", function startPlugin(p: Plugin) { - const { name, commands, flux } = p; + const { name, commands, flux, contextMenus } = p; if (p.start) { logger.info("Starting plugin", name); @@ -154,11 +155,17 @@ export const startPlugin = traceFunction("startPlugin", function startPlugin(p: } } + if (contextMenus) { + for (const navId in contextMenus) { + addContextMenuPatch(navId, contextMenus[navId]); + } + } + return true; }, p => `startPlugin ${p.name}`); export const stopPlugin = traceFunction("stopPlugin", function stopPlugin(p: Plugin) { - const { name, commands, flux } = p; + const { name, commands, flux, contextMenus } = p; if (p.stop) { logger.info("Stopping plugin", name); if (!p.started) { @@ -192,5 +199,11 @@ export const stopPlugin = traceFunction("stopPlugin", function stopPlugin(p: Plu } } + if (contextMenus) { + for (const navId in contextMenus) { + removeContextMenuPatch(navId, contextMenus[navId]); + } + } + return true; }, p => `stopPlugin ${p.name}`); diff --git a/src/plugins/messageLogger/index.tsx b/src/plugins/messageLogger/index.tsx index ef986bf87..8bc563b19 100644 --- a/src/plugins/messageLogger/index.tsx +++ b/src/plugins/messageLogger/index.tsx @@ -18,7 +18,7 @@ import "./messageLogger.css"; -import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; +import { NavContextMenuPatchCallback } from "@api/ContextMenu"; import { Settings } from "@api/Settings"; import { disableStyle, enableStyle } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; @@ -45,7 +45,7 @@ function addDeleteStyle() { const REMOVE_HISTORY_ID = "ml-remove-history"; const TOGGLE_DELETE_STYLE_ID = "ml-toggle-style"; -const patchMessageContextMenu: NavContextMenuPatchCallback = (children, props) => () => { +const patchMessageContextMenu: NavContextMenuPatchCallback = (children, props) => { const { message } = props; const { deleted, editHistory, id, channel_id } = message; @@ -94,13 +94,12 @@ export default definePlugin({ description: "Temporarily logs deleted and edited messages.", authors: [Devs.rushii, Devs.Ven, Devs.AutumnVN], - start() { - addDeleteStyle(); - addContextMenuPatch("message", patchMessageContextMenu); + contextMenus: { + "message": patchMessageContextMenu }, - stop() { - removeContextMenuPatch("message", patchMessageContextMenu); + start() { + addDeleteStyle(); }, renderEdit(edit: { timestamp: any, content: string; }) { diff --git a/src/plugins/permissionsViewer/index.tsx b/src/plugins/permissionsViewer/index.tsx index 9e0131e64..07f073d63 100644 --- a/src/plugins/permissionsViewer/index.tsx +++ b/src/plugins/permissionsViewer/index.tsx @@ -18,7 +18,7 @@ import "./styles.css"; -import { addContextMenuPatch, findGroupChildrenByChildId, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; +import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; @@ -125,10 +125,10 @@ function MenuItem(guildId: string, id?: string, type?: MenuItemParentType) { } function makeContextMenuPatch(childId: string | string[], type?: MenuItemParentType): NavContextMenuPatchCallback { - return (children, props) => () => { + return (children, props) => { if (!props) return; if ((type === MenuItemParentType.User && !props.user) || (type === MenuItemParentType.Guild && !props.guild) || (type === MenuItemParentType.Channel && (!props.channel || !props.guild))) - return children; + return; const group = findGroupChildrenByChildId(childId, children); @@ -173,19 +173,10 @@ export default definePlugin({ UserPermissions: (guild: Guild, guildMember: GuildMember | undefined, showBoder: boolean) => !!guildMember && , - userContextMenuPatch: makeContextMenuPatch("roles", MenuItemParentType.User), - channelContextMenuPatch: makeContextMenuPatch(["mute-channel", "unmute-channel"], MenuItemParentType.Channel), - guildContextMenuPatch: makeContextMenuPatch("privacy", MenuItemParentType.Guild), - - start() { - addContextMenuPatch("user-context", this.userContextMenuPatch); - addContextMenuPatch("channel-context", this.channelContextMenuPatch); - addContextMenuPatch(["guild-context", "guild-header-popout"], this.guildContextMenuPatch); - }, - - stop() { - removeContextMenuPatch("user-context", this.userContextMenuPatch); - removeContextMenuPatch("channel-context", this.channelContextMenuPatch); - removeContextMenuPatch(["guild-context", "guild-header-popout"], this.guildContextMenuPatch); - }, + contextMenus: { + "user-context": makeContextMenuPatch("roles", MenuItemParentType.User), + "channel-context": makeContextMenuPatch(["mute-channel", "unmute-channel"], MenuItemParentType.Channel), + "guild-context": makeContextMenuPatch("privacy", MenuItemParentType.Guild), + "guild-header-popout": makeContextMenuPatch("privacy", MenuItemParentType.Guild) + } }); diff --git a/src/plugins/pinDms/contextMenus.tsx b/src/plugins/pinDms/contextMenus.tsx index 7d89ec123..1db8b25a9 100644 --- a/src/plugins/pinDms/contextMenus.tsx +++ b/src/plugins/pinDms/contextMenus.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { addContextMenuPatch, findGroupChildrenByChildId, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; +import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; import { Menu } from "@webpack/common"; import { isPinned, movePin, PinOrder, settings, snapshotArray, togglePin } from "./settings"; @@ -50,13 +50,13 @@ function PinMenuItem(channelId: string) { ); } -const GroupDMContext: NavContextMenuPatchCallback = (children, props) => () => { +const GroupDMContext: NavContextMenuPatchCallback = (children, props) => { const container = findGroupChildrenByChildId("leave-channel", children); if (container) container.unshift(PinMenuItem(props.channel.id)); }; -const UserContext: NavContextMenuPatchCallback = (children, props) => () => { +const UserContext: NavContextMenuPatchCallback = (children, props) => { const container = findGroupChildrenByChildId("close-dm", children); if (container) { const idx = container.findIndex(c => c?.props?.id === "close-dm"); @@ -64,12 +64,7 @@ const UserContext: NavContextMenuPatchCallback = (children, props) => () => { } }; -export function addContextMenus() { - addContextMenuPatch("gdm-context", GroupDMContext); - addContextMenuPatch("user-context", UserContext); -} - -export function removeContextMenus() { - removeContextMenuPatch("gdm-context", GroupDMContext); - removeContextMenuPatch("user-context", UserContext); -} +export const contextMenus = { + "gdm-context": GroupDMContext, + "user-context": UserContext +}; diff --git a/src/plugins/pinDms/index.tsx b/src/plugins/pinDms/index.tsx index 792bdab6a..943f0f1b1 100644 --- a/src/plugins/pinDms/index.tsx +++ b/src/plugins/pinDms/index.tsx @@ -20,7 +20,7 @@ import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; import { Channel } from "discord-types/general"; -import { addContextMenus, removeContextMenus } from "./contextMenus"; +import { contextMenus } from "./contextMenus"; import { getPinAt, isPinned, settings, snapshotArray, sortedSnapshot, usePinnedDms } from "./settings"; export default definePlugin({ @@ -29,9 +29,7 @@ export default definePlugin({ authors: [Devs.Ven, Devs.Strencher], settings, - - start: addContextMenus, - stop: removeContextMenus, + contextMenus, usePinCount(channelIds: string[]) { const pinnedDms = usePinnedDms(); diff --git a/src/plugins/resurrectHome/index.tsx b/src/plugins/resurrectHome/index.tsx index 91ed87a02..6b0069a7f 100644 --- a/src/plugins/resurrectHome/index.tsx +++ b/src/plugins/resurrectHome/index.tsx @@ -16,11 +16,11 @@ * along with this program. If not, see . */ -import { addContextMenuPatch, findGroupChildrenByChildId, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; +import { findGroupChildrenByChildId } from "@api/ContextMenu"; import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { ContextMenuApi, Menu } from "@webpack/common"; +import { Menu } from "@webpack/common"; const settings = definePluginSettings({ forceServerHome: { @@ -30,25 +30,11 @@ const settings = definePluginSettings({ } }); -const contextMenuPatch: NavContextMenuPatchCallback = (children, props) => () => { - if (!props?.guild) return; +function useForceServerHome() { + const { forceServerHome } = settings.use(["forceServerHome"]); - const group = findGroupChildrenByChildId("hide-muted-channels", children); - - group?.unshift( - { - settings.store.forceServerHome = !settings.store.forceServerHome; - ContextMenuApi.closeContextMenu(); - }} - /> - - ); -}; + return forceServerHome; +} export default definePlugin({ name: "ResurrectHome", @@ -109,17 +95,25 @@ export default definePlugin({ } ], - start() { - addContextMenuPatch("guild-context", contextMenuPatch); - }, + useForceServerHome, - stop() { - removeContextMenuPatch("guild-context", contextMenuPatch); - }, + contextMenus: { + "guild-context"(children, props) { + const forceServerHome = useForceServerHome(); - useForceServerHome() { - const { forceServerHome } = settings.use(["forceServerHome"]); + if (!props?.guild) return; - return forceServerHome; + const group = findGroupChildrenByChildId("hide-muted-channels", children); + + group?.unshift( + settings.store.forceServerHome = !forceServerHome} + /> + ); + } } }); diff --git a/src/plugins/reverseImageSearch/index.tsx b/src/plugins/reverseImageSearch/index.tsx index 6c5f3e729..415dc13d8 100644 --- a/src/plugins/reverseImageSearch/index.tsx +++ b/src/plugins/reverseImageSearch/index.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { addContextMenuPatch, findGroupChildrenByChildId, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; +import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; import { Flex } from "@components/Flex"; import { OpenExternalIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; @@ -84,7 +84,7 @@ function makeSearchItem(src: string) { ); } -const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => () => { +const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => { if (props?.reverseImageSearchType !== "img") return; const src = props.itemHref ?? props.itemSrc; @@ -93,7 +93,7 @@ const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) = group?.push(makeSearchItem(src)); }; -const imageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => () => { +const imageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => { if (!props?.src) return; const group = findGroupChildrenByChildId("copy-native-link", children) ?? children; @@ -115,14 +115,8 @@ export default definePlugin({ } } ], - - start() { - addContextMenuPatch("message", messageContextMenuPatch); - addContextMenuPatch("image-context", imageContextMenuPatch); - }, - - stop() { - removeContextMenuPatch("message", messageContextMenuPatch); - removeContextMenuPatch("image-context", imageContextMenuPatch); + contextMenus: { + "message": messageContextMenuPatch, + "image-context": imageContextMenuPatch } }); diff --git a/src/plugins/reviewDB/index.tsx b/src/plugins/reviewDB/index.tsx index 50bb62184..ad24e9696 100644 --- a/src/plugins/reviewDB/index.tsx +++ b/src/plugins/reviewDB/index.tsx @@ -18,7 +18,7 @@ import "./style.css"; -import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; +import { NavContextMenuPatchCallback } from "@api/ContextMenu"; import ErrorBoundary from "@components/ErrorBoundary"; import ExpandableHeader from "@components/ExpandableHeader"; import { OpenExternalIcon } from "@components/Icons"; @@ -36,7 +36,7 @@ import { getCurrentUserInfo, readNotification } from "./reviewDbApi"; import { settings } from "./settings"; import { showToast } from "./utils"; -const guildPopoutPatch: NavContextMenuPatchCallback = (children, props: { guild: Guild, onClose(): void; }) => () => { +const guildPopoutPatch: NavContextMenuPatchCallback = (children, props: { guild: Guild, onClose(): void; }) => { children.push( { const [reviewCount, setReviewCount] = useState(); diff --git a/src/plugins/searchReply/index.tsx b/src/plugins/searchReply/index.tsx index b151712af..35b197874 100644 --- a/src/plugins/searchReply/index.tsx +++ b/src/plugins/searchReply/index.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { addContextMenuPatch, findGroupChildrenByChildId, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; +import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; import { ReplyIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; @@ -27,7 +27,7 @@ import { Message } from "discord-types/general"; const messageUtils = findByPropsLazy("replyToMessage"); -const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { message }: { message: Message; }) => () => { +const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { message }: { message: Message; }) => { // make sure the message is in the selected channel if (SelectedChannelStore.getChannelId() !== message.channel_id) return; const channel = ChannelStore.getChannel(message?.channel_id); @@ -38,7 +38,7 @@ const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { messag const dmGroup = findGroupChildrenByChildId("pin", children); if (dmGroup && !dmGroup.some(child => child?.props?.id === "reply")) { const pinIndex = dmGroup.findIndex(c => c?.props.id === "pin"); - return dmGroup.splice(pinIndex + 1, 0, ( + dmGroup.splice(pinIndex + 1, 0, ( messageUtils.replyToMessage(channel, message, e)} /> )); + return; } // servers const serverGroup = findGroupChildrenByChildId("mark-unread", children); if (serverGroup && !serverGroup.some(child => child?.props?.id === "reply")) { - return serverGroup.unshift(( + serverGroup.unshift(( messageUtils.replyToMessage(channel, message, e)} /> )); + return; } }; @@ -67,12 +69,7 @@ export default definePlugin({ name: "SearchReply", description: "Adds a reply button to search results", authors: [Devs.Aria], - - start() { - addContextMenuPatch("message", messageContextMenuPatch); - }, - - stop() { - removeContextMenuPatch("message", messageContextMenuPatch); + contextMenus: { + "message": messageContextMenuPatch } }); diff --git a/src/plugins/serverProfile/index.tsx b/src/plugins/serverProfile/index.tsx index 68f6193cc..9d495c9d3 100644 --- a/src/plugins/serverProfile/index.tsx +++ b/src/plugins/serverProfile/index.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { addContextMenuPatch, findGroupChildrenByChildId, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; +import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; import { Menu } from "@webpack/common"; @@ -12,7 +12,7 @@ import { Guild } from "discord-types/general"; import { openGuildProfileModal } from "./GuildProfileModal"; -const Patch: NavContextMenuPatchCallback = (children, { guild }: { guild: Guild; }) => () => { +const Patch: NavContextMenuPatchCallback = (children, { guild }: { guild: Guild; }) => { const group = findGroupChildrenByChildId("privacy", children); group?.push( @@ -29,12 +29,8 @@ export default definePlugin({ description: "Allows you to view info about a server by right clicking it in the server list", authors: [Devs.Ven, Devs.Nuckyz], tags: ["guild", "info"], - - start() { - addContextMenuPatch(["guild-context", "guild-header-popout"], Patch); - }, - - stop() { - removeContextMenuPatch(["guild-context", "guild-header-popout"], Patch); + contextMenus: { + "guild-context": Patch, + "guild-header-popout": Patch } }); diff --git a/src/plugins/translate/index.tsx b/src/plugins/translate/index.tsx index 702e60cf7..f602d1255 100644 --- a/src/plugins/translate/index.tsx +++ b/src/plugins/translate/index.tsx @@ -19,7 +19,7 @@ import "./styles.css"; import { addChatBarButton, removeChatBarButton } from "@api/ChatButtons"; -import { addContextMenuPatch, findGroupChildrenByChildId, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; +import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; import { addAccessory, removeAccessory } from "@api/MessageAccessories"; import { addPreSendListener, removePreSendListener } from "@api/MessageEvents"; import { addButton, removeButton } from "@api/MessagePopover"; @@ -32,7 +32,7 @@ import { TranslateChatBarIcon, TranslateIcon } from "./TranslateIcon"; import { handleTranslate, TranslationAccessory } from "./TranslationAccessory"; import { translate } from "./utils"; -const messageCtxPatch: NavContextMenuPatchCallback = (children, { message }) => () => { +const messageCtxPatch: NavContextMenuPatchCallback = (children, { message }) => { if (!message.content) return; const group = findGroupChildrenByChildId("copy-text", children); @@ -57,13 +57,15 @@ export default definePlugin({ authors: [Devs.Ven], dependencies: ["MessageAccessoriesAPI", "MessagePopoverAPI", "MessageEventsAPI", "ChatInputButtonAPI"], settings, + contextMenus: { + "message": messageCtxPatch + }, // not used, just here in case some other plugin wants it or w/e translate, start() { addAccessory("vc-translation", props => ); - addContextMenuPatch("message", messageCtxPatch); addChatBarButton("vc-translate", TranslateChatBarIcon); addButton("vc-translate", message => { @@ -91,7 +93,6 @@ export default definePlugin({ stop() { removePreSendListener(this.preSend); - removeContextMenuPatch("message", messageCtxPatch); removeChatBarButton("vc-translate"); removeButton("vc-translate"); removeAccessory("vc-translation"); diff --git a/src/plugins/unsuppressEmbeds/index.tsx b/src/plugins/unsuppressEmbeds/index.tsx index a21960774..0e87201c6 100644 --- a/src/plugins/unsuppressEmbeds/index.tsx +++ b/src/plugins/unsuppressEmbeds/index.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { addContextMenuPatch, findGroupChildrenByChildId, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; +import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; import { ImageInvisible, ImageVisible } from "@components/Icons"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; @@ -24,7 +24,7 @@ import { Menu, PermissionsBits, PermissionStore, RestAPI, UserStore } from "@web const EMBED_SUPPRESSED = 1 << 2; -const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { channel, message: { author, embeds, flags, id: messageId } }) => () => { +const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { channel, message: { author, embeds, flags, id: messageId } }) => { const isEmbedSuppressed = (flags & EMBED_SUPPRESSED) !== 0; if (!isEmbedSuppressed && !embeds.length) return; @@ -56,12 +56,7 @@ export default definePlugin({ name: "UnsuppressEmbeds", authors: [Devs.rad, Devs.HypedDomi], description: "Allows you to unsuppress embeds in messages", - - start() { - addContextMenuPatch("message", messageContextMenuPatch); - }, - - stop() { - removeContextMenuPatch("message", messageContextMenuPatch); - }, + contextMenus: { + "message": messageContextMenuPatch + } }); diff --git a/src/plugins/vencordToolbox/index.tsx b/src/plugins/vencordToolbox/index.tsx index 0a805a0d2..ba295fa72 100644 --- a/src/plugins/vencordToolbox/index.tsx +++ b/src/plugins/vencordToolbox/index.tsx @@ -19,7 +19,7 @@ import "./index.css"; import { openNotificationLogModal } from "@api/Notifications/notificationLog"; -import { Settings } from "@api/Settings"; +import { Settings, useSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; @@ -30,6 +30,8 @@ import type { ReactNode } from "react"; const HeaderBarIcon = findExportedComponentLazy("Icon", "Divider"); function VencordPopout(onClose: () => void) { + const { useQuickCss } = useSettings(); + const pluginEntries = [] as ReactNode[]; for (const plugin of Object.values(Vencord.Plugins.plugins)) { @@ -68,11 +70,10 @@ function VencordPopout(onClose: () => void) { /> { - Settings.useQuickCss = !Settings.useQuickCss; - onClose(); + Settings.useQuickCss = !useQuickCss; }} /> . */ -import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; +import { NavContextMenuPatchCallback } from "@api/ContextMenu"; import { definePluginSettings } from "@api/Settings"; import { ImageIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; @@ -80,7 +80,7 @@ function openImage(url: string) { }); } -const UserContext: NavContextMenuPatchCallback = (children, { user, guildId }: UserContextProps) => () => { +const UserContext: NavContextMenuPatchCallback = (children, { user, guildId }: UserContextProps) => { if (!user) return; const memberAvatar = GuildMemberStore.getMember(guildId!, user.id)?.avatar || null; @@ -109,7 +109,7 @@ const UserContext: NavContextMenuPatchCallback = (children, { user, guildId }: U )); }; -const GuildContext: NavContextMenuPatchCallback = (children, { guild }: GuildContextProps) => () => { +const GuildContext: NavContextMenuPatchCallback = (children, { guild }: GuildContextProps) => { if (!guild) return; const { id, icon, banner } = guild; @@ -155,14 +155,9 @@ export default definePlugin({ openImage, - start() { - addContextMenuPatch("user-context", UserContext); - addContextMenuPatch("guild-context", GuildContext); - }, - - stop() { - removeContextMenuPatch("user-context", UserContext); - removeContextMenuPatch("guild-context", GuildContext); + contextMenus: { + "user-context": UserContext, + "guild-context": GuildContext }, patches: [ diff --git a/src/plugins/viewRaw/index.tsx b/src/plugins/viewRaw/index.tsx index 08acdc4c5..68b33eed0 100644 --- a/src/plugins/viewRaw/index.tsx +++ b/src/plugins/viewRaw/index.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; +import { NavContextMenuPatchCallback } from "@api/ContextMenu"; import { addButton, removeButton } from "@api/MessagePopover"; import { definePluginSettings } from "@api/Settings"; import { CodeBlock } from "@components/CodeBlock"; @@ -117,8 +117,8 @@ const settings = definePluginSettings({ } }); -function MakeContextCallback(name: "Guild" | "User" | "Channel") { - const callback: NavContextMenuPatchCallback = (children, props) => () => { +function MakeContextCallback(name: "Guild" | "User" | "Channel"): NavContextMenuPatchCallback { + return (children, props) => { const value = props[name.toLowerCase()]; if (!value) return; if (props.label === i18n.Messages.CHANNEL_ACTIONS_MENU_LABEL) return; // random shit like notification settings @@ -141,16 +141,19 @@ function MakeContextCallback(name: "Guild" | "User" | "Channel") { /> ); }; - return callback; } - export default definePlugin({ name: "ViewRaw", description: "Copy and view the raw content/data of any message, channel or guild", authors: [Devs.KingFish, Devs.Ven, Devs.rad, Devs.ImLvna], dependencies: ["MessagePopoverAPI"], settings, + contextMenus: { + "guild-context": MakeContextCallback("Guild"), + "channel-context": MakeContextCallback("Channel"), + "user-context": MakeContextCallback("User") + }, start() { addButton("ViewRaw", msg => { @@ -187,16 +190,9 @@ export default definePlugin({ onContextMenu: handleContextMenu }; }); - - addContextMenuPatch("guild-context", MakeContextCallback("Guild")); - addContextMenuPatch("channel-context", MakeContextCallback("Channel")); - addContextMenuPatch("user-context", MakeContextCallback("User")); }, stop() { - removeButton("CopyRawMessage"); - removeContextMenuPatch("guild-context", MakeContextCallback("Guild")); - removeContextMenuPatch("channel-context", MakeContextCallback("Channel")); - removeContextMenuPatch("user-context", MakeContextCallback("User")); + removeButton("ViewRaw"); } }); diff --git a/src/plugins/voiceMessages/index.tsx b/src/plugins/voiceMessages/index.tsx index 2393ef2b6..2f232f341 100644 --- a/src/plugins/voiceMessages/index.tsx +++ b/src/plugins/voiceMessages/index.tsx @@ -18,7 +18,7 @@ import "./styles.css"; -import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; +import { NavContextMenuPatchCallback } from "@api/ContextMenu"; import { Microphone } from "@components/Icons"; import { Link } from "@components/Link"; import { Devs } from "@utils/constants"; @@ -48,18 +48,30 @@ export type VoiceRecorder = ComponentType<{ const VoiceRecorder = IS_DISCORD_DESKTOP ? VoiceRecorderDesktop : VoiceRecorderWeb; +const ctxMenuPatch: NavContextMenuPatchCallback = (children, props) => { + if (props.channel.guild_id && !(PermissionStore.can(PermissionsBits.SEND_VOICE_MESSAGES, props.channel) && PermissionStore.can(PermissionsBits.SEND_MESSAGES, props.channel))) return; + + children.push( + + +
Send voice message
+
+ } + action={() => openModal(modalProps => )} + /> + ); +}; + export default definePlugin({ name: "VoiceMessages", description: "Allows you to send voice messages like on mobile. To do so, right click the upload button and click Send Voice Message", authors: [Devs.Ven, Devs.Vap, Devs.Nickyux], settings, - - start() { - addContextMenuPatch("channel-attach", ctxMenuPatch); - }, - - stop() { - removeContextMenuPatch("channel-attach", ctxMenuPatch); + contextMenus: { + "channel-attach": ctxMenuPatch } }); @@ -234,20 +246,3 @@ function Modal({ modalProps }: { modalProps: ModalProps; }) { ); } - -const ctxMenuPatch: NavContextMenuPatchCallback = (children, props) => () => { - if (props.channel.guild_id && !(PermissionStore.can(PermissionsBits.SEND_VOICE_MESSAGES, props.channel) && PermissionStore.can(PermissionsBits.SEND_MESSAGES, props.channel))) return; - - children.push( - - -
Send voice message
- - } - action={() => openModal(modalProps => )} - /> - ); -}; diff --git a/src/utils/constants.ts b/src/utils/constants.ts index d66bdc826..d213ce272 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -418,6 +418,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({ Av32000: { name: "Av32000", id: 593436735380127770n, + }, + Kyuuhachi: { + name: "Kyuuhachi", + id: 236588665420251137n, } } satisfies Record); diff --git a/src/utils/types.ts b/src/utils/types.ts index 16867a43c..bec7cb0b3 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -17,6 +17,7 @@ */ import { Command } from "@api/Commands"; +import { NavContextMenuPatchCallback } from "@api/ContextMenu"; import { FluxEvents } from "@webpack/types"; import { Promisable } from "type-fest"; @@ -115,6 +116,10 @@ export interface PluginDef { flux?: { [E in FluxEvents]?: (event: any) => void; }; + /** + * Allows you to manipulate context menus + */ + contextMenus?: Record; /** * Allows you to add custom actions to the Vencord Toolbox. * The key will be used as text for the button From 980206d31568e479f0f0c7a32ab24035374f06d5 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 7 Mar 2024 09:36:59 -0300 Subject: [PATCH 46/79] Fix waitFor initial finds traces getting logged to the console even though they always fail --- src/webpack/webpack.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index a68890a83..0f7d8b73c 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -475,8 +475,10 @@ export function waitFor(filter: string | string[] | FilterFn, callback: Callback else if (typeof filter !== "function") throw new Error("filter must be a string, string[] or function, got " + typeof filter); - const [existing, id] = find(filter!, { isIndirect: true, isWaitFor: true }); - if (existing) return void callback(existing, id); + if (cache != null) { + const [existing, id] = find(filter, { isIndirect: true, isWaitFor: true }); + if (existing) return void callback(existing, id); + } subscriptions.set(filter, callback); } From a59c14f9aa8034fcb3df45af855a7a57c2882b82 Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Thu, 7 Mar 2024 19:51:14 +0700 Subject: [PATCH 47/79] CustomRPC: Change timestamp to milisecond (#2231) --- src/plugins/customRPC/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/customRPC/index.tsx b/src/plugins/customRPC/index.tsx index 3653a0776..e70f8c908 100644 --- a/src/plugins/customRPC/index.tsx +++ b/src/plugins/customRPC/index.tsx @@ -175,7 +175,7 @@ const settings = definePluginSettings({ }, startTime: { type: OptionType.NUMBER, - description: "Start timestamp (only for custom timestamp mode)", + description: "Start timestamp in milisecond (only for custom timestamp mode)", onChange: onChange, disabled: isTimestampDisabled, isValid: (value: number) => { @@ -185,7 +185,7 @@ const settings = definePluginSettings({ }, endTime: { type: OptionType.NUMBER, - description: "End timestamp (only for custom timestamp mode)", + description: "End timestamp in milisecond (only for custom timestamp mode)", onChange: onChange, disabled: isTimestampDisabled, isValid: (value: number) => { @@ -313,12 +313,12 @@ async function createActivity(): Promise { switch (settings.store.timestampMode) { case TimestampMode.NOW: activity.timestamps = { - start: Math.floor(Date.now() / 1000) + start: Date.now() }; break; case TimestampMode.TIME: activity.timestamps = { - start: Math.floor(Date.now() / 1000) - (new Date().getHours() * 3600) - (new Date().getMinutes() * 60) - new Date().getSeconds() + start: Date.now() - (new Date().getHours() * 3600 + new Date().getMinutes() * 60 + new Date().getSeconds()) * 1000 }; break; case TimestampMode.CUSTOM: From f70114238c1307e086db537640926a0db78adb10 Mon Sep 17 00:00:00 2001 From: Sam <149597648+cheesesamwich@users.noreply.github.com> Date: Thu, 7 Mar 2024 12:55:51 +0000 Subject: [PATCH 48/79] MemberCount: Add options to choose where the member count will be displayed (#2224) --- src/plugins/memberCount/index.tsx | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/plugins/memberCount/index.tsx b/src/plugins/memberCount/index.tsx index eb4ce372c..92e9a2057 100644 --- a/src/plugins/memberCount/index.tsx +++ b/src/plugins/memberCount/index.tsx @@ -18,10 +18,11 @@ import "./style.css"; +import { definePluginSettings } from "@api/Settings"; import { classNameFactory } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; -import definePlugin from "@utils/types"; +import definePlugin, { OptionType } from "@utils/types"; import { findStoreLazy } from "@webpack"; import { FluxStore } from "@webpack/types"; @@ -32,6 +33,21 @@ export const ChannelMemberStore = findStoreLazy("ChannelMemberStore") as FluxSto getProps(guildId: string, channelId: string): { groups: { count: number; id: string; }[]; }; }; +const settings = definePluginSettings({ + toolTip: { + type: OptionType.BOOLEAN, + description: "If the member count should be displayed on the server tooltip", + default: true, + restartNeeded: true + }, + memberList: { + type: OptionType.BOOLEAN, + description: "If the member count should be displayed on the member list", + default: true, + restartNeeded: true + } +}); + const sharedIntlNumberFormat = new Intl.NumberFormat(); export const numberFormat = (value: number) => sharedIntlNumberFormat.format(value); export const cl = classNameFactory("vc-membercount-"); @@ -40,6 +56,7 @@ export default definePlugin({ name: "MemberCount", description: "Shows the amount of online & total members in the server member list and tooltip", authors: [Devs.Ven, Devs.Commandtechno], + settings, patches: [ { @@ -47,17 +64,18 @@ export default definePlugin({ replacement: { match: /(?<=let\{className:(\i),.+?children):\[(\i\.useMemo[^}]+"aria-multiselectable")/, replace: ":[$1?.startsWith('members')?$self.render():null,$2" - } + }, + predicate: () => settings.store.memberList }, { find: ".invitesDisabledTooltip", replacement: { match: /(?<=\.VIEW_AS_ROLES_MENTIONS_WARNING.{0,100})]/, replace: ",$self.renderTooltip(arguments[0].guild)]" - } + }, + predicate: () => settings.store.toolTip } ], - render: ErrorBoundary.wrap(MemberCount, { noop: true }), renderTooltip: ErrorBoundary.wrap(guild => , { noop: true }) }); From 19799767adef192a8451adf0f8c1710933b21a41 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 7 Mar 2024 10:09:04 -0300 Subject: [PATCH 49/79] Fix trying to check for updates if origin doesn't have same branch --- src/main/updater/git.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/updater/git.ts b/src/main/updater/git.ts index 2ff3ba512..20a5d7003 100644 --- a/src/main/updater/git.ts +++ b/src/main/updater/git.ts @@ -49,9 +49,12 @@ async function getRepo() { async function calculateGitChanges() { await git("fetch"); - const branch = await git("branch", "--show-current"); + const branch = (await git("branch", "--show-current")).stdout.trim(); - const res = await git("log", `HEAD...origin/${branch.stdout.trim()}`, "--pretty=format:%an/%h/%s"); + const existsOnOrigin = (await git("ls-remote", "origin", branch)).stdout.length > 0; + if (!existsOnOrigin) return []; + + const res = await git("log", `HEAD...origin/${branch}`, "--pretty=format:%an/%h/%s"); const commits = res.stdout.trim(); return commits ? commits.split("\n").map(line => { From 102842d5288fd22821e2a6eb295255fd88603964 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 7 Mar 2024 11:33:00 -0300 Subject: [PATCH 50/79] Close Ipc FS watchers if window is closed --- src/main/ipcMain.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/ipcMain.ts b/src/main/ipcMain.ts index 47d400eb6..3ac8a14c5 100644 --- a/src/main/ipcMain.ts +++ b/src/main/ipcMain.ts @@ -23,7 +23,7 @@ import { debounce } from "@utils/debounce"; import { IpcEvents } from "@utils/IpcEvents"; import { Queue } from "@utils/Queue"; import { BrowserWindow, ipcMain, shell, systemPreferences } from "electron"; -import { mkdirSync, readFileSync, watch } from "fs"; +import { FSWatcher, mkdirSync, readFileSync, watch } from "fs"; import { open, readdir, readFile, writeFile } from "fs/promises"; import { join, normalize } from "path"; @@ -126,16 +126,23 @@ ipcMain.handle(IpcEvents.SET_SETTINGS, (_, s) => { export function initIpc(mainWindow: BrowserWindow) { + let quickCssWatcher: FSWatcher | undefined; + open(QUICKCSS_PATH, "a+").then(fd => { fd.close(); - watch(QUICKCSS_PATH, { persistent: false }, debounce(async () => { + quickCssWatcher = watch(QUICKCSS_PATH, { persistent: false }, debounce(async () => { mainWindow.webContents.postMessage(IpcEvents.QUICK_CSS_UPDATE, await readCss()); }, 50)); - }); + }).catch(() => { }); - watch(THEMES_DIR, { persistent: false }, debounce(() => { + const themesWatcher = watch(THEMES_DIR, { persistent: false }, debounce(() => { mainWindow.webContents.postMessage(IpcEvents.THEME_UPDATE, void 0); })); + + mainWindow.once("closed", () => { + quickCssWatcher?.close(); + themesWatcher.close(); + }); } ipcMain.handle(IpcEvents.OPEN_MONACO_EDITOR, async () => { From 1c1d82f9a820ce4b7611032488624d98624a7ecb Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 7 Mar 2024 13:06:08 -0300 Subject: [PATCH 51/79] VencordToolbox: don't subscribe to all settings Also remove one indirection from useSettings --- src/api/Settings.ts | 14 ++++++++------ src/plugins/vencordToolbox/index.tsx | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/api/Settings.ts b/src/api/Settings.ts index 004a8988b..c1ff6915b 100644 --- a/src/api/Settings.ts +++ b/src/api/Settings.ts @@ -223,13 +223,13 @@ export const Settings = makeProxy(settings); export function useSettings(paths?: UseSettings[]) { const [, forceUpdate] = React.useReducer(() => ({}), {}); - const onUpdate: SubscriptionCallback = paths - ? (value, path) => paths.includes(path as UseSettings) && forceUpdate() - : forceUpdate; + if (paths) { + (forceUpdate as SubscriptionCallback)._paths = paths; + } React.useEffect(() => { - subscriptions.add(onUpdate); - return () => void subscriptions.delete(onUpdate); + subscriptions.add(forceUpdate); + return () => void subscriptions.delete(forceUpdate); }, []); return Settings; @@ -253,8 +253,10 @@ type ResolvePropDeep = P extends "" ? T : export function addSettingsListener(path: Path, onUpdate: (newValue: Settings[Path], path: Path) => void): void; export function addSettingsListener(path: Path, onUpdate: (newValue: Path extends "" ? any : ResolvePropDeep, path: Path extends "" ? string : Path) => void): void; export function addSettingsListener(path: string, onUpdate: (newValue: any, path: string) => void) { - if (path) + if (path) { ((onUpdate as SubscriptionCallback)._paths ??= []).push(path); + } + subscriptions.add(onUpdate); } diff --git a/src/plugins/vencordToolbox/index.tsx b/src/plugins/vencordToolbox/index.tsx index ba295fa72..00805fbd3 100644 --- a/src/plugins/vencordToolbox/index.tsx +++ b/src/plugins/vencordToolbox/index.tsx @@ -30,7 +30,7 @@ import type { ReactNode } from "react"; const HeaderBarIcon = findExportedComponentLazy("Icon", "Divider"); function VencordPopout(onClose: () => void) { - const { useQuickCss } = useSettings(); + const { useQuickCss } = useSettings(["useQuickCss"]); const pluginEntries = [] as ReactNode[]; From 2e90d4c03d98b9e5ea1dd8505cb0740d90b051d0 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Thu, 7 Mar 2024 19:53:26 +0100 Subject: [PATCH 52/79] New plugin: BetterRoleContext ~ edit/copy colour shortcuts in profile --- src/plugins/betterRoleContext/README.md | 6 ++ src/plugins/betterRoleContext/index.tsx | 79 ++++++++++++++++++++++ src/webpack/common/settingsStores.ts | 7 +- src/webpack/common/types/index.d.ts | 4 +- src/webpack/common/types/settingsStores.ts | 11 +++ 5 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 src/plugins/betterRoleContext/README.md create mode 100644 src/plugins/betterRoleContext/index.tsx create mode 100644 src/webpack/common/types/settingsStores.ts diff --git a/src/plugins/betterRoleContext/README.md b/src/plugins/betterRoleContext/README.md new file mode 100644 index 000000000..3f3086bdb --- /dev/null +++ b/src/plugins/betterRoleContext/README.md @@ -0,0 +1,6 @@ +# BetterRoleContext + +Adds options to copy role color and edit role when right clicking roles in the user profile + +![](https://github.com/Vendicated/Vencord/assets/45497981/d1765e9e-7db2-4a3c-b110-139c59235326) + diff --git a/src/plugins/betterRoleContext/index.tsx b/src/plugins/betterRoleContext/index.tsx new file mode 100644 index 000000000..7a914293a --- /dev/null +++ b/src/plugins/betterRoleContext/index.tsx @@ -0,0 +1,79 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { Devs } from "@utils/constants"; +import { getCurrentGuild } from "@utils/discord"; +import definePlugin from "@utils/types"; +import { findByPropsLazy } from "@webpack"; +import { Clipboard, Menu, PermissionStore, TextAndImagesSettingsStores } from "@webpack/common"; + +const GuildSettingsActions = findByPropsLazy("open", "selectRole", "updateGuild"); + +function PencilIcon() { + return ( + + + + ); +} + +function AppearanceIcon() { + return ( + + + + ); +} + +export default definePlugin({ + name: "BetterRoleContext", + description: "Adds options to copy role color / edit role when right clicking roles in the user profile", + authors: [Devs.Ven], + + start() { + // DeveloperMode needs to be enabled for the context menu to be shown + TextAndImagesSettingsStores.DeveloperMode.updateSetting(true); + }, + + contextMenus: { + "dev-context"(children, { id }: { id: string; }) { + const guild = getCurrentGuild(); + const role = guild?.roles[id]; + if (!role) return; + + if (role.colorString) { + children.push( + Clipboard.copy(role.colorString!)} + icon={AppearanceIcon} + /> + ); + } + + if (PermissionStore.getGuildPermissionProps(guild).canManageRoles) { + children.push( + { + await GuildSettingsActions.open(guild.id, "ROLES"); + GuildSettingsActions.selectRole(id); + }} + icon={PencilIcon} + /> + ); + } + } + } +}); diff --git a/src/webpack/common/settingsStores.ts b/src/webpack/common/settingsStores.ts index 6db21949a..4a48efda6 100644 --- a/src/webpack/common/settingsStores.ts +++ b/src/webpack/common/settingsStores.ts @@ -6,7 +6,10 @@ import { findByPropsLazy } from "@webpack"; -export const TextAndImagesSettingsStores = findByPropsLazy("MessageDisplayCompact"); -export const StatusSettingsStores = findByPropsLazy("ShowCurrentGame"); +import * as t from "./types/settingsStores"; + + +export const TextAndImagesSettingsStores = findByPropsLazy("MessageDisplayCompact") as Record; +export const StatusSettingsStores = findByPropsLazy("ShowCurrentGame") as Record; export const UserSettingsActionCreators = findByPropsLazy("PreloadedUserSettingsActionCreators"); diff --git a/src/webpack/common/types/index.d.ts b/src/webpack/common/types/index.d.ts index af4b5e1fb..01c968553 100644 --- a/src/webpack/common/types/index.d.ts +++ b/src/webpack/common/types/index.d.ts @@ -16,9 +16,11 @@ * along with this program. If not, see . */ +export * from "./classes"; export * from "./components"; export * from "./fluxEvents"; +export * from "./i18nMessages"; export * from "./menu"; +export * from "./settingsStores"; export * from "./stores"; export * from "./utils"; - diff --git a/src/webpack/common/types/settingsStores.ts b/src/webpack/common/types/settingsStores.ts new file mode 100644 index 000000000..5453ca352 --- /dev/null +++ b/src/webpack/common/types/settingsStores.ts @@ -0,0 +1,11 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +export interface SettingsStore { + getSetting(): T; + updateSetting(value: T): void; + useSetting(): T; +} From 688ff255d27de8b5bd611c7e678fac69c7f47032 Mon Sep 17 00:00:00 2001 From: Amia <9750071+aamiaa@users.noreply.github.com> Date: Fri, 8 Mar 2024 04:18:18 +0100 Subject: [PATCH 53/79] fix(MutualGroupDMs): update regex (#2242) --- src/plugins/mutualGroupDMs/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/mutualGroupDMs/index.tsx b/src/plugins/mutualGroupDMs/index.tsx index 40d5201cb..f5e4b6149 100644 --- a/src/plugins/mutualGroupDMs/index.tsx +++ b/src/plugins/mutualGroupDMs/index.tsx @@ -47,8 +47,8 @@ export default definePlugin({ { find: ".Messages.USER_PROFILE_MODAL", // Note: the module is lazy-loaded replacement: { - match: /(?<=\.MUTUAL_GUILDS\}\),)(?=(\i\.bot).{0,20}(\(0,\i\.jsx\)\(.{0,100}id:))/, - replace: '($1||arguments[0].isCurrentUser)?null:$2"MUTUAL_GDMS",children:"Mutual Groups"}),' + match: /(?<=\.tabBarItem.{0,50}MUTUAL_GUILDS.+?}\),)(?=.+?(\(0,\i\.jsxs?\)\(.{0,100}id:))/, + replace: '(arguments[0].user.bot||arguments[0].isCurrentUser)?null:$1"MUTUAL_GDMS",children:"Mutual Groups"}),' } }, { From 10f33b3dec4f2d197f117f780a6c8283f6f1046d Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 8 Mar 2024 00:23:35 -0300 Subject: [PATCH 54/79] Make more finds use filters.componentByCode --- src/plugins/reviewDB/components/ReviewsView.tsx | 6 +++--- src/webpack/common/components.ts | 2 +- src/webpack/webpack.ts | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/plugins/reviewDB/components/ReviewsView.tsx b/src/plugins/reviewDB/components/ReviewsView.tsx index eea92bb81..46bd7fb84 100644 --- a/src/plugins/reviewDB/components/ReviewsView.tsx +++ b/src/plugins/reviewDB/components/ReviewsView.tsx @@ -16,8 +16,8 @@ * along with this program. If not, see . */ -import { LazyComponent, useAwaiter, useForceUpdater } from "@utils/react"; -import { find, findByPropsLazy } from "@webpack"; +import { useAwaiter, useForceUpdater } from "@utils/react"; +import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; import { Forms, React, RelationshipStore, useRef, UserStore } from "@webpack/common"; import { Auth, authorize } from "../auth"; @@ -31,7 +31,7 @@ import ReviewComponent from "./ReviewComponent"; const { Editor, Transforms } = findByPropsLazy("Editor", "Transforms"); const { ChatInputTypes } = findByPropsLazy("ChatInputTypes"); -const InputComponent = LazyComponent(() => find(m => m.default?.type?.render?.toString().includes("default.CHANNEL_TEXT_AREA")).default); +const InputComponent = findComponentByCodeLazy("default.CHANNEL_TEXT_AREA"); const { createChannelRecordFromServer } = findByPropsLazy("createChannelRecordFromServer"); interface UserProps { diff --git a/src/webpack/common/components.ts b/src/webpack/common/components.ts index d7bb5d759..048e65d68 100644 --- a/src/webpack/common/components.ts +++ b/src/webpack/common/components.ts @@ -51,7 +51,7 @@ export let Avatar: t.Avatar; /** css colour resolver stuff, no clue what exactly this does, just copied usage from Discord */ export let useToken: t.useToken; -export const MaskedLink = waitForComponent("MaskedLink", m => m?.type?.toString().includes("MASKED_LINK)")); +export const MaskedLink = waitForComponent("MaskedLink", filters.componentByCode("MASKED_LINK)")); export const Timestamp = waitForComponent("Timestamp", filters.byCode(".Messages.MESSAGE_EDITED_TIMESTAMP_A11Y_LABEL.format")); export const Flex = waitForComponent("Flex", ["Justify", "Align", "Wrap"]); diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index 0f7d8b73c..992bf38f3 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -60,6 +60,7 @@ export const filters = { return m => { if (filter(m)) return true; if (!m.$$typeof) return false; + if (m.type && m.type.render) return filter(m.type.render); // memo + forwardRef if (m.type) return filter(m.type); // memos if (m.render) return filter(m.render); // forwardRefs return false; From cf7830e747a5f2ff0b1bace93a9fab13f6936477 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 8 Mar 2024 00:24:04 -0300 Subject: [PATCH 55/79] ShowHiddenChannels: Fix patches --- src/plugins/showHiddenChannels/index.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugins/showHiddenChannels/index.tsx b/src/plugins/showHiddenChannels/index.tsx index 919f3f3c5..2d091c24a 100644 --- a/src/plugins/showHiddenChannels/index.tsx +++ b/src/plugins/showHiddenChannels/index.tsx @@ -305,27 +305,27 @@ export default definePlugin({ ] }, { - find: ".avatars),children", + find: '+1]})},"overflow"))', replacement: [ { // Create a variable for the channel prop - match: /maxUsers:\i,users:\i.+?=(\i).+?;/, + match: /maxUsers:\i,users:\i.+?}=(\i).*?;/, replace: (m, props) => `${m}let{shcChannel}=${props};` }, { // Make Discord always render the plus button if the component is used inside the HiddenChannelLockScreen match: /\i>0(?=&&.{0,60}renderPopout)/, - replace: m => `($self.isHiddenChannel(shcChannel,true)?true:${m})` + replace: m => `($self.isHiddenChannel(typeof shcChannel!=="undefined"?shcChannel:void 0,true)?true:${m})` }, { // Prevent Discord from overwriting the last children with the plus button if the overflow amount is <= 0 and the component is used inside the HiddenChannelLockScreen match: /(?<=\.value\(\),(\i)=.+?length-)1(?=\]=.{0,60}renderPopout)/, - replace: (_, amount) => `($self.isHiddenChannel(shcChannel,true)&&${amount}<=0?0:1)` + replace: (_, amount) => `($self.isHiddenChannel(typeof shcChannel!=="undefined"?shcChannel:void 0,true)&&${amount}<=0?0:1)` }, { // Show only the plus text without overflowed children amount if the overflow amount is <= 0 and the component is used inside the HiddenChannelLockScreen match: /(?<="\+",)(\i)\+1/, - replace: (m, amount) => `$self.isHiddenChannel(shcChannel,true)&&${amount}<=0?"":${m}` + replace: (m, amount) => `$self.isHiddenChannel(typeof shcChannel!=="undefined"?shcChannel:void 0,true)&&${amount}<=0?"":${m}` } ] }, From b0d37c981e6bbb47b4b25f28d3e87488f9038bac Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 8 Mar 2024 00:29:06 -0300 Subject: [PATCH 56/79] Bump to 1.7.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index dde55d311..dbf8aaf34 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.7.0", + "version": "1.7.1", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { From 992533245bc08ccaac258aaca803d362942dde74 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 8 Mar 2024 19:05:30 -0300 Subject: [PATCH 57/79] RoleColorEverywhere: Wrap roleGroupColor in ErrorBoundary --- src/plugins/roleColorEverywhere/index.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/roleColorEverywhere/index.tsx b/src/plugins/roleColorEverywhere/index.tsx index 968027163..14d38e8ee 100644 --- a/src/plugins/roleColorEverywhere/index.tsx +++ b/src/plugins/roleColorEverywhere/index.tsx @@ -17,6 +17,7 @@ */ import { definePluginSettings } from "@api/Settings"; +import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { ChannelStore, GuildMemberStore, GuildStore } from "@webpack/common"; @@ -112,7 +113,7 @@ export default definePlugin({ return colorString && parseInt(colorString.slice(1), 16); }, - roleGroupColor({ id, count, title, guildId, label }: { id: string; count: number; title: string; guildId: string; label: string; }) { + roleGroupColor: ErrorBoundary.wrap(({ id, count, title, guildId, label }: { id: string; count: number; title: string; guildId: string; label: string; }) => { const guild = GuildStore.getGuild(guildId); const role = guild?.roles[id]; @@ -125,7 +126,7 @@ export default definePlugin({ {title ?? label} — {count} ); - }, + }, { noop: true }), getVoiceProps({ user: { id: userId }, guildId }: { user: { id: string; }; guildId: string; }) { return { From 497f0de9a1dc7f228a67e73b25f12c0d6fa93240 Mon Sep 17 00:00:00 2001 From: Jack <30497388+FieryFlames@users.noreply.github.com> Date: Mon, 11 Mar 2024 10:52:11 -0400 Subject: [PATCH 58/79] chore(Decor): Change URL formula for cost savings (#2247) --- src/plugins/decor/index.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/plugins/decor/index.tsx b/src/plugins/decor/index.tsx index 8cfd8c036..713b17d76 100644 --- a/src/plugins/decor/index.tsx +++ b/src/plugins/decor/index.tsx @@ -131,9 +131,10 @@ export default definePlugin({ getDecorAvatarDecorationURL({ avatarDecoration, canAnimate }: { avatarDecoration: AvatarDecoration | null; canAnimate?: boolean; }) { // Only Decor avatar decorations have this SKU ID if (avatarDecoration?.skuId === SKU_ID) { - const url = new URL(`${CDN_URL}/${avatarDecoration.asset}.png`); - url.searchParams.set("animate", (!!canAnimate && isAnimatedAvatarDecoration(avatarDecoration.asset)).toString()); - return url.toString(); + const parts = avatarDecoration.asset.split("_"); + // Remove a_ prefix if it's animated and animation is disabled + if (isAnimatedAvatarDecoration(avatarDecoration.asset) && !canAnimate) parts.shift(); + return `${CDN_URL}/${parts.join("_")}.png`; } else if (avatarDecoration?.skuId === RAW_SKU_ID) { return avatarDecoration.asset; } From 34390e03652f523fd92e3974feb93395bb825bec Mon Sep 17 00:00:00 2001 From: Vendicated Date: Mon, 11 Mar 2024 16:13:07 +0100 Subject: [PATCH 59/79] Add workaround for guild role api changes on canary/ptb fixes RoleColorEverywhere, ServerInfo, PermissionViewer, BetterRoleContext --- src/plugins/betterRoleContext/index.tsx | 6 ++++-- .../components/RolesAndUsersPermissions.tsx | 8 +++++--- src/plugins/permissionsViewer/index.tsx | 3 ++- src/plugins/permissionsViewer/utils.ts | 13 ++++++++----- src/plugins/roleColorEverywhere/index.tsx | 6 +++--- src/plugins/serverProfile/GuildProfileModal.tsx | 4 ++-- src/plugins/xsOverlay.desktop/index.ts | 3 ++- src/utils/discord.tsx | 10 +++++++++- 8 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/plugins/betterRoleContext/index.tsx b/src/plugins/betterRoleContext/index.tsx index 7a914293a..e73779adf 100644 --- a/src/plugins/betterRoleContext/index.tsx +++ b/src/plugins/betterRoleContext/index.tsx @@ -5,7 +5,7 @@ */ import { Devs } from "@utils/constants"; -import { getCurrentGuild } from "@utils/discord"; +import { getCurrentGuild, getGuildRoles } from "@utils/discord"; import definePlugin from "@utils/types"; import { findByPropsLazy } from "@webpack"; import { Clipboard, Menu, PermissionStore, TextAndImagesSettingsStores } from "@webpack/common"; @@ -47,7 +47,9 @@ export default definePlugin({ contextMenus: { "dev-context"(children, { id }: { id: string; }) { const guild = getCurrentGuild(); - const role = guild?.roles[id]; + if (!guild) return; + + const role = getGuildRoles(guild.id)[id]; if (!role) return; if (role.colorString) { diff --git a/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx b/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx index e0d25c7ab..adadc90b5 100644 --- a/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx +++ b/src/plugins/permissionsViewer/components/RolesAndUsersPermissions.tsx @@ -19,7 +19,7 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Flex } from "@components/Flex"; import { InfoIcon, OwnerCrownIcon } from "@components/Icons"; -import { getUniqueUsername } from "@utils/discord"; +import { getGuildRoles, getUniqueUsername } from "@utils/discord"; import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; import { ContextMenuApi, FluxDispatcher, GuildMemberStore, Menu, PermissionsBits, Text, Tooltip, useEffect, UserStore, useState, useStateFromStores } from "@webpack/common"; import type { Guild } from "discord-types/general"; @@ -78,6 +78,8 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea const [selectedItemIndex, selectItem] = useState(0); const selectedItem = permissions[selectedItemIndex]; + const roles = getGuildRoles(guild.id); + return ( {permissions.map((permission, index) => { const user = UserStore.getUser(permission.id ?? ""); - const role = guild.roles[permission.id ?? ""]; + const role = roles[permission.id ?? ""]; return (