From 7dca07569d656ac1fd399e4d8dfecdc4ebaaa94d Mon Sep 17 00:00:00 2001 From: sadan4 <117494111+sadan4@users.noreply.github.com> Date: Thu, 23 Apr 2026 23:49:49 -0400 Subject: [PATCH] fix various broken things (#4141) Co-authored-by: Vendicated Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Co-authored-by: thororen1234 <78185467+thororen1234@users.noreply.github.com> --- src/api/PluginManager.ts | 3 + src/debug/loadLazyChunks.ts | 5 +- src/debug/runReporter.ts | 9 ++- .../_core/concatenatedComponentExtractor.tsx | 37 +++++++++ src/plugins/betterFolders/index.tsx | 2 +- src/plugins/betterSettings/index.tsx | 2 +- src/plugins/characterCounter/index.tsx | 4 +- src/plugins/ctrlEnterSend/index.ts | 78 ------------------- src/plugins/decor/index.tsx | 22 ++++-- src/plugins/decor/ui/components/index.ts | 4 +- src/plugins/experiments/index.tsx | 15 +++- src/plugins/friendsSince/index.tsx | 2 +- src/plugins/greetStickerPicker/index.tsx | 14 +++- src/plugins/imageZoom/index.tsx | 2 +- src/plugins/implicitRelationships/index.ts | 2 +- src/plugins/messageLinkEmbeds/index.tsx | 18 ++++- src/plugins/mutualGroupDMs/index.tsx | 15 +++- src/plugins/reviewDB/index.tsx | 3 +- src/plugins/seeSummaries/index.tsx | 26 ++++++- src/plugins/showConnections/index.tsx | 3 +- .../components/HiddenChannelLockScreen.tsx | 5 +- src/plugins/showHiddenChannels/index.tsx | 12 ++- src/plugins/vcDoubleClick/index.ts | 15 ++-- src/plugins/voiceMessages/VoicePreview.tsx | 10 +-- src/plugins/voiceMessages/index.tsx | 21 +++++ src/webpack/common/components.ts | 24 +++--- src/webpack/patchWebpack.ts | 2 - 27 files changed, 217 insertions(+), 138 deletions(-) create mode 100644 src/plugins/_core/concatenatedComponentExtractor.tsx delete mode 100644 src/plugins/ctrlEnterSend/index.ts diff --git a/src/api/PluginManager.ts b/src/api/PluginManager.ts index b8ea8081e..d6e7bcefb 100644 --- a/src/api/PluginManager.ts +++ b/src/api/PluginManager.ts @@ -55,6 +55,9 @@ export function isPluginEnabled(p: string) { } export function addPatch(newPatch: Omit, pluginName: string, pluginPath = `Vencord.Plugins.plugins[${JSON.stringify(pluginName)}]`) { + // TODO: this causes crashes + if (pluginName === "Vesktop" && newPatch.find === ".STREAMING_AUTO_STREAMER_MODE,") return; + const patch = newPatch as Patch; patch.plugin = pluginName; diff --git a/src/debug/loadLazyChunks.ts b/src/debug/loadLazyChunks.ts index 05a58ef1c..920ce3067 100644 --- a/src/debug/loadLazyChunks.ts +++ b/src/debug/loadLazyChunks.ts @@ -54,7 +54,10 @@ export async function loadLazyChunks() { async function searchAndLoadLazyChunks(factoryCode: string) { // Workaround to avoid loading the CSS debugging chunk which turns the app pink - const hasCssDebuggingLoad = foundCssDebuggingLoad ? false : (foundCssDebuggingLoad = factoryCode.includes(".cssDebuggingEnabled&&")); + // const hasCssDebuggingLoad = foundCssDebuggingLoad ? false : (foundCssDebuggingLoad = factoryCode.includes(".cssDebuggingEnabled&&")); + + // Disabled for now since this causes lots of chunks concatenated into the same module get marked as invalid, and thus not loaded. + const hasCssDebuggingLoad = foundCssDebuggingLoad = false; const lazyChunks = factoryCode.matchAll(hasCssDebuggingLoad ? CompleteLazyChunkRegex : PartialLazyChunkRegex); const validChunkGroups = new Set<[chunkIds: PropertyKey[], entryPoint: PropertyKey]>(); diff --git a/src/debug/runReporter.ts b/src/debug/runReporter.ts index bcd178338..2be235dfa 100644 --- a/src/debug/runReporter.ts +++ b/src/debug/runReporter.ts @@ -37,6 +37,13 @@ async function runReporter() { await loadLazyChunksDone; + // Manually require all modules to make sure all lazily required modules are patched + for (const moduleId of Object.keys(Webpack.wreq.m)) { + try { + Webpack.wreq(moduleId); + } catch { } + } + if (IS_REPORTER && IS_WEB && !IS_VESKTOP) { console.log("[REPORTER_META]", { buildNumber: getBuildNumber(), @@ -51,7 +58,7 @@ async function runReporter() { } for (const [plugin, moduleId, match, totalTime] of patchTimings) { - if (totalTime > 5) { + if (totalTime > 10) { new Logger("WebpackPatcher").warn(`Patch by ${plugin} took ${Math.round(totalTime * 100) / 100}ms (Module id is ${String(moduleId)}): ${match}`); } } diff --git a/src/plugins/_core/concatenatedComponentExtractor.tsx b/src/plugins/_core/concatenatedComponentExtractor.tsx new file mode 100644 index 000000000..7fd23ff77 --- /dev/null +++ b/src/plugins/_core/concatenatedComponentExtractor.tsx @@ -0,0 +1,37 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2026 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { Devs } from "@utils/constants"; +import definePlugin from "@utils/types"; +import { setColorPicker, setCreateScroller } from "@webpack/common"; + +export default definePlugin({ + name: "ConcatenatedComponentExtractor", + description: "Extract components that have been concatenated by the bundler", + authors: [Devs.sadan], + + required: true, + + patches: [ + { + find: "#{intl::USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR}),focusProps:", + replacement: { + match: /(?=function (\i)\(\i\)\{let\{onChange:\i,onClose:\i,[^}]+?showEyeDropper:)/, + replace: "$self.setColorPicker($1);" + } + }, + { + find: /="ltr",orientation:\i="vertical"[^}]+?customTheme:/, + replacement: { + match: /(?=function (\i)\(\i,\i,\i\)\{.{0,20}?return \i\.forwardRef\(function\(\i,\i\)\{let\{[^}]+?="ltr",orientation:)/, + replace: "$self.setCreateScroller($1);" + } + } + ], + + setCreateScroller, + setColorPicker, +}); diff --git a/src/plugins/betterFolders/index.tsx b/src/plugins/betterFolders/index.tsx index 8878b8d80..97677251f 100644 --- a/src/plugins/betterFolders/index.tsx +++ b/src/plugins/betterFolders/index.tsx @@ -197,7 +197,7 @@ export default definePlugin({ { // Modify the expanded prop to use the boolean if the above patch fails, or check if the folder is expanded from the list if it succeeds // Also export the list of expanded folders to the child folder component if the patch above succeeds, else export undefined - match: /(?<=folderNode:(\i),expanded:)\i(?=,)/, + match: /(?<=\(0,\i\.jsxs?\)\(\i,\{folderNode:(\i),expanded:)\i(?=,)/, replace: (isExpandedOrExpandedIds, folderNote) => "" + `typeof ${isExpandedOrExpandedIds}==="boolean"?${isExpandedOrExpandedIds}:${isExpandedOrExpandedIds}.has(${folderNote}.id),` + `betterFoldersExpandedIds:${isExpandedOrExpandedIds} instanceof Set?${isExpandedOrExpandedIds}:void 0` diff --git a/src/plugins/betterSettings/index.tsx b/src/plugins/betterSettings/index.tsx index d37fbe666..23f3c1ec8 100644 --- a/src/plugins/betterSettings/index.tsx +++ b/src/plugins/betterSettings/index.tsx @@ -155,7 +155,7 @@ export default definePlugin({ predicate: () => settings.store.organizeMenu, replacement: [ { - match: /children:\[(\i),(?<=\1=.{0,30}\.openUserSettings.+?)/, + match: /children:\[(\i),(?<=\1=(?:function|.{0,30}\.openUserSettings).+?)/, // TODO .{0,30}\.openUserSettings is stable compat replace: "children:[$self.transformSettingsEntries($1),", }, ] diff --git a/src/plugins/characterCounter/index.tsx b/src/plugins/characterCounter/index.tsx index 57b9c3f30..f2826bccf 100644 --- a/src/plugins/characterCounter/index.tsx +++ b/src/plugins/characterCounter/index.tsx @@ -50,8 +50,8 @@ export default definePlugin({ { find: "#{intl::PREMIUM_MESSAGE_LENGTH_UPSELL_TOOLTIP}", replacement: { - match: /return \i\?\i\(\):\i\(\)(?<=#{intl::PREMIUM_MESSAGE_LENGTH_UPSELL_TOOLTIP_WITHOUT_LINK}.{0,200}?)/, - replace: "return null" + match: /(?<=\.PREMIUM_UPSELL\);)(?=.{0,50}\.PREMIUM_UPSELL_VIEWED)/, + replace: "return null;" } } ], diff --git a/src/plugins/ctrlEnterSend/index.ts b/src/plugins/ctrlEnterSend/index.ts deleted file mode 100644 index bd52f5ad6..000000000 --- a/src/plugins/ctrlEnterSend/index.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2023 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -import { definePluginSettings } from "@api/Settings"; -import { Devs, IS_MAC } from "@utils/constants"; -import definePlugin, { OptionType } from "@utils/types"; - -export default definePlugin({ - name: "CtrlEnterSend", - authors: [Devs.UlyssesZhan], - description: "Use Ctrl+Enter to send messages (customizable)", - tags: ["Shortcuts", "Chat"], - settings: definePluginSettings({ - submitRule: { - description: "The way to send a message", - type: OptionType.SELECT, - options: [ - { - label: "Ctrl+Enter (Enter or Shift+Enter for new line) (cmd+enter on macOS)", - value: "ctrl+enter" - }, - { - label: "Shift+Enter (Enter for new line)", - value: "shift+enter" - }, - { - label: "Enter (Shift+Enter for new line; Discord default)", - value: "enter" - } - ], - default: "ctrl+enter" - }, - sendMessageInTheMiddleOfACodeBlock: { - description: "Whether to send a message in the middle of a code block", - type: OptionType.BOOLEAN, - default: true, - } - }), - patches: [ - // Only one of the two patches will be at effect; Discord often updates to switch between them. - // See: https://discord.com/channels/1015060230222131221/1032770730703716362/1261398512017477673 - { - find: ".selectPreviousCommandOption(", - replacement: { - match: /(?<=(\i)\.key!==\i\.\i.ENTER\|\|).{0,100}(\(0,\i\.\i\)\(\i\)).{0,100}(?=\|\|\(\i\.preventDefault)/, - replace: "!$self.shouldSubmit($1,$2)" - } - }, - { - find: "!this.hasOpenCodeBlock()", - replacement: { - match: /!(\i).shiftKey&&!(this.hasOpenCodeBlock\(\))&&\(.{0,100}?\)/, - replace: "$self.shouldSubmit($1, $2)" - } - } - ], - shouldSubmit(event: KeyboardEvent, codeblock: boolean): boolean { - let result = false; - switch (this.settings.store.submitRule) { - case "shift+enter": - result = event.shiftKey; - break; - case "ctrl+enter": - result = IS_MAC ? event.metaKey : event.ctrlKey; - break; - case "enter": - result = !event.shiftKey && !event.ctrlKey; - break; - } - if (!this.settings.store.sendMessageInTheMiddleOfACodeBlock) { - result &&= !codeblock; - } - return result; - } -}); diff --git a/src/plugins/decor/index.tsx b/src/plugins/decor/index.tsx index 94e9a3e59..280b70beb 100644 --- a/src/plugins/decor/index.tsx +++ b/src/plugins/decor/index.tsx @@ -16,7 +16,7 @@ import { useAuthorizationStore } from "./lib/stores/AuthorizationStore"; import { useCurrentUserDecorationsStore } from "./lib/stores/CurrentUserDecorationsStore"; import { useUserDecorAvatarDecoration, useUsersDecorationsStore } from "./lib/stores/UsersDecorationsStore"; import { settings } from "./settings"; -import { setDecorationGridDecoration, setDecorationGridItem } from "./ui/components"; +import { setAvatarDecorationModalPreview, setDecorationGridDecoration, setDecorationGridItem } from "./ui/components"; import DecorSection from "./ui/components/DecorSection"; export interface AvatarDecoration { @@ -60,7 +60,7 @@ export default definePlugin({ }, // Remove NEW label from decor avatar decorations { - match: /(?<=\.\i\.PURCHASE)(?=,)(?<=avatarDecoration:(\i).+?)/, + match: /(?<=\i\.PURCHASE)(?=,)(?<=avatarDecoration:(\i).+?)/, replace: "||$1.skuId===$self.SKU_ID" } ] @@ -111,10 +111,16 @@ export default definePlugin({ // Patch avatar decoration preview to display Decor avatar decorations as if they are purchased { find: "#{intl::PREMIUM_UPSELL_PROFILE_AVATAR_DECO_INLINE_UPSELL_DESCRIPTION}", - replacement: { - match: /(#{intl::PREMIUM_UPSELL_PROFILE_AVATAR_DECO_INLINE_UPSELL_DESCRIPTION}.+?return null!=(\i)&&\()(null==\i)/, - replace: (_, rest, avatarDecoration, hasPurchase) => `${rest}(${avatarDecoration}.skuId!==$self.SKU_ID&&${avatarDecoration}.skuId!==$self.RAW_SKU_ID&&${hasPurchase})` - } + replacement: [ + { + match: /(#{intl::PREMIUM_UPSELL_PROFILE_AVATAR_DECO_INLINE_UPSELL_DESCRIPTION}.+?return null!=(\i)&&\()(null==\i)/, + replace: (_, rest, avatarDecoration, hasPurchase) => `${rest}(${avatarDecoration}.skuId!==$self.SKU_ID&&${avatarDecoration}.skuId!==$self.RAW_SKU_ID&&${hasPurchase})` + }, + { + match: /(?<==)\i=>{let{user:\i,guildId:\i,avatarDecoration:/, + replace: "$self.AvatarDecorationModalPreview=$&" + } + ] } ], settings, @@ -138,6 +144,10 @@ export default definePlugin({ setDecorationGridDecoration(e); }, + set AvatarDecorationModalPreview(e: any) { + setAvatarDecorationModalPreview(e); + }, + SKU_ID, RAW_SKU_ID, diff --git a/src/plugins/decor/ui/components/index.ts b/src/plugins/decor/ui/components/index.ts index 713f8058b..fa81da177 100644 --- a/src/plugins/decor/ui/components/index.ts +++ b/src/plugins/decor/ui/components/index.ts @@ -5,7 +5,6 @@ */ import { AvatarDecoration } from "@plugins/decor"; -import { findComponentByCodeLazy } from "@webpack"; import type { ComponentType, HTMLProps, PropsWithChildren } from "react"; type DecorationGridItemComponent = ComponentType> & { @@ -16,7 +15,8 @@ type DecorationGridItemComponent = ComponentType DecorationGridItem = v; -export const AvatarDecorationModalPreview = findComponentByCodeLazy("#{intl::PREMIUM_UPSELL_PROFILE_AVATAR_DECO_INLINE_UPSELL_DESCRIPTION}"); +export let AvatarDecorationModalPreview: ComponentType = () => null; +export const setAvatarDecorationModalPreview = v => AvatarDecorationModalPreview = v; type DecorationGridDecorationComponent = React.ComponentType & { avatarDecoration: AvatarDecoration; diff --git a/src/plugins/experiments/index.tsx b/src/plugins/experiments/index.tsx index 69a063891..7447d532f 100644 --- a/src/plugins/experiments/index.tsx +++ b/src/plugins/experiments/index.tsx @@ -24,13 +24,13 @@ import { Paragraph } from "@components/Paragraph"; import { Devs, IS_MAC } from "@utils/constants"; import { Margins } from "@utils/margins"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy, findLazy } from "@webpack"; +import { findByPropsLazy } from "@webpack"; import { Forms, React } from "@webpack/common"; import hideBugReport from "./hideBugReport.css?managed"; const KbdStyles = findByPropsLazy("key", "combo"); -const BugReporterExperiment = findLazy(m => m?.definition?.name === "2026-01-bug-reporter"); +let BugReporterExperiment: any; const modKey = IS_MAC ? "cmd" : "ctrl"; const altKey = IS_MAC ? "opt" : "alt"; @@ -127,9 +127,20 @@ export default definePlugin({ match: /}getServerAssignment\((\i),\i,\i\){/, replace: "$&if($1==null)return;" } + }, + { + find: "2026-01-bug-reporter", + replacement: { + match: /(?<==)(?=\(0,\i\(.+?\)\.\i\)\({name:"2026-01-bug-reporter")/, + replace: "$self.BugReporterExperiment=" + } } ], + set BugReporterExperiment(value: any) { + BugReporterExperiment = value; + }, + start: () => !BugReporterExperiment.getConfig().hasBugReporterAccess && enableStyle(hideBugReport), stop: () => disableStyle(hideBugReport), diff --git a/src/plugins/friendsSince/index.tsx b/src/plugins/friendsSince/index.tsx index f246cd910..8adf143b6 100644 --- a/src/plugins/friendsSince/index.tsx +++ b/src/plugins/friendsSince/index.tsx @@ -27,7 +27,7 @@ export default definePlugin({ patches: [ // DM User Sidebar { - find: ".SIDEBAR}),nicknameIcons", + find: "#{intl::PREMIUM_GIFTING_BUTTON}),action:", replacement: { match: /#{intl::USER_PROFILE_MEMBER_SINCE}\),.{0,100}userId:(\i\.id)}\)}\)/, replace: "$&,$self.FriendsSinceComponent({userId:$1,isSidebar:true})" diff --git a/src/plugins/greetStickerPicker/index.tsx b/src/plugins/greetStickerPicker/index.tsx index 216498e24..326a6fcea 100644 --- a/src/plugins/greetStickerPicker/index.tsx +++ b/src/plugins/greetStickerPicker/index.tsx @@ -20,7 +20,6 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { Channel, Message } from "@vencord/discord-types"; -import { findLazy } from "@webpack"; import { ContextMenuApi, FluxDispatcher, Menu, MessageActions } from "@webpack/common"; enum GreetMode { @@ -42,7 +41,7 @@ const settings = definePluginSettings({ unholyMultiGreetEnabled?: boolean; }>(); -const WELCOME_STICKERS = findLazy(m => Array.isArray(m) && m[0]?.name === "Wave"); +let WELCOME_STICKERS: any; function greet(channel: Channel, message: Message, stickers: string[]) { const options = MessageActions.getSendMessageOptionsForReply({ @@ -165,9 +164,20 @@ export default definePlugin({ match: /className:\i\.\i,(?=.{0,40}?"sticker")(?<={channel:\i,message:\i}=(\i).+?)/, replace: "$&onContextMenu:(vcEvent)=>$self.pickSticker(vcEvent, $1)," } + }, + { + find: '"Wumpus waves hello"', + replacement: { + match: /(?<==)(?=\[{id:"749054660769218631",format_type:\d,description:"Wumpus waves hello")/, + replace: "$self.WELCOME_STICKERS=" + } } ], + set WELCOME_STICKERS(value: any) { + WELCOME_STICKERS = value; + }, + pickSticker( event: React.UIEvent, props: { diff --git a/src/plugins/imageZoom/index.tsx b/src/plugins/imageZoom/index.tsx index b1a6972f9..0d3aeda46 100644 --- a/src/plugins/imageZoom/index.tsx +++ b/src/plugins/imageZoom/index.tsx @@ -171,7 +171,7 @@ export default definePlugin({ replace: `id:"${ELEMENT_ID}",$&` }, { - match: /(?<=null!=(\i)\?.{0,20})\i\.\i,{children:\1/, + match: /(?<=null!=(\i)\?.{0,20})\i(?:\.\i)?,{children:\1/, // TODO: (?:\.\i)? is stable compat replace: "'div',{onClick:e=>e.stopPropagation(),children:$1" } ] diff --git a/src/plugins/implicitRelationships/index.ts b/src/plugins/implicitRelationships/index.ts index 3af463e3f..14126c7e3 100644 --- a/src/plugins/implicitRelationships/index.ts +++ b/src/plugins/implicitRelationships/index.ts @@ -96,7 +96,7 @@ export default definePlugin({ { find: ".REQUEST_GUILD_MEMBERS,", replacement: { - match: /\.send\(\i\.\i\.REQUEST_GUILD_MEMBERS,{/, + match: /\.send\(\i\.REQUEST_GUILD_MEMBERS,{/, replace: "$&nonce:arguments[1].nonce," } }, diff --git a/src/plugins/messageLinkEmbeds/index.tsx b/src/plugins/messageLinkEmbeds/index.tsx index 7dee0a9d4..6ce6c0a2a 100644 --- a/src/plugins/messageLinkEmbeds/index.tsx +++ b/src/plugins/messageLinkEmbeds/index.tsx @@ -40,7 +40,7 @@ import { Text, UserStore } from "@webpack/common"; -import { JSX } from "react"; +import { ComponentType, JSX } from "react"; const messageCache = new Map(); const Embed = findComponentLazy(m => m.prototype?.renderSuppressButton); -const AutoModEmbed = findComponentByCodeLazy("withFooter", "childrenMessageContent:"); const ChannelMessage = findComponentByCodeLazy("childrenExecutedCommand:", ".hideAccessories"); +let AutoModEmbed: ComponentType = () => null; const SearchResultClasses = findCssClassesLazy("message", "searchResult"); const EmbedClasses = findCssClassesLazy("embedAuthorIcon", "embedAuthor", "embedAuthor", "embedMargin"); @@ -373,6 +373,20 @@ export default definePlugin({ settings, + patches: [ + { + find: "!1,withFooter:", + replacement: { + match: /(?=function (\i)\(\i\){let{message:\i,channel:\i,[^}]+?withFooter:)/, + replace: "$self.AutoModEmbed=$1;" + } + } + ], + + set AutoModEmbed(value: any) { + AutoModEmbed = value; + }, + start() { addMessageAccessory("MessageLinkEmbeds", props => { if (!messageLinkRegex.test(props.message.content)) diff --git a/src/plugins/mutualGroupDMs/index.tsx b/src/plugins/mutualGroupDMs/index.tsx index 88cc6d15a..d98519ed9 100644 --- a/src/plugins/mutualGroupDMs/index.tsx +++ b/src/plugins/mutualGroupDMs/index.tsx @@ -25,9 +25,9 @@ import { isNonNullish } from "@utils/guards"; import { Logger } from "@utils/Logger"; import definePlugin from "@utils/types"; import { Channel, User } from "@vencord/discord-types"; -import { findByPropsLazy, findComponentByCodeLazy, findCssClassesLazy } from "@webpack"; +import { findByPropsLazy, findCssClassesLazy } from "@webpack"; import { Avatar, ChannelStore, Clickable, IconUtils, RelationshipStore, ScrollerThin, Text, useMemo, UserStore } from "@webpack/common"; -import { JSX } from "react"; +import { ComponentType, JSX } from "react"; const SelectedChannelActionCreators = findByPropsLazy("selectPrivateChannel"); const UserUtils = findByPropsLazy("getGlobalName"); @@ -35,7 +35,8 @@ const UserUtils = findByPropsLazy("getGlobalName"); const ProfileListClasses = findCssClassesLazy("empty", "textContainer", "connectionIcon"); const TabBarClasses = findCssClassesLazy("tabPanelScroller", "tabBarPanel"); const MutualsListClasses = findCssClassesLazy("row", "icon", "name", "details"); -const ExpandableList = findComponentByCodeLazy('action:"PRESS_SECTION"', '"section"'); + +let ExpandableList: ComponentType = () => null; function getGroupDMName(channel: Channel) { return channel.name || @@ -140,11 +141,19 @@ export default definePlugin({ match: /\.openUserProfileModal.+?\)}\)}\)(?<=,(\i)&&(\i)&&(\(0,\i\.jsxs?\)\(\i\.\i,{className:(\i)\.\i}\)).{0,50}?"MUTUAL_FRIENDS".+?)/, replace: (m, hasMutualGuilds, hasMutualFriends, Divider, classes) => "" + `${m},$self.renderDMPageList({user:arguments[0].user,hasDivider:${hasMutualGuilds}||${hasMutualFriends},Divider:${Divider},listStyle:${classes}.list})` + }, + { + match: /(?=function (\i)\(\i\){let{section:\i,header:\i[^}]+?onExpand:)/, + replace: "$self.ExpandableList=$1;" } ] } ], + set ExpandableList(value: any) { + ExpandableList = value; + }, + getMutualGroupDms(userId: string) { try { return getMutualGroupDms(userId); diff --git a/src/plugins/reviewDB/index.tsx b/src/plugins/reviewDB/index.tsx index 6c80cdf62..78332f91f 100644 --- a/src/plugins/reviewDB/index.tsx +++ b/src/plugins/reviewDB/index.tsx @@ -93,7 +93,8 @@ export default definePlugin({ }, { // User popout - find: /\.POPOUT,onClose:\i}\),nicknameIcons:.+?\.isProvisional/, + // Same find as ShowConnections + find: "#{intl::XcTHmQ::raw}", replacement: { match: /user:(\i),widgets:.{0,100}?\}\),/, replace: "$&$self.renderProfileComponent({user:$1})," diff --git a/src/plugins/seeSummaries/index.tsx b/src/plugins/seeSummaries/index.tsx index 29dd7d383..1b5ff70c5 100644 --- a/src/plugins/seeSummaries/index.tsx +++ b/src/plugins/seeSummaries/index.tsx @@ -9,11 +9,10 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import { hasGuildFeature } from "@utils/discord"; import definePlugin, { OptionType } from "@utils/types"; -import { findByCodeLazy, findByPropsLazy } from "@webpack"; +import { findByPropsLazy } from "@webpack"; import { ChannelStore, GuildStore } from "@webpack/common"; const SummaryStore = findByPropsLazy("allSummaries", "findSummary"); -const createSummaryFromServer = findByCodeLazy(".people)),startId:", ".type}"); const settings = definePluginSettings({ summaryExpiryThresholdDays: { @@ -39,7 +38,7 @@ interface Summary { unsafe: boolean; } -interface ChannelSummaries { +interface ChannelSummary { type: string; channel_id: string; guild_id: string; @@ -48,6 +47,21 @@ interface ChannelSummaries { // custom property time?: number; } +// TODO: these types are wrong and evil and incorrect +function createChannelSummaryFromServer(s: Summary, channelId: string): ChannelSummary { + return { + id: s.id, + topic: s.topic, + summShort: s.summ_short, + people: Array.from(new Set(s.people)), + startId: s.start_id, + endId: s.end_id, + count: s.count, + channelId, + source: s.source, + type: s.type as any, + } as any as ChannelSummary; +} export default definePlugin({ name: "Summaries", @@ -71,9 +85,13 @@ export default definePlugin({ } } ], + flux: { CONVERSATION_SUMMARY_UPDATE(data) { - const incomingSummaries: ChannelSummaries[] = data.summaries.map((summary: any) => ({ ...createSummaryFromServer(summary), time: Date.now() })); + const incomingSummaries: ChannelSummary[] = data.summaries.map((summary: any) => ({ + ...createChannelSummaryFromServer(summary, undefined!), + time: Date.now() + })); // idk if this is good for performance but it doesnt seem to be a problem in my experience DataStore.update("summaries-data", summaries => { diff --git a/src/plugins/showConnections/index.tsx b/src/plugins/showConnections/index.tsx index 5ec9e38c0..4e4196068 100644 --- a/src/plugins/showConnections/index.tsx +++ b/src/plugins/showConnections/index.tsx @@ -161,7 +161,8 @@ export default definePlugin({ patches: [ { - find: /\.POPOUT,onClose:\i}\),nicknameIcons:.+?\.isProvisional/, + // Same find as ReviewDB + find: "#{intl::XcTHmQ::raw}", replacement: { match: /userId:\i\.id,guild:\i\}\)(?=])/, replace: "$&,$self.profilePopoutComponent(arguments[0])" diff --git a/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx b/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx index 9850a7514..e9f6c52c7 100644 --- a/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx +++ b/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx @@ -27,6 +27,7 @@ import { formatDuration } from "@utils/text"; import type { Channel, RoleOrUserPermission } from "@vencord/discord-types"; import { findByPropsLazy, findComponentByCodeLazy, findCssClassesLazy } from "@webpack"; import { EmojiStore, FluxDispatcher, GuildMemberStore, GuildStore, Parser, PermissionsBits, PermissionStore, SnowflakeUtils, Text, Timestamp, Tooltip, useEffect, useState } from "@webpack/common"; +import { ComponentType } from "react"; import { cl, settings } from ".."; @@ -65,9 +66,11 @@ const enum ChannelFlags { } const ChatScrollClasses = findCssClassesLazy("auto", "managedReactiveScroller", "customTheme"); -const ChannelBeginHeader = findComponentByCodeLazy("#{intl::ROLE_REQUIRED_SINGLE_USER_MESSAGE}"); const TagComponent = findComponentByCodeLazy("#{intl::FORUM_TAG_A11Y_FILTER_BY_TAG}"); +let ChannelBeginHeader: ComponentType = () => null; +export const setChannelBeginHeader = v => ChannelBeginHeader = v; + const EmojiParser = findByPropsLazy("convertSurrogateToName"); const EmojiUtils = findByPropsLazy("getURL", "getEmojiColors"); diff --git a/src/plugins/showHiddenChannels/index.tsx b/src/plugins/showHiddenChannels/index.tsx index 609fc95a1..a95860e13 100644 --- a/src/plugins/showHiddenChannels/index.tsx +++ b/src/plugins/showHiddenChannels/index.tsx @@ -28,7 +28,7 @@ import type { Channel, Role } from "@vencord/discord-types"; import { findCssClassesLazy } from "@webpack"; import { ChannelStore, PermissionsBits, PermissionStore, Tooltip } from "@webpack/common"; -import HiddenChannelLockScreen from "./components/HiddenChannelLockScreen"; +import HiddenChannelLockScreen, { setChannelBeginHeader } from "./components/HiddenChannelLockScreen"; export const cl = classNameFactory("vc-shc-"); @@ -476,9 +476,19 @@ export default definePlugin({ match: /(getVoiceStateForUser.{0,150}?)&&\i\.\i\.canWithPartialContext.{0,20}VIEW_CHANNEL.+?}\)(?=\?)/, replace: "$1" } + }, + { + find: "#{intl::ROLE_REQUIRED_SINGLE_USER_MESSAGE}", + replacement: { + match: /(?=function (\i)\(\i\){let{channel:.{0,200}?getSortedRoles\()/, + replace: "$self.ChannelBeginHeader=$1;" + } } ], + set ChannelBeginHeader(value: any) { + setChannelBeginHeader(value); + }, swapViewChannelWithConnectPermission(mergedPermissions: bigint, channel: Channel) { if (!PermissionStore.can(PermissionsBits.CONNECT, channel)) { diff --git a/src/plugins/vcDoubleClick/index.ts b/src/plugins/vcDoubleClick/index.ts index deadebd0a..0e56fb167 100644 --- a/src/plugins/vcDoubleClick/index.ts +++ b/src/plugins/vcDoubleClick/index.ts @@ -31,11 +31,12 @@ export default definePlugin({ tags: ["Voice"], authors: [Devs.Ven, Devs.D3SOX], patches: [ - ...[ - ".handleVoiceStatusClick", // voice channels - ".handleClickChat" // stage channels - ].map(find => ({ - find, + // Stage Channels & Voice Channels + // the find is for stage channels, but it also handles voice + // channels because they're both in the same concatenated module + // the find for voice channels was `.handleVoiceStatusClick` + { + find: ".handleClickChat", // hack: these are not React onClick, it is a custom prop handled by Discord // thus, replacing this with onDoubleClick won't work, and you also cannot check // e.detail since instead of the event they pass the channel. @@ -46,10 +47,10 @@ export default definePlugin({ replace: "onClick:()=>{$self.schedule(()=>{this.handleClick()},this)", }, ] - })), + }, { // channel mentions - find: 'className:"channelMention",children', + find: 'className:"channelMention",children:[null!=', replacement: { match: /onClick:(\i)(?=,.{0,30}className:"channelMention".+?(\i)\.inContent)/, replace: (_, onClick, props) => "" diff --git a/src/plugins/voiceMessages/VoicePreview.tsx b/src/plugins/voiceMessages/VoicePreview.tsx index 05bf23d48..dfc61d9c9 100644 --- a/src/plugins/voiceMessages/VoicePreview.tsx +++ b/src/plugins/voiceMessages/VoicePreview.tsx @@ -17,21 +17,15 @@ */ import { useTimer } from "@utils/react"; -import { findComponentByCodeLazy } from "@webpack"; -import { cl } from "."; - -interface VoiceMessageProps { - src: string; - waveform: string; -} -const VoiceMessage = findComponentByCodeLazy("waveform:", "onVolumeChange"); +import { cl, VoiceMessage } from "."; export type VoicePreviewOptions = { src?: string; waveform: string; recording?: boolean; }; + export const VoicePreview = ({ src, waveform, diff --git a/src/plugins/voiceMessages/index.tsx b/src/plugins/voiceMessages/index.tsx index c9adcca65..6ab387ea2 100644 --- a/src/plugins/voiceMessages/index.tsx +++ b/src/plugins/voiceMessages/index.tsx @@ -49,6 +49,12 @@ export type VoiceRecorder = ComponentType<{ onRecordingChange?(recording: boolean): void; }>; +export interface VoiceMessageProps { + src: string; + waveform: string; +} +export let VoiceMessage: ComponentType = () => null; + const VoiceRecorder = IS_DISCORD_DESKTOP ? VoiceRecorderDesktop : VoiceRecorderWeb; const ctxMenuPatch: NavContextMenuPatchCallback = (children, props) => { @@ -74,6 +80,21 @@ export default definePlugin({ tags: ["Voice"], authors: [Devs.Ven, Devs.Vap, Devs.Nickyux], settings, + + patches: [ + { + find: "#{intl::3XohGn::raw}", + replacement: { + match: /(?<=\i=)(?=\i\.memo\(.{0,50}?=1,onVolumeChange:[^}]+?waveform:[^}]+?playbackCacheKey:)/, + replace: "$self.VoiceMessage=", + } + } + ], + + set VoiceMessage(value) { + VoiceMessage = value; + }, + contextMenus: { "channel-attach": ctxMenuPatch } diff --git a/src/webpack/common/components.ts b/src/webpack/common/components.ts index 98d453abf..9024f8705 100644 --- a/src/webpack/common/components.ts +++ b/src/webpack/common/components.ts @@ -57,7 +57,7 @@ export const Tooltip = waitForComponent("Tooltip", m => m.prototype?. export const TooltipContainer = TooltipContainerComponent as never; export const TextInput = waitForComponent("TextInput", filters.componentByCode("#{intl::MAXIMUM_LENGTH_ERROR}", '"input"')); -export const TextArea = waitForComponent("TextArea", filters.componentByCode("this.getPaddingRight()},id:")); +export const TextArea = waitForComponent("TextArea", filters.componentByCode('"data-mana-component":"text-area"')); export const Select = waitForComponent("Select", filters.componentByCode('selectionMode:"single",onSelectionChange:', "isSelected:")); export const SearchableSelect = waitForComponent("SearchableSelect", filters.componentByCode('?"multiple":"single",required:')); export const Slider = waitForComponent("Slider", filters.componentByCode("markDash", "this.renderMark(")); @@ -69,12 +69,21 @@ export const Paginator = waitForComponent("Paginator", filters.comp export const Clickable = waitForComponent("Clickable", filters.componentByCode("this.context?this.renderNonInteractive():")); export const Avatar = waitForComponent("Avatar", filters.componentByCode(".size-1.375*")); -export const ColorPicker = waitForComponent("ColorPicker", filters.componentByCode("#{intl::USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR}", "showEyeDropper")); +export let ColorPicker: t.ColorPicker = () => null; +export function setColorPicker(component: t.ColorPicker) { + ColorPicker = component; +} + export const UserSummaryItem = waitForComponent("UserSummaryItem", filters.componentByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers")); -export let createScroller: (scrollbarClassName: string, fadeClassName: string, customThemeClassName: string) => t.ScrollerThin; +export let createScroller: ((scrollbarClassName: string, fadeClassName: string, customThemeClassName: string) => t.ScrollerThin) | undefined; +export function setCreateScroller(cs: NonNullable) { + createScroller = cs; +} + export let createListScroller: (scrollBarClassName: string, fadeClassName: string, someOtherClassIdkMan: string, resizeObserverClass: typeof ResizeObserver) => t.ListScrollerThin; +waitFor(filters.byCode("getScrollerNode:", "resizeObserver:", "sectionHeight:"), m => createListScroller = m); const listScrollerClassnames = ["thin", "auto", "fade"] as const; export const scrollerClasses = findCssClassesLazy("thin", "auto", "fade", "customTheme", "none"); @@ -88,12 +97,9 @@ export const listScrollerClasses = proxyLazyWebpack(() => { return mapMangledCssClasses(mod, listScrollerClassnames); }); -waitFor(filters.byCode('="ltr",orientation:', "customTheme:", "forwardRef"), m => createScroller = m); -waitFor(filters.byCode("getScrollerNode:", "resizeObserver:", "sectionHeight:"), m => createListScroller = m); - -export const ScrollerNone = LazyComponent(() => createScroller(scrollerClasses.none, scrollerClasses.fade, scrollerClasses.customTheme)); -export const ScrollerThin = LazyComponent(() => createScroller(scrollerClasses.thin, scrollerClasses.fade, scrollerClasses.customTheme)); -export const ScrollerAuto = LazyComponent(() => createScroller(scrollerClasses.auto, scrollerClasses.fade, scrollerClasses.customTheme)); +export const ScrollerNone = LazyComponent(() => createScroller?.(scrollerClasses.none, scrollerClasses.fade, scrollerClasses.customTheme)!); +export const ScrollerThin = LazyComponent(() => createScroller?.(scrollerClasses.thin, scrollerClasses.fade, scrollerClasses.customTheme)!); +export const ScrollerAuto = LazyComponent(() => createScroller?.(scrollerClasses.auto, scrollerClasses.fade, scrollerClasses.customTheme)!); export const ListScrollerThin = LazyComponent(() => createListScroller(listScrollerClasses.thin, listScrollerClasses.fade, "", ResizeObserver)); export const ListScrollerAuto = LazyComponent(() => createListScroller(listScrollerClasses.auto, listScrollerClasses.fade, "", ResizeObserver)); diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index cc68751e7..a25199e1b 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -306,7 +306,6 @@ function proxyFactoryAndUpdateExisting(moduleFactories: AnyWebpackRequire["m"], */ function updateExistingFactory(moduleFactories: AnyWebpackRequire["m"], moduleId: PropertyKey, newFactory: AnyModuleFactory, newProxiedFactory: AnyModuleFactory, ignoreExistingInTarget: boolean) { let existingFactory: AnyModuleFactory | undefined; - let moduleFactoriesWithFactory: AnyWebpackRequire["m"] | undefined; for (const wreq of allWebpackInstances) { const instanceModuleFactories = wreq.m[SYM_ORIGINAL_MODULE_FACTORIES] ?? wreq.m; @@ -316,7 +315,6 @@ function updateExistingFactory(moduleFactories: AnyWebpackRequire["m"], moduleId if (Object.hasOwn(wreq.m, moduleId)) { existingFactory = wreq.m[moduleId]; // Must use wreq.m here instead of instanceModuleFactories to use our proxy in case the module factories are already proxied. - moduleFactoriesWithFactory = instanceModuleFactories; break; } }