From 12376c622ee1568d7c4633272362534ca7aea2df Mon Sep 17 00:00:00 2001 From: Vendicated Date: Tue, 14 May 2024 18:52:35 +0200 Subject: [PATCH 01/16] fix settings ui on canary --- src/plugins/_core/settings.tsx | 112 ++++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 37 deletions(-) diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index 772ee9b6..0a6aea8b 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -26,7 +26,7 @@ import UpdaterTab from "@components/VencordSettings/UpdaterTab"; import VencordTab from "@components/VencordSettings/VencordTab"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { React } from "@webpack/common"; +import { i18n, React } from "@webpack/common"; import gitHash from "~git-hash"; @@ -36,41 +36,55 @@ export default definePlugin({ authors: [Devs.Ven, Devs.Megu], required: true, - patches: [{ - find: ".versionHash", - replacement: [ - { - match: /\[\(0,.{1,3}\.jsxs?\)\((.{1,10}),(\{[^{}}]+\{.{0,20}.versionHash,.+?\})\)," "/, - replace: (m, component, props) => { - props = props.replace(/children:\[.+\]/, ""); - return `${m},$self.makeInfoElements(${component}, ${props})`; + patches: [ + { + find: ".versionHash", + replacement: [ + { + match: /\[\(0,.{1,3}\.jsxs?\)\((.{1,10}),(\{[^{}}]+\{.{0,20}.versionHash,.+?\})\)," "/, + replace: (m, component, props) => { + props = props.replace(/children:\[.+\]/, ""); + return `${m},$self.makeInfoElements(${component}, ${props})`; + } } + ] + }, + // Discord Stable + // FIXME: remove once change merged to stable + { + find: "Messages.ACTIVITY_SETTINGS", + replacement: { + get match() { + switch (Settings.plugins.Settings.settingsLocation) { + case "top": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.USER_SETTINGS/; + case "aboveNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.BILLING_SETTINGS/; + case "belowNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.APP_SETTINGS/; + case "belowActivity": return /(?<=\{section:(\i\.\i)\.DIVIDER},)\{section:"changelog"/; + case "bottom": return /\{section:(\i\.\i)\.CUSTOM,\s*element:.+?}/; + case "aboveActivity": + default: + return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.ACTIVITY_SETTINGS/; + } + }, + replace: "...$self.makeSettingsCategories($1),$&" + } + }, + // Discord Canary + { + find: "Messages.ACTIVITY_SETTINGS", + replacement: { + match: /(?<=section:(.{0,50})\.DIVIDER\}\))([,;])(?=.{0,200}(\i)\.push.{0,100}label:(\i)\.header)/, + replace: (_, sectionTypes, commaOrSemi, elements, element) => `${commaOrSemi} $self.addSettings(${elements}, ${element}, ${sectionTypes}) ${commaOrSemi}` + } + }, + { + find: "Messages.USER_SETTINGS_ACTIONS_MENU_LABEL", + replacement: { + match: /(?<=function\((\i),\i\)\{)(?=let \i=Object.values\(\i.UserSettingsSections\).*?(\i)\.default\.open\()/, + replace: "$2.default.open($1);return;" } - ] - }, { - find: "Messages.ACTIVITY_SETTINGS", - replacement: { - get match() { - switch (Settings.plugins.Settings.settingsLocation) { - case "top": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.USER_SETTINGS/; - case "aboveNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.BILLING_SETTINGS/; - case "belowNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.APP_SETTINGS/; - case "belowActivity": return /(?<=\{section:(\i\.\i)\.DIVIDER},)\{section:"changelog"/; - case "bottom": return /\{section:(\i\.\i)\.CUSTOM,\s*element:.+?}/; - case "aboveActivity": - default: - return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.ACTIVITY_SETTINGS/; - } - }, - replace: "...$self.makeSettingsCategories($1),$&" } - }, { - find: "Messages.USER_SETTINGS_ACTIONS_MENU_LABEL", - replacement: { - match: /(?<=function\((\i),\i\)\{)(?=let \i=Object.values\(\i.UserSettingsSections\).*?(\i)\.default\.open\()/, - replace: "$2.default.open($1);return;" - } - }], + ], customSections: [] as ((SectionTypes: Record) => any)[], @@ -130,19 +144,43 @@ export default definePlugin({ ].filter(Boolean); }, + isRightSpot({ header, settings }: { header?: string; settings?: string[]; }) { + const firstChild = settings?.[0]; + // lowest two elements... sanity backup + if (firstChild === "LOGOUT" || firstChild === "SOCIAL_LINKS") return true; + + const { settingsLocation } = Settings.plugins.Settings; + + if (settingsLocation === "bottom") return firstChild === "LOGOUT"; + if (settingsLocation === "belowActivity") return firstChild === "CHANGELOG"; + + const names = { + top: i18n.Messages.USER_SETTINGS, + aboveNitro: i18n.Messages.BILLING_SETTINGS, + belowNitro: i18n.Messages.APP_SETTINGS, + aboveActivity: i18n.Messages.ACTIVITY_SETTINGS + }; + return header === names[settingsLocation]; + }, + + addSettings(elements: any[], element: { header?: string; settings: string[]; }, sectionTypes: Record) { + if (!this.isRightSpot(element)) return; + + elements.push(...this.makeSettingsCategories(sectionTypes)); + }, + options: { settingsLocation: { type: OptionType.SELECT, description: "Where to put the Vencord settings section", options: [ { label: "At the very top", value: "top" }, - { label: "Above the Nitro section", value: "aboveNitro" }, + { label: "Above the Nitro section", value: "aboveNitro", default: true }, { label: "Below the Nitro section", value: "belowNitro" }, - { label: "Above Activity Settings", value: "aboveActivity", default: true }, + { label: "Above Activity Settings", value: "aboveActivity" }, { label: "Below Activity Settings", value: "belowActivity" }, { label: "At the very bottom", value: "bottom" }, - ], - restartNeeded: true + ] }, }, From a54b55edad3e33c44ac1419d7d20c1724a528f7e Mon Sep 17 00:00:00 2001 From: Vendicated Date: Tue, 14 May 2024 18:54:00 +0200 Subject: [PATCH 02/16] bump to v1.8.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0e584598..95f98a8b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.8.3", + "version": "1.8.4", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { From 719c6140f3fafb0ae04323268651f0b4f919af91 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Tue, 14 May 2024 21:18:43 +0200 Subject: [PATCH 03/16] fix Vencord Settings section being added multiple times --- src/plugins/_core/settings.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index 0a6aea8b..b09af2c0 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -154,6 +154,8 @@ export default definePlugin({ if (settingsLocation === "bottom") return firstChild === "LOGOUT"; if (settingsLocation === "belowActivity") return firstChild === "CHANGELOG"; + if (!header) return; + const names = { top: i18n.Messages.USER_SETTINGS, aboveNitro: i18n.Messages.BILLING_SETTINGS, @@ -163,8 +165,12 @@ export default definePlugin({ return header === names[settingsLocation]; }, + patchedSettings: new WeakSet(), + addSettings(elements: any[], element: { header?: string; settings: string[]; }, sectionTypes: Record) { - if (!this.isRightSpot(element)) return; + if (this.patchedSettings.has(elements) || !this.isRightSpot(element)) return; + + this.patchedSettings.add(elements); elements.push(...this.makeSettingsCategories(sectionTypes)); }, From d8b3869b8113f304a352677907c6ee9975ee0cd1 Mon Sep 17 00:00:00 2001 From: Board <26598490+BoardTM@users.noreply.github.com> Date: Wed, 15 May 2024 01:07:33 +0200 Subject: [PATCH 04/16] ThemeAttributes: add larger avatar url variables to avatars (#2449) Co-authored-by: vee --- src/plugins/themeAttributes/README.md | 10 ++++++++- src/plugins/themeAttributes/index.ts | 30 ++++++++++++++++++++++++++- src/utils/constants.ts | 4 ++++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/plugins/themeAttributes/README.md b/src/plugins/themeAttributes/README.md index 87cb803c..89001aae 100644 --- a/src/plugins/themeAttributes/README.md +++ b/src/plugins/themeAttributes/README.md @@ -1,6 +1,6 @@ # ThemeAttributes -This plugin adds data attributes to various elements inside Discord +This plugin adds data attributes and CSS variables to various elements inside Discord This allows themes to more easily theme those elements or even do things that otherwise wouldn't be possible @@ -19,3 +19,11 @@ This allows themes to more easily theme those elements or even do things that ot - `data-is-self` is a boolean indicating whether this is the current user's message ![image](https://github.com/Vendicated/Vencord/assets/45497981/34bd5053-3381-402f-82b2-9c812cc7e122) + +## CSS Variables + +### Avatars + +`--avatar-url-` contains a URL for the users avatar with the size attribute adjusted for the resolutions `128, 256, 512, 1024, 2048, 4096`. + +![image](https://github.com/Vendicated/Vencord/assets/26598490/192ddac0-c827-472f-9933-fa99ff36f723) diff --git a/src/plugins/themeAttributes/index.ts b/src/plugins/themeAttributes/index.ts index b8ceac62..b8084454 100644 --- a/src/plugins/themeAttributes/index.ts +++ b/src/plugins/themeAttributes/index.ts @@ -9,10 +9,11 @@ import definePlugin from "@utils/types"; import { UserStore } from "@webpack/common"; import { Message } from "discord-types/general"; + export default definePlugin({ name: "ThemeAttributes", description: "Adds data attributes to various elements for theming purposes", - authors: [Devs.Ven], + authors: [Devs.Ven, Devs.Board], patches: [ // Add data-tab-id to all tab bar items @@ -32,9 +33,36 @@ export default definePlugin({ match: /\.messageListItem(?=,"aria)/, replace: "$&,...$self.getMessageProps(arguments[0])" } + }, + + // add --avatar-url- css variable to avatar img elements + // popout profiles + { + find: ".LABEL_WITH_ONLINE_STATUS", + replacement: { + match: /src:null!=\i\?(\i).{1,50}"aria-hidden":!0/, + replace: "$&,style:$self.getAvatarStyles($1)" + } + }, + // chat avatars + { + find: "showCommunicationDisabledStyles", + replacement: { + match: /src:(\i),"aria-hidden":!0/, + replace: "$&,style:$self.getAvatarStyles($1)" + } } ], + getAvatarStyles(src: string) { + return Object.fromEntries( + [128, 256, 512, 1024, 2048, 4096].map(size => [ + `--avatar-url-${size}`, + `url(${src.replace(/\d+$/, String(size))})` + ]) + ); + }, + getMessageProps(props: { message: Message; }) { const author = props.message?.author; const authorId = author?.id; diff --git a/src/utils/constants.ts b/src/utils/constants.ts index e446a27b..69c18cd5 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -382,6 +382,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "ant0n", id: 145224646868860928n }, + Board: { + name: "BoardTM", + id: 285475344817848320n, + }, philipbry: { name: "philipbry", id: 554994003318276106n From 5232a85319d3db6fa30e6dd638f19582aae092c5 Mon Sep 17 00:00:00 2001 From: Ulysses Zhan Date: Tue, 14 May 2024 16:18:30 -0700 Subject: [PATCH 05/16] new plugin NoServerEmoji ~ hides server emojis from autocomplete (#1787) Co-authored-by: vee --- src/plugins/noServerEmojis/index.ts | 50 +++++++++++++++++++++++++++++ src/utils/constants.ts | 4 +++ 2 files changed, 54 insertions(+) create mode 100644 src/plugins/noServerEmojis/index.ts diff --git a/src/plugins/noServerEmojis/index.ts b/src/plugins/noServerEmojis/index.ts new file mode 100644 index 00000000..ed843769 --- /dev/null +++ b/src/plugins/noServerEmojis/index.ts @@ -0,0 +1,50 @@ +/* + * 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 } from "@utils/constants"; +import definePlugin, { OptionType } from "@utils/types"; + +const settings = definePluginSettings({ + shownEmojis: { + description: "The types of emojis to show in the autocomplete menu.", + type: OptionType.SELECT, + default: "onlyUnicode", + options: [ + { label: "Only unicode emojis", value: "onlyUnicode" }, + { label: "Unicode emojis and server emojis from current server", value: "currentServer" }, + { label: "Unicode emojis and all server emojis (Discord default)", value: "all" } + ] + } +}); + +export default definePlugin({ + name: "NoServerEmojis", + authors: [Devs.UlyssesZhan], + description: "Do not show server emojis in the autocomplete menu.", + settings, + patches: [ + { + find: "}searchWithoutFetchingLatest(", + replacement: { + match: /searchWithoutFetchingLatest.{20,300}get\((\i).{10,40}?reduce\(\((\i),(\i)\)=>\{/, + replace: "$& if ($self.shouldSkip($1, $3)) return $2;" + } + } + ], + shouldSkip(guildId: string, emoji: any) { + if (emoji.type !== "GUILD_EMOJI") { + return false; + } + if (settings.store.shownEmojis === "onlyUnicode") { + return true; + } + if (settings.store.shownEmojis === "currentServer") { + return emoji.guildId !== guildId; + } + return false; + } +}); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 69c18cd5..21d89190 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -378,6 +378,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "ProffDea", id: 609329952180928513n }, + UlyssesZhan: { + name: "UlyssesZhan", + id: 586808226058862623n + }, ant0n: { name: "ant0n", id: 145224646868860928n From 81d3f5df1a5b0faee051a7b8cd74f4f45386e4c3 Mon Sep 17 00:00:00 2001 From: Ulysses Zhan Date: Tue, 14 May 2024 16:21:00 -0700 Subject: [PATCH 06/16] new plugin CtrlEnterSend (#1794) Co-authored-by: vee --- src/plugins/ctrlEnterSend/index.ts | 68 ++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/plugins/ctrlEnterSend/index.ts diff --git a/src/plugins/ctrlEnterSend/index.ts b/src/plugins/ctrlEnterSend/index.ts new file mode 100644 index 00000000..4b9dd8e0 --- /dev/null +++ b/src/plugins/ctrlEnterSend/index.ts @@ -0,0 +1,68 @@ +/* + * 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 } 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)", + settings: definePluginSettings({ + submitRule: { + description: "The way to send a message", + type: OptionType.SELECT, + options: [ + { + label: "Ctrl+Enter (Enter or Shift+Enter for new line)", + 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: [ + { + find: "KeyboardKeys.ENTER&&(!", + replacement: { + match: /(?<=(\i)\.which===\i\.KeyboardKeys.ENTER&&).{0,100}(\(0,\i\.hasOpenPlainTextCodeBlock\)\(\i\)).{0,100}(?=&&\(\i\.preventDefault)/, + 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 = event.ctrlKey; + break; + case "enter": + result = !event.shiftKey && !event.ctrlKey; + break; + } + if (!this.settings.store.sendMessageInTheMiddleOfACodeBlock) { + result &&= !codeblock; + } + return result; + } +}); From 97dd56ccdab091ba65b6e25efd172b0464b191d5 Mon Sep 17 00:00:00 2001 From: ! Sleepy <109904491+eepyfemboi@users.noreply.github.com> Date: Tue, 14 May 2024 17:17:19 -0700 Subject: [PATCH 07/16] MoreUserTags: Add chat moderator tag (#2424) Co-authored-by: Vendicated --- src/plugins/moreUserTags/index.tsx | 42 ++++++++++-------------------- 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/src/plugins/moreUserTags/index.tsx b/src/plugins/moreUserTags/index.tsx index df47e545..1257b452 100644 --- a/src/plugins/moreUserTags/index.tsx +++ b/src/plugins/moreUserTags/index.tsx @@ -50,6 +50,7 @@ interface TagSettings { MODERATOR_STAFF: TagSetting, MODERATOR: TagSetting, VOICE_MODERATOR: TagSetting, + TRIAL_MODERATOR: TagSetting, [k: string]: TagSetting; } @@ -93,6 +94,11 @@ const tags: Tag[] = [ displayName: "VC Mod", description: "Can manage voice chats", permissions: ["MOVE_MEMBERS", "MUTE_MEMBERS", "DEAFEN_MEMBERS"] + }, { + name: "CHAT_MODERATOR", + displayName: "Chat Mod", + description: "Can timeout people", + permissions: ["MODERATE_MEMBERS"] } ]; const defaultSettings = Object.fromEntries( @@ -263,34 +269,14 @@ export default definePlugin({ ], start() { - if (settings.store.tagSettings) return; - // @ts-ignore - if (!settings.store.visibility_WEBHOOK) settings.store.tagSettings = defaultSettings; - else { - const newSettings = { ...defaultSettings }; - Object.entries(Vencord.PlainSettings.plugins.MoreUserTags).forEach(([name, value]) => { - const [setting, tag] = name.split("_"); - if (setting === "visibility") { - switch (value) { - case "always": - // its the default - break; - case "chat": - newSettings[tag].showInNotChat = false; - break; - case "not-chat": - newSettings[tag].showInChat = false; - break; - case "never": - newSettings[tag].showInChat = false; - newSettings[tag].showInNotChat = false; - break; - } - } - settings.store.tagSettings = newSettings; - delete Vencord.Settings.plugins.MoreUserTags[name]; - }); - } + settings.store.tagSettings ??= defaultSettings; + + // newly added field might be missing from old users + settings.store.tagSettings.CHAT_MODERATOR ??= { + text: "Chat Mod", + showInChat: true, + showInNotChat: true + }; }, getPermissions(user: User, channel: Channel): string[] { From 840d571ce2b756f2bde31296bec433930df14413 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Tue, 14 May 2024 21:34:25 -0300 Subject: [PATCH 08/16] Fix BetterSettings & StartupTimings patch --- src/plugins/betterSettings/index.tsx | 2 +- src/plugins/startupTimings/index.tsx | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/plugins/betterSettings/index.tsx b/src/plugins/betterSettings/index.tsx index 5064bd53..e90e5c82 100644 --- a/src/plugins/betterSettings/index.tsx +++ b/src/plugins/betterSettings/index.tsx @@ -119,7 +119,7 @@ export default definePlugin({ { // Settings cog context menu find: "Messages.USER_SETTINGS_ACTIONS_MENU_LABEL", replacement: { - match: /\(0,\i.default\)\(\)(?=\.filter\(\i=>\{let\{section:\i\}=)/, + match: /\(0,\i.useDefaultUserSettingsSections\)\(\)(?=\.filter\(\i=>\{let\{section:\i\}=)/, replace: "$self.wrapMenu($&)" } } diff --git a/src/plugins/startupTimings/index.tsx b/src/plugins/startupTimings/index.tsx index 742d822a..cf366df3 100644 --- a/src/plugins/startupTimings/index.tsx +++ b/src/plugins/startupTimings/index.tsx @@ -26,10 +26,12 @@ export default definePlugin({ description: "Adds Startup Timings to the Settings menu", authors: [Devs.Megu], patches: [{ - find: "UserSettingsSections.PAYMENT_FLOW_MODAL_TEST_PAGE,", + find: "Messages.ACTIVITY_SETTINGS", replacement: { - match: /{section:\i\.UserSettingsSections\.PAYMENT_FLOW_MODAL_TEST_PAGE/, - replace: '{section:"StartupTimings",label:"Startup Timings",element:$self.StartupTimingPage},$&' + match: /(?<=}\)([,;])(\i\.settings)\.forEach.+?(\i)\.push.+}\))/, + replace: (_, commaOrSemi, settings, elements) => "" + + `${commaOrSemi}${settings}?.[0]==="CHANGELOG"` + + `&&${elements}.push({section:"StartupTimings",label:"Startup Timings",element:$self.StartupTimingPage})` } }], StartupTimingPage From 0e4724ec0d2ee3c9bd15f81d29dcbd8fec1211ce Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 15 May 2024 03:14:02 +0200 Subject: [PATCH 09/16] Settings: remove obsolete patch; add redundancy & more useful dbg copy --- src/plugins/_core/settings.tsx | 79 +++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 31 deletions(-) diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index b09af2c0..81d1d179 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -30,6 +30,9 @@ import { i18n, React } from "@webpack/common"; import gitHash from "~git-hash"; +type SectionType = "HEADER" | "DIVIDER" | "CUSTOM"; +type SectionTypes = Record; + export default definePlugin({ name: "Settings", description: "Adds Settings UI and debug info", @@ -41,34 +44,18 @@ export default definePlugin({ find: ".versionHash", replacement: [ { - match: /\[\(0,.{1,3}\.jsxs?\)\((.{1,10}),(\{[^{}}]+\{.{0,20}.versionHash,.+?\})\)," "/, + match: /\[\(0,\i\.jsxs?\)\((.{1,10}),(\{[^{}}]+\{.{0,20}.versionHash,.+?\})\)," "/, replace: (m, component, props) => { props = props.replace(/children:\[.+\]/, ""); return `${m},$self.makeInfoElements(${component}, ${props})`; } + }, + { + match: /copyValue:\i\.join\(" "\)/, + replace: "$& + $self.getInfoString()" } ] }, - // Discord Stable - // FIXME: remove once change merged to stable - { - find: "Messages.ACTIVITY_SETTINGS", - replacement: { - get match() { - switch (Settings.plugins.Settings.settingsLocation) { - case "top": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.USER_SETTINGS/; - case "aboveNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.BILLING_SETTINGS/; - case "belowNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.APP_SETTINGS/; - case "belowActivity": return /(?<=\{section:(\i\.\i)\.DIVIDER},)\{section:"changelog"/; - case "bottom": return /\{section:(\i\.\i)\.CUSTOM,\s*element:.+?}/; - case "aboveActivity": - default: - return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.ACTIVITY_SETTINGS/; - } - }, - replace: "...$self.makeSettingsCategories($1),$&" - } - }, // Discord Canary { find: "Messages.ACTIVITY_SETTINGS", @@ -77,6 +64,13 @@ export default definePlugin({ replace: (_, sectionTypes, commaOrSemi, elements, element) => `${commaOrSemi} $self.addSettings(${elements}, ${element}, ${sectionTypes}) ${commaOrSemi}` } }, + { + find: "useDefaultUserSettingsSections:function", + replacement: { + match: /(?<=useDefaultUserSettingsSections:function\(\){return )(\i)\}/, + replace: "$self.wrapSettingsHook($1)}" + } + }, { find: "Messages.USER_SETTINGS_ACTIONS_MENU_LABEL", replacement: { @@ -86,9 +80,9 @@ export default definePlugin({ } ], - customSections: [] as ((SectionTypes: Record) => any)[], + customSections: [] as ((SectionTypes: SectionTypes) => any)[], - makeSettingsCategories(SectionTypes: Record) { + makeSettingsCategories(SectionTypes: SectionTypes) { return [ { section: SectionTypes.HEADER, @@ -167,7 +161,7 @@ export default definePlugin({ patchedSettings: new WeakSet(), - addSettings(elements: any[], element: { header?: string; settings: string[]; }, sectionTypes: Record) { + addSettings(elements: any[], element: { header?: string; settings: string[]; }, sectionTypes: SectionTypes) { if (this.patchedSettings.has(elements) || !this.isRightSpot(element)) return; this.patchedSettings.add(elements); @@ -175,6 +169,20 @@ export default definePlugin({ elements.push(...this.makeSettingsCategories(sectionTypes)); }, + wrapSettingsHook(originalHook: (...args: any[]) => Record[]) { + return (...args: any[]) => { + const elements = originalHook(...args); + if (!this.patchedSettings.has(elements)) + elements.unshift(...this.makeSettingsCategories({ + HEADER: "HEADER", + DIVIDER: "DIVIDER", + CUSTOM: "CUSTOM" + })); + + return elements; + }; + }, + options: { settingsLocation: { type: OptionType.SELECT, @@ -213,15 +221,24 @@ export default definePlugin({ return ""; }, - makeInfoElements(Component: React.ComponentType, props: React.PropsWithChildren) { + getInfoRows() { const { electronVersion, chromiumVersion, additionalInfo } = this; - return ( - <> - Vencord {gitHash}{additionalInfo} - {electronVersion && Electron {electronVersion}} - {chromiumVersion && Chromium {chromiumVersion}} - + const rows = [`Vencord ${gitHash}${additionalInfo}`]; + + if (electronVersion) rows.push(`Electron ${electronVersion}`); + if (chromiumVersion) rows.push(`Chromium ${chromiumVersion}`); + + return rows; + }, + + getInfoString() { + return "\n" + this.getInfoRows().join("\n"); + }, + + makeInfoElements(Component: React.ComponentType, props: React.PropsWithChildren) { + return this.getInfoRows().map((text, i) => + {text} ); } }); From 46801de21fc587118710873bee7e78f517932cf0 Mon Sep 17 00:00:00 2001 From: mcpower Date: Wed, 15 May 2024 11:42:09 +1000 Subject: [PATCH 10/16] chore: tidy up suggested vscode extensions list (#2221) Co-authored-by: Vendicated --- .vscode/extensions.json | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index f16f1e27..e86effb1 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,11 +1,9 @@ { "recommendations": [ "dbaeumer.vscode-eslint", - "eamodio.gitlens", "EditorConfig.EditorConfig", - "ExodiusStudios.comment-anchors", - "formulahendry.auto-rename-tag", "GregorBiswanger.json2ts", - "stylelint.vscode-stylelint" + "stylelint.vscode-stylelint", + "Vendicated.vencord-companion" ] } From 1fea8420938a4dfd519881132d5782a30604112a Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Tue, 14 May 2024 22:47:19 -0300 Subject: [PATCH 11/16] BetterFolders: Fix scrolling --- src/plugins/betterFolders/index.tsx | 30 +++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/plugins/betterFolders/index.tsx b/src/plugins/betterFolders/index.tsx index d252682f..795f1990 100644 --- a/src/plugins/betterFolders/index.tsx +++ b/src/plugins/betterFolders/index.tsx @@ -20,7 +20,7 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { findByPropsLazy, findStoreLazy } from "@webpack"; -import { FluxDispatcher, i18n } from "@webpack/common"; +import { FluxDispatcher, i18n, useMemo } from "@webpack/common"; import FolderSideBar from "./FolderSideBar"; @@ -117,8 +117,8 @@ export default definePlugin({ }, // If we are rendering the Better Folders sidebar, we filter out guilds that are not in folders and unexpanded folders { - match: /(useStateFromStoresArray\).{0,25}let \i)=(\i\.\i.getGuildsTree\(\))/, - replace: (_, rest, guildsTree) => `${rest}=$self.getGuildTree(!!arguments[0].isBetterFolders,${guildsTree},arguments[0].betterFoldersExpandedIds)` + match: /\[(\i)\]=(\(0,\i\.useStateFromStoresArray\).{0,40}getGuildsTree\(\).+?}\))(?=,)/, + replace: (_, originalTreeVar, rest) => `[betterFoldersOriginalTree]=${rest},${originalTreeVar}=$self.getGuildTree(!!arguments[0].isBetterFolders,betterFoldersOriginalTree,arguments[0].betterFoldersExpandedIds)` }, // If we are rendering the Better Folders sidebar, we filter out everything but the servers and folders from the GuildsBar Guild List children { @@ -252,19 +252,21 @@ export default definePlugin({ } }, - getGuildTree(isBetterFolders: boolean, oldTree: any, expandedFolderIds?: Set) { - if (!isBetterFolders || expandedFolderIds == null) return oldTree; + getGuildTree(isBetterFolders: boolean, originalTree: any, expandedFolderIds?: Set) { + return useMemo(() => { + if (!isBetterFolders || expandedFolderIds == null) return originalTree; - const newTree = new GuildsTree(); - // Children is every folder and guild which is not in a folder, this filters out only the expanded folders - newTree.root.children = oldTree.root.children.filter(guildOrFolder => expandedFolderIds.has(guildOrFolder.id)); - // Nodes is every folder and guild, even if it's in a folder, this filters out only the expanded folders and guilds inside them - newTree.nodes = Object.fromEntries( - Object.entries(oldTree.nodes) - .filter(([_, guildOrFolder]: any[]) => expandedFolderIds.has(guildOrFolder.id) || expandedFolderIds.has(guildOrFolder.parentId)) - ); + const newTree = new GuildsTree(); + // Children is every folder and guild which is not in a folder, this filters out only the expanded folders + newTree.root.children = originalTree.root.children.filter(guildOrFolder => expandedFolderIds.has(guildOrFolder.id)); + // Nodes is every folder and guild, even if it's in a folder, this filters out only the expanded folders and guilds inside them + newTree.nodes = Object.fromEntries( + Object.entries(originalTree.nodes) + .filter(([_, guildOrFolder]: any[]) => expandedFolderIds.has(guildOrFolder.id) || expandedFolderIds.has(guildOrFolder.parentId)) + ); - return newTree; + return newTree; + }, [isBetterFolders, originalTree, expandedFolderIds]); }, makeGuildsBarGuildListFilter(isBetterFolders: boolean) { From 4d572670f15d1ce2ffde979eb70714e251d09443 Mon Sep 17 00:00:00 2001 From: Eric <45801973+waresnew@users.noreply.github.com> Date: Tue, 14 May 2024 22:10:29 -0400 Subject: [PATCH 12/16] new plugin ValidReply ~ fix "Message could not be loaded" (#2337) Co-authored-by: V --- src/plugins/validReply/README.md | 7 ++ src/plugins/validReply/index.ts | 106 +++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 src/plugins/validReply/README.md create mode 100644 src/plugins/validReply/index.ts diff --git a/src/plugins/validReply/README.md b/src/plugins/validReply/README.md new file mode 100644 index 00000000..49e313cf --- /dev/null +++ b/src/plugins/validReply/README.md @@ -0,0 +1,7 @@ +# ValidReply + +Fixes referenced (replied to) messages showing as "Message could not be loaded". + +Hover the text to load the message! + +![](https://github.com/Vendicated/Vencord/assets/45801973/d3286acf-e822-4b7f-a4e7-8ced18f581af) diff --git a/src/plugins/validReply/index.ts b/src/plugins/validReply/index.ts new file mode 100644 index 00000000..21a1bdd1 --- /dev/null +++ b/src/plugins/validReply/index.ts @@ -0,0 +1,106 @@ +/* + * 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 definePlugin from "@utils/types"; +import { findByPropsLazy } from "@webpack"; +import { FluxDispatcher, RestAPI } from "@webpack/common"; +import { Message, User } from "discord-types/general"; +import { Channel } from "discord-types/general/index.js"; + +const enum ReferencedMessageState { + Loaded, + NotLoaded, + Deleted +} + +interface Reply { + baseAuthor: User, + baseMessage: Message; + channel: Channel; + referencedMessage: { state: ReferencedMessageState; }; + compact: boolean; + isReplyAuthorBlocked: boolean; +} + +const fetching = new Map(); +let ReplyStore: any; + +const { createMessageRecord } = findByPropsLazy("createMessageRecord"); + +export default definePlugin({ + name: "ValidReply", + description: 'Fixes "Message could not be loaded" upon hovering over the reply', + authors: [Devs.newwares], + patches: [ + { + find: "Messages.REPLY_QUOTE_MESSAGE_NOT_LOADED", + replacement: { + match: /Messages\.REPLY_QUOTE_MESSAGE_NOT_LOADED/, + replace: "$&,onMouseEnter:()=>$self.fetchReply(arguments[0])" + } + }, + { + find: "ReferencedMessageStore", + replacement: { + match: /constructor\(\)\{\i\(this,"_channelCaches",new Map\)/, + replace: "$&;$self.setReplyStore(this);" + } + } + ], + + setReplyStore(store: any) { + ReplyStore = store; + }, + + async fetchReply(reply: Reply) { + const { channel_id: channelId, message_id: messageId } = reply.baseMessage.messageReference!; + + if (fetching.has(messageId)) { + return; + } + fetching.set(messageId, channelId); + + RestAPI.get({ + url: `/channels/${channelId}/messages`, + query: { + limit: 1, + around: messageId + }, + retries: 2 + }) + .then(res => { + const reply: Message | undefined = res?.body?.[0]; + if (!reply) return; + + if (reply.id !== messageId) { + ReplyStore.set(channelId, messageId, { + state: ReferencedMessageState.Deleted + }); + + FluxDispatcher.dispatch({ + type: "MESSAGE_DELETE", + channelId: channelId, + message: messageId + }); + } else { + ReplyStore.set(reply.channel_id, reply.id, { + state: ReferencedMessageState.Loaded, + message: createMessageRecord(reply) + }); + + FluxDispatcher.dispatch({ + type: "MESSAGE_UPDATE", + message: reply + }); + } + }) + .catch(() => { }) + .finally(() => { + fetching.delete(messageId); + }); + } +}); From f4d64616904dabc1b8bd4dd15fccc31c45c147d0 Mon Sep 17 00:00:00 2001 From: Aztup <55710232+Aztup@users.noreply.github.com> Date: Wed, 15 May 2024 04:35:00 +0200 Subject: [PATCH 13/16] feat(plugins/openInApp) Add tidal support (#2404) Co-authored-by: vee --- src/main/utils/constants.ts | 1 + src/plugins/openInApp/index.ts | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/main/utils/constants.ts b/src/main/utils/constants.ts index 6c076c32..9513da51 100644 --- a/src/main/utils/constants.ts +++ b/src/main/utils/constants.ts @@ -35,6 +35,7 @@ export const ALLOWED_PROTOCOLS = [ "steam:", "spotify:", "com.epicgames.launcher:", + "tidal:" ]; export const IS_VANILLA = /* @__PURE__ */ process.argv.includes("--vanilla"); diff --git a/src/plugins/openInApp/index.ts b/src/plugins/openInApp/index.ts index 0835c061..83da5f3c 100644 --- a/src/plugins/openInApp/index.ts +++ b/src/plugins/openInApp/index.ts @@ -26,6 +26,7 @@ const ShortUrlMatcher = /^https:\/\/(spotify\.link|s\.team)\/.+$/; const SpotifyMatcher = /^https:\/\/open\.spotify\.com\/(track|album|artist|playlist|user|episode)\/(.+)(?:\?.+?)?$/; const SteamMatcher = /^https:\/\/(steamcommunity\.com|(?:help|store)\.steampowered\.com)\/.+$/; const EpicMatcher = /^https:\/\/store\.epicgames\.com\/(.+)$/; +const TidalMatcher = /^https:\/\/tidal\.com\/browse\/(track|album|artist|playlist|user|video|mix)\/(.+)(?:\?.+?)?$/; const settings = definePluginSettings({ spotify: { @@ -42,6 +43,11 @@ const settings = definePluginSettings({ type: OptionType.BOOLEAN, description: "Open Epic Games links in the Epic Games Launcher", default: true, + }, + tidal: { + type: OptionType.BOOLEAN, + description: "Open Tidal links in the Tidal app", + default: true, } }); @@ -49,7 +55,7 @@ const Native = VencordNative.pluginHelpers.OpenInApp as PluginNative Date: Tue, 14 May 2024 22:44:47 -0400 Subject: [PATCH 14/16] PetPet: Fix Upload Image Option (#2461) --- src/plugins/crashHandler/index.ts | 25 +++++++++++++------------ src/plugins/fakeNitro/index.tsx | 5 ++--- src/plugins/petpet/index.ts | 17 ++++++++++++----- src/utils/constants.ts | 6 +++++- src/webpack/common/stores.ts | 7 +------ src/webpack/common/types/stores.d.ts | 9 +++++++++ src/webpack/common/utils.ts | 2 ++ src/webpack/webpack.ts | 2 +- 8 files changed, 45 insertions(+), 28 deletions(-) diff --git a/src/plugins/crashHandler/index.ts b/src/plugins/crashHandler/index.ts index 10053021..3297ca30 100644 --- a/src/plugins/crashHandler/index.ts +++ b/src/plugins/crashHandler/index.ts @@ -24,22 +24,20 @@ import { closeAllModals } from "@utils/modal"; import definePlugin, { OptionType } from "@utils/types"; import { maybePromptToUpdate } from "@utils/updater"; import { filters, findBulk, proxyLazyWebpack } from "@webpack"; -import { FluxDispatcher, NavigationRouter, SelectedChannelStore } from "@webpack/common"; +import { DraftType, FluxDispatcher, NavigationRouter, SelectedChannelStore } from "@webpack/common"; const CrashHandlerLogger = new Logger("CrashHandler"); -const { ModalStack, DraftManager, DraftType, closeExpressionPicker } = proxyLazyWebpack(() => { - const modules = findBulk( + +const { ModalStack, DraftManager, closeExpressionPicker } = proxyLazyWebpack(() => { + const [ModalStack, DraftManager, ExpressionManager] = findBulk( filters.byProps("pushLazy", "popAll"), filters.byProps("clearDraft", "saveDraft"), - filters.byProps("DraftType"), - filters.byProps("closeExpressionPicker", "openExpressionPicker"), - ); + filters.byProps("closeExpressionPicker", "openExpressionPicker"),); return { - ModalStack: modules[0], - DraftManager: modules[1], - DraftType: modules[2]?.DraftType, - closeExpressionPicker: modules[3]?.closeExpressionPicker, + ModalStack, + DraftManager, + closeExpressionPicker: ExpressionManager?.closeExpressionPicker, }; }); @@ -137,8 +135,11 @@ export default definePlugin({ try { const channelId = SelectedChannelStore.getChannelId(); - DraftManager.clearDraft(channelId, DraftType.ChannelMessage); - DraftManager.clearDraft(channelId, DraftType.FirstThreadMessage); + for (const key in DraftType) { + if (!Number.isNaN(Number(key))) continue; + + DraftManager.clearDraft(channelId, DraftType[key]); + } } catch (err) { CrashHandlerLogger.debug("Failed to clear drafts.", err); } diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx index 29318366..a55a7771 100644 --- a/src/plugins/fakeNitro/index.tsx +++ b/src/plugins/fakeNitro/index.tsx @@ -24,13 +24,12 @@ import { getCurrentGuild } from "@utils/discord"; import { Logger } from "@utils/Logger"; import definePlugin, { OptionType } from "@utils/types"; import { findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack"; -import { Alerts, ChannelStore, EmojiStore, FluxDispatcher, Forms, IconUtils, lodash, Parser, PermissionsBits, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common"; +import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, Forms, IconUtils, lodash, Parser, PermissionsBits, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common"; import type { CustomEmoji } from "@webpack/types"; import type { Message } from "discord-types/general"; import { applyPalette, GIFEncoder, quantize } from "gifenc"; import type { ReactElement, ReactNode } from "react"; -const DRAFT_TYPE = 0; const StickerStore = findStoreLazy("StickersStore") as { getPremiumPacks(): StickerPack[]; getAllGuildStickers(): Map; @@ -807,7 +806,7 @@ export default definePlugin({ gif.finish(); const file = new File([gif.bytesView()], `${stickerId}.gif`, { type: "image/gif" }); - UploadHandler.promptToUpload([file], ChannelStore.getChannel(channelId), DRAFT_TYPE); + UploadHandler.promptToUpload([file], ChannelStore.getChannel(channelId), DraftType.ChannelMessage); }, canUseEmote(e: CustomEmoji, channelId: string) { diff --git a/src/plugins/petpet/index.ts b/src/plugins/petpet/index.ts index 3f974325..2e06d0b1 100644 --- a/src/plugins/petpet/index.ts +++ b/src/plugins/petpet/index.ts @@ -21,10 +21,9 @@ import { Devs } from "@utils/constants"; import { makeLazy } from "@utils/lazy"; import definePlugin from "@utils/types"; import { findByPropsLazy } from "@webpack"; -import { UploadHandler, UserUtils } from "@webpack/common"; +import { DraftType, UploadHandler, UploadManager, UserUtils } from "@webpack/common"; import { applyPalette, GIFEncoder, quantize } from "gifenc"; -const DRAFT_TYPE = 0; const DEFAULT_DELAY = 20; const DEFAULT_RESOLUTION = 128; const FRAMES = 10; @@ -59,9 +58,12 @@ async function resolveImage(options: Argument[], ctx: CommandContext, noServerPf for (const opt of options) { switch (opt.name) { case "image": - const upload = UploadStore.getUploads(ctx.channel.id, DRAFT_TYPE)[0]; + const upload = UploadStore.getUpload(ctx.channel.id, opt.name, DraftType.SlashCommand); if (upload) { - if (!upload.isImage) throw "Upload is not an image"; + if (!upload.isImage) { + UploadManager.clearAll(ctx.channel.id, DraftType.SlashCommand); + throw "Upload is not an image"; + } return upload.item.file; } break; @@ -73,10 +75,12 @@ async function resolveImage(options: Argument[], ctx: CommandContext, noServerPf return user.getAvatarURL(noServerPfp ? void 0 : ctx.guild?.id, 2048).replace(/\?size=\d+$/, "?size=2048"); } catch (err) { console.error("[petpet] Failed to fetch user\n", err); + UploadManager.clearAll(ctx.channel.id, DraftType.SlashCommand); throw "Failed to fetch user. Check the console for more info."; } } } + UploadManager.clearAll(ctx.channel.id, DraftType.SlashCommand); return null; } @@ -130,6 +134,7 @@ export default definePlugin({ var url = await resolveImage(opts, cmdCtx, noServerPfp); if (!url) throw "No Image specified!"; } catch (err) { + UploadManager.clearAll(cmdCtx.channel.id, DraftType.SlashCommand); sendBotMessage(cmdCtx.channel.id, { content: String(err), }); @@ -147,6 +152,8 @@ export default definePlugin({ canvas.width = canvas.height = resolution; const ctx = canvas.getContext("2d")!; + UploadManager.clearAll(cmdCtx.channel.id, DraftType.SlashCommand); + for (let i = 0; i < FRAMES; i++) { ctx.clearRect(0, 0, canvas.width, canvas.height); @@ -174,7 +181,7 @@ export default definePlugin({ const file = new File([gif.bytesView()], "petpet.gif", { type: "image/gif" }); // Immediately after the command finishes, Discord clears all input, including pending attachments. // Thus, setTimeout is needed to make this execute after Discord cleared the input - setTimeout(() => UploadHandler.promptToUpload([file], cmdCtx.channel, DRAFT_TYPE), 10); + setTimeout(() => UploadHandler.promptToUpload([file], cmdCtx.channel, DraftType.ChannelMessage), 10); }, }, ] diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 21d89190..a77edf7d 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -485,7 +485,11 @@ export const Devs = /* #__PURE__*/ Object.freeze({ xocherry: { name: "xocherry", id: 221288171013406720n - } + }, + ScattrdBlade: { + name: "ScattrdBlade", + id: 678007540608532491n + }, } satisfies Record); // iife so #__PURE__ works correctly diff --git a/src/webpack/common/stores.ts b/src/webpack/common/stores.ts index be5721ff..123c62b0 100644 --- a/src/webpack/common/stores.ts +++ b/src/webpack/common/stores.ts @@ -27,12 +27,7 @@ export const Flux: t.Flux = findByPropsLazy("connectStores"); export type GenericStore = t.FluxStore & Record; -export enum DraftType { - ChannelMessage = 0, - ThreadSettings = 1, - FirstThreadMessage = 2, - ApplicationLauncherCommand = 3 -} +export const { DraftType }: { DraftType: typeof t.DraftType; } = findByPropsLazy("DraftType"); export let MessageStore: Omit & { getMessages(chanId: string): any; diff --git a/src/webpack/common/types/stores.d.ts b/src/webpack/common/types/stores.d.ts index d6bf3aaf..27715b5e 100644 --- a/src/webpack/common/types/stores.d.ts +++ b/src/webpack/common/types/stores.d.ts @@ -173,6 +173,15 @@ export class DraftStore extends FluxStore { getThreadSettings(channelId: string): any | null; } +export enum DraftType { + ChannelMessage, + ThreadSettings, + FirstThreadMessage, + ApplicationLauncherCommand, + Poll, + SlashCommand, +} + export class GuildStore extends FluxStore { getGuild(guildId: string): Guild; getGuildCount(): number; diff --git a/src/webpack/common/utils.ts b/src/webpack/common/utils.ts index 6d74e9b2..2cd636d8 100644 --- a/src/webpack/common/utils.ts +++ b/src/webpack/common/utils.ts @@ -119,6 +119,8 @@ export function showToast(message: string, type = ToastType.MESSAGE) { } export const UserUtils = findByPropsLazy("getUser", "fetchCurrentUser") as { getUser: (id: string) => Promise; }; + +export const UploadManager = findByPropsLazy("clearAll", "addFile"); export const UploadHandler = findByPropsLazy("showUploadFileSizeExceededError", "promptToUpload") as { promptToUpload: (files: File[], channel: Channel, draftType: Number) => void; }; diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index 34a9b69c..8ea6713d 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -432,7 +432,7 @@ export async function extractAndLoadChunks(code: string[], matcher: RegExp = Def } const [, rawChunkIds, entryPointId] = match; - if (Number.isNaN(entryPointId)) { + if (Number.isNaN(Number(entryPointId))) { const err = new Error("extractAndLoadChunks: Matcher didn't return a capturing group with the chunk ids array, or the entry point id returned as the second group wasn't a number"); logger.warn(err, "Code:", code, "Matcher:", matcher); From f74da73086b2b361490e028c999c80b3ac2ba76a Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Tue, 14 May 2024 23:57:43 -0300 Subject: [PATCH 15/16] feat: Allow finds to use regex (#2452) --- .../VencordSettings/PatchHelperTab.tsx | 44 ++++++++++++++----- src/utils/types.ts | 8 +++- src/webpack/patchWebpack.ts | 7 ++- 3 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/components/VencordSettings/PatchHelperTab.tsx b/src/components/VencordSettings/PatchHelperTab.tsx index 064c872a..9e2980e7 100644 --- a/src/components/VencordSettings/PatchHelperTab.tsx +++ b/src/components/VencordSettings/PatchHelperTab.tsx @@ -180,7 +180,8 @@ function ReplacementInput({ replacement, setReplacement, replacementError }) { return ( <> - replacement + {/* FormTitle adds a class if className is not set, so we set it to an empty string to prevent that */} + replacement {!isFunc && (
- Cheat Sheet + Cheat Sheet {Object.entries({ "\\i": "Special regex escape sequence that matches identifiers (varnames, classnames, etc.)", "$$": "Insert a $", @@ -220,11 +221,12 @@ function ReplacementInput({ replacement, setReplacement, replacementError }) { interface FullPatchInputProps { setFind(v: string): void; + setParsedFind(v: string | RegExp): void; setMatch(v: string): void; setReplacement(v: string | ReplaceFn): void; } -function FullPatchInput({ setFind, setMatch, setReplacement }: FullPatchInputProps) { +function FullPatchInput({ setFind, setParsedFind, setMatch, setReplacement }: FullPatchInputProps) { const [fullPatch, setFullPatch] = React.useState(""); const [fullPatchError, setFullPatchError] = React.useState(""); @@ -233,6 +235,7 @@ function FullPatchInput({ setFind, setMatch, setReplacement }: FullPatchInputPro setFullPatchError(""); setFind(""); + setParsedFind(""); setMatch(""); setReplacement(""); return; @@ -256,7 +259,8 @@ function FullPatchInput({ setFind, setMatch, setReplacement }: FullPatchInputPro if (!parsed.replacement.match) throw new Error("No 'replacement.match' field"); if (!parsed.replacement.replace) throw new Error("No 'replacement.replace' field"); - setFind(parsed.find); + setFind(parsed.find instanceof RegExp ? parsed.find.toString() : parsed.find); + setParsedFind(parsed.find); setMatch(parsed.replacement.match instanceof RegExp ? parsed.replacement.match.source : parsed.replacement.match); setReplacement(parsed.replacement.replace); setFullPatchError(""); @@ -266,7 +270,7 @@ function FullPatchInput({ setFind, setMatch, setReplacement }: FullPatchInputPro } return <> - Paste your full JSON patch here to fill out the fields + Paste your full JSON patch here to fill out the fields