From c25c95eecd1dcda62db73169877d84e9f12b38d5 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sat, 21 Oct 2023 12:00:09 -0300 Subject: [PATCH 001/632] Fix IgnoreActivities making reporter angry --- src/plugins/ignoreActivities/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/ignoreActivities/index.tsx b/src/plugins/ignoreActivities/index.tsx index e2888dd1e..18dd7993e 100644 --- a/src/plugins/ignoreActivities/index.tsx +++ b/src/plugins/ignoreActivities/index.tsx @@ -106,7 +106,7 @@ export default definePlugin({ } }, { - find: ".Messages.EMBEDDED_ACTIVITIES_HAVE_PLAYED_ONE_KNOWN", + find: ".Messages.EMBEDDED_ACTIVITIES_DEVELOPER_SHELF_SUBTITLE", replacement: [ { match: /(?<=\(\)\.activityTitleText.+?children:(\i)\.name.*?}\),)/, From b2a1410a96c8cfa21c86383415098eec6d5c765e Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sat, 21 Oct 2023 12:03:54 -0300 Subject: [PATCH 002/632] Remove useless Experiments patch --- src/plugins/experiments/index.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/plugins/experiments/index.tsx b/src/plugins/experiments/index.tsx index cf5f4e6fd..149c436c2 100644 --- a/src/plugins/experiments/index.tsx +++ b/src/plugins/experiments/index.tsx @@ -77,15 +77,6 @@ export default definePlugin({ } ] }, - // Fix search history being disabled / broken with isStaff - { - find: 'get("disable_new_search")', - predicate: () => settings.store.enableIsStaff, - replacement: { - match: /(?<=showNewSearch"\);return)\s?!/, - replace: "!1&&!" - } - }, { find: 'H1,title:"Experiments"', replacement: { From 6c1b8b0d8a2c30ea28ddaea79d133b5c763369a4 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sat, 21 Oct 2023 12:04:22 -0300 Subject: [PATCH 003/632] Fix MessageDecorationsAPI --- src/plugins/_api/messageDecorations.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/_api/messageDecorations.ts b/src/plugins/_api/messageDecorations.ts index a3b251830..39f77e544 100644 --- a/src/plugins/_api/messageDecorations.ts +++ b/src/plugins/_api/messageDecorations.ts @@ -27,7 +27,7 @@ export default definePlugin({ { find: ".withMentionPrefix", replacement: { - match: /(.roleDot.{10,50}{children:.{1,2})}\)/, + match: /(currentUserIsPremium:.{10,50}{children:.{1,2})}\)/, replace: "$1.concat(Vencord.Api.MessageDecorations.__addDecorationsToMessage(arguments[0]))})" } } From 5a0b2ee3f50c1d14bb7bd6af20d8fdd5532cac71 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sat, 21 Oct 2023 12:04:32 -0300 Subject: [PATCH 004/632] Fix FakeNitro patch --- src/plugins/fakeNitro/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/fakeNitro/index.ts b/src/plugins/fakeNitro/index.ts index a11a43de0..a6ddb4723 100644 --- a/src/plugins/fakeNitro/index.ts +++ b/src/plugins/fakeNitro/index.ts @@ -295,7 +295,7 @@ export default definePlugin({ }, { predicate: () => settings.store.transformStickers, - match: /renderAttachments=function\(\i\){var (\i)=\i.attachments.+?;/, + match: /renderAttachments=function\(\i\){var \i=this,(\i)=\i.attachments.+?;/, replace: (m, attachments) => `${m}${attachments}=$self.filterAttachments(${attachments});` } ] From fe60a72b804fd8a609c36fc568bd81143dac2b19 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sat, 21 Oct 2023 12:13:49 -0300 Subject: [PATCH 005/632] Fix NoPendingCount patch --- src/plugins/noPendingCount/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/noPendingCount/index.ts b/src/plugins/noPendingCount/index.ts index 2ce375e00..21bd196a6 100644 --- a/src/plugins/noPendingCount/index.ts +++ b/src/plugins/noPendingCount/index.ts @@ -84,8 +84,8 @@ export default definePlugin({ find: "showProgressBadge:", predicate: () => settings.store.hidePremiumOffersCount, replacement: { - match: /\(function\(\){return \i\.\i\.getUnacknowledgedOffers\(\i\)\.length}\)/, - replace: "(function(){return 0})" + match: /=\i\.unviewedTrialCount\+\i\.unviewedDiscountCount/, + replace: "=0" } } ], From ae1dc4eab0b668318d3a1d274178c52f62a8e46d Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sat, 21 Oct 2023 12:51:07 -0300 Subject: [PATCH 006/632] Make reporter ignore useless Discord errors (#1829) --- scripts/generateReport.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 68e3c8081..180212126 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -61,6 +61,13 @@ const report = { otherErrors: [] as string[] }; +const IGNORED_DISCORD_ERRORS = [ + "KeybindStore: Looking for callback action", + "Unable to process domain list delta: Client revision number is null", + "Downloading the full bad domains file", + /\[GatewaySocket\].{0,110}Cannot access '/ +] as Array; + function toCodeBlock(s: string) { s = s.replace(/```/g, "`\u200B`\u200B`"); return "```" + s + " ```"; @@ -86,6 +93,8 @@ async function printReport() { console.log(` - Error: ${toCodeBlock(p.error)}`); }); + report.otherErrors = report.otherErrors.filter(e => !IGNORED_DISCORD_ERRORS.some(regex => e.match(regex))); + console.log("## Discord Errors"); report.otherErrors.forEach(e => { console.log(`- ${toCodeBlock(e)}`); From f659c46031c05f8255a2849cf99b43038650257a Mon Sep 17 00:00:00 2001 From: zImPatrick <23613354+zImPatrick@users.noreply.github.com> Date: Sat, 21 Oct 2023 17:53:00 +0200 Subject: [PATCH 007/632] FakeNitro: Add app icon customization (#1822) Co-authored-by: V --- src/plugins/fakeNitro/index.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/plugins/fakeNitro/index.ts b/src/plugins/fakeNitro/index.ts index a6ddb4723..677286e72 100644 --- a/src/plugins/fakeNitro/index.ts +++ b/src/plugins/fakeNitro/index.ts @@ -329,6 +329,20 @@ export default definePlugin({ match: /(?<=\.Messages\.EMOJI_POPOUT_ADDED_PACK_DESCRIPTION.+?return ).{0,1200}\.Messages\.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION.+?(?=}\()/, replace: reactNode => `$self.addFakeNotice(${FakeNoticeType.Emoji},${reactNode},!!arguments[0]?.fakeNitroNode?.fake)` } + }, + { + find: "canUsePremiumAppIcons:function", + replacement: { + match: /canUsePremiumAppIcons:function\(\i\){/, + replace: "$&return true;" + } + }, + { + find: "location:\"AppIconHome\"", + replacement: { + match: /\i\.\i\.isPremium\(\i\.\i\.getCurrentUser\(\)\)/, + replace: "true" + } } ], From fada76ec81d8d18bc4d1a92c09078bee42305cb8 Mon Sep 17 00:00:00 2001 From: Macintosh II <95250141+mctaylors@users.noreply.github.com> Date: Sat, 21 Oct 2023 19:41:56 +0300 Subject: [PATCH 008/632] PlatformIndicators: make size same as other memberlist icons (#1789) Co-authored-by: V --- src/plugins/platformIndicators/index.tsx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/plugins/platformIndicators/index.tsx b/src/plugins/platformIndicators/index.tsx index 6a7a8be74..7233cd13d 100644 --- a/src/plugins/platformIndicators/index.tsx +++ b/src/plugins/platformIndicators/index.tsx @@ -30,13 +30,13 @@ import { User } from "discord-types/general"; const SessionsStore = findStoreLazy("SessionsStore"); function Icon(path: string, opts?: { viewBox?: string; width?: number; height?: number; }) { - return ({ color, tooltip }: { color: string; tooltip: string; }) => ( + return ({ color, tooltip, small }: { color: string; tooltip: string; small: boolean; }) => ( {(tooltipProps: any) => ( @@ -57,16 +57,16 @@ type Platform = keyof typeof Icons; const getStatusColor = findByCodeLazy(".TWITCH", ".STREAMING", ".INVISIBLE"); -const PlatformIcon = ({ platform, status }: { platform: Platform, status: string; }) => { +const PlatformIcon = ({ platform, status, small }: { platform: Platform, status: string; small: boolean; }) => { const tooltip = platform[0].toUpperCase() + platform.slice(1); const Icon = Icons[platform] ?? Icons.desktop; - return ; + return ; }; const getStatus = (id: string): Record => PresenceStore.getState()?.clientStatuses?.[id]; -const PlatformIndicator = ({ user, wantMargin = true, wantTopMargin = false }: { user: User; wantMargin?: boolean; wantTopMargin?: boolean; }) => { +const PlatformIndicator = ({ user, wantMargin = true, wantTopMargin = false, small = false }: { user: User; wantMargin?: boolean; wantTopMargin?: boolean; small?: boolean; }) => { if (!user || user.bot) return null; if (user.id === UserStore.getCurrentUser().id) { @@ -99,6 +99,7 @@ const PlatformIndicator = ({ user, wantMargin = true, wantTopMargin = false }: { key={platform} platform={platform as Platform} status={status} + small={small} /> )); @@ -137,7 +138,7 @@ const indicatorLocations = { description: "In the member list", onEnable: () => addDecorator("platform-indicator", props => - + ), onDisable: () => removeDecorator("platform-indicator") From e4485165d0a7c5781b4078cf52a5e32213c6a854 Mon Sep 17 00:00:00 2001 From: Dea Date: Sat, 21 Oct 2023 16:42:37 +0000 Subject: [PATCH 009/632] onePingPerDM: add settings (#1802) Co-authored-by: V Co-authored-by: Dea --- src/plugins/onePingPerDM/index.ts | 40 +++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/src/plugins/onePingPerDM/index.ts b/src/plugins/onePingPerDM/index.ts index 47502ebe8..e69620ae7 100644 --- a/src/plugins/onePingPerDM/index.ts +++ b/src/plugins/onePingPerDM/index.ts @@ -4,20 +4,44 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; -import definePlugin from "@utils/types"; -import { ChannelStore, ReadStateStore } from "@webpack/common"; -import { Message } from "discord-types/general"; +import definePlugin, { OptionType } from "@utils/types"; +import { ChannelStore, ReadStateStore, UserStore } from "@webpack/common"; +import { MessageJSON } from "discord-types/general"; const enum ChannelType { DM = 1, GROUP_DM = 3 } +const settings = definePluginSettings({ + channelToAffect: { + type: OptionType.SELECT, + description: "Select the type of DM for the plugin to affect", + options: [ + { label: "Both", value: "both_dms", default: true }, + { label: "User DMs", value: "user_dm" }, + { label: "Group DMs", value: "group_dm" }, + ] + }, + allowMentions: { + type: OptionType.BOOLEAN, + description: "Receive audio pings for @mentions", + default: false, + }, + allowEveryone: { + type: OptionType.BOOLEAN, + description: "Receive audio pings for @everyone and @here in group DMs", + default: false, + }, +}); + export default definePlugin({ name: "OnePingPerDM", description: "If unread messages are sent by a user in DMs multiple times, you'll only receive one audio ping. Read the messages to reset the limit", authors: [Devs.ProffDea], + settings, patches: [{ find: ".getDesktopType()===", replacement: [{ @@ -29,11 +53,19 @@ export default definePlugin({ replace: "sound:!$self.isPrivateChannelRead(arguments[0]?.message)?undefined:$1" }] }], - isPrivateChannelRead(message: Message) { + isPrivateChannelRead(message: MessageJSON) { const channelType = ChannelStore.getChannel(message.channel_id)?.type; if (channelType !== ChannelType.DM && channelType !== ChannelType.GROUP_DM) { return false; } + if ( + (channelType === ChannelType.DM && settings.store.channelToAffect === "group_dm") || + (channelType === ChannelType.GROUP_DM && settings.store.channelToAffect === "user_dm") || + (settings.store.allowMentions && message.mentions.some(m => m.id === UserStore.getCurrentUser().id)) || + (settings.store.allowEveryone && message.mention_everyone) + ) { + return true; + } return ReadStateStore.getOldestUnreadMessageId(message.channel_id) === message.id; }, }); From 544edce9f9da6263a20a293e6b4d0a60e5657253 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 21 Oct 2023 19:26:29 +0200 Subject: [PATCH 010/632] bump to v1.5.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7a3ba0e25..9ab6b2233 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.5.7", + "version": "1.5.8", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { From 4a2def03e764138c46607ad2981f9d539df19465 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Mon, 23 Oct 2023 22:42:23 -0300 Subject: [PATCH 011/632] Fix MessageDecorationsAPI patch --- src/plugins/_api/messageDecorations.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/_api/messageDecorations.ts b/src/plugins/_api/messageDecorations.ts index 39f77e544..3c235d768 100644 --- a/src/plugins/_api/messageDecorations.ts +++ b/src/plugins/_api/messageDecorations.ts @@ -27,8 +27,8 @@ export default definePlugin({ { find: ".withMentionPrefix", replacement: { - match: /(currentUserIsPremium:.{10,50}{children:.{1,2})}\)/, - replace: "$1.concat(Vencord.Api.MessageDecorations.__addDecorationsToMessage(arguments[0]))})" + match: /currentUserIsPremium:.{0,70}{children:\i(?=}\))/, + replace: "$&.concat(Vencord.Api.MessageDecorations.__addDecorationsToMessage(arguments[0]))" } } ], From 1a1d9b07e8fc5976989437caa1407b41eb96d3e9 Mon Sep 17 00:00:00 2001 From: V Date: Wed, 25 Oct 2023 00:17:11 +0200 Subject: [PATCH 012/632] Fix canary crashing (#1833) --- src/plugins/_core/settings.tsx | 22 +++++----- src/plugins/messageLogger/index.tsx | 2 +- .../components/PronounsChatComponent.tsx | 13 +++--- src/plugins/pronoundb/index.ts | 4 +- src/utils/modal.tsx | 9 +--- src/webpack/common/stores.ts | 12 +++++- src/webpack/common/utils.ts | 12 +++++- src/webpack/patchWebpack.ts | 6 +-- src/webpack/webpack.ts | 43 ++++++++++++++++++- 9 files changed, 87 insertions(+), 36 deletions(-) diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index f7fcdd0aa..6f43b76a8 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -63,26 +63,26 @@ export default definePlugin({ replacement: { get match() { switch (Settings.plugins.Settings.settingsLocation) { - case "top": return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.USER_SETTINGS\}/; - case "aboveNitro": return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.BILLING_SETTINGS\}/; - case "belowNitro": return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.APP_SETTINGS\}/; - case "belowActivity": return /(?<=\{section:(\i)\.ID\.DIVIDER},)\{section:"changelog"/; - case "bottom": return /\{section:(\i)\.ID\.CUSTOM,\s*element:.+?}/; + 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)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.ACTIVITY_SETTINGS\}/; + return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.ACTIVITY_SETTINGS\}/; } }, replace: "...$self.makeSettingsCategories($1),$&" } }], - customSections: [] as ((ID: Record) => any)[], + customSections: [] as ((SectionTypes: Record) => any)[], - makeSettingsCategories({ ID }: { ID: Record; }) { + makeSettingsCategories(SectionTypes: Record) { return [ { - section: ID.HEADER, + section: SectionTypes.HEADER, label: "Vencord", className: "vc-settings-header" }, @@ -128,9 +128,9 @@ export default definePlugin({ element: require("@components/VencordSettings/PatchHelperTab").default, className: "vc-patch-helper" }, - ...this.customSections.map(func => func(ID)), + ...this.customSections.map(func => func(SectionTypes)), { - section: ID.DIVIDER + section: SectionTypes.DIVIDER } ].filter(Boolean); }, diff --git a/src/plugins/messageLogger/index.tsx b/src/plugins/messageLogger/index.tsx index 645892810..934df2fbe 100644 --- a/src/plugins/messageLogger/index.tsx +++ b/src/plugins/messageLogger/index.tsx @@ -360,7 +360,7 @@ export default definePlugin({ { // Render editHistory in the deepest div for message content match: /(\)\("div",\{id:.+?children:\[)/, - replace: "$1 (arguments[0].message.editHistory.length > 0 ? arguments[0].message.editHistory.map(edit => $self.renderEdit(edit)) : null), " + replace: "$1 (arguments[0].message.editHistory?.length > 0 ? arguments[0].message.editHistory.map(edit => $self.renderEdit(edit)) : null), " } ] }, diff --git a/src/plugins/pronoundb/components/PronounsChatComponent.tsx b/src/plugins/pronoundb/components/PronounsChatComponent.tsx index 97d24943d..64fac18ba 100644 --- a/src/plugins/pronoundb/components/PronounsChatComponent.tsx +++ b/src/plugins/pronoundb/components/PronounsChatComponent.tsx @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +import ErrorBoundary from "@components/ErrorBoundary"; import { classes } from "@utils/misc"; import { findByPropsLazy } from "@webpack"; import { UserStore } from "@webpack/common"; @@ -39,17 +40,17 @@ function shouldShow(message: Message): boolean { return true; } -export function PronounsChatComponentWrapper({ message }: { message: Message; }) { +export const PronounsChatComponentWrapper = ErrorBoundary.wrap(({ message }: { message: Message; }) => { return shouldShow(message) ? : null; -} +}, { noop: true }); -export function CompactPronounsChatComponentWrapper({ message }: { message: Message; }) { +export const CompactPronounsChatComponentWrapper = ErrorBoundary.wrap(({ message }: { message: Message; }) => { return shouldShow(message) ? : null; -} +}, { noop: true }); function PronounsChatComponent({ message }: { message: Message; }) { const [result] = useFormattedPronouns(message.author.id); @@ -63,7 +64,7 @@ function PronounsChatComponent({ message }: { message: Message; }) { : null; } -export function CompactPronounsChatComponent({ message }: { message: Message; }) { +export const CompactPronounsChatComponent = ErrorBoundary.wrap(({ message }: { message: Message; }) => { const [result] = useFormattedPronouns(message.author.id); return result @@ -73,4 +74,4 @@ export function CompactPronounsChatComponent({ message }: { message: Message; }) >• {result} ) : null; -} +}, { noop: true }); diff --git a/src/plugins/pronoundb/index.ts b/src/plugins/pronoundb/index.ts index c9dc2725e..bd5b2c5a9 100644 --- a/src/plugins/pronoundb/index.ts +++ b/src/plugins/pronoundb/index.ts @@ -41,7 +41,7 @@ export default definePlugin({ find: "showCommunicationDisabledStyles", replacement: { match: /("span",{id:\i,className:\i,children:\i}\))/, - replace: "$1, $self.CompactPronounsChatComponentWrapper(e)" + replace: "$1, $self.CompactPronounsChatComponentWrapper(arguments[0])" } }, // Patch the chat timestamp element (normal mode) @@ -49,7 +49,7 @@ export default definePlugin({ find: "showCommunicationDisabledStyles", replacement: { match: /(?<=return\s*\(0,\i\.jsxs?\)\(.+!\i&&)(\(0,\i.jsxs?\)\(.+?\{.+?\}\))/, - replace: "[$1, $self.PronounsChatComponentWrapper(e)]" + replace: "[$1, $self.PronounsChatComponentWrapper(arguments[0])]" } }, // Patch the profile popout username header to use our pronoun hook instead of Discord's pronouns diff --git a/src/utils/modal.tsx b/src/utils/modal.tsx index 4ac6f9b1f..d7a054a5b 100644 --- a/src/utils/modal.tsx +++ b/src/utils/modal.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { filters, findByCode, mapMangledModuleLazy } from "@webpack"; +import { filters, findByCode, findByPropsLazy, mapMangledModuleLazy } from "@webpack"; import type { ComponentType, PropsWithChildren, ReactNode, Ref } from "react"; import { LazyComponent } from "./react"; @@ -132,12 +132,7 @@ export const ModalContent = LazyComponent(() => Modals.ModalContent); export const ModalFooter = LazyComponent(() => Modals.ModalFooter); export const ModalCloseButton = LazyComponent(() => Modals.ModalCloseButton); -const ModalAPI = mapMangledModuleLazy("onCloseRequest:null!=", { - openModal: filters.byCode("onCloseRequest:null!="), - closeModal: filters.byCode("onCloseCallback&&"), - openModalLazy: m => m?.length === 1 && filters.byCode(".apply(this,arguments)")(m), - closeAllModals: filters.byCode(".value.key,") -}); +const ModalAPI = findByPropsLazy("openModalLazy"); /** * Wait for the render promise to resolve, then open a modal with it. diff --git a/src/webpack/common/stores.ts b/src/webpack/common/stores.ts index d42cb6b56..c4ebd4a95 100644 --- a/src/webpack/common/stores.ts +++ b/src/webpack/common/stores.ts @@ -16,10 +16,11 @@ * along with this program. If not, see . */ +import { proxyLazy } from "@utils/lazy"; import type * as Stores from "discord-types/stores"; // eslint-disable-next-line path-alias/no-relative -import { filters, findByCodeLazy, findByPropsLazy, mapMangledModuleLazy } from "../webpack"; +import { filters, findByCode, findByProps, findByPropsLazy, mapMangledModuleLazy } from "../webpack"; import { waitForStore } from "./internal"; import * as t from "./types/stores"; @@ -83,7 +84,14 @@ export const useStateFromStores: ( idk?: any, isEqual?: (old: T, newer: T) => boolean ) => T - = findByCodeLazy("useStateFromStores"); + // FIXME: hack to support old stable and new canary + = proxyLazy(() => { + try { + return findByProps("useStateFromStores").useStateFromStores; + } catch { + return findByCode('("useStateFromStores")'); + } + }); waitForStore("DraftStore", s => DraftStore = s); waitForStore("UserStore", s => UserStore = s); diff --git a/src/webpack/common/utils.ts b/src/webpack/common/utils.ts index 2c814de57..bc97f5e74 100644 --- a/src/webpack/common/utils.ts +++ b/src/webpack/common/utils.ts @@ -16,10 +16,11 @@ * along with this program. If not, see . */ +import { proxyLazy } from "@utils/lazy"; import type { User } from "discord-types/general"; // eslint-disable-next-line path-alias/no-relative -import { _resolveReady, filters, findByCodeLazy, findByPropsLazy, findLazy, mapMangledModuleLazy, waitFor } from "../webpack"; +import { _resolveReady, filters, find, findByCodeLazy, findByPropsLazy, findLazy, mapMangledModuleLazy, waitFor } from "../webpack"; import type * as t from "./types/utils"; export let FluxDispatcher: t.FluxDispatcher; @@ -126,4 +127,11 @@ waitFor("parseTopic", m => Parser = m); export let SettingsRouter: any; waitFor(["open", "saveAccountChanges"], m => SettingsRouter = m); -export const PermissionsBits: t.PermissionsBits = findLazy(m => typeof m.ADMINISTRATOR === "bigint"); +// FIXME: hack to support old stable and new canary +export const PermissionsBits: t.PermissionsBits = proxyLazy(() => { + try { + return find(m => m.Permissions?.ADMINISTRATOR).Permissions; + } catch { + return find(m => typeof m.ADMINISTRATOR === "bigint"); + } +}); diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index f422372c1..9e7af6b73 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -36,9 +36,8 @@ if (window[WEBPACK_CHUNK]) { Object.defineProperty(window, WEBPACK_CHUNK, { get: () => webpackChunk, set: v => { - if (v?.push !== Array.prototype.push) { + if (v?.push !== Array.prototype.push && _initWebpack(v)) { logger.info(`Patching ${WEBPACK_CHUNK}.push`); - _initWebpack(v); patchPush(); // @ts-ignore delete window[WEBPACK_CHUNK]; @@ -85,10 +84,9 @@ function patchPush() { logger.error("Error in patched chunk", err); return void originalMod(module, exports, require); } - // There are (at the time of writing) 11 modules exporting the window // Make these non enumerable to improve webpack search performance - if (module.exports === window) { + if (exports === window) { Object.defineProperty(require.c, id, { value: require.c[id], enumerable: false, diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index b4a896086..1fdfa43e9 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -62,9 +62,50 @@ export type CallbackFn = (mod: any, id: number) => void; export function _initWebpack(instance: typeof window.webpackChunkdiscord_app) { if (cache !== void 0) throw "no."; - wreq = instance.push([[Symbol("Vencord")], {}, r => r]); + instance.push([[Symbol("Vencord")], {}, r => wreq = r]); + if (!wreq) return false; + cache = wreq.c; instance.pop(); + + for (const id in cache) { + const { exports } = cache[id]; + if (!exports) continue; + + const numberId = Number(id); + + for (const callback of listeners) { + try { + callback(exports, numberId); + } catch (err) { + logger.error("Error in webpack listener", err); + } + } + + for (const [filter, callback] of subscriptions) { + try { + if (filter(exports)) { + subscriptions.delete(filter); + callback(exports, numberId); + } else if (typeof exports === "object") { + if (exports.default && filter(exports.default)) { + subscriptions.delete(filter); + callback(exports.default, numberId); + } + + for (const nested in exports) if (nested.length <= 3) { + if (exports[nested] && filter(exports[nested])) { + subscriptions.delete(filter); + callback(exports[nested], numberId); + } + } + } + } catch (err) { + logger.error("Error while firing callback for webpack chunk", err); + } + } + } + return true; } if (IS_DEV && IS_DISCORD_DESKTOP) { From 97c0face2f246b9534c1a1c30bc402976924ad02 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 25 Oct 2023 00:38:02 +0200 Subject: [PATCH 013/632] PinDMs: Fix canary crash --- src/plugins/pinDms/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/pinDms/index.tsx b/src/plugins/pinDms/index.tsx index 02fe332dc..a7406aa41 100644 --- a/src/plugins/pinDms/index.tsx +++ b/src/plugins/pinDms/index.tsx @@ -81,7 +81,7 @@ export default definePlugin({ // array with the count, or an empty array. Due to spreading, only in the former // case will an element be added to the outer array // Thanks for the fix, Strencher! - replace: "$&...$1.props.pinCount," + replace: "$&...($1.props.pinCount ?? [])," }, { // Patch renderSection (renders the header) to set the text to "Pinned DMs" instead of "Direct Messages" From 4e27722b543f8a7c86270276df68d7cb89adeec3 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 25 Oct 2023 14:26:10 +0200 Subject: [PATCH 014/632] fix webpack patching --- src/plugins/_core/noTrack.ts | 2 +- src/webpack/patchWebpack.ts | 36 ++++++++++++++++++++++++------------ 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/plugins/_core/noTrack.ts b/src/plugins/_core/noTrack.ts index 79ef5a6ef..4267a6280 100644 --- a/src/plugins/_core/noTrack.ts +++ b/src/plugins/_core/noTrack.ts @@ -24,7 +24,7 @@ export default definePlugin({ description: "Disable Discord's tracking ('science'), metrics and Sentry crash reporting", authors: [Devs.Cyn, Devs.Ven, Devs.Nuckyz, Devs.Arrow], required: true, - patches: [ + patches: true ? [] : [ { find: "TRACKING_URL:", replacement: { diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index 9e7af6b73..a3bc7a6f6 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -31,17 +31,23 @@ const logger = new Logger("WebpackInterceptor", "#8caaee"); if (window[WEBPACK_CHUNK]) { logger.info(`Patching ${WEBPACK_CHUNK}.push (was already existant, likely from cache!)`); _initWebpack(window[WEBPACK_CHUNK]); - patchPush(); + patchPush(window[WEBPACK_CHUNK]); } else { Object.defineProperty(window, WEBPACK_CHUNK, { get: () => webpackChunk, set: v => { - if (v?.push !== Array.prototype.push && _initWebpack(v)) { - logger.info(`Patching ${WEBPACK_CHUNK}.push`); - patchPush(); - // @ts-ignore - delete window[WEBPACK_CHUNK]; - window[WEBPACK_CHUNK] = v; + if (v?.push) { + if (!v.push.$$vencordOriginal) { + logger.info(`Patching ${WEBPACK_CHUNK}.push`); + patchPush(v); + } + + if (_initWebpack(v)) { + logger.info("Successfully initialised Vencord webpack"); + // @ts-ignore + delete window[WEBPACK_CHUNK]; + window[WEBPACK_CHUNK] = v; + } } webpackChunk = v; }, @@ -49,7 +55,7 @@ if (window[WEBPACK_CHUNK]) { }); } -function patchPush() { +function patchPush(webpackGlobal: any) { function handlePush(chunk: any) { try { const modules = chunk[1]; @@ -213,13 +219,19 @@ function patchPush() { logger.error("Error in handlePush", err); } - return handlePush.original.call(window[WEBPACK_CHUNK], chunk); + return handlePush.$$vencordOriginal.call(webpackGlobal, chunk); } - handlePush.original = window[WEBPACK_CHUNK].push; - Object.defineProperty(window[WEBPACK_CHUNK], "push", { + handlePush.$$vencordOriginal = webpackGlobal.push; + Object.defineProperty(webpackGlobal, "push", { get: () => handlePush, - set: v => (handlePush.original = v), + set(v) { + Object.defineProperty(this, "push", { + value: v, + configurable: true + }); + patchPush(this); + }, configurable: true }); } From 4da79abb2149013ca6e3ed8b28d7073695bf1106 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 25 Oct 2023 14:37:54 +0200 Subject: [PATCH 015/632] Fix components filter --- src/webpack/common/components.ts | 2 +- src/webpack/patchWebpack.ts | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/webpack/common/components.ts b/src/webpack/common/components.ts index 55d3b84d4..e44b1c9f6 100644 --- a/src/webpack/common/components.ts +++ b/src/webpack/common/components.ts @@ -55,7 +55,7 @@ export const MaskedLink = waitForComponent("MaskedLink", m => m?.t export const Timestamp = waitForComponent("Timestamp", filters.byCode(".Messages.MESSAGE_EDITED_TIMESTAMP_A11Y_LABEL.format")); export const Flex = waitForComponent("Flex", ["Justify", "Align", "Wrap"]); -waitFor("FormItem", m => { +waitFor(["FormItem", "Button"], m => { ({ useToken, Card, Button, FormSwitch: Switch, Tooltip, TextInput, TextArea, Text, Select, SearchableSelect, Slider, ButtonLooks, TabBar, Popout, Dialog, Paginator, ScrollerThin, Clickable, Avatar } = m); Forms = m; }); diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index a3bc7a6f6..395c35fe5 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -90,6 +90,11 @@ function patchPush(webpackGlobal: any) { logger.error("Error in patched chunk", err); return void originalMod(module, exports, require); } + + exports = module.exports; + + if (!exports) return; + // There are (at the time of writing) 11 modules exporting the window // Make these non enumerable to improve webpack search performance if (exports === window) { @@ -226,11 +231,9 @@ function patchPush(webpackGlobal: any) { Object.defineProperty(webpackGlobal, "push", { get: () => handlePush, set(v) { - Object.defineProperty(this, "push", { - value: v, - configurable: true - }); - patchPush(this); + delete webpackGlobal.push; + webpackGlobal.push = v; + patchPush(webpackGlobal); }, configurable: true }); From 4beef9f73be01716a9e5dc880e28afd0c821a571 Mon Sep 17 00:00:00 2001 From: megumin Date: Wed, 25 Oct 2023 13:39:57 +0100 Subject: [PATCH 016/632] fix: ComponentDispatch and GifPaste plugin (#1843) Co-authored-by: V --- .eslintrc.json | 5 ++++- src/plugins/gifPaste/index.ts | 2 +- src/webpack/common/utils.ts | 4 +++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 8fa3386ab..2ee24e8b3 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -51,7 +51,10 @@ "eqeqeq": ["error", "always", { "null": "ignore" }], "spaced-comment": ["error", "always", { "markers": ["!"] }], "yoda": "error", - "prefer-destructuring": ["error", { "object": true, "array": false }], + "prefer-destructuring": ["error", { + "VariableDeclarator": { "array": false, "object": true }, + "AssignmentExpression": { "array": false, "object": false } + }], "operator-assignment": ["error", "always"], "no-useless-computed-key": "error", "no-unneeded-ternary": ["error", { "defaultAssignment": false }], diff --git a/src/plugins/gifPaste/index.ts b/src/plugins/gifPaste/index.ts index f1dfb2ff9..8c1303ba4 100644 --- a/src/plugins/gifPaste/index.ts +++ b/src/plugins/gifPaste/index.ts @@ -33,7 +33,7 @@ export default definePlugin({ patches: [{ find: ".handleSelectGIF=", replacement: { - match: /\.handleSelectGIF=function.+?\{/, + match: /\.handleSelectGIF=\i=>\{/, replace: ".handleSelectGIF=function(gif){return $self.handleSelect(gif);" } }], diff --git a/src/webpack/common/utils.ts b/src/webpack/common/utils.ts index bc97f5e74..25fda58bb 100644 --- a/src/webpack/common/utils.ts +++ b/src/webpack/common/utils.ts @@ -24,7 +24,9 @@ import { _resolveReady, filters, find, findByCodeLazy, findByPropsLazy, findLazy import type * as t from "./types/utils"; export let FluxDispatcher: t.FluxDispatcher; -export const ComponentDispatch = findLazy(m => m.emitter?._events?.INSERT_TEXT); +export let ComponentDispatch; +waitFor(["ComponentDispatch", "ComponentDispatcher"], m => ComponentDispatch = m.ComponentDispatch); + export const RestAPI: t.RestAPI = findByPropsLazy("getAPIBaseURL", "get"); export const moment: typeof import("moment") = findByPropsLazy("parseTwoDigitYear"); From cb93c11e166964b9f1f03a28cd1d0b7b246f5407 Mon Sep 17 00:00:00 2001 From: Erik <81252038+Captain8771@users.noreply.github.com> Date: Wed, 25 Oct 2023 14:40:17 +0200 Subject: [PATCH 017/632] Fix CustomRPC (#1846) --- src/plugins/customRPC/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/customRPC/index.tsx b/src/plugins/customRPC/index.tsx index a58e54260..2ec7f53f7 100644 --- a/src/plugins/customRPC/index.tsx +++ b/src/plugins/customRPC/index.tsx @@ -32,7 +32,7 @@ const Colors = findByPropsLazy("profileColors"); const assetManager = mapMangledModuleLazy( "getAssetImage: size must === [number, number] for Twitch", { - getAsset: filters.byCode("apply("), + getAsset: filters.byCode("APPLICATION_ASSETS_FETCH_SUCCESS"), } ); From 922e3ce6fec18a78488ca8efabfef9b6047c0f1d Mon Sep 17 00:00:00 2001 From: Syncx <47534062+Syncxv@users.noreply.github.com> Date: Wed, 25 Oct 2023 18:10:50 +0530 Subject: [PATCH 018/632] fix: FavoriteGifSearch (#1842) --- src/plugins/favGifSearch/index.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugins/favGifSearch/index.tsx b/src/plugins/favGifSearch/index.tsx index d2966e95c..0cb5166e1 100644 --- a/src/plugins/favGifSearch/index.tsx +++ b/src/plugins/favGifSearch/index.tsx @@ -91,13 +91,13 @@ export default definePlugin({ patches: [ { - find: "renderCategoryExtras", + find: "renderHeaderContent()", replacement: [ { - // https://regex101.com/r/4uHtTE/1 + // https://regex101.com/r/07gpzP/1 // ($1 renderHeaderContent=function { ... switch (x) ... case FAVORITES:return) ($2) ($3 case default:return r.jsx(($), {...props})) - match: /(renderHeaderContent=function.{1,150}FAVORITES:return)(.{1,150};)(case.{1,200}default:return\(0,\i\.jsx\)\((?\i\.\i))/, - replace: "$1 this.state.resultType === \"Favorites\" ? $self.renderSearchBar(this, $) : $2; $3" + match: /(renderHeaderContent\(\).{1,150}FAVORITES:return)(.{1,150});(case.{1,200}default:return\(0,\i\.jsx\)\((?\i\..{1,10}),)/, + replace: "$1 this.state.resultType === 'Favorites' ? $self.renderSearchBar(this, $) : $2;$3" }, { // to persist filtered favorites when component re-renders. @@ -182,7 +182,7 @@ function SearchBar({ instance, SearchBarComponent }: { instance: Instance; Searc ref={ref} autoFocus={true} className={containerClasses.searchBar} - size={SearchBarComponent.Sizes.MEDIUM} + size={SearchBarComponent.Sizes.SMALL} onChange={onChange} onClear={() => { setQuery(""); From 09b646b86031312692a988c489bc6bad810906e3 Mon Sep 17 00:00:00 2001 From: Hugo C Date: Wed, 25 Oct 2023 14:41:09 +0200 Subject: [PATCH 019/632] fix: PictureInPicture (#1839) Co-authored-by: V --- src/plugins/pictureInPicture/index.tsx | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/plugins/pictureInPicture/index.tsx b/src/plugins/pictureInPicture/index.tsx index bb6aee18f..f5f0a5128 100644 --- a/src/plugins/pictureInPicture/index.tsx +++ b/src/plugins/pictureInPicture/index.tsx @@ -4,8 +4,6 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import "./styles.css"; - import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; @@ -26,13 +24,12 @@ export default definePlugin({ description: "Adds picture in picture to videos (next to the Download button)", authors: [Devs.Lumap], settings, - patches: [ { - find: ".onRemoveAttachment,", + find: ".nonMediaAttachment]", replacement: { - match: /\.nonMediaAttachment,!(\i).{0,7}children:\[(\i),/, - replace: "$&$1&&$2&&$self.renderPiPButton()," + match: /\.nonMediaAttachment\].{0,10}children:\[\S{3}/, + replace: "$&&&$self.renderPiPButton()," }, }, ], @@ -43,7 +40,6 @@ export default definePlugin({ {tooltipProps => (
From 940193c30bd0e426799e1507f74d47560d38e90a Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Wed, 25 Oct 2023 19:47:08 +0700 Subject: [PATCH 020/632] noMosaic: fix (#1849) --- src/plugins/noMosaic/index.ts | 26 ++++++++++++++------------ src/plugins/noMosaic/styles.css | 2 +- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/plugins/noMosaic/index.ts b/src/plugins/noMosaic/index.ts index 60576ba93..49343503d 100644 --- a/src/plugins/noMosaic/index.ts +++ b/src/plugins/noMosaic/index.ts @@ -15,23 +15,25 @@ export default definePlugin({ authors: [Devs.AutumnVN], description: "Removes Discord new image mosaic", tags: ["image", "mosaic", "media"], - patches: [{ - find: "Media Mosaic", - replacement: [ - { + patches: [ + { + find: ".oneByTwoLayoutThreeGrid", + replacement: [{ match: /mediaLayoutType:\i\.\i\.MOSAIC/, - replace: 'mediaLayoutType:"RESPONSIVE"', - }, - { - match: /\i===\i\.\i\.MOSAIC/, - replace: "true", + replace: 'mediaLayoutType:"RESPONSIVE"' }, { match: /null!==\(\i=\i\.get\(\i\)\)&&void 0!==\i\?\i:"INVALID"/, replace: '"INVALID"', - }, - ], - }], + },] + }, + { + find: "Messages.REMOVE_ATTACHMENT_TOOLTIP_TEXT", + replacement: { + match: /\i===\i\.\i\.MOSAIC/, + replace: "true" + } + }], start() { enableStyle(style); }, diff --git a/src/plugins/noMosaic/styles.css b/src/plugins/noMosaic/styles.css index 546168584..05ea24899 100644 --- a/src/plugins/noMosaic/styles.css +++ b/src/plugins/noMosaic/styles.css @@ -1,3 +1,3 @@ -[class^="nonMediaAttachmentsContainer-"] [class*="messageAttachment-"] { +[class^="nonMediaAttachmentsContainer_"] [class*="messageAttachment_"] { position: relative; } From baa7d8c078aab9a2e71a03784f5069e129be4716 Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Wed, 25 Oct 2023 19:48:05 +0700 Subject: [PATCH 021/632] noDevToolsWarning: fix (#1851) --- src/plugins/noDevtoolsWarning/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/noDevtoolsWarning/index.ts b/src/plugins/noDevtoolsWarning/index.ts index 188b8fa3f..e897e5793 100644 --- a/src/plugins/noDevtoolsWarning/index.ts +++ b/src/plugins/noDevtoolsWarning/index.ts @@ -26,8 +26,8 @@ export default definePlugin({ patches: [{ find: "setDevtoolsCallbacks", replacement: { - match: /if\(.{0,10}\|\|"0.0.0"!==.{0,2}\.remoteApp\.getVersion\(\)\)/, - replace: "if(false)" + match: /if\(null!=\i&&"0.0.0"===\i\.remoteApp\.getVersion\(\)\)/, + replace: "if(true)" } }] }); From 2a56081bc24c069df55857ed08c70c84449f1a40 Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Wed, 25 Oct 2023 19:48:42 +0700 Subject: [PATCH 022/632] callTimer: fix (#1850) --- src/plugins/callTimer/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/callTimer/index.tsx b/src/plugins/callTimer/index.tsx index 2e0aa9650..c018cc715 100644 --- a/src/plugins/callTimer/index.tsx +++ b/src/plugins/callTimer/index.tsx @@ -73,9 +73,9 @@ export default definePlugin({ }, patches: [{ - find: ".renderConnectionStatus=", + find: "renderConnectionStatus(){", replacement: { - match: /(?<=renderConnectionStatus=.+\.channel,children:)\w/, + match: /(?<=renderConnectionStatus\(\)\{.+\.channel,children:)\i/, replace: "[$&, $self.renderTimer(this.props.channel.id)]" } }], From 69b54535c33bc08495b068876ad61b1024b20610 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 25 Oct 2023 14:56:20 +0200 Subject: [PATCH 023/632] Fix NoTrack --- src/plugins/_core/noTrack.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/_core/noTrack.ts b/src/plugins/_core/noTrack.ts index 4267a6280..ca1126593 100644 --- a/src/plugins/_core/noTrack.ts +++ b/src/plugins/_core/noTrack.ts @@ -24,9 +24,9 @@ export default definePlugin({ description: "Disable Discord's tracking ('science'), metrics and Sentry crash reporting", authors: [Devs.Cyn, Devs.Ven, Devs.Nuckyz, Devs.Arrow], required: true, - patches: true ? [] : [ + patches: [ { - find: "TRACKING_URL:", + find: "AnalyticsActionHandlers.handle", replacement: { match: /^.+$/, replace: "()=>{}", @@ -43,8 +43,8 @@ export default definePlugin({ find: ".METRICS,", replacement: [ { - match: /this\._intervalId.+?12e4\)/, - replace: "" + match: /this\._intervalId=/, + replace: "this._intervalId=undefined&&" }, { match: /(?<=increment=function\(\i\){)/, From 7ee9a8bb990c6d28db1ecbac856fac6a82ac0cf4 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 25 Oct 2023 14:58:59 +0200 Subject: [PATCH 024/632] SpotifyControls: Fix --- src/webpack/common/menu.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webpack/common/menu.ts b/src/webpack/common/menu.ts index e2ad37202..c0d9c5f9d 100644 --- a/src/webpack/common/menu.ts +++ b/src/webpack/common/menu.ts @@ -22,7 +22,7 @@ import type * as t from "./types/menu"; export let Menu = {} as t.Menu; -waitFor("MenuItem", m => Menu = m); +waitFor(["MenuItem", "MenuSliderControl"], m => Menu = m); export const ContextMenu: t.ContextMenuApi = mapMangledModuleLazy('type:"CONTEXT_MENU_OPEN"', { open: filters.byCode("stopPropagation"), From 322ecc5e88b3dd027e62f2e83b550529741f4340 Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Wed, 25 Oct 2023 19:59:35 +0700 Subject: [PATCH 025/632] usrbg: fix (#1853) --- src/plugins/usrbg/index.css | 2 +- src/plugins/usrbg/index.tsx | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/plugins/usrbg/index.css b/src/plugins/usrbg/index.css index 93e37ef48..69c5b1857 100644 --- a/src/plugins/usrbg/index.css +++ b/src/plugins/usrbg/index.css @@ -7,6 +7,6 @@ top: 76px; } -[style*="background-image"] [class*="background-"] { +[style*="background-image"] [class*="background_"] { background-color: transparent !important; } diff --git a/src/plugins/usrbg/index.tsx b/src/plugins/usrbg/index.tsx index 3e8c12257..ce6bd709b 100644 --- a/src/plugins/usrbg/index.tsx +++ b/src/plugins/usrbg/index.tsx @@ -59,8 +59,8 @@ export default definePlugin({ replace: "$self.premiumHook($1)||$&" }, { - match: /(\i)\.bannerSrc,/, - replace: "$self.useBannerHook($1)," + match: /(?<=function \i\((\i)\)\{)(?=var.{30,50},bannerSrc:)/, + replace: "$1.bannerSrc=$self.useBannerHook($1);" }, { match: /\?\(0,\i\.jsx\)\(\i,{type:\i,shown/, @@ -73,8 +73,8 @@ export default definePlugin({ predicate: () => settings.store.voiceBackground, replacement: [ { - match: /(\i)\.style,/, - replace: "$self.voiceBackgroundHook($1)," + match: /(?<=function\((\i),\i\)\{)(?=let.{20,40},style:)/, + replace: "$1.style=$self.voiceBackgroundHook($1);" } ] } @@ -90,7 +90,7 @@ export default definePlugin({ }, voiceBackgroundHook({ className, participantUserId }: any) { - if (className.includes("tile-")) { + if (className.includes("tile_")) { if (data[participantUserId]) { return { backgroundImage: `url(${data[participantUserId]})`, From 44c96757956428c7b74f37e52bb7e2a8eb7cf7cf Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 25 Oct 2023 15:07:45 +0200 Subject: [PATCH 026/632] ContextMenuApi: fix --- src/plugins/_api/contextMenu.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/_api/contextMenu.ts b/src/plugins/_api/contextMenu.ts index d04e0e662..55fdf3eae 100644 --- a/src/plugins/_api/contextMenu.ts +++ b/src/plugins/_api/contextMenu.ts @@ -29,8 +29,8 @@ export default definePlugin({ { find: "♫ (つ。◕‿‿◕。)つ ♪", replacement: { - match: /(?<=function \i\((\i)\){)(?=var \i,\i=\i\.navId)/, - replace: (_, props) => `Vencord.Api.ContextMenu._patchContextMenu(${props});` + match: /let{navId:/, + replace: "Vencord.Api.ContextMenu._patchContextMenu(arguments[0]);$&" } }, { From 554bb18e9b6a554c9b7b5fd6d5e5613a3e529d3c Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 25 Oct 2023 15:24:06 +0200 Subject: [PATCH 027/632] BadgeAPI: fix --- src/plugins/_api/badges.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/plugins/_api/badges.tsx b/src/plugins/_api/badges.tsx index d10d00210..11e843db4 100644 --- a/src/plugins/_api/badges.tsx +++ b/src/plugins/_api/badges.tsx @@ -85,17 +85,19 @@ export default definePlugin({ }, { // alt: "", aria-hidden: false, src: originalSrc - match: /alt:" ","aria-hidden":!0,src:(?=(\i)\.src)/g, + match: /alt:" ","aria-hidden":!0,src:(?=(\i)\.src)/, // ...badge.props, ..., src: badge.image ?? ... replace: "...$1.props,$& $1.image??" }, + // replace their component with ours if applicable { - match: /children:function(?<=(\i)\.(?:tooltip|description),spacing:\d.+?)/g, - replace: "children:$1.component ? () => $self.renderBadgeComponent($1) : function" + match: /(?<=text:(\i)\.description,spacing:12,)children:/, + replace: "children:$1.component ? () => $self.renderBadgeComponent($1) :" }, + // conditionally override their onClick with badge.onClick if it exists { - match: /onClick:function(?=.{0,200}href:(\i)\.link)/, - replace: "onClick:$1.onClick??function" + match: /href:(\i)\.link/, + replace: "...($1.onClick && { onClick: $1.onClick }),$&" } ] } From fb22c57da13c5033d4d1764aabe355c0f509a4b4 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 25 Oct 2023 15:28:50 +0200 Subject: [PATCH 028/632] MessageAccessoriesAPI: Fix --- src/plugins/_api/messageAccessories.ts | 5 ++--- src/webpack/webpack.ts | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/plugins/_api/messageAccessories.ts b/src/plugins/_api/messageAccessories.ts index 5bb13cfe1..a98fdb32b 100644 --- a/src/plugins/_api/messageAccessories.ts +++ b/src/plugins/_api/messageAccessories.ts @@ -27,9 +27,8 @@ export default definePlugin({ { find: ".Messages.REMOVE_ATTACHMENT_BODY", replacement: { - match: /(.container\)?,children:)(\[[^\]]+\])(}\)\};return)/, - replace: (_, pre, accessories, post) => - `${pre}Vencord.Api.MessageAccessories._modifyAccessories(${accessories},this.props)${post}`, + match: /(?<=.container\)?,children:)(\[.+?\])/, + replace: "Vencord.Api.MessageAccessories._modifyAccessories($1,this.props)", }, }, ], diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index 1fdfa43e9..812e36079 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -458,7 +458,7 @@ export function extract(id: number) { // WARNING: This module was extracted to be more easily readable. // This module is NOT ACTUALLY USED! This means putting breakpoints will have NO EFFECT!! -${mod.toString()} +0,${mod.toString()} //# sourceURL=ExtractedWebpackModule${id} `; const extracted = (0, eval)(code); From 8c998b9330af7e7db089709ba05170e2a3c45cfc Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Wed, 25 Oct 2023 20:34:00 +0700 Subject: [PATCH 029/632] experiments: fix (#1855) --- src/plugins/experiments/index.tsx | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/plugins/experiments/index.tsx b/src/plugins/experiments/index.tsx index 149c436c2..89fcf33e7 100644 --- a/src/plugins/experiments/index.tsx +++ b/src/plugins/experiments/index.tsx @@ -52,7 +52,7 @@ export default definePlugin({ { find: "Object.defineProperties(this,{isDeveloper", replacement: { - match: /(?<={isDeveloper:\{[^}]+?,get:function\(\)\{return )\w/, + match: /(?<={isDeveloper:\{[^}]+?,get:\(\)=>)\i/, replace: "true" } }, @@ -64,19 +64,28 @@ export default definePlugin({ } }, { - find: ".isStaff=function(){", + find: ".isStaff=()", predicate: () => settings.store.enableIsStaff, replacement: [ { - match: /return\s*?(\i)\.hasFlag\((\i\.\i)\.STAFF\)}/, - replace: (_, user, flags) => `return Vencord.Webpack.Common.UserStore.getCurrentUser()?.id===${user}.id||${user}.hasFlag(${flags}.STAFF)}` + match: /=>*?(\i)\.hasFlag\((\i\.\i)\.STAFF\)}/, + replace: (_, user, flags) => `=>Vencord.Webpack.Common.UserStore.getCurrentUser()?.id===${user}.id||${user}.hasFlag(${flags}.STAFF)}` }, { - match: /hasFreePremium=function\(\){return this.isStaff\(\)\s*?\|\|/, - replace: "hasFreePremium=function(){return ", + match: /hasFreePremium\(\){return this.isStaff\(\)\s*?\|\|/, + replace: "hasFreePremium(){return ", } ] }, + // Fix search history being disabled / broken with isStaff + { + find: '("showNewSearch")', + predicate: () => settings.store.enableIsStaff, + replacement: { + match: /(?<=showNewSearch"\);return)\s?/, + replace: "!1&&" + } + }, { find: 'H1,title:"Experiments"', replacement: { From cb2532d22cb39232f362453e2fc1e935640e047f Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Wed, 25 Oct 2023 20:34:17 +0700 Subject: [PATCH 030/632] disableDMCallIdle: fix (#1856) --- src/plugins/disableDMCallIdle/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/disableDMCallIdle/index.ts b/src/plugins/disableDMCallIdle/index.ts index 26ea3cdac..bce119d01 100644 --- a/src/plugins/disableDMCallIdle/index.ts +++ b/src/plugins/disableDMCallIdle/index.ts @@ -27,7 +27,7 @@ export default definePlugin({ { find: ".Messages.BOT_CALL_IDLE_DISCONNECT", replacement: { - match: /(?<=function \i\(\){)(?=.{1,100}\.Messages\.BOT_CALL_IDLE_DISCONNECT)/, + match: /(?<=function \i\(\){)(?=.{1,120}\.Messages\.BOT_CALL_IDLE_DISCONNECT)/, replace: "return;" } } From 4d8e4e62ca2783bea7963ce3267ebd1ab0d94bfb Mon Sep 17 00:00:00 2001 From: megumin Date: Wed, 25 Oct 2023 14:41:57 +0100 Subject: [PATCH 031/632] fix: notrack failing patches (#1857) --- src/plugins/_core/noTrack.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/plugins/_core/noTrack.ts b/src/plugins/_core/noTrack.ts index ca1126593..424e62c04 100644 --- a/src/plugins/_core/noTrack.ts +++ b/src/plugins/_core/noTrack.ts @@ -47,16 +47,17 @@ export default definePlugin({ replace: "this._intervalId=undefined&&" }, { - match: /(?<=increment=function\(\i\){)/, - replace: "return;" + match: /(increment\(\i\){)/, + replace: "$1return;" } ] }, { find: ".installedLogHooks)", replacement: { - match: /if\(\i\.getDebugLogging\(\)&&!\i\.installedLogHooks\)/, - replace: "if(false)" + // if getDebugLogging() returns false, the hooks don't get installed. + match: "getDebugLogging(){", + replace: "getDebugLogging(){return false;" } }, ] From cf5e93ee52ad8f36014f07d6f3303cef88b2b875 Mon Sep 17 00:00:00 2001 From: Jack <30497388+FieryFlames@users.noreply.github.com> Date: Wed, 25 Oct 2023 09:45:19 -0400 Subject: [PATCH 032/632] fix: fakeProfileThemes (#1837) --- src/plugins/fakeProfileThemes/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/fakeProfileThemes/index.tsx b/src/plugins/fakeProfileThemes/index.tsx index 70003e5ab..c61437269 100644 --- a/src/plugins/fakeProfileThemes/index.tsx +++ b/src/plugins/fakeProfileThemes/index.tsx @@ -87,15 +87,15 @@ export default definePlugin({ authors: [Devs.Alyxia, Devs.Remty], patches: [ { - find: "getUserProfile=", + find: "UserProfileStore", replacement: { - match: /(?<=getUserProfile=function\(\i\){return )(\i\[\i\])/, + match: /(?<=getUserProfile\(\i\){return )(\i\[\i\])/, replace: "$self.colorDecodeHook($1)" } }, { find: ".USER_SETTINGS_PROFILE_THEME_ACCENT", replacement: { - match: /RESET_PROFILE_THEME}\)(?<=},color:(\i).+?},color:(\i).+?)/, + match: /RESET_PROFILE_THEME}\)(?<=color:(\i),.{0,500}?color:(\i),.{0,500}?)/, replace: "$&,$self.addCopy3y3Button({primary:$1,accent:$2})" } } From 65d39dcf21ecbcb1d45fefd38eee55965668de4b Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Wed, 25 Oct 2023 14:56:43 +0100 Subject: [PATCH 033/632] ShowConnections, NoProfileThemes: Fix (#1854) --- src/plugins/noProfileThemes/index.ts | 10 +++++----- src/plugins/showConnections/index.tsx | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/plugins/noProfileThemes/index.ts b/src/plugins/noProfileThemes/index.ts index 97d195e36..c31f42838 100644 --- a/src/plugins/noProfileThemes/index.ts +++ b/src/plugins/noProfileThemes/index.ts @@ -34,19 +34,19 @@ export default definePlugin({ } }, { - find: "().avatarPositionPremiumNoBanner,default:", + find: ".avatarPositionPremiumNoBanner,default:", replacement: { // premiumUserWithoutBanner: foo().avatarPositionPremiumNoBanner, default: foo().avatarPositionNormal - match: /\.avatarPositionPremiumNoBanner(?=,default:\i\(\)\.(\i))/, + match: /\.avatarPositionPremiumNoBanner(?=,default:\i\.(\i))/, // premiumUserWithoutBanner: foo().avatarPositionNormal... replace: ".$1" } }, { - find: ".hasThemeColors=function(){", + find: "hasThemeColors(){", replacement: { - match: /(?<=key:"canUsePremiumProfileCustomization",get:function\(\){return)/, - replace: " false;" + match: /get canUsePremiumProfileCustomization\(\){return /, + replace: "$&false &&" } } ] diff --git a/src/plugins/showConnections/index.tsx b/src/plugins/showConnections/index.tsx index 1f6ef34ef..948bdb83b 100644 --- a/src/plugins/showConnections/index.tsx +++ b/src/plugins/showConnections/index.tsx @@ -32,7 +32,7 @@ import { User } from "discord-types/general"; import { VerifiedIcon } from "./VerifiedIcon"; -const Section = LazyComponent(() => findByCode("().lastSection")); +const Section = LazyComponent(() => findByCode(".lastSection]:")); const ThemeStore = findStoreLazy("ThemeStore"); const platforms: { get(type: string): ConnectionPlatform; } = findByPropsLazy("isSupported", "getByUrl"); const getTheme: (user: User, displayProfile: any) => any = findByCodeLazy(',"--profile-gradient-primary-color"'); @@ -74,12 +74,12 @@ interface ConnectionPlatform { icon: { lightSVG: string, darkSVG: string; }; } -const profilePopoutComponent = ErrorBoundary.wrap(e => - +const profilePopoutComponent = ErrorBoundary.wrap(({ user, displayProfile }: { user: User, displayProfile; }) => + ); -const profilePanelComponent = ErrorBoundary.wrap(e => - +const profilePanelComponent = ErrorBoundary.wrap(({ id }: { id: string; }) => + ); function ConnectionsComponent({ id, theme }: { id: string, theme: string; }) { @@ -175,18 +175,18 @@ export default definePlugin({ authors: [Devs.TheKodeToad], patches: [ { - find: ".Messages.BOT_PROFILE_SLASH_COMMANDS", + find: "{isUsingGuildBio:null!==(", replacement: { - match: /,theme:\i\}\)(?=,.{0,100}setNote:)/, - replace: "$&,$self.profilePopoutComponent(arguments[0])" + match: /,theme:\i\}\)(?=,.{0,150}setNote:)/, + replace: "$&,$self.profilePopoutComponent({ user: arguments[0].user, displayProfile: arguments[0].displayProfile })" } }, { find: "\"Profile Panel: user cannot be undefined\"", replacement: { // createElement(Divider, {}), createElement(NoteComponent) - match: /\(0,\i\.jsx\)\(\i\.\i,\{\}\).{0,100}setNote:/, - replace: "$self.profilePanelComponent(arguments[0]),$&" + match: /\(0,\i\.jsx\)\(\i\.\i,\{\}\).{0,100}setNote:(?=.+?channelId:(\i).id)/, + replace: "$self.profilePanelComponent({ id: $1.recipients[0] }),$&" } } ], From bb771153e3e620e52202935a5ec603f59b68ecfe Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 25 Oct 2023 16:12:01 +0200 Subject: [PATCH 034/632] MessageEvents: Fix --- src/plugins/_api/messageEvents.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/plugins/_api/messageEvents.ts b/src/plugins/_api/messageEvents.ts index fa467522c..bc5f5abf2 100644 --- a/src/plugins/_api/messageEvents.ts +++ b/src/plugins/_api/messageEvents.ts @@ -27,10 +27,8 @@ export default definePlugin({ { find: '"MessageActionCreators"', replacement: { - // editMessage: function (...) { - match: /\beditMessage:(function\(.+?\))\{/, - // editMessage: async function (...) { await handlePreEdit(...); ... - replace: "editMessage:async $1{await Vencord.Api.MessageEvents._handlePreEdit(...arguments);" + match: /async editMessage\(.+?\)\{/, + replace: "$&await Vencord.Api.MessageEvents._handlePreEdit(...arguments);" } }, { @@ -38,7 +36,7 @@ export default definePlugin({ replacement: { // props.chatInputType...then((function(isMessageValid)... var parsedMessage = b.c.parse(channel,... var replyOptions = f.g.getSendMessageOptionsForReply(pendingReply); // Lookbehind: validateMessage)({openWarningPopout:..., type: i.props.chatInputType, content: t, stickers: r, ...}).then((function(isMessageValid) - match: /(props\.chatInputType.+?\.then\(\()(function.+?var (\i)=\i\.\i\.parse\((\i),.+?var (\i)=\i\.\i\.getSendMessageOptionsForReply\(\i\);)(?<=\)\(({.+?})\)\.then.+?)/, + match: /(type:this\.props\.chatInputType.+?\.then\()(\i=>\{.+?let (\i)=\i\.\i\.parse\((\i),.+?let (\i)=\i\.\i\.getSendMessageOptionsForReply\(\i\);)(?<=\)\(({.+?})\)\.then.+?)/, // props.chatInputType...then((async function(isMessageValid)... var replyOptions = f.g.getSendMessageOptionsForReply(pendingReply); if(await Vencord.api...) return { shoudClear:true, shouldRefocus:true }; replace: (_, rest1, rest2, parsedMessage, channel, replyOptions, extra) => "" + `${rest1}async ${rest2}` + @@ -49,10 +47,10 @@ export default definePlugin({ { find: '("interactionUsernameProfile', replacement: { - match: /var \i=(\i)\.id,\i=(\i)\.id;return \i\.useCallback\(\(?function\((\i)\){/, + match: /let\{id:\i}=(\i),{id:\i}=(\i);return \i\.useCallback\((\i)=>\{/, replace: (m, message, channel, event) => // the message param is shadowed by the event param, so need to alias them - `var _msg=${message},_chan=${channel};${m}Vencord.Api.MessageEvents._handleClick(_msg, _chan, ${event});` + `const vcMsg=${message},vcChan=${channel};${m}Vencord.Api.MessageEvents._handleClick(vcMsg, vcChan, ${event});` } } ] From 7e0fc1d0ea227743c88ce36df6f16a9658d44a95 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 25 Oct 2023 16:15:52 +0200 Subject: [PATCH 035/632] ServerListAPI: fix --- src/plugins/_api/serverList.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/_api/serverList.ts b/src/plugins/_api/serverList.ts index ec377fce1..f45bbf104 100644 --- a/src/plugins/_api/serverList.ts +++ b/src/plugins/_api/serverList.ts @@ -27,15 +27,15 @@ export default definePlugin({ { find: "Messages.DISCODO_DISABLED", replacement: { - match: /(Messages\.DISCODO_DISABLED.+?return)(\(.{0,75}?tutorialContainer.+?}\))(?=}function)/, - replace: "$1[$2].concat(Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.Above))" + match: /(?<=Messages\.DISCODO_DISABLED.+?return)(\(.{0,75}?tutorialContainer.+?}\))(?=}function)/, + replace: "[$1].concat(Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.Above))" } }, { find: "Messages.SERVERS,children", replacement: { - match: /(Messages\.SERVERS,children:)(.+?default:return null\}\}\)\))/, - replace: "$1Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.In).concat($2)" + match: /(?<=Messages\.SERVERS,children:).+?default:return null\}\}\)/, + replace: "Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.In).concat($&)" } } ] From 98955409433ae8c38243dee36500adbb427ccf74 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 25 Oct 2023 16:29:37 +0200 Subject: [PATCH 036/632] MemberListDecoratorAPI: Fix --- src/plugins/_api/memberListDecorators.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/plugins/_api/memberListDecorators.ts b/src/plugins/_api/memberListDecorators.ts index a6d4125d3..bf7377e85 100644 --- a/src/plugins/_api/memberListDecorators.ts +++ b/src/plugins/_api/memberListDecorators.ts @@ -25,18 +25,23 @@ export default definePlugin({ authors: [Devs.TheSun, Devs.Ven], patches: [ { - find: "lostPermissionTooltipText,", - replacement: { - match: /decorators:.{0,100}?children:\[(?<=(\i)\.lostPermissionTooltipText.+?)/, - replace: "$&...Vencord.Api.MemberListDecorators.__getDecorators($1)," - } + find: ".lostPermission)", + replacement: [ + { + match: /let\{[^}]*lostPermissionTooltipText:\i[^}]*\}=(\i),/, + replace: "$&vencordProps=$1," + }, { + match: /decorators:.{0,100}?children:\[/, + replace: "$&...(typeof vencordProps=='undefined'?[]:Vencord.Api.MemberListDecorators.__getDecorators(vencordProps))," + } + ] }, { find: "PrivateChannel.renderAvatar", replacement: [ // props are shadowed by nested props so we have to do this { - match: /\i=(\i)\.applicationStream,/, + match: /let\{[^}]*applicationStream:\i[^}]*\}=(\i),/, replace: "$&vencordProps=$1," }, { From 892a79b2a7c21d04aafdf8de54a117fb7522c2c0 Mon Sep 17 00:00:00 2001 From: redstonekasi Date: Wed, 25 Oct 2023 16:16:16 +0200 Subject: [PATCH 037/632] fix: CommandsAPI --- src/api/Commands/commandHelpers.ts | 6 +++--- src/plugins/_api/commands.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/api/Commands/commandHelpers.ts b/src/api/Commands/commandHelpers.ts index dd1196f9f..2fd189032 100644 --- a/src/api/Commands/commandHelpers.ts +++ b/src/api/Commands/commandHelpers.ts @@ -17,14 +17,14 @@ */ import { mergeDefaults } from "@utils/misc"; -import { findByCodeLazy, findByPropsLazy } from "@webpack"; +import { findByPropsLazy } from "@webpack"; import { SnowflakeUtils } from "@webpack/common"; import { Message } from "discord-types/general"; import type { PartialDeep } from "type-fest"; import { Argument } from "./types"; -const createBotMessage = findByCodeLazy('username:"Clyde"'); +const MessageCreator = findByPropsLazy("createBotMessage"); const MessageSender = findByPropsLazy("receiveMessage"); export function generateId() { @@ -38,7 +38,7 @@ export function generateId() { * @returns {Message} */ export function sendBotMessage(channelId: string, message: PartialDeep): Message { - const botMessage = createBotMessage({ channelId, content: "", embeds: [] }); + const botMessage = MessageCreator.createBotMessage({ channelId, content: "", embeds: [] }); MessageSender.receiveMessage(channelId, mergeDefaults(message, botMessage)); diff --git a/src/plugins/_api/commands.ts b/src/plugins/_api/commands.ts index b7d6ef9fe..3a7619365 100644 --- a/src/plugins/_api/commands.ts +++ b/src/plugins/_api/commands.ts @@ -44,8 +44,8 @@ export default definePlugin({ find: "Unexpected value for option", replacement: { // return [2, cmd.execute(args, ctx)] - match: /,(.{1,2})\.execute\((.{1,2}),(.{1,2})\)]/, - replace: (_, cmd, args, ctx) => `,Vencord.Api.Commands._handleCommand(${cmd}, ${args}, ${ctx})]` + match: /,(\i)\.execute\((\i),(\i)\)/, + replace: (_, cmd, args, ctx) => `,Vencord.Api.Commands._handleCommand(${cmd}, ${args}, ${ctx})` } }, // Show plugin name instead of "Built-In" From b69ac1cdad0de10dd20ae1954581f05bbef36a2e Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 25 Oct 2023 16:50:09 +0200 Subject: [PATCH 038/632] Fix PlatformIndicators --- src/plugins/platformIndicators/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/platformIndicators/index.tsx b/src/plugins/platformIndicators/index.tsx index 7233cd13d..5ccc3b783 100644 --- a/src/plugins/platformIndicators/index.tsx +++ b/src/plugins/platformIndicators/index.tsx @@ -23,7 +23,7 @@ import { Settings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByCodeLazy, findStoreLazy } from "@webpack"; +import { findByPropsLazy, findStoreLazy } from "@webpack"; import { PresenceStore, Tooltip, UserStore } from "@webpack/common"; import { User } from "discord-types/general"; @@ -55,13 +55,13 @@ const Icons = { }; type Platform = keyof typeof Icons; -const getStatusColor = findByCodeLazy(".TWITCH", ".STREAMING", ".INVISIBLE"); +const StatusUtils = findByPropsLazy("getStatusColor", "StatusTypes"); const PlatformIcon = ({ platform, status, small }: { platform: Platform, status: string; small: boolean; }) => { const tooltip = platform[0].toUpperCase() + platform.slice(1); const Icon = Icons[platform] ?? Icons.desktop; - return ; + return ; }; const getStatus = (id: string): Record => PresenceStore.getState()?.clientStatuses?.[id]; From 788d22f9e90276e08ccfce83d113ce793f2655c8 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 25 Oct 2023 16:52:59 +0200 Subject: [PATCH 039/632] MessageDecorationsAPI: fix --- src/plugins/_api/messageDecorations.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/_api/messageDecorations.ts b/src/plugins/_api/messageDecorations.ts index 3c235d768..3f0fd11db 100644 --- a/src/plugins/_api/messageDecorations.ts +++ b/src/plugins/_api/messageDecorations.ts @@ -25,7 +25,7 @@ export default definePlugin({ authors: [Devs.TheSun], patches: [ { - find: ".withMentionPrefix", + find: "UsernameDecorationTypes:", replacement: { match: /currentUserIsPremium:.{0,70}{children:\i(?=}\))/, replace: "$&.concat(Vencord.Api.MessageDecorations.__addDecorationsToMessage(arguments[0]))" From 2478ffb695ee4e8225eb3dee7bc8dd57ee1ad4db Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Wed, 25 Oct 2023 21:57:50 +0700 Subject: [PATCH 040/632] fix userutils (#1861) --- src/components/PluginSettings/PluginModal.tsx | 2 +- src/plugins/relationshipNotifier/functions.ts | 2 +- src/plugins/relationshipNotifier/utils.ts | 4 ++-- src/plugins/serverProfile/GuildProfileModal.tsx | 2 +- src/utils/discord.tsx | 2 +- src/webpack/common/utils.ts | 6 ++---- 6 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/components/PluginSettings/PluginModal.tsx b/src/components/PluginSettings/PluginModal.tsx index d8b949066..03789ac6c 100644 --- a/src/components/PluginSettings/PluginModal.tsx +++ b/src/components/PluginSettings/PluginModal.tsx @@ -94,7 +94,7 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti (async () => { for (const user of plugin.authors.slice(0, 6)) { const author = user.id - ? await UserUtils.fetchUser(`${user.id}`) + ? await UserUtils.getUser(`${user.id}`) .catch(() => makeDummyUser({ username: user.name })) : makeDummyUser({ username: user.name }); diff --git a/src/plugins/relationshipNotifier/functions.ts b/src/plugins/relationshipNotifier/functions.ts index e712382b5..980b11300 100644 --- a/src/plugins/relationshipNotifier/functions.ts +++ b/src/plugins/relationshipNotifier/functions.ts @@ -37,7 +37,7 @@ export async function onRelationshipRemove({ relationship: { type, id } }: Relat return; } - const user = await UserUtils.fetchUser(id) + const user = await UserUtils.getUser(id) .catch(() => null); if (!user) return; diff --git a/src/plugins/relationshipNotifier/utils.ts b/src/plugins/relationshipNotifier/utils.ts index 5de9d75a7..16f1892af 100644 --- a/src/plugins/relationshipNotifier/utils.ts +++ b/src/plugins/relationshipNotifier/utils.ts @@ -68,7 +68,7 @@ export async function syncAndRunChecks() { for (const id of oldFriends.friends) { if (friends.friends.includes(id)) continue; - const user = await UserUtils.fetchUser(id).catch(() => void 0); + const user = await UserUtils.getUser(id).catch(() => void 0); if (user) notify( `You are no longer friends with ${getUniqueUsername(user)}.`, @@ -85,7 +85,7 @@ export async function syncAndRunChecks() { [RelationshipType.FRIEND, RelationshipType.BLOCKED, RelationshipType.OUTGOING_REQUEST].includes(RelationshipStore.getRelationshipType(id)) ) continue; - const user = await UserUtils.fetchUser(id).catch(() => void 0); + const user = await UserUtils.getUser(id).catch(() => void 0); if (user) notify( `Friend request from ${getUniqueUsername(user)} has been revoked.`, diff --git a/src/plugins/serverProfile/GuildProfileModal.tsx b/src/plugins/serverProfile/GuildProfileModal.tsx index 2be9b57d9..2a7958a10 100644 --- a/src/plugins/serverProfile/GuildProfileModal.tsx +++ b/src/plugins/serverProfile/GuildProfileModal.tsx @@ -162,7 +162,7 @@ function Owner(guildId: string, owner: User) { } function ServerInfoTab({ guild }: GuildProps) { - const [owner] = useAwaiter(() => UserUtils.fetchUser(guild.ownerId), { + const [owner] = useAwaiter(() => UserUtils.getUser(guild.ownerId), { deps: [guild.ownerId], fallbackValue: null }); diff --git a/src/utils/discord.tsx b/src/utils/discord.tsx index 458509b42..a36ecfbb9 100644 --- a/src/utils/discord.tsx +++ b/src/utils/discord.tsx @@ -103,7 +103,7 @@ export function openImageModal(url: string, props?: Partial Promise, -}; +export const UserUtils = findByPropsLazy("getUser", "fetchCurrentUser") as { getUser: (id: string) => Promise; }; export const Clipboard = mapMangledModuleLazy('document.queryCommandEnabled("copy")||document.queryCommandSupported("copy")', { copy: filters.byCode(".copy("), From e69236c5f83c935b8e00658928536cae380a8776 Mon Sep 17 00:00:00 2001 From: Nico Date: Wed, 25 Oct 2023 16:58:02 +0200 Subject: [PATCH 041/632] fix(vcDoubleClick): update for new discord build (#1862) --- src/plugins/vcDoubleClick/index.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/plugins/vcDoubleClick/index.ts b/src/plugins/vcDoubleClick/index.ts index 31543510e..a55ac7b67 100644 --- a/src/plugins/vcDoubleClick/index.ts +++ b/src/plugins/vcDoubleClick/index.ts @@ -30,20 +30,22 @@ export default definePlugin({ description: "Join voice chats via double click instead of single click", authors: [Devs.Ven, Devs.D3SOX], patches: [ - { - find: "VoiceChannel.renderPopout", + ...[ + ".handleVoiceStatusClick", // voice channels + ".handleClickChat" // stage channels + ].map(find => ({ + find, // 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. // do this timer workaround instead replacement: [ - // voice/stage channels { - match: /onClick:function\(\)\{(e\.handleClick.+?)}/g, - replace: "onClick:function(){$self.schedule(()=>{$1},e)}", + match: /onClick:\(\)=>\{this.handleClick\(\)/g, + replace: "onClick:()=>{$self.schedule(()=>{this.handleClick()},this)", }, - ], - }, + ] + })), { // channel mentions find: ".shouldCloseDefaultModals", From cd06980016b2646d4941b3a5bcf24278cd06123f Mon Sep 17 00:00:00 2001 From: megumin Date: Wed, 25 Oct 2023 16:00:24 +0100 Subject: [PATCH 042/632] fix: pronoundb profile popout (#1860) --- src/plugins/pronoundb/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/pronoundb/index.ts b/src/plugins/pronoundb/index.ts index bd5b2c5a9..6eac86698 100644 --- a/src/plugins/pronoundb/index.ts +++ b/src/plugins/pronoundb/index.ts @@ -57,8 +57,8 @@ export default definePlugin({ find: ".userTagNoNickname", replacement: [ { - match: /,(\i)=(\i)\.pronouns/, - replace: ",[$1,vcPronounSource]=$self.useProfilePronouns($2.user.id)" + match: /{user:(\i),[^}]*,pronouns:(\i),[^}]*}=\i;/, + replace: "$&let vcPronounSource;[$2,vcPronounSource]=$self.useProfilePronouns($1.id);" }, PRONOUN_TOOLTIP_PATCH ] From 7de1b5dcb6fea56f53a164894dedebb1cb9de7e3 Mon Sep 17 00:00:00 2001 From: megumin Date: Wed, 25 Oct 2023 16:00:39 +0100 Subject: [PATCH 043/632] fix: sortFriendRequests patches (#1848) --- src/plugins/sortFriendRequests/index.tsx | 36 +++++++++++++----------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/plugins/sortFriendRequests/index.tsx b/src/plugins/sortFriendRequests/index.tsx index b9732afb8..3698379c8 100644 --- a/src/plugins/sortFriendRequests/index.tsx +++ b/src/plugins/sortFriendRequests/index.tsx @@ -29,26 +29,28 @@ export default definePlugin({ description: "Sorts friend requests by date of receipt", patches: [{ - find: ".PENDING_INCOMING||", - replacement: [{ - match: /\.sortBy\(\(function\((\w)\){return \w{1,3}\.comparator}\)\)/, - // If the row type is 3 or 4 (pendinng incoming or outgoing), sort by date of receipt - // Otherwise, use the default comparator - replace: (_, row) => `.sortBy((function(${row}) { - return ${row}.type === 3 || ${row}.type === 4 - ? -Vencord.Plugins.plugins.SortFriendRequests.getSince(${row}.user) - : ${row}.comparator - }))` - }, { + find: "getRelationshipCounts(){", + replacement: { + match: /\.sortBy\(\i=>\i\.comparator\)/, + replace: ".sortBy((row) => $self.sortList(row))" + } + }, { + find: "RelationshipTypes.PENDING_INCOMING?", + replacement: { predicate: () => Settings.plugins.SortFriendRequests.showDates, - match: /(user:(\w{1,3}),.{10,30}),subText:(\w{1,3}),(.{10,30}userInfo}\))/, - // Show dates in the friend request list - replace: (_, pre, user, subText, post) => `${pre}, - subText: Vencord.Plugins.plugins.SortFriendRequests.makeSubtext(${subText}, ${user}), - ${post}` - }] + match: /(user:(\i),.{10,50}),subText:(\i),(className:\i\.userInfo}\))/, + replace: (_, pre, user, subtext, post) => `${pre}, + subText: $self.makeSubtext(${subtext}, ${user}), + ${post}` + } }], + sortList(row: any) { + return row.type === 3 || row.type === 4 + ? -this.getSince(row.user) + : row.comparator; + }, + getSince(user: User) { return new Date(RelationshipStore.getSince(user.id)); }, From af1aa396479f62bc5e7e9960d133c604606446a1 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 25 Oct 2023 17:03:17 +0200 Subject: [PATCH 044/632] Delete RNNoise - Krisp is now on web, so this is obsolete --- src/plugins/rnnoise.web/icons.tsx | 21 --- src/plugins/rnnoise.web/index.tsx | 250 ----------------------------- src/plugins/rnnoise.web/styles.css | 29 ---- 3 files changed, 300 deletions(-) delete mode 100644 src/plugins/rnnoise.web/icons.tsx delete mode 100644 src/plugins/rnnoise.web/index.tsx delete mode 100644 src/plugins/rnnoise.web/styles.css diff --git a/src/plugins/rnnoise.web/icons.tsx b/src/plugins/rnnoise.web/icons.tsx deleted file mode 100644 index 8fda9839a..000000000 --- a/src/plugins/rnnoise.web/icons.tsx +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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 . -*/ - -export const SupressionIcon = ({ enabled }: { enabled: boolean; }) => enabled - ? - : ; diff --git a/src/plugins/rnnoise.web/index.tsx b/src/plugins/rnnoise.web/index.tsx deleted file mode 100644 index 8de6557e7..000000000 --- a/src/plugins/rnnoise.web/index.tsx +++ /dev/null @@ -1,250 +0,0 @@ -/* - * 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 "./styles.css"; - -import { definePluginSettings } from "@api/Settings"; -import { classNameFactory } from "@api/Styles"; -import { Switch } from "@components/Switch"; -import { loadRnnoise, RnnoiseWorkletNode } from "@sapphi-red/web-noise-suppressor"; -import { Devs } from "@utils/constants"; -import { rnnoiseWasmSrc, rnnoiseWorkletSrc } from "@utils/dependencies"; -import { makeLazy } from "@utils/lazy"; -import { Logger } from "@utils/Logger"; -import { LazyComponent } from "@utils/react"; -import definePlugin from "@utils/types"; -import { findByCode } from "@webpack"; -import { FluxDispatcher, Popout, React } from "@webpack/common"; -import { MouseEvent, ReactNode } from "react"; - -import { SupressionIcon } from "./icons"; - -const RNNOISE_OPTION = "RNNOISE"; - -interface PanelButtonProps { - tooltipText: string; - icon: () => ReactNode; - onClick: (event: MouseEvent) => void; - tooltipClassName?: string; - disabled?: boolean; - shouldShow?: boolean; -} -const PanelButton = LazyComponent(() => findByCode("Masks.PANEL_BUTTON")); -const enum SpinnerType { - SpinningCircle = "spinningCircle", - ChasingDots = "chasingDots", - LowMotion = "lowMotion", - PulsingEllipsis = "pulsingEllipsis", - WanderingCubes = "wanderingCubes", -} -export interface SpinnerProps { - type: SpinnerType; - animated?: boolean; - className?: string; - itemClassName?: string; -} -const Spinner = LazyComponent(() => findByCode(".spinningCircleInner")); - -function createExternalStore(init: () => S) { - const subscribers = new Set<() => void>(); - let state = init(); - - return { - get: () => state, - set: (newStateGetter: (oldState: S) => S) => { - state = newStateGetter(state); - for (const cb of subscribers) cb(); - }, - use: () => { - return React.useSyncExternalStore(onStoreChange => { - subscribers.add(onStoreChange); - return () => subscribers.delete(onStoreChange); - }, () => state); - }, - } as const; -} - -const cl = classNameFactory("vc-rnnoise-"); - -const loadedStore = createExternalStore(() => ({ - isLoaded: false, - isLoading: false, - isError: false, -})); -const getRnnoiseWasm = makeLazy(() => { - loadedStore.set(s => ({ ...s, isLoading: true })); - return loadRnnoise({ - url: rnnoiseWasmSrc(), - simdUrl: rnnoiseWasmSrc(true), - }).then(buffer => { - // Check WASM magic number cus fetch doesnt throw on 4XX or 5XX - if (new DataView(buffer.slice(0, 4)).getUint32(0) !== 0x0061736D) throw buffer; - - loadedStore.set(s => ({ ...s, isLoaded: true })); - return buffer; - }).catch(error => { - if (error instanceof ArrayBuffer) error = new TextDecoder().decode(error); - logger.error("Failed to load RNNoise WASM:", error); - loadedStore.set(s => ({ ...s, isError: true })); - return null; - }).finally(() => { - loadedStore.set(s => ({ ...s, isLoading: false })); - }); -}); - -const logger = new Logger("RNNoise"); -const settings = definePluginSettings({}).withPrivateSettings<{ isEnabled: boolean; }>(); -const setEnabled = (enabled: boolean) => { - settings.store.isEnabled = enabled; - FluxDispatcher.dispatch({ type: "AUDIO_SET_NOISE_SUPPRESSION", enabled }); -}; - -function NoiseSupressionPopout() { - const { isEnabled } = settings.use(); - const { isLoading, isError } = loadedStore.use(); - const isWorking = isEnabled && !isError; - - return
-
- Noise Supression -
- {isLoading && } - -
-
- Enable AI noise suppression! Make some noise—like becoming an air conditioner, or a vending machine fan—while speaking. Your friends will hear nothing but your beautiful voice ✨ -
-
; -} - -export default definePlugin({ - name: "AI Noise Suppression", - description: "Uses an open-source AI model (RNNoise) to remove background noise from your microphone", - authors: [Devs.Vap], - settings, - enabledByDefault: true, - - patches: [ - { - // Pass microphone stream to RNNoise - find: "window.webkitAudioContext", - replacement: { - match: /(?<=\i\.acquire=function\((\i)\)\{return )navigator\.mediaDevices\.getUserMedia\(\1\)(?=\})/, - replace: "$&.then(stream => $self.connectRnnoise(stream, $1.audio))" - }, - }, - { - // Noise suppression button in call modal - find: "renderNoiseCancellation()", - replacement: { - match: /(?<=(\i)\.jsxs?.{0,70}children:\[)(?=\i\?\i\.renderNoiseCancellation\(\))/, - replace: (_, react) => `${react}.jsx($self.NoiseSupressionButton, {}),` - }, - }, - { - // Give noise suppression component a "shouldShow" prop - find: "Masks.PANEL_BUTTON", - replacement: { - match: /(?<==(\i)\.tooltipForceOpen.{0,100})(?=tooltipClassName:)/, - replace: (_, props) => `shouldShow: ${props}.shouldShow,` - } - }, - { - // Noise suppression option in voice settings - find: "Messages.USER_SETTINGS_NOISE_CANCELLATION_KRISP", - replacement: [{ - match: /(?<=(\i)=\i\?\i\.KRISP:\i.{1,20}?;)/, - replace: (_, option) => `if ($self.isEnabled()) ${option} = ${JSON.stringify(RNNOISE_OPTION)};`, - }, { - match: /(?=\i&&(\i)\.push\(\{name:(?:\i\.){1,2}Messages.USER_SETTINGS_NOISE_CANCELLATION_KRISP)/, - replace: (_, options) => `${options}.push({ name: "AI (RNNoise)", value: "${RNNOISE_OPTION}" });`, - }, { - match: /(?<=onChange:function\((\i)\)\{)(?=(?:\i\.){1,2}setNoiseCancellation)/, - replace: (_, option) => `$self.setEnabled(${option}.value === ${JSON.stringify(RNNOISE_OPTION)});`, - }], - }, - ], - - setEnabled, - isEnabled: () => settings.store.isEnabled, - async connectRnnoise(stream: MediaStream, isAudio: boolean): Promise { - if (!isAudio) return stream; - if (!settings.store.isEnabled) return stream; - - const audioCtx = new AudioContext(); - await audioCtx.audioWorklet.addModule(rnnoiseWorkletSrc); - - const rnnoiseWasm = await getRnnoiseWasm(); - if (!rnnoiseWasm) { - logger.warn("Failed to load RNNoise, noise suppression won't work"); - return stream; - } - - const rnnoise = new RnnoiseWorkletNode(audioCtx, { - wasmBinary: rnnoiseWasm, - maxChannels: 1, - }); - - const source = audioCtx.createMediaStreamSource(stream); - source.connect(rnnoise); - - const dest = audioCtx.createMediaStreamDestination(); - rnnoise.connect(dest); - - // Cleanup - const onEnded = () => { - rnnoise.disconnect(); - source.disconnect(); - audioCtx.close(); - rnnoise.destroy(); - }; - stream.addEventListener("inactive", onEnded, { once: true }); - - return dest.stream; - }, - NoiseSupressionButton(): ReactNode { - const { isEnabled } = settings.use(); - const { isLoading, isError } = loadedStore.use(); - - return } - spacing={8} - > - {(props, { isShown }) => ( -
- -
} - /> - )} -
; - }, -}); diff --git a/src/plugins/rnnoise.web/styles.css b/src/plugins/rnnoise.web/styles.css deleted file mode 100644 index 7945c496a..000000000 --- a/src/plugins/rnnoise.web/styles.css +++ /dev/null @@ -1,29 +0,0 @@ -.vc-rnnoise-popout { - background: var(--background-floating); - border-radius: 0.25em; - padding: 1em; - width: 16em; -} - -.vc-rnnoise-popout-heading { - color: var(--text-normal); - font-weight: 500; - display: flex; - justify-content: space-between; - align-items: center; - font-size: 1.1em; - margin-bottom: 1em; - gap: 0.5em; -} - -.vc-rnnoise-popout-desc { - color: var(--text-muted); - font-size: 0.9em; - display: flex; - align-items: center; - line-height: 1.5; -} - -.vc-rnnoise-tooltip { - text-align: center; -} From cf3c28e1ff32dae2ae3d89338c9a645ae8c96ebd Mon Sep 17 00:00:00 2001 From: Dea Date: Wed, 25 Oct 2023 15:19:09 +0000 Subject: [PATCH 045/632] fix: onePingPerDM (#1867) --- src/plugins/onePingPerDM/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/onePingPerDM/index.ts b/src/plugins/onePingPerDM/index.ts index e69620ae7..37ca87125 100644 --- a/src/plugins/onePingPerDM/index.ts +++ b/src/plugins/onePingPerDM/index.ts @@ -45,11 +45,11 @@ export default definePlugin({ patches: [{ find: ".getDesktopType()===", replacement: [{ - match: /if\((\i\.\i\.getDesktopType\(\)===\i\.\i\.NEVER)\){/, - replace: "if($1){if(!$self.isPrivateChannelRead(arguments[0]?.message))return;" + match: /(\i\.\i\.getDesktopType\(\)===\i\.\i\.NEVER)\)/, + replace: "$&if(!$self.isPrivateChannelRead(arguments[0]?.message))return;else " }, { - match: /sound:(\i\?\i:void 0,volume:\i,onClick:)/, + match: /sound:(\i\?\i:void 0,volume:\i,onClick)/, replace: "sound:!$self.isPrivateChannelRead(arguments[0]?.message)?undefined:$1" }] }], From 8d1561aed4baa9babdb3bcfccfa91e08f7cf7aab Mon Sep 17 00:00:00 2001 From: redstonekasi Date: Wed, 25 Oct 2023 17:19:53 +0200 Subject: [PATCH 046/632] fix: NoticesAPI (#1863) --- src/plugins/_api/notices.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/_api/notices.ts b/src/plugins/_api/notices.ts index af7cb15e8..0648afa04 100644 --- a/src/plugins/_api/notices.ts +++ b/src/plugins/_api/notices.ts @@ -29,12 +29,12 @@ export default definePlugin({ find: 'displayName="NoticeStore"', replacement: [ { - match: /(?=;\i=null;.{0,70}getPremiumSubscription)/g, - replace: ";if(Vencord.Api.Notices.currentNotice)return false" + match: /\i=null;(?=.{0,80}getPremiumSubscription\(\))/g, + replace: "if(Vencord.Api.Notices.currentNotice)return false;$&" }, { - match: /(?<=,NOTICE_DISMISS:function\(\i\){)(?=if\(null==(\i)\))/, - replace: (_, notice) => `if(${notice}.id=="VencordNotice")return(${notice}=null,Vencord.Api.Notices.nextNotice(),true);` + match: /(?<=,NOTICE_DISMISS:function\(\i\){)return null!=(\i)/, + replace: "if($1.id==\"VencordNotice\")return($1=null,Vencord.Api.Notices.nextNotice(),true);$&" } ] } From 3917193e8fee9832e958a71c5109d39cb808c1da Mon Sep 17 00:00:00 2001 From: Syncx <47534062+Syncxv@users.noreply.github.com> Date: Wed, 25 Oct 2023 20:50:11 +0530 Subject: [PATCH 047/632] fix: ImageZoom (#1864) --- src/plugins/imageZoom/index.tsx | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/src/plugins/imageZoom/index.tsx b/src/plugins/imageZoom/index.tsx index 1f0d7e127..23dcddde7 100644 --- a/src/plugins/imageZoom/index.tsx +++ b/src/plugins/imageZoom/index.tsx @@ -37,13 +37,6 @@ export const settings = definePluginSettings({ default: true, }, - preventCarouselFromClosingOnClick: { - type: OptionType.BOOLEAN, - // Thanks chat gpt - description: "Allow the image modal in the image slideshow thing / carousel to remain open when clicking on the image", - default: true, - }, - invertScroll: { type: OptionType.BOOLEAN, description: "Invert scroll", @@ -163,10 +156,14 @@ export default definePlugin({ patches: [ { - find: '"renderLinkComponent","maxWidth"', + find: "Messages.OPEN_IN_BROWSER", replacement: { - match: /(return\(.{1,100}\(\)\.wrapper.{1,200})(src)/, - replace: `$1id: '${ELEMENT_ID}',$2` + // there are 2 image thingies. one for carosuel and one for the single image. + // so thats why i added global flag. + // also idk if this patch is good, should it be more specific? + // https://regex101.com/r/xfvNvV/1 + match: /return.{1,200}\.wrapper.{1,200}src:\i,/g, + replace: `$&id: '${ELEMENT_ID}',` } }, @@ -174,28 +171,21 @@ export default definePlugin({ find: "handleImageLoad=", replacement: [ { - match: /showThumbhashPlaceholder:/, + match: /showThumbhashPlaceholder:\i,/, replace: "...$self.makeProps(this),$&" }, { - match: /componentDidMount=function\(\){/, + match: /componentDidMount\(\){/, replace: "$&$self.renderMagnifier(this);", }, { - match: /componentWillUnmount=function\(\){/, + match: /componentWillUnmount\(\){/, replace: "$&$self.unMountMagnifier();" } ] }, - { - find: ".carouselModal,", - replacement: { - match: /onClick:(\i),/, - replace: "onClick:$self.settings.store.preventCarouselFromClosingOnClick ? () => {} : $1," - } - } ], settings, From 06b4dffa628d39914eaaaf3358d3d0e63d1811eb Mon Sep 17 00:00:00 2001 From: Nico Date: Wed, 25 Oct 2023 17:26:28 +0200 Subject: [PATCH 048/632] forceOwnerCrown: fix (#1858) --- src/plugins/forceOwnerCrown/index.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/plugins/forceOwnerCrown/index.ts b/src/plugins/forceOwnerCrown/index.ts index eaee62840..15b1f6f56 100644 --- a/src/plugins/forceOwnerCrown/index.ts +++ b/src/plugins/forceOwnerCrown/index.ts @@ -27,18 +27,17 @@ export default definePlugin({ authors: [Devs.D3SOX, Devs.Nickyux], patches: [ { - // This is the logic where it decides whether to render the owner crown or not - find: ".MULTIPLE_AVATAR", + find: "AVATAR_DECORATION_PADDING:", replacement: { - match: /(\i)=(\i)\.isOwner,/, - replace: "$1=$self.isGuildOwner($2)," + match: /,isOwner:(\i),/, + replace: ",_isOwner:$1=$self.isGuildOwner(e)," } } ], - isGuildOwner(props: { user: User, channel: Channel, guildId?: string; }) { - if (!props?.user?.id) return false; + isGuildOwner(props: { user: User, channel: Channel, isOwner: boolean, guildId?: string; }) { + if (!props?.user?.id) return props.isOwner; if (props.channel?.type === 3 /* GROUP_DM */) - return false; + return props.isOwner; // guild id is in props twice, fallback if the first is undefined const guildId = props.guildId ?? props.channel?.guild_id; From 131e91a37c7fae071df80817f90791a5d5a63f11 Mon Sep 17 00:00:00 2001 From: redstonekasi Date: Wed, 25 Oct 2023 17:29:07 +0200 Subject: [PATCH 049/632] fix: AlwaysTrust (#1868) --- src/plugins/alwaysTrust/index.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugins/alwaysTrust/index.ts b/src/plugins/alwaysTrust/index.ts index 79193b0af..07e92afce 100644 --- a/src/plugins/alwaysTrust/index.ts +++ b/src/plugins/alwaysTrust/index.ts @@ -27,15 +27,15 @@ export default definePlugin({ { find: ".displayName=\"MaskedLinkStore\"", replacement: { - match: /\.isTrustedDomain=function\(.\){return.+?};/, - replace: ".isTrustedDomain=function(){return true};" + match: /(?<=isTrustedDomain\(\i\){)return \i\(\i\)/, + replace: "return true" } }, { - find: '"7z","ade","adp"', + find: "isSuspiciousDownload:", replacement: { - match: /JSON\.parse\('\[.+?'\)/, - replace: "[]" + match: /function \i\(\i\){(?=.{0,60}\.parse\(\i\))/, + replace: "$&return null;" } } ] From c5dd50ad8fbee871b182e3d1203a812d6c43396e Mon Sep 17 00:00:00 2001 From: sunnie <78964224+sunnniee@users.noreply.github.com> Date: Wed, 25 Oct 2023 18:33:22 +0300 Subject: [PATCH 050/632] fix messageLinkEmbeds, moreUserTags (#1859) --- src/plugins/messageLinkEmbeds/index.tsx | 27 +++------- src/plugins/moreUserTags/index.tsx | 69 +++++++++++++------------ src/webpack/common/index.ts | 1 + src/webpack/common/settingsStores.ts | 9 ++++ 4 files changed, 52 insertions(+), 54 deletions(-) create mode 100644 src/webpack/common/settingsStores.ts diff --git a/src/plugins/messageLinkEmbeds/index.tsx b/src/plugins/messageLinkEmbeds/index.tsx index c7b3bd012..e600d737d 100644 --- a/src/plugins/messageLinkEmbeds/index.tsx +++ b/src/plugins/messageLinkEmbeds/index.tsx @@ -18,7 +18,6 @@ import { addAccessory } from "@api/MessageAccessories"; import { definePluginSettings } from "@api/Settings"; -import { getSettingStoreLazy } from "@api/SettingsStore"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants.js"; import { classes } from "@utils/misc"; @@ -36,6 +35,7 @@ import { PermissionStore, RestAPI, Text, + TextAndImagesSettingsStores, UserStore } from "@webpack/common"; import { Channel, Guild, Message } from "discord-types/general"; @@ -46,12 +46,11 @@ const messageCache = new Map(); const Embed = LazyComponent(() => findByCode(".inlineMediaEmbed")); -const ChannelMessage = LazyComponent(() => find(m => m.type?.toString()?.includes('["message","compact","className",'))); +const AutoModEmbed = LazyComponent(() => findByCode(".withFooter]:", "childrenMessageContent:")); +const ChannelMessage = LazyComponent(() => find(m => m.type?.toString()?.includes("renderSimpleAccessories)"))); const SearchResultClasses = findByPropsLazy("message", "searchResult"); -let AutoModEmbed: React.ComponentType = () => null; - const messageLinkRegex = /(?; } -const compactModeEnabled = getSettingStoreLazy("textAndImages", "messageDisplayCompact")!; - function AutomodEmbedAccessory(props: MessageEmbedProps): JSX.Element | null { const { message, channel, guildID } = props; + const compact = TextAndImagesSettingsStores.MessageDisplayCompact.useSetting(); const isDM = guildID === "@me"; const images = getImages(message); const { parse } = Parser; @@ -338,7 +336,7 @@ function AutomodEmbedAccessory(props: MessageEmbedProps): JSX.Element | null { {isDM ? " - Direct Message" : " - " + GuildStore.getGuild(channel.guild_id)?.name} } - compact={compactModeEnabled.getSetting()} + compact={compact} content={ <> {message.content || message.attachments.length <= images.length @@ -365,20 +363,7 @@ export default definePlugin({ name: "MessageLinkEmbeds", description: "Adds a preview to messages that link another message", authors: [Devs.TheSun, Devs.Ven, Devs.RyanCaoDev], - dependencies: ["MessageAccessoriesAPI", "SettingsStoreAPI"], - patches: [ - { - find: ".embedCard", - replacement: [{ - match: /function (\i)\(\i\){var \i=\i\.message,\i=\i\.channel.{0,200}\.hideTimestamp/, - replace: "$self.AutoModEmbed=$1;$&" - }] - } - ], - - set AutoModEmbed(e: any) { - AutoModEmbed = e; - }, + dependencies: ["MessageAccessoriesAPI"], settings, diff --git a/src/plugins/moreUserTags/index.tsx b/src/plugins/moreUserTags/index.tsx index c8820f8be..9921bca8e 100644 --- a/src/plugins/moreUserTags/index.tsx +++ b/src/plugins/moreUserTags/index.tsx @@ -22,7 +22,7 @@ import { Devs } from "@utils/constants"; import { Margins } from "@utils/margins"; import definePlugin, { OptionType } from "@utils/types"; import { findByPropsLazy, findLazy } from "@webpack"; -import { Card, ChannelStore, Forms, GuildStore, Switch, TextInput, Tooltip, useState } from "@webpack/common"; +import { Card, ChannelStore, Forms, GuildStore, PermissionsBits, Switch, TextInput, Tooltip, useState } from "@webpack/common"; import { RC } from "@webpack/types"; import { Channel, Message, User } from "discord-types/general"; @@ -58,7 +58,6 @@ const PermissionUtil = findByPropsLazy("computePermissions", "canEveryoneRole") computePermissions({ ...args }): bigint; }; -const Permissions = findByPropsLazy("SEND_MESSAGES", "VIEW_CREATOR_MONETIZATION_ANALYTICS") as Record; const Tag = findLazy(m => m.Types?.[0] === "BOT") as RC<{ type?: number, className?: string, useRemSizes?: boolean; }> & { Types: Record; }; const isWebhook = (message: Message, user: User) => !!message?.webhookId && user.isNonUserBot(); @@ -188,17 +187,14 @@ export default definePlugin({ patches: [ // add tags to the tag list { - find: '.BOT=0]="BOT"', - replacement: [ - // add tags to the exported tags list (Tag.Types) - { - match: /(\i)\[.\.BOT=0\]="BOT";/, - replace: "$&$1=$self.addTagVariants($1);" - } - ] + find: "BotTagTypes:", + replacement: { + match: /\((\i)=\{\}\)\)\[(\i)\.BOT/, + replace: "($1=$self.getTagTypes()))[$2.BOT" + } }, { - find: ".DISCORD_SYSTEM_MESSAGE_BOT_TAG_TOOLTIP;", + find: ".DISCORD_SYSTEM_MESSAGE_BOT_TAG_TOOLTIP,", replacement: [ // make the tag show the right text { @@ -213,25 +209,25 @@ export default definePlugin({ }, // add HTML data attributes (for easier theming) { - match: /children:\[(?=\i\?null:\i,\i,\(0,\i\.jsx\)\("span",{className:\i\(\)\.botText,children:(\i)}\)\])/, - replace: "'data-tag':$1.toLowerCase(),children:[" + match: /.botText,children:(\i)}\)]/, + replace: "$&,'data-tag':$1.toLowerCase()" } ], }, // in messages { - find: ".Types.ORIGINAL_POSTER", + find: "renderSystemTag:", replacement: { - match: /return null==(\i)\?null:\(0,/, - replace: "$1=$self.getTag({...arguments[0],origType:$1,location:'chat'});$&" + match: /;return\((\(null==\i\?void 0:\i\.isSystemDM\(\).+?.Types.ORIGINAL_POSTER\)),null==(\i)\)/, + replace: ";$1;$2=$self.getTag({...arguments[0],origType:$2,location:'chat'});return $2 == null" } }, // in the member list { find: ".Messages.GUILD_OWNER,", replacement: { - match: /(?\i)=\(null==.{0,50}\.BOT,null!=(?\i)&&\i\.bot/, - replace: "$ = $self.getTag({user: $, channel: arguments[0].channel, origType: $.bot ? 0 : null, location: 'not-chat' }), typeof $ === 'number'" + match: /(?\i)=\(null==.{0,100}\.BOT;return null!=(?\i)&&\i\.bot/, + replace: "$ = $self.getTag({user: $, channel: arguments[0].channel, origType: $.bot ? 0 : null, location: 'not-chat' }); return typeof $ === 'number'" } }, // pass channel id down props to be used in profiles @@ -251,11 +247,18 @@ export default definePlugin({ }, // in profiles { - find: "showStreamerModeTooltip:", - replacement: { - match: /,botType:(\i\((\i)\)),/g, - replace: ",botType:$self.getTag({user:$2,channelId:arguments[0].moreTags_channelId,origType:$1,location:'not-chat'})," - } + find: ",overrideDiscriminator:", + replacement: [ + { + // prevent channel id from getting ghosted + // it's either this or extremely long lookbehind + match: /user:\i,nick:\i,/, + replace: "$&moreTags_channelId," + }, { + match: /,botType:(\i\((\i)\)),/g, + replace: ",botType:$self.getTag({user:$2,channelId:moreTags_channelId,origType:$1,location:'not-chat'})," + } + ] }, ], @@ -295,24 +298,25 @@ export default definePlugin({ if (!guild) return []; const permissions = PermissionUtil.computePermissions({ user, context: guild, overwrites: channel.permissionOverwrites }); - return Object.entries(Permissions) + return Object.entries(PermissionsBits) .map(([perm, permInt]) => permissions & permInt ? perm : "" ) .filter(Boolean); }, - addTagVariants(tagConstant) { + getTagTypes() { + const obj = {}; let i = 100; tags.forEach(({ name }) => { - tagConstant[name] = ++i; - tagConstant[i] = name; - tagConstant[`${name}-BOT`] = ++i; - tagConstant[i] = `${name}-BOT`; - tagConstant[`${name}-OP`] = ++i; - tagConstant[i] = `${name}-OP`; + obj[name] = ++i; + obj[i] = name; + obj[`${name}-BOT`] = ++i; + obj[i] = `${name}-BOT`; + obj[`${name}-OP`] = ++i; + obj[i] = `${name}-OP`; }); - return tagConstant; + return obj; }, isOPTag: (tag: number) => tag === Tag.Types.ORIGINAL_POSTER || tags.some(t => tag === Tag.Types[`${t.name}-OP`]), @@ -377,7 +381,6 @@ export default definePlugin({ break; } } - return type; } }); diff --git a/src/webpack/common/index.ts b/src/webpack/common/index.ts index 2ad9c54ec..5da3cc68b 100644 --- a/src/webpack/common/index.ts +++ b/src/webpack/common/index.ts @@ -20,6 +20,7 @@ export * from "./classes"; export * from "./components"; export * from "./menu"; export * from "./react"; +export * from "./settingsStores"; export * from "./stores"; export * as ComponentTypes from "./types/components.d"; export * as MenuTypes from "./types/menu.d"; diff --git a/src/webpack/common/settingsStores.ts b/src/webpack/common/settingsStores.ts new file mode 100644 index 000000000..ab281e569 --- /dev/null +++ b/src/webpack/common/settingsStores.ts @@ -0,0 +1,9 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { findByPropsLazy } from "@webpack"; + +export const TextAndImagesSettingsStores = findByPropsLazy("MessageDisplayCompact"); From 9c60b38acc13c7fe6b5334beaadfe36288ad825d Mon Sep 17 00:00:00 2001 From: rini Date: Wed, 25 Oct 2023 12:34:23 -0300 Subject: [PATCH 051/632] Fix PinDMs, SMYN, SilentTyping, ValidUser, PlatformIndicators (#1865) --- src/plugins/_api/memberListDecorators.ts | 15 ++++--------- src/plugins/_api/messageDecorations.ts | 2 +- src/plugins/pinDms/index.tsx | 28 ++++++++++++------------ src/plugins/showMeYourName/index.tsx | 28 +++++++----------------- src/plugins/silentTyping/index.tsx | 6 ++--- src/plugins/validUser/index.tsx | 11 ++++------ 6 files changed, 34 insertions(+), 56 deletions(-) diff --git a/src/plugins/_api/memberListDecorators.ts b/src/plugins/_api/memberListDecorators.ts index bf7377e85..1251c3578 100644 --- a/src/plugins/_api/memberListDecorators.ts +++ b/src/plugins/_api/memberListDecorators.ts @@ -38,17 +38,10 @@ export default definePlugin({ }, { find: "PrivateChannel.renderAvatar", - replacement: [ - // props are shadowed by nested props so we have to do this - { - match: /let\{[^}]*applicationStream:\i[^}]*\}=(\i),/, - replace: "$&vencordProps=$1," - }, - { - match: /decorators:(\i\.isSystemDM\(\))\?(.+?):null/, - replace: "decorators:[...(typeof vencordProps=='undefined'?[]:Vencord.Api.MemberListDecorators.__getDecorators(vencordProps)), $1?$2:null]" - } - ] + replacement: { + match: /decorators:(\i\.isSystemDM\(\))\?(.+?):null/, + replace: "decorators:[...Vencord.Api.MemberListDecorators.__getDecorators(arguments[0]), $1?$2:null]" + } } ], }); diff --git a/src/plugins/_api/messageDecorations.ts b/src/plugins/_api/messageDecorations.ts index 3f0fd11db..1646ad645 100644 --- a/src/plugins/_api/messageDecorations.ts +++ b/src/plugins/_api/messageDecorations.ts @@ -25,7 +25,7 @@ export default definePlugin({ authors: [Devs.TheSun], patches: [ { - find: "UsernameDecorationTypes:", + find: '"Message Username"', replacement: { match: /currentUserIsPremium:.{0,70}{children:\i(?=}\))/, replace: "$&.concat(Vencord.Api.MessageDecorations.__addDecorationsToMessage(arguments[0]))" diff --git a/src/plugins/pinDms/index.tsx b/src/plugins/pinDms/index.tsx index a7406aa41..ac5957b33 100644 --- a/src/plugins/pinDms/index.tsx +++ b/src/plugins/pinDms/index.tsx @@ -66,7 +66,7 @@ export default definePlugin({ // filter Discord's privateChannelIds list to remove pins, and pass // pinCount as prop. This needs to be here so that the entire DM list receives // updates on pin/unpin - match: /privateChannelIds:(\i),/, + match: /(?<=\i,{channels:\i,)privateChannelIds:(\i),/, replace: "privateChannelIds:$1.filter(c=>!$self.isPinned(c)),pinCount:$self.usePinCount($1)," }, { @@ -75,39 +75,39 @@ export default definePlugin({ // - Section 1: buttons for pages like Friends & Library // - Section 2: our pinned dms // - Section 3: the normal dm list - match: /(?<=renderRow:(\i)\.renderRow,)sections:\[\i,/, + match: /(?<=renderRow:this\.renderRow,)sections:\[\i,/, // For some reason, adding our sections when no private channels are ready yet // makes DMs infinitely load. Thus usePinCount returns either a single element // array with the count, or an empty array. Due to spreading, only in the former // case will an element be added to the outer array // Thanks for the fix, Strencher! - replace: "$&...($1.props.pinCount ?? [])," + replace: "$&...this.props.pinCount??[]," }, { // Patch renderSection (renders the header) to set the text to "Pinned DMs" instead of "Direct Messages" // lookbehind is used to lookup parameter name. We could use arguments[0], but // if children ever is wrapped in an iife, it will break - match: /children:(\i\.\i\.Messages.DIRECT_MESSAGES)(?<=renderSection=function\((\i)\).+?)/, + match: /children:(\i\.\i\.Messages.DIRECT_MESSAGES)(?<=renderSection=(\i)=>{.+?)/, replace: "children:$2.section===1?'Pinned DMs':$1" }, { // Patch channel lookup inside renderDM // channel=channels[channelIds[row]]; - match: /(?<=preRenderedChildren,(\i)=)((\i)\[\i\[\i\]\]);/, + match: /(?<=renderDM=\((\i),(\i)\)=>{.*?this.state,\i=\i\[\i\],\i=)((\i)\[\i\]);/, // section 1 is us, manually get our own channel // section === 1 ? getChannel(channels, row) : channels[channelIds[row]]; - replace: "arguments[0]===1?$self.getChannel($3,arguments[1]):$2;" + replace: "$1===1?$self.getChannel($4,$2):$3;" }, { // Fix getRowHeight's check for whether this is the DMs section // section === DMS - match: /===\i.DMS&&0/, + match: /===\i\.DMS&&0/, // section -1 === DMS replace: "-1$&" }, { // Override scrollToChannel to properly account for pinned channels - match: /(?<=else\{\i\+=)(\i)\*\(.+?(?=;)/, + match: /(?<=scrollTo\(\{to:\i\}\):\(\i\+=)(\d+)\*\(.+?(?=,)/, replace: "$self.getScrollOffset(arguments[0],$1,this.props.padding,this.state.preRenderedChildren,$&)" } ] @@ -115,19 +115,19 @@ export default definePlugin({ // Fix Alt Up/Down navigation { - find: '"mod+alt+right"', + find: ".Routes.APPLICATION_STORE&&", replacement: { - // channelIds = __OVERLAY__ ? stuff : toArray(getStaticPaths()).concat(toArray(channelIds)) - match: /(?<=(\i)=__OVERLAY__\?\i:.{0,10})\.concat\((.{0,10})\)/, + // channelIds = __OVERLAY__ ? stuff : [...getStaticPaths(),...channelIds)] + match: /(?<=\i=__OVERLAY__\?\i:\[\.\.\.\i\(\),\.\.\.)\i/, // ....concat(pins).concat(toArray(channelIds).filter(c => !isPinned(c))) - replace: ".concat($self.getSnapshot()).concat($2.filter(c=>!$self.isPinned(c)))" + replace: "$self.getSnapshot().concat($&.filter(c=>!$self.isPinned(c)))" } }, // fix alt+shift+up/down { - find: '"alt+shift+down"', + find: ".getFlattenedGuildIds()],", replacement: { - match: /(?<=return \i===\i\.ME\?)\i\.\i\.getPrivateChannelIds\(\)/, + match: /(?<=\i===\i\.ME\?)\i\.\i\.getPrivateChannelIds\(\)/, replace: "$self.getSnapshot().concat($&.filter(c=>!$self.isPinned(c)))" } }, diff --git a/src/plugins/showMeYourName/index.tsx b/src/plugins/showMeYourName/index.tsx index 4ccaeea8e..6986f69f2 100644 --- a/src/plugins/showMeYourName/index.tsx +++ b/src/plugins/showMeYourName/index.tsx @@ -1,20 +1,8 @@ /* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2023 Sofia Lima - * - * 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 . -*/ + * Vencord, a Discord client mod + * Copyright (c) 2023 rini + * SPDX-License-Identifier: GPL-3.0-or-later + */ import "./styles.css"; @@ -56,13 +44,13 @@ const settings = definePluginSettings({ export default definePlugin({ name: "ShowMeYourName", description: "Display usernames next to nicks, or no nicks at all", - authors: [Devs.dzshn, Devs.TheKodeToad], + authors: [Devs.Rini, Devs.TheKodeToad], patches: [ { - find: ".withMentionPrefix", + find: '"Message Username"', replacement: { - match: /(?<=onContextMenu:\i,children:)\i\+\i/, - replace: "$self.renderUsername(arguments[0])" + match: /(?<=onContextMenu:\i,children:).*?\}/, + replace: "$self.renderUsername(arguments[0])}" } }, ], diff --git a/src/plugins/silentTyping/index.tsx b/src/plugins/silentTyping/index.tsx index a4dc2568d..d4cb27ba4 100644 --- a/src/plugins/silentTyping/index.tsx +++ b/src/plugins/silentTyping/index.tsx @@ -74,13 +74,13 @@ function SilentTypingToggle(chatBoxProps: { export default definePlugin({ name: "SilentTyping", - authors: [Devs.Ven, Devs.dzshn], + authors: [Devs.Ven, Devs.Rini], description: "Hide that you are typing", patches: [ { - find: "startTyping:", + find: '.dispatch({type:"TYPING_START_LOCAL"', replacement: { - match: /startTyping:.+?,stop/, + match: /startTyping\(\i\){.+?},stop/, replace: "startTyping:$self.startTyping,stop" } }, diff --git a/src/plugins/validUser/index.tsx b/src/plugins/validUser/index.tsx index a3862d4d0..b0c77cb46 100644 --- a/src/plugins/validUser/index.tsx +++ b/src/plugins/validUser/index.tsx @@ -21,14 +21,11 @@ import { Devs } from "@utils/constants"; import { sleep } from "@utils/misc"; import { Queue } from "@utils/Queue"; import definePlugin from "@utils/types"; -import { findByCodeLazy } from "@webpack"; -import { UserStore, useState } from "@webpack/common"; -import type { User } from "discord-types/general"; +import { UserStore, UserUtils, useState } from "@webpack/common"; import type { ComponentType, ReactNode } from "react"; const fetching = new Set(); const queue = new Queue(5); -const fetchUser = findByCodeLazy("USER(") as (id: string) => Promise; interface MentionProps { data: { @@ -88,7 +85,7 @@ function MentionWrapper({ data, UserMention, RoleMention, parse, props }: Mentio fetching.add(id); queue.unshift(() => - fetchUser(id) + UserUtils.getUser(id) .then(() => { setUserId(id); fetching.delete(id); @@ -122,9 +119,9 @@ export default definePlugin({ find: 'className:"mention"', replacement: { // mention = { react: function (data, parse, props) { if (data.userId == null) return RoleMention() else return UserMention() - match: /react:(?=function\(\i,\i,\i\).{0,50}return null==\i\?\(0,\i\.jsx\)\((\i),.+?jsx\)\((\i),\{className:"mention")/, + match: /react(?=\(\i,\i,\i\).{0,50}return null==\i\?\(0,\i\.jsx\)\((\i\.\i),.+?jsx\)\((\i\.\i),\{className:"mention")/, // react: (...args) => OurWrapper(RoleMention, UserMention, ...args), originalReact: theirFunc - replace: "react:(...args)=>$self.renderMention($1,$2,...args),originalReact:" + replace: "react:(...args)=>$self.renderMention($1,$2,...args),originalReact" } }], From b659d4e9c1f350769284a4dc2d983aeb942c9e7a Mon Sep 17 00:00:00 2001 From: Syncx <47534062+Syncxv@users.noreply.github.com> Date: Wed, 25 Oct 2023 21:05:50 +0530 Subject: [PATCH 052/632] fix: FavoriteEmojiFirst (#1844) --- src/plugins/favEmojiFirst/index.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/plugins/favEmojiFirst/index.ts b/src/plugins/favEmojiFirst/index.ts index fec0b045d..f34b13884 100644 --- a/src/plugins/favEmojiFirst/index.ts +++ b/src/plugins/favEmojiFirst/index.ts @@ -39,22 +39,27 @@ export default definePlugin({ description: "Puts your favorite emoji first in the emoji autocomplete.", patches: [ { - find: ".activeCommandOption", + find: "renderResults({results:", replacement: [ { - // = someFunc(a.selectedIndex); ...trackEmojiSearch({ state: theState, isInPopoutExperimental: someBool }) - match: /=\i\(\i\.selectedIndex\);(?=.+?state:(\i),isInPopoutExperiment:\i)/, - // self.sortEmojis(theState) - replace: "$&$self.sortEmojis($1);" + // https://regex101.com/r/N7kpLM/1 + match: /let \i=.{1,100}renderResults\({results:(\i)\.query\.results,/, + replace: "$self.sortEmojis($1);$&" }, + ], + }, + { + find: "MAX_AUTOCOMPLETE_RESULTS+", + replacement: [ // set maxCount to Infinity so our sortEmojis callback gets the entire list, not just the first 10 // and remove Discord's emojiResult slice, storing the endIndex on the array for us to use later { + // https://regex101.com/r/x2mobQ/1 // searchEmojis(...,maxCount: stuff) ... endEmojis = emojis.slice(0, maxCount - gifResults.length) - match: /,maxCount:(\i)(.+?)=(\i)\.slice\(0,(\1-\i\.length)\)/, + match: /,maxCount:(\i)(.{1,500}\i)=(\i)\.slice\(0,(\i-\i\.length)\)/, // ,maxCount:Infinity ... endEmojis = (emojis.sliceTo = n, emojis) - replace: ",maxCount:Infinity$2=($3.sliceTo=$4,$3)" + replace: ",maxCount:Infinity$2=($3.sliceTo = $4, $3)" } ] } From 3bd657611cbc6b3cf3142ae59c39713922d167df Mon Sep 17 00:00:00 2001 From: redstonekasi Date: Wed, 25 Oct 2023 17:36:19 +0200 Subject: [PATCH 053/632] fix: StartupTimings (#1869) --- src/plugins/startupTimings/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/startupTimings/index.tsx b/src/plugins/startupTimings/index.tsx index 3eec9d0cd..c7417f0fc 100644 --- a/src/plugins/startupTimings/index.tsx +++ b/src/plugins/startupTimings/index.tsx @@ -25,9 +25,9 @@ export default definePlugin({ description: "Adds Startup Timings to the Settings menu", authors: [Devs.Megu], patches: [{ - find: "PAYMENT_FLOW_MODAL_TEST_PAGE,", + find: "UserSettingsSections.PAYMENT_FLOW_MODAL_TEST_PAGE,", replacement: { - match: /{section:.{1,2}\..{1,3}\.PAYMENT_FLOW_MODAL_TEST_PAGE/, + match: /{section:\i\.UserSettingsSections\.PAYMENT_FLOW_MODAL_TEST_PAGE/, replace: '{section:"StartupTimings",label:"Startup Timings",element:$self.StartupTimingPage},$&' } }], From 1130521e4b2e5f5f06bab458767224dca44b3b33 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 25 Oct 2023 18:07:12 +0200 Subject: [PATCH 054/632] Fix WebContextMenus --- src/plugins/webContextMenus.web/index.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/plugins/webContextMenus.web/index.ts b/src/plugins/webContextMenus.web/index.ts index 26ae19c5d..cd31e7aa8 100644 --- a/src/plugins/webContextMenus.web/index.ts +++ b/src/plugins/webContextMenus.web/index.ts @@ -98,8 +98,8 @@ export default definePlugin({ replacement: [ { // if (!IS_WEB || null == - match: /if\(!\i\.\i\|\|null==/, - replace: "if(null==" + match: /!\i\.isPlatformEmbedded/, + replace: "false" }, { match: /return\s*?\[\i\.\i\.canCopyImage\(\)/, @@ -132,23 +132,22 @@ export default definePlugin({ find: '"interactionUsernameProfile"', predicate: () => settings.store.addBack, replacement: { - match: /if\("A"===\i\.tagName&&""!==\i\.textContent\)/, - replace: "if(false)" + match: /if\((?="A"===\i\.tagName&&""!==\i\.textContent)/, + replace: "if(false&&" } }, // Add back slate / text input context menu { - find: '"slate-toolbar"', + find: 'getElementById("slate-toolbar"', predicate: () => settings.store.addBack, replacement: { - match: /(?<=\.handleContextMenu=.+?"bottom";)\i\.\i\?/, - replace: "true?" + match: /(?<=handleContextMenu\(\i\)\{.{0,200}isPlatformEmbedded)\?/, + replace: "||true?" } }, { find: 'navId:"textarea-context"', - all: true, predicate: () => settings.store.addBack, replacement: [ { @@ -167,7 +166,7 @@ export default definePlugin({ find: '"add-to-dictionary"', predicate: () => settings.store.addBack, replacement: { - match: /var \i=\i\.text,/, + match: /let\{text:\i=""/, replace: "return [null,null];$&" } } From 024a77c577cb7b082c46ce24b0ee33851abface9 Mon Sep 17 00:00:00 2001 From: Jack Matthews Date: Wed, 25 Oct 2023 11:48:17 -0400 Subject: [PATCH 055/632] fix: typingIndicator --- src/plugins/typingIndicator/index.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/plugins/typingIndicator/index.tsx b/src/plugins/typingIndicator/index.tsx index 5f7df4770..8ac344606 100644 --- a/src/plugins/typingIndicator/index.tsx +++ b/src/plugins/typingIndicator/index.tsx @@ -26,7 +26,12 @@ import { ChannelStore, GuildMemberStore, RelationshipStore, Tooltip, UserStore, import { buildSeveralUsers } from "../typingTweaks"; -const ThreeDots = LazyComponent(() => find(m => m.type?.render?.toString()?.includes("().dots"))); +const ThreeDots = LazyComponent(() => { + // This doesn't really need to explicitly find Dots' own module, but it's fine + const res = find(m => m.Dots && !m.Menu); + + return res?.Dots; +}); const TypingStore = findStoreLazy("TypingStore"); const UserGuildSettingsStore = findStoreLazy("UserGuildSettingsStore"); @@ -126,8 +131,8 @@ export default definePlugin({ { find: ".UNREAD_HIGHLIGHT", replacement: { - match: /\(\).children.+?:null(?<=(\i)=\i\.channel,.+?)/, - replace: (m, channel) => `${m},$self.TypingIndicator(${channel}.id)` + match: /channel:(\i).{0,100}?channelEmoji,.{0,250}?\.children.{0,50}?:null/, + replace: "$&,$self.TypingIndicator($1.id)" } } ], From 4f8c75372cd63f891f21452f6144823a646c26f6 Mon Sep 17 00:00:00 2001 From: redstonekasi Date: Wed, 25 Oct 2023 18:09:04 +0200 Subject: [PATCH 056/632] fix: NoBlockedMessages (#1870) --- src/plugins/noBlockedMessages/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/noBlockedMessages/index.ts b/src/plugins/noBlockedMessages/index.ts index 54bb2d051..f9de3e13c 100644 --- a/src/plugins/noBlockedMessages/index.ts +++ b/src/plugins/noBlockedMessages/index.ts @@ -29,11 +29,11 @@ export default definePlugin({ authors: [Devs.rushii, Devs.Samu], patches: [ { - find: 'safety_prompt:"DMSpamExperiment",response:"show_redacted_messages"', + find: "Messages.BLOCKED_MESSAGES_HIDE", replacement: [ { - match: /\.collapsedReason;return/, - replace: ".collapsedReason;return null;return;" + match: /let\{[^}]*collapsedReason[^}]*\}/, + replace: "return null;$&" } ] }, From ddc39fe84d8b382bf2b5aa93b52c21fc4636330b Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 25 Oct 2023 18:15:12 +0200 Subject: [PATCH 057/632] Fix SettingStores, GameActivityToggle --- src/api/SettingsStore.ts | 69 ------------------------ src/api/index.ts | 5 -- src/plugins/_api/settingsStore.ts | 38 ------------- src/plugins/gameActivityToggle/index.tsx | 10 ++-- src/plugins/gameActivityToggle/style.css | 2 +- src/plugins/ignoreActivities/index.tsx | 7 +-- src/webpack/common/settingsStores.ts | 1 + 7 files changed, 8 insertions(+), 124 deletions(-) delete mode 100644 src/api/SettingsStore.ts delete mode 100644 src/plugins/_api/settingsStore.ts diff --git a/src/api/SettingsStore.ts b/src/api/SettingsStore.ts deleted file mode 100644 index d9369a956..000000000 --- a/src/api/SettingsStore.ts +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 { proxyLazy } from "@utils/lazy"; -import { Logger } from "@utils/Logger"; -import { findModuleId, wreq } from "@webpack"; - -import { Settings } from "./Settings"; - -interface Setting { - /** - * Get the setting value - */ - getSetting(): T; - /** - * Update the setting value - * @param value The new value - */ - updateSetting(value: T | ((old: T) => T)): Promise; - /** - * React hook for automatically updating components when the setting is updated - */ - useSetting(): T; - settingsStoreApiGroup: string; - settingsStoreApiName: string; -} - -const SettingsStores: Array> | undefined = proxyLazy(() => { - const modId = findModuleId('"textAndImages","renderSpoilers"'); - if (modId == null) return new Logger("SettingsStoreAPI").error("Didn't find stores module."); - - const mod = wreq(modId); - if (mod == null) return; - - return Object.values(mod).filter((s: any) => s?.settingsStoreApiGroup) as any; -}); - -/** - * Get the store for a setting - * @param group The setting group - * @param name The name of the setting - */ -export function getSettingStore(group: string, name: string): Setting | undefined { - if (!Settings.plugins.SettingsStoreAPI.enabled) throw new Error("Cannot use SettingsStoreAPI without setting as dependency."); - - return SettingsStores?.find(s => s?.settingsStoreApiGroup === group && s?.settingsStoreApiName === name); -} - -/** - * getSettingStore but lazy - */ -export function getSettingStoreLazy(group: string, name: string) { - return proxyLazy(() => getSettingStore(group, name)); -} diff --git a/src/api/index.ts b/src/api/index.ts index f2c47e559..08f238104 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -29,7 +29,6 @@ import * as $Notices from "./Notices"; import * as $Notifications from "./Notifications"; import * as $ServerList from "./ServerList"; import * as $Settings from "./Settings"; -import * as $SettingsStore from "./SettingsStore"; import * as $Styles from "./Styles"; /** @@ -91,10 +90,6 @@ export const MemberListDecorators = $MemberListDecorators; * An API allowing you to persist data */ export const Settings = $Settings; -/** - * An API allowing you to read, manipulate and automatically update components based on Discord settings - */ -export const SettingsStore = $SettingsStore; /** * An API allowing you to dynamically load styles * a diff --git a/src/plugins/_api/settingsStore.ts b/src/plugins/_api/settingsStore.ts deleted file mode 100644 index ca1dc65bf..000000000 --- a/src/plugins/_api/settingsStore.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2022 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 { Devs } from "@utils/constants"; -import definePlugin from "@utils/types"; - -export default definePlugin({ - name: "SettingsStoreAPI", - description: "Patches Discord's SettingsStores to expose their group and name", - authors: [Devs.Nuckyz], - - patches: [ - { - find: '"textAndImages","renderSpoilers"', - replacement: [ - { - match: /(?<=INFREQUENT_USER_ACTION.{0,20}),useSetting:function/, - replace: ",settingsStoreApiGroup:arguments[0],settingsStoreApiName:arguments[1]$&" - } - ] - } - ] -}); diff --git a/src/plugins/gameActivityToggle/index.tsx b/src/plugins/gameActivityToggle/index.tsx index 40318c023..735f124c5 100644 --- a/src/plugins/gameActivityToggle/index.tsx +++ b/src/plugins/gameActivityToggle/index.tsx @@ -16,16 +16,15 @@ * along with this program. If not, see . */ -import { getSettingStoreLazy } from "@api/SettingsStore"; import { disableStyle, enableStyle } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; import { findByCodeLazy } from "@webpack"; +import { StatusSettingsStores } from "@webpack/common"; import style from "./style.css?managed"; -const ShowCurrentGame = getSettingStoreLazy("status", "showCurrentGame"); const Button = findByCodeLazy("Button.Sizes.NONE,disabled:"); function makeIcon(showCurrentGame?: boolean) { @@ -40,7 +39,7 @@ function makeIcon(showCurrentGame?: boolean) { {!showCurrentGame && <> - + } @@ -50,7 +49,7 @@ function makeIcon(showCurrentGame?: boolean) { } function GameActivityToggleButton() { - const showCurrentGame = ShowCurrentGame?.useSetting(); + const showCurrentGame = StatusSettingsStores.ShowCurrentGame.useSetting(); return (
{Object.entries(PermissionsBits).map(([permissionName, bit]) => ( -
+
{(() => { const { permissions, overwriteAllow, overwriteDeny } = selectedItem; diff --git a/src/plugins/permissionsViewer/components/UserPermissions.tsx b/src/plugins/permissionsViewer/components/UserPermissions.tsx index 37d9d0f42..61d4dcb51 100644 --- a/src/plugins/permissionsViewer/components/UserPermissions.tsx +++ b/src/plugins/permissionsViewer/components/UserPermissions.tsx @@ -109,7 +109,7 @@ function UserPermissionsComponent({ guild, guildMember }: { guild: Guild; guildM } defaultState={settings.store.defaultPermissionsDropdownState} buttons={[ - ( + ( {tooltipProps => ( diff --git a/src/plugins/spotifyControls/PlayerComponent.tsx b/src/plugins/spotifyControls/PlayerComponent.tsx index 43c499ab7..64ee52fc1 100644 --- a/src/plugins/spotifyControls/PlayerComponent.tsx +++ b/src/plugins/spotifyControls/PlayerComponent.tsx @@ -38,19 +38,21 @@ function msToHuman(ms: number) { } function Svg(path: string, label: string) { - return () => ( - - - - ); + return function Icon() { + return ( + + + + ); + }; } // KraXen's icons :yesyes: diff --git a/src/plugins/startupTimings/StartupTimingPage.tsx b/src/plugins/startupTimings/StartupTimingPage.tsx index c8cf51da2..0950b0688 100644 --- a/src/plugins/startupTimings/StartupTimingPage.tsx +++ b/src/plugins/startupTimings/StartupTimingPage.tsx @@ -120,8 +120,8 @@ function ServerTrace({ trace }: ServerTraceProps) { - {lines.map(line => ( - {line} + {lines.map((line, i) => ( + {line} ))} diff --git a/src/plugins/typingTweaks/index.tsx b/src/plugins/typingTweaks/index.tsx index 62bcd41f6..ccb863ce7 100644 --- a/src/plugins/typingTweaks/index.tsx +++ b/src/plugins/typingTweaks/index.tsx @@ -21,7 +21,7 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { openUserProfile } from "@utils/discord"; import definePlugin, { OptionType } from "@utils/types"; -import { Avatar, GuildMemberStore, React, RelationshipStore } from "@webpack/common"; +import { Avatar, GuildMemberStore, RelationshipStore } from "@webpack/common"; import { User } from "discord-types/general"; const settings = definePluginSettings({ @@ -135,7 +135,7 @@ export default definePlugin({ return children.map(c => c.type === "strong" - ? + ? : c ); } diff --git a/src/utils/react.tsx b/src/utils/react.tsx index 0181c95b8..43a4d121e 100644 --- a/src/utils/react.tsx +++ b/src/utils/react.tsx @@ -133,7 +133,7 @@ export function useForceUpdater(withDep?: true) { export function LazyComponent(factory: () => React.ComponentType, attempts = 5) { const get = makeLazy(factory, attempts); - return (props: T) => { + return function Lazy(props: T) { const Component = get() ?? NoopComponent; return ; }; From 584885acf5a650afdff145b3f6bac971995d4a2c Mon Sep 17 00:00:00 2001 From: Vendicated Date: Tue, 31 Oct 2023 23:56:13 +0100 Subject: [PATCH 144/632] [skip ci] Revert "add react linting" doesnt work properly :( This reverts commit 18fdc33ee7d1f60d58645c2a98f402988b97e996. --- .eslintrc.json | 12 +- package.json | 1 - pnpm-lock.yaml | 768 ------------------ .../Notifications/NotificationComponent.tsx | 2 +- src/components/ErrorBoundary.tsx | 5 +- src/components/Heart.tsx | 2 +- src/components/Icons.tsx | 2 +- src/components/PluginSettings/index.tsx | 20 +- .../VencordSettings/PatchHelperTab.tsx | 4 +- src/components/VencordSettings/ThemesTab.tsx | 2 +- src/components/VencordSettings/UpdaterTab.tsx | 4 +- src/plugins/betterFolders/FolderSideBar.tsx | 2 +- src/plugins/callTimer/index.tsx | 1 + src/plugins/consoleShortcuts/index.ts | 1 - src/plugins/customRPC/index.tsx | 2 +- src/plugins/emoteCloner/index.tsx | 42 +- src/plugins/gameActivityToggle/index.tsx | 2 +- src/plugins/messageLinkEmbeds/index.tsx | 4 +- src/plugins/moreUserTags/index.tsx | 2 +- src/plugins/mutualGroupDMs/index.tsx | 1 - .../components/RolesAndUsersPermissions.tsx | 3 +- .../components/UserPermissions.tsx | 4 +- src/plugins/pictureInPicture/index.tsx | 2 +- src/plugins/platformIndicators/index.tsx | 32 +- .../readAllNotificationsButton/index.tsx | 2 +- src/plugins/sendTimestamps/index.tsx | 2 +- .../serverProfile/GuildProfileModal.tsx | 1 - .../components/Code.tsx | 6 +- .../components/Highlighter.tsx | 2 +- src/plugins/showConnections/index.tsx | 2 +- .../components/HiddenChannelLockScreen.tsx | 2 +- src/plugins/silentMessageToggle/index.tsx | 2 +- src/plugins/silentTyping/index.tsx | 4 +- .../spotifyControls/PlayerComponent.tsx | 28 +- .../startupTimings/StartupTimingPage.tsx | 4 +- src/plugins/typingTweaks/index.tsx | 4 +- src/utils/react.tsx | 2 +- 37 files changed, 97 insertions(+), 884 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 5d1b7c763..2ee24e8b3 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -9,7 +9,6 @@ "unused-imports", "path-alias" ], - "extends": ["plugin:react/recommended", "plugin:react/jsx-runtime"], "settings": { "import/resolver": { "alias": { @@ -21,9 +20,6 @@ ["@components", "./src/components"] ] } - }, - "react": { - "version": "18.2" } }, "rules": { @@ -97,12 +93,6 @@ "unused-imports/no-unused-imports": "error", - "path-alias/no-relative": "error", - - "react/no-unescaped-entities": "off", - "react/prop-types": "off", - - /* we dont target ancient browsers */ - "react/jsx-no-target-blank": "off" + "path-alias/no-relative": "error" } } diff --git a/package.json b/package.json index ec23a4cb0..da8fc449a 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,6 @@ "eslint": "^8.46.0", "eslint-import-resolver-alias": "^1.1.2", "eslint-plugin-path-alias": "^1.0.0", - "eslint-plugin-react": "^7.33.2", "eslint-plugin-simple-import-sort": "^10.0.0", "eslint-plugin-unused-imports": "^2.0.0", "highlight.js": "10.6.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2eb545560..be7befab3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -87,9 +87,6 @@ devDependencies: eslint-plugin-path-alias: specifier: ^1.0.0 version: 1.0.0(patch_hash=m6sma4g6bh67km3q6igf6uxaja)(eslint@8.46.0) - eslint-plugin-react: - specifier: ^7.33.2 - version: 7.33.2(eslint@8.46.0) eslint-plugin-simple-import-sort: specifier: ^10.0.0 version: 10.0.0(eslint@8.46.0) @@ -858,24 +855,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /array-buffer-byte-length@1.0.0: - resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} - dependencies: - call-bind: 1.0.5 - is-array-buffer: 3.0.2 - dev: true - - /array-includes@3.1.7: - resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.5 - define-properties: 1.2.1 - es-abstract: 1.22.3 - get-intrinsic: 1.2.2 - is-string: 1.0.7 - dev: true - /array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} @@ -886,49 +865,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /array.prototype.flat@1.3.2: - resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.5 - define-properties: 1.2.1 - es-abstract: 1.22.3 - es-shim-unscopables: 1.0.2 - dev: true - - /array.prototype.flatmap@1.3.2: - resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.5 - define-properties: 1.2.1 - es-abstract: 1.22.3 - es-shim-unscopables: 1.0.2 - dev: true - - /array.prototype.tosorted@1.1.2: - resolution: {integrity: sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==} - dependencies: - call-bind: 1.0.5 - define-properties: 1.2.1 - es-abstract: 1.22.3 - es-shim-unscopables: 1.0.2 - get-intrinsic: 1.2.2 - dev: true - - /arraybuffer.prototype.slice@1.0.2: - resolution: {integrity: sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==} - engines: {node: '>= 0.4'} - dependencies: - array-buffer-byte-length: 1.0.0 - call-bind: 1.0.5 - define-properties: 1.2.1 - es-abstract: 1.22.3 - get-intrinsic: 1.2.2 - is-array-buffer: 3.0.2 - is-shared-array-buffer: 1.0.2 - dev: true - /arrify@1.0.1: resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} engines: {node: '>=0.10.0'} @@ -948,23 +884,12 @@ packages: resolution: {integrity: sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==} dev: true - /asynciterator.prototype@1.0.0: - resolution: {integrity: sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==} - dependencies: - has-symbols: 1.0.3 - dev: true - /atob@2.1.2: resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} engines: {node: '>= 4.5.0'} hasBin: true dev: true - /available-typed-arrays@1.0.5: - resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} - engines: {node: '>= 0.4'} - dev: true - /balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true @@ -1042,14 +967,6 @@ packages: unset-value: 1.0.0 dev: true - /call-bind@1.0.5: - resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==} - dependencies: - function-bind: 1.1.2 - get-intrinsic: 1.2.2 - set-function-length: 1.1.1 - dev: true - /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -1259,24 +1176,6 @@ packages: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true - /define-data-property@1.1.1: - resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==} - engines: {node: '>= 0.4'} - dependencies: - get-intrinsic: 1.2.2 - gopd: 1.0.1 - has-property-descriptors: 1.0.1 - dev: true - - /define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} - dependencies: - define-data-property: 1.1.1 - has-property-descriptors: 1.0.1 - object-keys: 1.1.1 - dev: true - /define-property@0.2.5: resolution: {integrity: sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==} engines: {node: '>=0.10.0'} @@ -1322,13 +1221,6 @@ packages: moment: 2.29.4 dev: true - /doctrine@2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} - engines: {node: '>=0.10.0'} - dependencies: - esutils: 2.0.3 - dev: true - /doctrine@3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} @@ -1352,94 +1244,6 @@ packages: is-arrayish: 0.2.1 dev: true - /es-abstract@1.22.3: - resolution: {integrity: sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==} - engines: {node: '>= 0.4'} - dependencies: - array-buffer-byte-length: 1.0.0 - arraybuffer.prototype.slice: 1.0.2 - available-typed-arrays: 1.0.5 - call-bind: 1.0.5 - es-set-tostringtag: 2.0.2 - es-to-primitive: 1.2.1 - function.prototype.name: 1.1.6 - get-intrinsic: 1.2.2 - get-symbol-description: 1.0.0 - globalthis: 1.0.3 - gopd: 1.0.1 - has-property-descriptors: 1.0.1 - has-proto: 1.0.1 - has-symbols: 1.0.3 - hasown: 2.0.0 - internal-slot: 1.0.6 - is-array-buffer: 3.0.2 - is-callable: 1.2.7 - is-negative-zero: 2.0.2 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.2 - is-string: 1.0.7 - is-typed-array: 1.1.12 - is-weakref: 1.0.2 - object-inspect: 1.13.1 - object-keys: 1.1.1 - object.assign: 4.1.4 - regexp.prototype.flags: 1.5.1 - safe-array-concat: 1.0.1 - safe-regex-test: 1.0.0 - string.prototype.trim: 1.2.8 - string.prototype.trimend: 1.0.7 - string.prototype.trimstart: 1.0.7 - typed-array-buffer: 1.0.0 - typed-array-byte-length: 1.0.0 - typed-array-byte-offset: 1.0.0 - typed-array-length: 1.0.4 - unbox-primitive: 1.0.2 - which-typed-array: 1.1.13 - dev: true - - /es-iterator-helpers@1.0.15: - resolution: {integrity: sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==} - dependencies: - asynciterator.prototype: 1.0.0 - call-bind: 1.0.5 - define-properties: 1.2.1 - es-abstract: 1.22.3 - es-set-tostringtag: 2.0.2 - function-bind: 1.1.1 - get-intrinsic: 1.2.2 - globalthis: 1.0.3 - has-property-descriptors: 1.0.1 - has-proto: 1.0.1 - has-symbols: 1.0.3 - internal-slot: 1.0.6 - iterator.prototype: 1.1.2 - safe-array-concat: 1.0.1 - dev: true - - /es-set-tostringtag@2.0.2: - resolution: {integrity: sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==} - engines: {node: '>= 0.4'} - dependencies: - get-intrinsic: 1.2.2 - has-tostringtag: 1.0.0 - hasown: 2.0.0 - dev: true - - /es-shim-unscopables@1.0.2: - resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} - dependencies: - hasown: 2.0.0 - dev: true - - /es-to-primitive@1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} - engines: {node: '>= 0.4'} - dependencies: - is-callable: 1.2.7 - is-date-object: 1.0.5 - is-symbol: 1.0.4 - dev: true - /esbuild-android-64@0.15.18: resolution: {integrity: sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==} engines: {node: '>=12'} @@ -1720,34 +1524,6 @@ packages: dev: true patched: true - /eslint-plugin-react@7.33.2(eslint@8.46.0): - resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==} - engines: {node: '>=4'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - peerDependenciesMeta: - eslint: - optional: true - dependencies: - array-includes: 3.1.7 - array.prototype.flatmap: 1.3.2 - array.prototype.tosorted: 1.1.2 - doctrine: 2.1.0 - es-iterator-helpers: 1.0.15 - eslint: 8.46.0(patch_hash=xm46kqcmdgzlmm4aifkfpxaho4) - estraverse: 5.3.0 - jsx-ast-utils: 3.3.5 - minimatch: 3.1.2 - object.entries: 1.1.7 - object.fromentries: 2.0.7 - object.hasown: 1.1.3 - object.values: 1.1.7 - prop-types: 15.8.1 - resolve: 2.0.0-next.5 - semver: 6.3.1 - string.prototype.matchall: 4.0.10 - dev: true - /eslint-plugin-simple-header@1.0.2: resolution: {integrity: sha512-K1EJ/ueBIjPRA8qR44Ymo+GDmPYYmfoODtainGxVr7PSbX6QiaY+pTuGCrOhO+AtVsYJs8GLSVdGUTXyAxAtOA==} dev: false @@ -2015,12 +1791,6 @@ packages: resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} dev: true - /for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - dependencies: - is-callable: 1.2.7 - dev: true - /for-in@1.0.2: resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==} engines: {node: '>=0.10.0'} @@ -2053,38 +1823,11 @@ packages: resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} dev: true - /function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - dev: true - - /function.prototype.name@1.1.6: - resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.5 - define-properties: 1.2.1 - es-abstract: 1.22.3 - functions-have-names: 1.2.3 - dev: true - - /functions-have-names@1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - dev: true - /get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} dev: true - /get-intrinsic@1.2.2: - resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==} - dependencies: - function-bind: 1.1.2 - has-proto: 1.0.1 - has-symbols: 1.0.3 - hasown: 2.0.0 - dev: true - /get-stream@5.2.0: resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} engines: {node: '>=8'} @@ -2092,14 +1835,6 @@ packages: pump: 3.0.0 dev: true - /get-symbol-description@1.0.0: - resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.5 - get-intrinsic: 1.2.2 - dev: true - /get-tsconfig@4.5.0: resolution: {integrity: sha512-MjhiaIWCJ1sAU4pIQ5i5OfOuHHxVo1oYeNsWTON7jxYkod8pHocXeh+SSbmu5OZZZK73B6cbJ2XADzXehLyovQ==} dev: true @@ -2157,13 +1892,6 @@ packages: type-fest: 0.20.2 dev: true - /globalthis@1.0.3: - resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} - engines: {node: '>= 0.4'} - dependencies: - define-properties: 1.2.1 - dev: true - /globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -2180,12 +1908,6 @@ packages: resolution: {integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==} dev: true - /gopd@1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} - dependencies: - get-intrinsic: 1.2.2 - dev: true - /graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} dev: true @@ -2203,10 +1925,6 @@ packages: engines: {node: '>=6'} dev: true - /has-bigints@1.0.2: - resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} - dev: true - /has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} @@ -2217,29 +1935,6 @@ packages: engines: {node: '>=8'} dev: true - /has-property-descriptors@1.0.1: - resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==} - dependencies: - get-intrinsic: 1.2.2 - dev: true - - /has-proto@1.0.1: - resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} - engines: {node: '>= 0.4'} - dev: true - - /has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} - engines: {node: '>= 0.4'} - dev: true - - /has-tostringtag@1.0.0: - resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} - engines: {node: '>= 0.4'} - dependencies: - has-symbols: 1.0.3 - dev: true - /has-value@0.3.1: resolution: {integrity: sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==} engines: {node: '>=0.10.0'} @@ -2278,13 +1973,6 @@ packages: function-bind: 1.1.1 dev: true - /hasown@2.0.0: - resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==} - engines: {node: '>= 0.4'} - dependencies: - function-bind: 1.1.2 - dev: true - /highlight.js@10.6.0: resolution: {integrity: sha512-8mlRcn5vk/r4+QcqerapwBYTe+iPL5ih6xrNylxrnBdHQiijDETfXX7VIxC3UiCRiINBJfANBAsPzAvRQj8RpQ==} dev: true @@ -2362,15 +2050,6 @@ packages: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} dev: true - /internal-slot@1.0.6: - resolution: {integrity: sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==} - engines: {node: '>= 0.4'} - dependencies: - get-intrinsic: 1.2.2 - hasown: 2.0.0 - side-channel: 1.0.4 - dev: true - /is-accessor-descriptor@0.1.6: resolution: {integrity: sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==} engines: {node: '>=0.10.0'} @@ -2385,60 +2064,20 @@ packages: kind-of: 6.0.3 dev: true - /is-array-buffer@3.0.2: - resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} - dependencies: - call-bind: 1.0.5 - get-intrinsic: 1.2.2 - is-typed-array: 1.1.12 - dev: true - /is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} dev: true - /is-async-function@2.0.0: - resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} - engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.0 - dev: true - - /is-bigint@1.0.4: - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} - dependencies: - has-bigints: 1.0.2 - dev: true - - /is-boolean-object@1.1.2: - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.5 - has-tostringtag: 1.0.0 - dev: true - /is-buffer@1.1.6: resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} dev: true - /is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - dev: true - /is-core-module@2.12.0: resolution: {integrity: sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==} dependencies: has: 1.0.3 dev: true - /is-core-module@2.13.1: - resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==} - dependencies: - hasown: 2.0.0 - dev: true - /is-data-descriptor@0.1.4: resolution: {integrity: sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==} engines: {node: '>=0.10.0'} @@ -2453,13 +2092,6 @@ packages: kind-of: 6.0.3 dev: true - /is-date-object@1.0.5: - resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} - engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.0 - dev: true - /is-descriptor@0.1.6: resolution: {integrity: sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==} engines: {node: '>=0.10.0'} @@ -2495,24 +2127,11 @@ packages: engines: {node: '>=0.10.0'} dev: true - /is-finalizationregistry@1.0.2: - resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} - dependencies: - call-bind: 1.0.5 - dev: true - /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} dev: true - /is-generator-function@1.0.10: - resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} - engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.0 - dev: true - /is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -2520,22 +2139,6 @@ packages: is-extglob: 2.1.1 dev: true - /is-map@2.0.2: - resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} - dev: true - - /is-negative-zero@2.0.2: - resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} - engines: {node: '>= 0.4'} - dev: true - - /is-number-object@1.0.7: - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} - engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.0 - dev: true - /is-number@3.0.0: resolution: {integrity: sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==} engines: {node: '>=0.10.0'} @@ -2570,62 +2173,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /is-regex@1.1.4: - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.5 - has-tostringtag: 1.0.0 - dev: true - - /is-set@2.0.2: - resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} - dev: true - - /is-shared-array-buffer@1.0.2: - resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} - dependencies: - call-bind: 1.0.5 - dev: true - - /is-string@1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} - engines: {node: '>= 0.4'} - dependencies: - has-tostringtag: 1.0.0 - dev: true - - /is-symbol@1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} - engines: {node: '>= 0.4'} - dependencies: - has-symbols: 1.0.3 - dev: true - - /is-typed-array@1.1.12: - resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==} - engines: {node: '>= 0.4'} - dependencies: - which-typed-array: 1.1.13 - dev: true - - /is-weakmap@2.0.1: - resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} - dev: true - - /is-weakref@1.0.2: - resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} - dependencies: - call-bind: 1.0.5 - dev: true - - /is-weakset@2.0.2: - resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==} - dependencies: - call-bind: 1.0.5 - get-intrinsic: 1.2.2 - dev: true - /is-windows@1.0.2: resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} engines: {node: '>=0.10.0'} @@ -2635,10 +2182,6 @@ packages: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} dev: true - /isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - dev: true - /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true @@ -2655,16 +2198,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /iterator.prototype@1.1.2: - resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} - dependencies: - define-properties: 1.2.1 - get-intrinsic: 1.2.2 - has-symbols: 1.0.3 - reflect.getprototypeof: 1.0.4 - set-function-name: 2.0.1 - dev: true - /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} dev: true @@ -2696,16 +2229,6 @@ packages: resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} dev: false - /jsx-ast-utils@3.3.5: - resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} - engines: {node: '>=4.0'} - dependencies: - array-includes: 3.1.7 - array.prototype.flat: 1.3.2 - object.assign: 4.1.4 - object.values: 1.1.7 - dev: true - /jszip@2.7.0: resolution: {integrity: sha512-JIsRKRVC3gTRo2vM4Wy9WBC3TRcfnIZU8k65Phi3izkvPH975FowRYtKGT6PxevA0XnJ/yO8b0QwV0ydVyQwfw==} dependencies: @@ -2774,13 +2297,6 @@ packages: resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} dev: true - /loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true - dependencies: - js-tokens: 4.0.0 - dev: true - /lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -2976,11 +2492,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - dev: true - /object-copy@0.1.0: resolution: {integrity: sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==} engines: {node: '>=0.10.0'} @@ -2990,15 +2501,6 @@ packages: kind-of: 3.2.2 dev: true - /object-inspect@1.13.1: - resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} - dev: true - - /object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - dev: true - /object-visit@1.0.1: resolution: {integrity: sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==} engines: {node: '>=0.10.0'} @@ -3006,41 +2508,6 @@ packages: isobject: 3.0.1 dev: true - /object.assign@4.1.4: - resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.5 - define-properties: 1.2.1 - has-symbols: 1.0.3 - object-keys: 1.1.1 - dev: true - - /object.entries@1.1.7: - resolution: {integrity: sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.5 - define-properties: 1.2.1 - es-abstract: 1.22.3 - dev: true - - /object.fromentries@2.0.7: - resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.5 - define-properties: 1.2.1 - es-abstract: 1.22.3 - dev: true - - /object.hasown@1.1.3: - resolution: {integrity: sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==} - dependencies: - define-properties: 1.2.1 - es-abstract: 1.22.3 - dev: true - /object.pick@1.3.0: resolution: {integrity: sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==} engines: {node: '>=0.10.0'} @@ -3048,15 +2515,6 @@ packages: isobject: 3.0.1 dev: true - /object.values@1.1.7: - resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.5 - define-properties: 1.2.1 - es-abstract: 1.22.3 - dev: true - /once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: @@ -3219,14 +2677,6 @@ packages: engines: {node: '>=0.4.0'} dev: true - /prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - react-is: 16.13.1 - dev: true - /proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} dev: true @@ -3285,10 +2735,6 @@ packages: engines: {node: '>=8'} dev: true - /react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - dev: true - /read-pkg-up@7.0.1: resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} engines: {node: '>=8'} @@ -3325,18 +2771,6 @@ packages: strip-indent: 3.0.0 dev: true - /reflect.getprototypeof@1.0.4: - resolution: {integrity: sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.5 - define-properties: 1.2.1 - es-abstract: 1.22.3 - get-intrinsic: 1.2.2 - globalthis: 1.0.3 - which-builtin-type: 1.1.3 - dev: true - /regex-not@1.0.2: resolution: {integrity: sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==} engines: {node: '>=0.10.0'} @@ -3345,15 +2779,6 @@ packages: safe-regex: 1.1.0 dev: true - /regexp.prototype.flags@1.5.1: - resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.5 - define-properties: 1.2.1 - set-function-name: 2.0.1 - dev: true - /require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -3388,15 +2813,6 @@ packages: supports-preserve-symlinks-flag: 1.0.0 dev: true - /resolve@2.0.0-next.5: - resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} - hasBin: true - dependencies: - is-core-module: 2.13.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - dev: true - /ret@0.1.15: resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} engines: {node: '>=0.12'} @@ -3420,28 +2836,10 @@ packages: queue-microtask: 1.2.3 dev: true - /safe-array-concat@1.0.1: - resolution: {integrity: sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==} - engines: {node: '>=0.4'} - dependencies: - call-bind: 1.0.5 - get-intrinsic: 1.2.2 - has-symbols: 1.0.3 - isarray: 2.0.5 - dev: true - /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} dev: true - /safe-regex-test@1.0.0: - resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} - dependencies: - call-bind: 1.0.5 - get-intrinsic: 1.2.2 - is-regex: 1.1.4 - dev: true - /safe-regex@1.1.0: resolution: {integrity: sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==} dependencies: @@ -3453,11 +2851,6 @@ packages: hasBin: true dev: true - /semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - dev: true - /semver@7.5.0: resolution: {integrity: sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==} engines: {node: '>=10'} @@ -3466,25 +2859,6 @@ packages: lru-cache: 6.0.0 dev: true - /set-function-length@1.1.1: - resolution: {integrity: sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==} - engines: {node: '>= 0.4'} - dependencies: - define-data-property: 1.1.1 - get-intrinsic: 1.2.2 - gopd: 1.0.1 - has-property-descriptors: 1.0.1 - dev: true - - /set-function-name@2.0.1: - resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==} - engines: {node: '>= 0.4'} - dependencies: - define-data-property: 1.1.1 - functions-have-names: 1.2.3 - has-property-descriptors: 1.0.1 - dev: true - /set-value@2.0.1: resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==} engines: {node: '>=0.10.0'} @@ -3507,14 +2881,6 @@ packages: engines: {node: '>=8'} dev: true - /side-channel@1.0.4: - resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} - dependencies: - call-bind: 1.0.5 - get-intrinsic: 1.2.2 - object-inspect: 1.13.1 - dev: true - /signal-exit@4.0.1: resolution: {integrity: sha512-uUWsN4aOxJAS8KOuf3QMyFtgm1pkb6I+KRZbRF/ghdf5T7sM+B1lLLzPDxswUjkmHyxQAVzEgG35E3NzDM9GVw==} engines: {node: '>=14'} @@ -3640,45 +3006,6 @@ packages: strip-ansi: 6.0.1 dev: true - /string.prototype.matchall@4.0.10: - resolution: {integrity: sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==} - dependencies: - call-bind: 1.0.5 - define-properties: 1.2.1 - es-abstract: 1.22.3 - get-intrinsic: 1.2.2 - has-symbols: 1.0.3 - internal-slot: 1.0.6 - regexp.prototype.flags: 1.5.1 - set-function-name: 2.0.1 - side-channel: 1.0.4 - dev: true - - /string.prototype.trim@1.2.8: - resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.5 - define-properties: 1.2.1 - es-abstract: 1.22.3 - dev: true - - /string.prototype.trimend@1.0.7: - resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==} - dependencies: - call-bind: 1.0.5 - define-properties: 1.2.1 - es-abstract: 1.22.3 - dev: true - - /string.prototype.trimstart@1.0.7: - resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} - dependencies: - call-bind: 1.0.5 - define-properties: 1.2.1 - es-abstract: 1.22.3 - dev: true - /string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} dependencies: @@ -3936,59 +3263,12 @@ packages: engines: {node: '>=14.16'} dev: true - /typed-array-buffer@1.0.0: - resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.5 - get-intrinsic: 1.2.2 - is-typed-array: 1.1.12 - dev: true - - /typed-array-byte-length@1.0.0: - resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.5 - for-each: 0.3.3 - has-proto: 1.0.1 - is-typed-array: 1.1.12 - dev: true - - /typed-array-byte-offset@1.0.0: - resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==} - engines: {node: '>= 0.4'} - dependencies: - available-typed-arrays: 1.0.5 - call-bind: 1.0.5 - for-each: 0.3.3 - has-proto: 1.0.1 - is-typed-array: 1.1.12 - dev: true - - /typed-array-length@1.0.4: - resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} - dependencies: - call-bind: 1.0.5 - for-each: 0.3.3 - is-typed-array: 1.1.12 - dev: true - /typescript@5.0.4: resolution: {integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==} engines: {node: '>=12.20'} hasBin: true dev: true - /unbox-primitive@1.0.2: - resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} - dependencies: - call-bind: 1.0.5 - has-bigints: 1.0.2 - has-symbols: 1.0.3 - which-boxed-primitive: 1.0.2 - dev: true - /unbzip2-stream@1.4.3: resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} dependencies: @@ -4068,54 +3348,6 @@ packages: webidl-conversions: 3.0.1 dev: true - /which-boxed-primitive@1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} - dependencies: - is-bigint: 1.0.4 - is-boolean-object: 1.1.2 - is-number-object: 1.0.7 - is-string: 1.0.7 - is-symbol: 1.0.4 - dev: true - - /which-builtin-type@1.1.3: - resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} - engines: {node: '>= 0.4'} - dependencies: - function.prototype.name: 1.1.6 - has-tostringtag: 1.0.0 - is-async-function: 2.0.0 - is-date-object: 1.0.5 - is-finalizationregistry: 1.0.2 - is-generator-function: 1.0.10 - is-regex: 1.1.4 - is-weakref: 1.0.2 - isarray: 2.0.5 - which-boxed-primitive: 1.0.2 - which-collection: 1.0.1 - which-typed-array: 1.1.13 - dev: true - - /which-collection@1.0.1: - resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} - dependencies: - is-map: 2.0.2 - is-set: 2.0.2 - is-weakmap: 2.0.1 - is-weakset: 2.0.2 - dev: true - - /which-typed-array@1.1.13: - resolution: {integrity: sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==} - engines: {node: '>= 0.4'} - dependencies: - available-typed-arrays: 1.0.5 - call-bind: 1.0.5 - for-each: 0.3.3 - gopd: 1.0.1 - has-tostringtag: 1.0.0 - dev: true - /which@1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} hasBin: true diff --git a/src/api/Notifications/NotificationComponent.tsx b/src/api/Notifications/NotificationComponent.tsx index 71c40c736..caa4b64ef 100644 --- a/src/api/Notifications/NotificationComponent.tsx +++ b/src/api/Notifications/NotificationComponent.tsx @@ -21,7 +21,7 @@ import "./styles.css"; import { useSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { classes } from "@utils/misc"; -import { useEffect, useMemo, useState, useStateFromStores, WindowStore } from "@webpack/common"; +import { React, useEffect, useMemo, useState, useStateFromStores, WindowStore } from "@webpack/common"; import { NotificationData } from "./Notifications"; diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx index 02e0e939b..ea2e02b51 100644 --- a/src/components/ErrorBoundary.tsx +++ b/src/components/ErrorBoundary.tsx @@ -80,7 +80,10 @@ const ErrorBoundary = LazyComponent(() => { if (this.props.noop) return null; if (this.props.fallback) - return {this.props.children}; + return ; const msg = this.props.message || "An error occurred while rendering this Component. More info can be found below and in your console."; diff --git a/src/components/Heart.tsx b/src/components/Heart.tsx index 017b41645..b33b83643 100644 --- a/src/components/Heart.tsx +++ b/src/components/Heart.tsx @@ -27,7 +27,7 @@ export function Heart() { > diff --git a/src/components/Icons.tsx b/src/components/Icons.tsx index 01012d1fc..93b1323e7 100644 --- a/src/components/Icons.tsx +++ b/src/components/Icons.tsx @@ -58,7 +58,7 @@ export function LinkIcon({ height = 24, width = 24, className }: IconProps) { className={classes(className, "vc-link-icon")} viewBox="0 0 24 24" > - + diff --git a/src/components/PluginSettings/index.tsx b/src/components/PluginSettings/index.tsx index 19e9fb3f4..fe111fa9d 100644 --- a/src/components/PluginSettings/index.tsx +++ b/src/components/PluginSettings/index.tsx @@ -30,7 +30,7 @@ import { ChangeList } from "@utils/ChangeList"; import { Logger } from "@utils/Logger"; import { Margins } from "@utils/margins"; import { classes, isObjectEmpty } from "@utils/misc"; -import { openModal } from "@utils/modal"; +import { openModalLazy } from "@utils/modal"; import { useAwaiter } from "@utils/react"; import { Plugin } from "@utils/types"; import { findByPropsLazy } from "@webpack"; @@ -95,14 +95,12 @@ export function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, on const isEnabled = () => settings.enabled ?? false; - function openPluginModal() { - openModal(modalProps => ( - onRestartNeeded(plugin.name)} - /> - )); + function openModal() { + openModalLazy(async () => { + return modalProps => { + return onRestartNeeded(plugin.name)} />; + }; + }); } function toggleEnabled() { @@ -161,7 +159,7 @@ export function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, on onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} infoButton={ -
{Object.entries(PermissionsBits).map(([permissionName, bit]) => ( -
+
{(() => { const { permissions, overwriteAllow, overwriteDeny } = selectedItem; diff --git a/src/plugins/permissionsViewer/components/UserPermissions.tsx b/src/plugins/permissionsViewer/components/UserPermissions.tsx index 61d4dcb51..37d9d0f42 100644 --- a/src/plugins/permissionsViewer/components/UserPermissions.tsx +++ b/src/plugins/permissionsViewer/components/UserPermissions.tsx @@ -109,7 +109,7 @@ function UserPermissionsComponent({ guild, guildMember }: { guild: Guild; guildM } defaultState={settings.store.defaultPermissionsDropdownState} buttons={[ - ( + ( {tooltipProps => ( diff --git a/src/plugins/spotifyControls/PlayerComponent.tsx b/src/plugins/spotifyControls/PlayerComponent.tsx index 64ee52fc1..43c499ab7 100644 --- a/src/plugins/spotifyControls/PlayerComponent.tsx +++ b/src/plugins/spotifyControls/PlayerComponent.tsx @@ -38,21 +38,19 @@ function msToHuman(ms: number) { } function Svg(path: string, label: string) { - return function Icon() { - return ( - - - - ); - }; + return () => ( + + + + ); } // KraXen's icons :yesyes: diff --git a/src/plugins/startupTimings/StartupTimingPage.tsx b/src/plugins/startupTimings/StartupTimingPage.tsx index 0950b0688..c8cf51da2 100644 --- a/src/plugins/startupTimings/StartupTimingPage.tsx +++ b/src/plugins/startupTimings/StartupTimingPage.tsx @@ -120,8 +120,8 @@ function ServerTrace({ trace }: ServerTraceProps) { - {lines.map((line, i) => ( - {line} + {lines.map(line => ( + {line} ))} diff --git a/src/plugins/typingTweaks/index.tsx b/src/plugins/typingTweaks/index.tsx index ccb863ce7..62bcd41f6 100644 --- a/src/plugins/typingTweaks/index.tsx +++ b/src/plugins/typingTweaks/index.tsx @@ -21,7 +21,7 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { openUserProfile } from "@utils/discord"; import definePlugin, { OptionType } from "@utils/types"; -import { Avatar, GuildMemberStore, RelationshipStore } from "@webpack/common"; +import { Avatar, GuildMemberStore, React, RelationshipStore } from "@webpack/common"; import { User } from "discord-types/general"; const settings = definePluginSettings({ @@ -135,7 +135,7 @@ export default definePlugin({ return children.map(c => c.type === "strong" - ? + ? : c ); } diff --git a/src/utils/react.tsx b/src/utils/react.tsx index 43a4d121e..0181c95b8 100644 --- a/src/utils/react.tsx +++ b/src/utils/react.tsx @@ -133,7 +133,7 @@ export function useForceUpdater(withDep?: true) { export function LazyComponent(factory: () => React.ComponentType, attempts = 5) { const get = makeLazy(factory, attempts); - return function Lazy(props: T) { + return (props: T) => { const Component = get() ?? NoopComponent; return ; }; From 9af2ec65ae5ebcfabedbc3fc703b25868ed0d65b Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 1 Nov 2023 02:09:43 +0100 Subject: [PATCH 145/632] OnePingPerDM: fix server pings --- src/plugins/onePingPerDM/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/plugins/onePingPerDM/index.ts b/src/plugins/onePingPerDM/index.ts index 37ca87125..ae38343db 100644 --- a/src/plugins/onePingPerDM/index.ts +++ b/src/plugins/onePingPerDM/index.ts @@ -55,10 +55,8 @@ export default definePlugin({ }], isPrivateChannelRead(message: MessageJSON) { const channelType = ChannelStore.getChannel(message.channel_id)?.type; - if (channelType !== ChannelType.DM && channelType !== ChannelType.GROUP_DM) { - return false; - } if ( + (channelType !== ChannelType.DM && channelType !== ChannelType.GROUP_DM) || (channelType === ChannelType.DM && settings.store.channelToAffect === "group_dm") || (channelType === ChannelType.GROUP_DM && settings.store.channelToAffect === "user_dm") || (settings.store.allowMentions && message.mentions.some(m => m.id === UserStore.getCurrentUser().id)) || From 9d78233afad79402b1422c484f4d233fd9a60540 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sat, 28 Oct 2023 20:32:53 -0300 Subject: [PATCH 146/632] Fix displaying BetterFolders sidebar when watching streams in fullscreen --- src/plugins/betterFolders/FolderSideBar.tsx | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/plugins/betterFolders/FolderSideBar.tsx b/src/plugins/betterFolders/FolderSideBar.tsx index 5504e1d56..97959873f 100644 --- a/src/plugins/betterFolders/FolderSideBar.tsx +++ b/src/plugins/betterFolders/FolderSideBar.tsx @@ -18,16 +18,19 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { LazyComponent } from "@utils/react"; -import { find, findByPropsLazy } from "@webpack"; -import { React, useStateFromStores } from "@webpack/common"; +import { find, findByPropsLazy, findStoreLazy } from "@webpack"; +import { useStateFromStores } from "@webpack/common"; +import type { CSSProperties } from "react"; import { ExpandedGuildFolderStore, settings } from "."; +const ChannelRTCStore = findStoreLazy("ChannelRTCStore"); const Animations = findByPropsLazy("a", "animated", "useTransition"); const GuildsBar = LazyComponent(() => find(m => m.type?.toString().includes('("guildsnav")'))); export default ErrorBoundary.wrap(guildsBarProps => { const expandedFolders = useStateFromStores([ExpandedGuildFolderStore], () => ExpandedGuildFolderStore.getExpandedFolders()); + const isFullscreen = useStateFromStores([ChannelRTCStore], () => ChannelRTCStore.isFullscreenInContext()); const Sidebar = ( { const visible = !!expandedFolders.size; const guilds = document.querySelector(guildsBarProps.className.split(" ").map(c => `.${c}`).join("")); + // We need to display none if we are in fullscreen. Yes this seems horrible doing with css, but it's literally how Discord does it. + // Also display flex otherwise to fix scrolling + const barStyle = { + display: isFullscreen ? "none" : "flex", + } as CSSProperties; + if (!guilds || !settings.store.sidebarAnim) { return visible - ?
{Sidebar}
+ ?
{Sidebar}
: null; } @@ -54,9 +63,9 @@ export default ErrorBoundary.wrap(guildsBarProps => { leave={{ width: 0 }} config={{ duration: 200 }} > - {(style, show) => + {(animationStyle, show) => show && ( - + {Sidebar} ) From 56a9d79f856fd1bffd703e8723b5c1dfb6de6be1 Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Wed, 1 Nov 2023 08:14:32 +0700 Subject: [PATCH 147/632] PiP: fix issues / styles (#1927) --- src/plugins/pictureInPicture/index.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/plugins/pictureInPicture/index.tsx b/src/plugins/pictureInPicture/index.tsx index 4b6f87970..ba4aa8387 100644 --- a/src/plugins/pictureInPicture/index.tsx +++ b/src/plugins/pictureInPicture/index.tsx @@ -4,6 +4,8 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +import "./styles.css"; + import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; @@ -28,8 +30,8 @@ export default definePlugin({ { find: ".nonMediaAttachment]", replacement: { - match: /\.nonMediaAttachment\].{0,10}children:\[(\S)/, - replace: "$&,$1&&$self.renderPiPButton()," + match: /\.nonMediaAttachment\]:!(\i).{0,10}children:\[(\S)/, + replace: "$&,$1&&$2&&$self.renderPiPButton()," }, }, ], @@ -40,6 +42,7 @@ export default definePlugin({ {tooltipProps => (
From e37f62ac0a8d93bd354f09567e3e19dd7e8b7d63 Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Wed, 1 Nov 2023 08:19:02 +0700 Subject: [PATCH 148/632] imageZoom: dont close carousel modal on image click (#1926) Co-authored-by: Vendicated --- src/plugins/imageZoom/index.tsx | 7 +++++++ src/plugins/imageZoom/styles.css | 18 ++++++++---------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/plugins/imageZoom/index.tsx b/src/plugins/imageZoom/index.tsx index 08f2e29d3..75c944ebd 100644 --- a/src/plugins/imageZoom/index.tsx +++ b/src/plugins/imageZoom/index.tsx @@ -186,6 +186,13 @@ export default definePlugin({ } ] }, + { + find: ".carouselModal", + replacement: { + match: /(?<=\.carouselModal.{0,100}onClick:)\i,/, + replace: "()=>{}," + } + } ], settings, diff --git a/src/plugins/imageZoom/styles.css b/src/plugins/imageZoom/styles.css index 09c3c8580..51e225c05 100644 --- a/src/plugins/imageZoom/styles.css +++ b/src/plugins/imageZoom/styles.css @@ -15,19 +15,17 @@ border-radius: 0; } -.vc-imgzoom-nearest-neighbor > img { - image-rendering: pixelated; /* https://googlechrome.github.io/samples/image-rendering-pixelated/index.html */ +.vc-imgzoom-nearest-neighbor>img { + image-rendering: pixelated; + + /* https://googlechrome.github.io/samples/image-rendering-pixelated/index.html */ } /* make the carousel take up less space so we can click the backdrop and exit out of it */ -[class|="carouselModal"] { - height: fit-content; - box-shadow: none; +[class*="modalCarouselWrapper_"] { + top: 0 !important; } -[class|="wrapper"]:has(> #vc-imgzoom-magnify-modal) { - position: absolute; - left: 50%; - top: 50%; - transform: translate(-50%, -50%); +[class*="carouselModal_"] { + height: 0 !important; } From 9de6c2d4ff17ccf9a2a205b00cccc534692a6c51 Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Wed, 1 Nov 2023 08:19:26 +0700 Subject: [PATCH 149/632] previewMessage: fix button (#1919) --- src/plugins/previewMessage/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/previewMessage/index.tsx b/src/plugins/previewMessage/index.tsx index bc6751141..f2634ae6b 100644 --- a/src/plugins/previewMessage/index.tsx +++ b/src/plugins/previewMessage/index.tsx @@ -137,5 +137,5 @@ export default definePlugin({ }, ], - previewIcon: ErrorBoundary.wrap(PreviewButton, { noop: true }), + chatBarIcon: ErrorBoundary.wrap(PreviewButton, { noop: true }), }); From 5dc0d06be16733e2d5a49b7b8ad8127fea9660ae Mon Sep 17 00:00:00 2001 From: Haruka <96925398+nakoyasha@users.noreply.github.com> Date: Wed, 1 Nov 2023 03:23:45 +0200 Subject: [PATCH 150/632] EmoteCloner: make the error toasts useful (#1938) Co-authored-by: Vendicated --- src/plugins/emoteCloner/index.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/plugins/emoteCloner/index.tsx b/src/plugins/emoteCloner/index.tsx index c44731152..219ce435f 100644 --- a/src/plugins/emoteCloner/index.tsx +++ b/src/plugins/emoteCloner/index.tsx @@ -155,10 +155,15 @@ async function doClone(guildId: string, data: Sticker | Emoji) { type: Toasts.Type.SUCCESS, id: Toasts.genId() }); - } catch (e) { + } catch (e: any) { + let message = "Something went wrong (check console!)"; + try { + message = JSON.parse(e.text).message; + } catch { } + new Logger("EmoteCloner").error("Failed to clone", data.name, "to", guildId, e); Toasts.show({ - message: "Oopsie something went wrong :( Check console!!!", + message: "Failed to clone: " + message, type: Toasts.Type.FAILURE, id: Toasts.genId() }); From dd61b0c9992c53958508104e3489b63954285121 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Wed, 1 Nov 2023 02:25:13 +0100 Subject: [PATCH 151/632] bump to v1.6.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index da8fc449a..fa463823c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.6.1", + "version": "1.6.2", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { From 9cf88d42324f3930f707cfdb74613e4b1fac9ff7 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 1 Nov 2023 22:46:06 -0300 Subject: [PATCH 152/632] Fix MessageDecorationsAPI --- src/plugins/_api/messageDecorations.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/_api/messageDecorations.ts b/src/plugins/_api/messageDecorations.ts index 1646ad645..b41ec0be9 100644 --- a/src/plugins/_api/messageDecorations.ts +++ b/src/plugins/_api/messageDecorations.ts @@ -27,8 +27,8 @@ export default definePlugin({ { find: '"Message Username"', replacement: { - match: /currentUserIsPremium:.{0,70}{children:\i(?=}\))/, - replace: "$&.concat(Vencord.Api.MessageDecorations.__addDecorationsToMessage(arguments[0]))" + match: /\.Messages\.GUILD_COMMUNICATION_DISABLED_BOTTOM_SHEET_TITLE.+?}\),\i(?=\])/, + replace: "$&,...Vencord.Api.MessageDecorations.__addDecorationsToMessage(arguments[0])" } } ], From 098da8c3fd95cdd1f299b86d0b919b001009a1df Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 1 Nov 2023 23:26:13 -0300 Subject: [PATCH 153/632] Fix ViewIcons --- src/plugins/viewIcons/index.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/plugins/viewIcons/index.tsx b/src/plugins/viewIcons/index.tsx index fd6b7b6bf..ccbc8ddca 100644 --- a/src/plugins/viewIcons/index.tsx +++ b/src/plugins/viewIcons/index.tsx @@ -65,7 +65,9 @@ const settings = definePluginSettings({ } }); -function openImage(url: string) { +function openImage(url?: string) { + if (!url) return; + const format = url.startsWith("/") ? "png" : settings.store.format; const u = new URL(url, window.location.href); @@ -171,12 +173,12 @@ export default definePlugin({ }, patches: [ - // Make pfps clickable + // Make pfps clickable and add pointer cursor { - find: "onAddFriend:function", + find: ".AVATAR_DECORATION_STATUS_ROUND_16;", replacement: { - match: /\{src:(\i)(?=,avatarDecoration)/, - replace: "{src:$1,onClick:()=>$self.openImage($1)" + match: /memo\(.{0,50}(?=let{statusColor:\i,status:\i,...\i}=(\i),)/, + replace: (m, props) => `${m}${props}.onClick=()=>$self.openImage(${props}.src);${props}.style={cursor:${props}.src?"pointer":void 0};` } }, // Make banners clickable From 27fffc8bc3fcb6bdca22704f268710f72cc94c4c Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 1 Nov 2023 23:28:52 -0300 Subject: [PATCH 154/632] Fix ShowMeYourName --- src/plugins/showMeYourName/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/showMeYourName/index.tsx b/src/plugins/showMeYourName/index.tsx index 6986f69f2..62d0645d2 100644 --- a/src/plugins/showMeYourName/index.tsx +++ b/src/plugins/showMeYourName/index.tsx @@ -47,7 +47,7 @@ export default definePlugin({ authors: [Devs.Rini, Devs.TheKodeToad], patches: [ { - find: '"Message Username"', + find: ".useCanSeeRemixBadge)", replacement: { match: /(?<=onContextMenu:\i,children:).*?\}/, replace: "$self.renderUsername(arguments[0])}" From 44b21394b317079721ec5ec48cc3d00bcfeda873 Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Fri, 3 Nov 2023 07:56:31 +0700 Subject: [PATCH 155/632] gifPaste: fix unable to use gif picker in profile customization (#1947) --- src/plugins/gifPaste/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/gifPaste/index.ts b/src/plugins/gifPaste/index.ts index 8c1303ba4..9ce88f9d2 100644 --- a/src/plugins/gifPaste/index.ts +++ b/src/plugins/gifPaste/index.ts @@ -33,8 +33,8 @@ export default definePlugin({ patches: [{ find: ".handleSelectGIF=", replacement: { - match: /\.handleSelectGIF=\i=>\{/, - replace: ".handleSelectGIF=function(gif){return $self.handleSelect(gif);" + match: /\.handleSelectGIF=(\i)=>\{/, + replace: ".handleSelectGIF=$1=>{if (!this.props.className) return $self.handleSelect($1);" } }], From fa9da2d693fb6dde3c973bf467a6e14674f9ea06 Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Fri, 3 Nov 2023 07:57:39 +0700 Subject: [PATCH 156/632] noMosaic: play video inline + optional media layout type (#1946) --- src/plugins/noMosaic/index.ts | 45 +++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/src/plugins/noMosaic/index.ts b/src/plugins/noMosaic/index.ts index 49343503d..7f9fad53c 100644 --- a/src/plugins/noMosaic/index.ts +++ b/src/plugins/noMosaic/index.ts @@ -4,28 +4,58 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +import { definePluginSettings } from "@api/Settings"; import { disableStyle, enableStyle } from "@api/Styles"; import { Devs } from "@utils/constants"; -import definePlugin from "@utils/types"; +import definePlugin, { OptionType } from "@utils/types"; import style from "./styles.css?managed"; +const settings = definePluginSettings({ + inlineVideo: { + description: "Play videos without carousel modal", + type: OptionType.BOOLEAN, + default: true, + restartNeeded: true + }, + mediaLayoutType: { + description: "Choose media layout type", + type: OptionType.SELECT, + restartNeeded: true, + options: [ + { label: "STATIC, render loading image but image isn't resposive, no problem unless discord window width is too small", value: "STATIC", default: true }, + { label: "RESPONSIVE, image is responsive but not render loading image, cause messages shift when loaded", value: "RESPONSIVE" }, + ] + } +}); + export default definePlugin({ name: "NoMosaic", authors: [Devs.AutumnVN], description: "Removes Discord new image mosaic", tags: ["image", "mosaic", "media"], + + settings, + patches: [ { find: ".oneByTwoLayoutThreeGrid", replacement: [{ match: /mediaLayoutType:\i\.\i\.MOSAIC/, - replace: 'mediaLayoutType:"RESPONSIVE"' + replace: "mediaLayoutType:$self.mediaLayoutType()", }, { match: /null!==\(\i=\i\.get\(\i\)\)&&void 0!==\i\?\i:"INVALID"/, replace: '"INVALID"', - },] + }] + }, + { + find: "renderAttachments(", + predicate: () => settings.store.inlineVideo, + replacement: { + match: /url:(\i)\.url\}\);return /, + replace: "$&$1.content_type?.startsWith('image/')&&" + } }, { find: "Messages.REMOVE_ATTACHMENT_TOOLTIP_TEXT", @@ -33,10 +63,17 @@ export default definePlugin({ match: /\i===\i\.\i\.MOSAIC/, replace: "true" } - }], + } + ], + + mediaLayoutType() { + return settings.store.mediaLayoutType; + }, + start() { enableStyle(style); }, + stop() { disableStyle(style); } From fcf2bdda70201c87585b2f3060f9b380e13938e3 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Fri, 3 Nov 2023 02:03:53 +0100 Subject: [PATCH 157/632] fix TypingTweaks --- src/plugins/typingTweaks/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/typingTweaks/index.tsx b/src/plugins/typingTweaks/index.tsx index 62bcd41f6..d3f4a1409 100644 --- a/src/plugins/typingTweaks/index.tsx +++ b/src/plugins/typingTweaks/index.tsx @@ -112,7 +112,7 @@ export default definePlugin({ { find: "getCooldownTextStyle", replacement: { - match: /(?<=(\i)\.length\?\i.\i\.Messages.THREE_USERS_TYPING\.format\({\i:(\i),\i:(\i),\i:\i}\):)\i\.\i\.Messages\.SEVERAL_USERS_TYPING/, + match: /(?<=(\i)\.length\?\i.\i\.Messages.THREE_USERS_TYPING\.format\({\i:(\i),(?:\i:)?(\i),\i:\i}\):)\i\.\i\.Messages\.SEVERAL_USERS_TYPING/, replace: (_, users, a, b) => `$self.buildSeveralUsers({ a: ${a}, b: ${b}, count: ${users}.length - 2 })` }, predicate: () => settings.store.alternativeFormatting From 7f73e133647fe612ca5b2e571062f768f47f6cda Mon Sep 17 00:00:00 2001 From: Lewis Crichton Date: Sat, 4 Nov 2023 17:41:38 +0000 Subject: [PATCH 158/632] docs: point people to advisories for security bugs (#1957) --- .github/ISSUE_TEMPLATE/bug_report.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 74b2a418b..d79f5e490 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -14,7 +14,8 @@ body: DO NOT USE THIS FORM, unless - you are a vencord contributor - you were given explicit permission to use this form by a moderator in our support server - - you are filing a security related report + + DO NOT USE THIS FORM FOR SECURITY RELATED ISSUES. [CREATE A SECURITY ADVISORY INSTEAD.](https://github.com/Vendicated/Vencord/security/advisories/new) - type: input id: discord From 37b9a6246081400091a815205cc63cb217535661 Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Sun, 5 Nov 2023 00:44:29 +0700 Subject: [PATCH 159/632] favGifSearch: fix search bar (#1955) --- src/plugins/favGifSearch/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/favGifSearch/index.tsx b/src/plugins/favGifSearch/index.tsx index 0cb5166e1..592d8f547 100644 --- a/src/plugins/favGifSearch/index.tsx +++ b/src/plugins/favGifSearch/index.tsx @@ -60,7 +60,7 @@ interface Instance { } -const containerClasses: { searchBar: string; } = findByPropsLazy("searchBar", "searchHeader", "searchInput"); +const containerClasses: { searchBar: string; } = findByPropsLazy("searchBar", "searchBarFullRow"); export const settings = definePluginSettings({ searchOption: { @@ -182,7 +182,7 @@ function SearchBar({ instance, SearchBarComponent }: { instance: Instance; Searc ref={ref} autoFocus={true} className={containerClasses.searchBar} - size={SearchBarComponent.Sizes.SMALL} + size={SearchBarComponent.Sizes.MEDIUM} onChange={onChange} onClear={() => { setQuery(""); From 77659be4f0d7e6cc8d643037cf4188d1255c143f Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Sun, 5 Nov 2023 00:44:53 +0700 Subject: [PATCH 160/632] fakeNitro: disallow emoji in add reaction (#1954) --- src/plugins/fakeNitro/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/fakeNitro/index.ts b/src/plugins/fakeNitro/index.ts index efec9a8b5..e4311106d 100644 --- a/src/plugins/fakeNitro/index.ts +++ b/src/plugins/fakeNitro/index.ts @@ -201,7 +201,7 @@ export default definePlugin({ predicate: () => settings.store.enableEmojiBypass, replacement: { match: /((?:canUseEmojisEverywhere|canUseAnimatedEmojis):function\(\i)\){(.+?\))(?=})/g, - replace: (_, rest, premiumCheck) => `${rest},fakeNitroIntention){${premiumCheck}||fakeNitroIntention!=null||[${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)` + replace: (_, rest, premiumCheck) => `${rest},fakeNitroIntention){${premiumCheck}||fakeNitroIntention==null||[${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)` } }, // Allow stickers to be sent everywhere From a257926609d9c88f44015ee0b06040be5936d85f Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Sun, 5 Nov 2023 00:45:17 +0700 Subject: [PATCH 161/632] customRpc: fix discord attachment link (#1949) --- src/plugins/customRPC/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/customRPC/index.tsx b/src/plugins/customRPC/index.tsx index 8ed741215..feed52fde 100644 --- a/src/plugins/customRPC/index.tsx +++ b/src/plugins/customRPC/index.tsx @@ -30,6 +30,7 @@ const ActivityClassName = findByPropsLazy("activity", "buttonColor"); const Colors = findByPropsLazy("profileColors"); async function getApplicationAsset(key: string): Promise { + if (/https?:\/\/(cdn|media)\.discordapp\.(com|net)\/attachments\//.test(key)) return "mp:" + key.replace(/https?:\/\/(cdn|media)\.discordapp\.(com|net)\//, ""); return (await ApplicationAssetUtils.fetchAssetIds(settings.store.appID!, [key]))[0]; } From 2c9793202d1e9538736ec7e795e1b466a46a66cb Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 4 Nov 2023 18:54:02 +0100 Subject: [PATCH 162/632] Revert "Fix ViewIcons" This reverts commit 098da8c3fd95cdd1f299b86d0b919b001009a1df. --- src/plugins/viewIcons/index.tsx | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/plugins/viewIcons/index.tsx b/src/plugins/viewIcons/index.tsx index ccbc8ddca..fd6b7b6bf 100644 --- a/src/plugins/viewIcons/index.tsx +++ b/src/plugins/viewIcons/index.tsx @@ -65,9 +65,7 @@ const settings = definePluginSettings({ } }); -function openImage(url?: string) { - if (!url) return; - +function openImage(url: string) { const format = url.startsWith("/") ? "png" : settings.store.format; const u = new URL(url, window.location.href); @@ -173,12 +171,12 @@ export default definePlugin({ }, patches: [ - // Make pfps clickable and add pointer cursor + // Make pfps clickable { - find: ".AVATAR_DECORATION_STATUS_ROUND_16;", + find: "onAddFriend:function", replacement: { - match: /memo\(.{0,50}(?=let{statusColor:\i,status:\i,...\i}=(\i),)/, - replace: (m, props) => `${m}${props}.onClick=()=>$self.openImage(${props}.src);${props}.style={cursor:${props}.src?"pointer":void 0};` + match: /\{src:(\i)(?=,avatarDecoration)/, + replace: "{src:$1,onClick:()=>$self.openImage($1)" } }, // Make banners clickable From a67c7f841d3a4fc96cb64a61aa37d00454d658bc Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 4 Nov 2023 18:54:29 +0100 Subject: [PATCH 163/632] Fix ViewIcons correctly --- src/plugins/viewIcons/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/viewIcons/index.tsx b/src/plugins/viewIcons/index.tsx index fd6b7b6bf..2da3e21c2 100644 --- a/src/plugins/viewIcons/index.tsx +++ b/src/plugins/viewIcons/index.tsx @@ -173,7 +173,7 @@ export default definePlugin({ patches: [ // Make pfps clickable { - find: "onAddFriend:function", + find: "User Profile Modal - Context Menu", replacement: { match: /\{src:(\i)(?=,avatarDecoration)/, replace: "{src:$1,onClick:()=>$self.openImage($1)" From 370b3d366df87b6359c5844e6f7784f31fbcd748 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 4 Nov 2023 18:59:16 +0100 Subject: [PATCH 164/632] OpenInApp: Fix links in messages --- src/plugins/openInApp/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/openInApp/index.ts b/src/plugins/openInApp/index.ts index e26a01eb9..a65c49317 100644 --- a/src/plugins/openInApp/index.ts +++ b/src/plugins/openInApp/index.ts @@ -55,8 +55,8 @@ export default definePlugin({ { find: "trackAnnouncementMessageLinkClicked({", replacement: { - match: /(?<=handleClick:function\(\)\{return (\i)\}.+?)async function \1\(.+?\)\{/, - replace: "$& if(await $self.handleLink(...arguments)) return;" + match: /(?<=handleClick:function\(\)\{return (\i)\}.+?)function \1\(.+?\)\{/, + replace: "async $& if(await $self.handleLink(...arguments)) return;" } }, // Make Spotify profile activity links open in app on web From dd44ac1ad2280672e2c60587a177f51a351c77fa Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sat, 4 Nov 2023 19:08:44 +0100 Subject: [PATCH 165/632] OpenInApp: Support podcasts --- src/plugins/openInApp/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/openInApp/index.ts b/src/plugins/openInApp/index.ts index a65c49317..5a2641e2a 100644 --- a/src/plugins/openInApp/index.ts +++ b/src/plugins/openInApp/index.ts @@ -23,7 +23,7 @@ import { showToast, Toasts } from "@webpack/common"; import type { MouseEvent } from "react"; const ShortUrlMatcher = /^https:\/\/(spotify\.link|s\.team)\/.+$/; -const SpotifyMatcher = /^https:\/\/open\.spotify\.com\/(track|album|artist|playlist|user)\/(.+)(?:\?.+?)?$/; +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\/(.+)$/; From 86e94343cca10b950f2dc8d18d496d6db9f3b728 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Sun, 5 Nov 2023 02:07:33 +0100 Subject: [PATCH 166/632] bump to v1.6.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fa463823c..f3307d7d5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.6.2", + "version": "1.6.3", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { From 04d2dd26c412884e7699f4a104bda4ac6259b995 Mon Sep 17 00:00:00 2001 From: Marvin Witt Date: Sun, 5 Nov 2023 02:06:08 +0100 Subject: [PATCH 167/632] fix(dearrow): don't replace thumbnail if only original available (#1959) --- src/plugins/dearrow/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/dearrow/index.tsx b/src/plugins/dearrow/index.tsx index c6bf4e05d..f6b9ef952 100644 --- a/src/plugins/dearrow/index.tsx +++ b/src/plugins/dearrow/index.tsx @@ -63,7 +63,7 @@ async function embedDidMount(this: Component) { embed.rawTitle = titles[0].title; } - if (thumbnails[0]?.votes >= 0) { + if (thumbnails[0]?.votes >= 0 && thumbnails[0].timestamp) { embed.dearrow.oldThumb = embed.thumbnail.proxyURL; embed.thumbnail.proxyURL = `https://dearrow-thumb.ajay.app/api/v1/getThumbnail?videoID=${videoId}&time=${thumbnails[0].timestamp}`; } From 32f204319394ca7545e779b0d5ffb415d64a6e56 Mon Sep 17 00:00:00 2001 From: zImPatrick <23613354+zImPatrick@users.noreply.github.com> Date: Tue, 7 Nov 2023 19:58:10 +0100 Subject: [PATCH 168/632] Fix FakeNitro sticker bypass (#1964) --- src/plugins/fakeNitro/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/fakeNitro/index.ts b/src/plugins/fakeNitro/index.ts index e4311106d..4d6b7957b 100644 --- a/src/plugins/fakeNitro/index.ts +++ b/src/plugins/fakeNitro/index.ts @@ -206,10 +206,10 @@ export default definePlugin({ }, // Allow stickers to be sent everywhere { - find: "canUseStickersEverywhere:function", + find: "canUseCustomStickersEverywhere:function", predicate: () => settings.store.enableStickerBypass, replacement: { - match: /canUseStickersEverywhere:function\(\i\){/, + match: /canUseCustomStickersEverywhere:function\(\i\){/, replace: "$&return true;" }, }, From 119b628f331e9282342df213f941851f98630ebb Mon Sep 17 00:00:00 2001 From: V Date: Thu, 9 Nov 2023 02:32:34 +0100 Subject: [PATCH 169/632] feat: simple plugin natives (#1965) --- scripts/build/build.mjs | 64 ++++++++++++++- scripts/build/common.mjs | 12 ++- src/VencordNative.ts | 20 +++-- src/main/ipcPlugins.ts | 77 ++++--------------- src/modules.d.ts | 5 ++ .../fixSpotifyEmbeds.desktop/native.ts | 27 +++++++ src/plugins/openInApp/index.ts | 6 +- src/plugins/openInApp/native.ts | 31 ++++++++ src/plugins/voiceMessages/DesktopRecorder.tsx | 5 +- src/plugins/voiceMessages/native.ts | 24 ++++++ src/utils/IpcEvents.ts | 2 + src/utils/types.ts | 7 ++ 12 files changed, 200 insertions(+), 80 deletions(-) create mode 100644 src/plugins/fixSpotifyEmbeds.desktop/native.ts create mode 100644 src/plugins/openInApp/native.ts create mode 100644 src/plugins/voiceMessages/native.ts diff --git a/scripts/build/build.mjs b/scripts/build/build.mjs index f606f1b08..89cca7e47 100755 --- a/scripts/build/build.mjs +++ b/scripts/build/build.mjs @@ -18,8 +18,10 @@ */ import esbuild from "esbuild"; +import { readdir } from "fs/promises"; +import { join } from "path"; -import { BUILD_TIMESTAMP, commonOpts, globPlugins, isStandalone, updaterDisabled, VERSION, watch } from "./common.mjs"; +import { BUILD_TIMESTAMP, commonOpts, existsAsync, globPlugins, isStandalone, updaterDisabled, VERSION, watch } from "./common.mjs"; const defines = { IS_STANDALONE: isStandalone, @@ -43,13 +45,59 @@ const nodeCommonOpts = { format: "cjs", platform: "node", target: ["esnext"], - external: ["electron", "original-fs", ...commonOpts.external], + external: ["electron", "original-fs", "~pluginNatives", ...commonOpts.external], define: defines, }; const sourceMapFooter = s => watch ? "" : `//# sourceMappingURL=vencord://${s}.js.map`; const sourcemap = watch ? "inline" : "external"; +/** + * @type {import("esbuild").Plugin} + */ +const globNativesPlugin = { + name: "glob-natives-plugin", + setup: build => { + const filter = /^~pluginNatives$/; + build.onResolve({ filter }, args => { + return { + namespace: "import-natives", + path: args.path + }; + }); + + build.onLoad({ filter, namespace: "import-natives" }, async () => { + const pluginDirs = ["plugins", "userplugins"]; + let code = ""; + let natives = "\n"; + let i = 0; + for (const dir of pluginDirs) { + const dirPath = join("src", dir); + if (!await existsAsync(dirPath)) continue; + const plugins = await readdir(dirPath); + for (const p of plugins) { + if (!await existsAsync(join(dirPath, p, "native.ts"))) continue; + + const nameParts = p.split("."); + const namePartsWithoutTarget = nameParts.length === 1 ? nameParts : nameParts.slice(0, -1); + // pluginName.thing.desktop -> PluginName.thing + const cleanPluginName = p[0].toUpperCase() + namePartsWithoutTarget.join(".").slice(1); + + const mod = `p${i}`; + code += `import * as ${mod} from "./${dir}/${p}/native";\n`; + natives += `${JSON.stringify(cleanPluginName)}:${mod},\n`; + i++; + } + } + code += `export default {${natives}};`; + return { + contents: code, + resolveDir: "./src" + }; + }); + } +}; + await Promise.all([ // Discord Desktop main & renderer & preload esbuild.build({ @@ -62,7 +110,11 @@ await Promise.all([ ...defines, IS_DISCORD_DESKTOP: true, IS_VESKTOP: false - } + }, + plugins: [ + ...nodeCommonOpts.plugins, + globNativesPlugin + ] }), esbuild.build({ ...commonOpts, @@ -107,7 +159,11 @@ await Promise.all([ ...defines, IS_DISCORD_DESKTOP: false, IS_VESKTOP: true - } + }, + plugins: [ + ...nodeCommonOpts.plugins, + globNativesPlugin + ] }), esbuild.build({ ...commonOpts, diff --git a/scripts/build/common.mjs b/scripts/build/common.mjs index 8efe2be60..5488b1b3b 100644 --- a/scripts/build/common.mjs +++ b/scripts/build/common.mjs @@ -20,8 +20,8 @@ import "../suppressExperimentalWarnings.js"; import "../checkNodeVersion.js"; import { exec, execSync } from "child_process"; -import { existsSync, readFileSync } from "fs"; -import { readdir, readFile } from "fs/promises"; +import { constants as FsConstants, readFileSync } from "fs"; +import { access, readdir, readFile } from "fs/promises"; import { join, relative } from "path"; import { promisify } from "util"; @@ -47,6 +47,12 @@ export const banner = { const isWeb = process.argv.slice(0, 2).some(f => f.endsWith("buildWeb.mjs")); +export function existsAsync(path) { + return access(path, FsConstants.F_OK) + .then(() => true) + .catch(() => false); +} + // https://github.com/evanw/esbuild/issues/619#issuecomment-751995294 /** * @type {import("esbuild").Plugin} @@ -79,7 +85,7 @@ export const globPlugins = kind => ({ let plugins = "\n"; let i = 0; for (const dir of pluginDirs) { - if (!existsSync(`./src/${dir}`)) continue; + if (!await existsAsync(`./src/${dir}`)) continue; const files = await readdir(`./src/${dir}`); for (const file of files) { if (file.startsWith("_") || file.startsWith(".")) continue; diff --git a/src/VencordNative.ts b/src/VencordNative.ts index dd97b5d26..0faa5569b 100644 --- a/src/VencordNative.ts +++ b/src/VencordNative.ts @@ -7,6 +7,7 @@ import { IpcEvents } from "@utils/IpcEvents"; import { IpcRes } from "@utils/types"; import { ipcRenderer } from "electron"; +import { PluginIpcMappings } from "main/ipcPlugins"; import type { UserThemeHeader } from "main/themes"; function invoke(event: IpcEvents, ...args: any[]) { @@ -17,6 +18,16 @@ export function sendSync(event: IpcEvents, ...args: any[]) { return ipcRenderer.sendSync(event, ...args) as T; } +const PluginHelpers = {} as Record Promise>>; +const pluginIpcMap = sendSync(IpcEvents.GET_PLUGIN_IPC_METHOD_MAP); + +for (const [plugin, methods] of Object.entries(pluginIpcMap)) { + const map = PluginHelpers[plugin] = {}; + for (const [methodName, method] of Object.entries(methods)) { + map[methodName] = (...args: any[]) => invoke(method as IpcEvents, ...args); + } +} + export default { themes: { uploadTheme: (fileName: string, fileData: string) => invoke(IpcEvents.UPLOAD_THEME, fileName, fileData), @@ -61,12 +72,5 @@ export default { openExternal: (url: string) => invoke(IpcEvents.OPEN_EXTERNAL, url) }, - pluginHelpers: { - OpenInApp: { - resolveRedirect: (url: string) => invoke(IpcEvents.OPEN_IN_APP__RESOLVE_REDIRECT, url), - }, - VoiceMessages: { - readRecording: (path: string) => invoke(IpcEvents.VOICE_MESSAGES_READ_RECORDING, path), - } - } + pluginHelpers: PluginHelpers }; diff --git a/src/main/ipcPlugins.ts b/src/main/ipcPlugins.ts index 3034fb436..5d679fc0b 100644 --- a/src/main/ipcPlugins.ts +++ b/src/main/ipcPlugins.ts @@ -17,73 +17,26 @@ */ import { IpcEvents } from "@utils/IpcEvents"; -import { app, ipcMain } from "electron"; -import { readFile } from "fs/promises"; -import { request } from "https"; -import { basename, normalize } from "path"; +import { ipcMain } from "electron"; -import { getSettings } from "./ipcMain"; +import PluginNatives from "~pluginNatives"; -// FixSpotifyEmbeds -app.on("browser-window-created", (_, win) => { - win.webContents.on("frame-created", (_, { frame }) => { - frame.once("dom-ready", () => { - if (frame.url.startsWith("https://open.spotify.com/embed/")) { - const settings = getSettings().plugins?.FixSpotifyEmbeds; - if (!settings?.enabled) return; +const PluginIpcMappings = {} as Record>; +export type PluginIpcMappings = typeof PluginIpcMappings; - frame.executeJavaScript(` - const original = Audio.prototype.play; - Audio.prototype.play = function() { - this.volume = ${(settings.volume / 100) || 0.1}; - return original.apply(this, arguments); - } - `); - } - }); - }); -}); +for (const [plugin, methods] of Object.entries(PluginNatives)) { + const entries = Object.entries(methods); + if (!entries.length) continue; -// #region OpenInApp -// These links don't support CORS, so this has to be native -const validRedirectUrls = /^https:\/\/(spotify\.link|s\.team)\/.+$/; + const mappings = PluginIpcMappings[plugin] = {}; -function getRedirect(url: string) { - return new Promise((resolve, reject) => { - const req = request(new URL(url), { method: "HEAD" }, res => { - resolve( - res.headers.location - ? getRedirect(res.headers.location) - : url - ); - }); - req.on("error", reject); - req.end(); - }); + for (const [methodName, method] of entries) { + const key = `VencordPluginNative_${plugin}_${methodName}`; + ipcMain.handle(key, method); + mappings[methodName] = key; + } } -ipcMain.handle(IpcEvents.OPEN_IN_APP__RESOLVE_REDIRECT, async (_, url: string) => { - if (!validRedirectUrls.test(url)) return url; - - return getRedirect(url); +ipcMain.on(IpcEvents.GET_PLUGIN_IPC_METHOD_MAP, e => { + e.returnValue = PluginIpcMappings; }); -// #endregion - - -// #region VoiceMessages -ipcMain.handle(IpcEvents.VOICE_MESSAGES_READ_RECORDING, async (_, filePath: string) => { - filePath = normalize(filePath); - const filename = basename(filePath); - const discordBaseDirWithTrailingSlash = normalize(app.getPath("userData") + "/"); - console.log(filename, discordBaseDirWithTrailingSlash, filePath); - if (filename !== "recording.ogg" || !filePath.startsWith(discordBaseDirWithTrailingSlash)) return null; - - try { - const buf = await readFile(filePath); - return new Uint8Array(buf.buffer); - } catch { - return null; - } -}); - -// #endregion diff --git a/src/modules.d.ts b/src/modules.d.ts index d75a84f74..24f34664d 100644 --- a/src/modules.d.ts +++ b/src/modules.d.ts @@ -24,6 +24,11 @@ declare module "~plugins" { export default plugins; } +declare module "~pluginNatives" { + const pluginNatives: Record unknown>>; + export default pluginNatives; +} + declare module "~git-hash" { const hash: string; export default hash; diff --git a/src/plugins/fixSpotifyEmbeds.desktop/native.ts b/src/plugins/fixSpotifyEmbeds.desktop/native.ts new file mode 100644 index 000000000..f779c400a --- /dev/null +++ b/src/plugins/fixSpotifyEmbeds.desktop/native.ts @@ -0,0 +1,27 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { app } from "electron"; +import { getSettings } from "main/ipcMain"; + +app.on("browser-window-created", (_, win) => { + win.webContents.on("frame-created", (_, { frame }) => { + frame.once("dom-ready", () => { + if (frame.url.startsWith("https://open.spotify.com/embed/")) { + const settings = getSettings().plugins?.FixSpotifyEmbeds; + if (!settings?.enabled) return; + + frame.executeJavaScript(` + const original = Audio.prototype.play; + Audio.prototype.play = function() { + this.volume = ${(settings.volume / 100) || 0.1}; + return original.apply(this, arguments); + } + `); + } + }); + }); +}); diff --git a/src/plugins/openInApp/index.ts b/src/plugins/openInApp/index.ts index 5a2641e2a..0835c0612 100644 --- a/src/plugins/openInApp/index.ts +++ b/src/plugins/openInApp/index.ts @@ -18,7 +18,7 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; -import definePlugin, { OptionType } from "@utils/types"; +import definePlugin, { OptionType, PluginNative } from "@utils/types"; import { showToast, Toasts } from "@webpack/common"; import type { MouseEvent } from "react"; @@ -45,6 +45,8 @@ const settings = definePluginSettings({ } }); +const Native = VencordNative.pluginHelpers.OpenInApp as PluginNative; + export default definePlugin({ name: "OpenInApp", description: "Open Spotify, Steam and Epic Games URLs in their respective apps instead of your browser", @@ -84,7 +86,7 @@ export default definePlugin({ if (!IS_WEB && ShortUrlMatcher.test(url)) { event?.preventDefault(); // CORS jumpscare - url = await VencordNative.pluginHelpers.OpenInApp.resolveRedirect(url); + url = await Native.resolveRedirect(url); } spotify: { diff --git a/src/plugins/openInApp/native.ts b/src/plugins/openInApp/native.ts new file mode 100644 index 000000000..25637422c --- /dev/null +++ b/src/plugins/openInApp/native.ts @@ -0,0 +1,31 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { IpcMainInvokeEvent } from "electron"; +import { request } from "https"; + +// These links don't support CORS, so this has to be native +const validRedirectUrls = /^https:\/\/(spotify\.link|s\.team)\/.+$/; + +function getRedirect(url: string) { + return new Promise((resolve, reject) => { + const req = request(new URL(url), { method: "HEAD" }, res => { + resolve( + res.headers.location + ? getRedirect(res.headers.location) + : url + ); + }); + req.on("error", reject); + req.end(); + }); +} + +export async function resolveRedirect(_: IpcMainInvokeEvent, url: string) { + if (!validRedirectUrls.test(url)) return url; + + return getRedirect(url); +} diff --git a/src/plugins/voiceMessages/DesktopRecorder.tsx b/src/plugins/voiceMessages/DesktopRecorder.tsx index 36f6a60ab..a69739a41 100644 --- a/src/plugins/voiceMessages/DesktopRecorder.tsx +++ b/src/plugins/voiceMessages/DesktopRecorder.tsx @@ -16,11 +16,14 @@ * along with this program. If not, see . */ +import { PluginNative } from "@utils/types"; import { Button, showToast, Toasts, useState } from "@webpack/common"; import type { VoiceRecorder } from "."; import { settings } from "./settings"; +const Native = VencordNative.pluginHelpers.VoiceMessages as PluginNative; + export const VoiceRecorderDesktop: VoiceRecorder = ({ setAudioBlob, onRecordingChange }) => { const [recording, setRecording] = useState(false); @@ -49,7 +52,7 @@ export const VoiceRecorderDesktop: VoiceRecorder = ({ setAudioBlob, onRecordingC } else { discordVoice.stopLocalAudioRecording(async (filePath: string) => { if (filePath) { - const buf = await VencordNative.pluginHelpers.VoiceMessages.readRecording(filePath); + const buf = await Native.readRecording(filePath); if (buf) setAudioBlob(new Blob([buf], { type: "audio/ogg; codecs=opus" })); else diff --git a/src/plugins/voiceMessages/native.ts b/src/plugins/voiceMessages/native.ts new file mode 100644 index 000000000..bbc19c891 --- /dev/null +++ b/src/plugins/voiceMessages/native.ts @@ -0,0 +1,24 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { app } from "electron"; +import { readFile } from "fs/promises"; +import { basename, normalize } from "path"; + +export async function readRecording(_, filePath: string) { + filePath = normalize(filePath); + const filename = basename(filePath); + const discordBaseDirWithTrailingSlash = normalize(app.getPath("userData") + "/"); + console.log(filename, discordBaseDirWithTrailingSlash, filePath); + if (filename !== "recording.ogg" || !filePath.startsWith(discordBaseDirWithTrailingSlash)) return null; + + try { + const buf = await readFile(filePath); + return new Uint8Array(buf.buffer); + } catch { + return null; + } +} diff --git a/src/utils/IpcEvents.ts b/src/utils/IpcEvents.ts index 16bcfa659..2027df9cf 100644 --- a/src/utils/IpcEvents.ts +++ b/src/utils/IpcEvents.ts @@ -38,6 +38,8 @@ export const enum IpcEvents { BUILD = "VencordBuild", OPEN_MONACO_EDITOR = "VencordOpenMonacoEditor", + GET_PLUGIN_IPC_METHOD_MAP = "VencordGetPluginIpcMethodMap", + OPEN_IN_APP__RESOLVE_REDIRECT = "VencordOIAResolveRedirect", VOICE_MESSAGES_READ_RECORDING = "VencordVMReadRecording", } diff --git a/src/utils/types.ts b/src/utils/types.ts index ff2c79af3..b32b127b0 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -307,3 +307,10 @@ export type PluginOptionBoolean = PluginSettingBooleanDef & PluginSettingCommon export type PluginOptionSelect = PluginSettingSelectDef & PluginSettingCommon & IsDisabled & IsValid; export type PluginOptionSlider = PluginSettingSliderDef & PluginSettingCommon & IsDisabled & IsValid; export type PluginOptionComponent = PluginSettingComponentDef & PluginSettingCommon; + +export type PluginNative any>> = { + [key in keyof PluginExports]: + PluginExports[key] extends (event: Electron.IpcMainInvokeEvent, ...args: infer Args) => infer Return + ? (...args: Args) => Return extends Promise ? Return : Promise + : never; +}; From 394d2060eb90e5d7104798026fa8a18a02a24d93 Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Thu, 9 Nov 2023 08:34:40 +0700 Subject: [PATCH 170/632] searchReply: fix (#1961) --- src/plugins/searchReply/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/searchReply/index.tsx b/src/plugins/searchReply/index.tsx index b28ca8ec4..b151712af 100644 --- a/src/plugins/searchReply/index.tsx +++ b/src/plugins/searchReply/index.tsx @@ -20,12 +20,12 @@ import { addContextMenuPatch, findGroupChildrenByChildId, NavContextMenuPatchCal import { ReplyIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { findByCodeLazy } from "@webpack"; +import { findByPropsLazy } from "@webpack"; import { ChannelStore, i18n, Menu, PermissionsBits, PermissionStore, SelectedChannelStore } from "@webpack/common"; import { Message } from "discord-types/general"; -const replyFn = findByCodeLazy("showMentionToggle", "TEXTAREA_FOCUS", "shiftKey"); +const messageUtils = findByPropsLazy("replyToMessage"); const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { message }: { message: Message; }) => () => { // make sure the message is in the selected channel @@ -43,7 +43,7 @@ const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { messag id="reply" label={i18n.Messages.MESSAGE_ACTION_REPLY} icon={ReplyIcon} - action={(e: React.MouseEvent) => replyFn(channel, message, e)} + action={(e: React.MouseEvent) => messageUtils.replyToMessage(channel, message, e)} /> )); } @@ -56,7 +56,7 @@ const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { messag id="reply" label={i18n.Messages.MESSAGE_ACTION_REPLY} icon={ReplyIcon} - action={(e: React.MouseEvent) => replyFn(channel, message, e)} + action={(e: React.MouseEvent) => messageUtils.replyToMessage(channel, message, e)} /> )); } From 5edc94062c2940a415edb8d66fe9fb346b9cb4fb Mon Sep 17 00:00:00 2001 From: V Date: Thu, 9 Nov 2023 02:42:24 +0100 Subject: [PATCH 171/632] make packageManager key less specific --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f3307d7d5..1bc2129fb 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "typescript": "^5.0.4", "zip-local": "^0.3.5" }, - "packageManager": "pnpm@8.1.1", + "packageManager": "^pnpm@8.1.1", "pnpm": { "patchedDependencies": { "eslint-plugin-path-alias@1.0.0": "patches/eslint-plugin-path-alias@1.0.0.patch", From 9bd82943e3f178324e38eebe8a683cc1c1ded94c Mon Sep 17 00:00:00 2001 From: AM Date: Thu, 9 Nov 2023 03:42:35 +0000 Subject: [PATCH 172/632] [Plugin] Super Reaction Tweaks (#1958) Co-authored-by: V Co-authored-by: Jack Matthews --- src/plugins/superReactionTweaks/README.md | 11 ++++ src/plugins/superReactionTweaks/index.ts | 63 +++++++++++++++++++++++ src/utils/constants.ts | 4 ++ 3 files changed, 78 insertions(+) create mode 100644 src/plugins/superReactionTweaks/README.md create mode 100644 src/plugins/superReactionTweaks/index.ts diff --git a/src/plugins/superReactionTweaks/README.md b/src/plugins/superReactionTweaks/README.md new file mode 100644 index 000000000..624ab8669 --- /dev/null +++ b/src/plugins/superReactionTweaks/README.md @@ -0,0 +1,11 @@ +# Super Reaction Tweaks + +This plugin applies configurable various tweaks to super reactions. + +![Screenshot](https://user-images.githubusercontent.com/22851444/281598795-58f07116-9f95-4f64-940b-23a5499f2302.png) + +## Features: + +**Super React By Default** - The reaction picker will default to super reactions instead of normal reactions. + +**Super Reaction Play Limit** - Allows you to decide how many super reaction animations can play at once, including removing the limit entirely. diff --git a/src/plugins/superReactionTweaks/index.ts b/src/plugins/superReactionTweaks/index.ts new file mode 100644 index 000000000..2652eef07 --- /dev/null +++ b/src/plugins/superReactionTweaks/index.ts @@ -0,0 +1,63 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated, ant0n, FieryFlames 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 const settings = definePluginSettings({ + superReactByDefault: { + type: OptionType.BOOLEAN, + description: "Reaction picker will default to Super Reactions", + default: true, + }, + unlimitedSuperReactionPlaying: { + type: OptionType.BOOLEAN, + description: "Remove the limit on Super Reactions playing at once", + default: false, + }, + + superReactionPlayingLimit: { + description: "Max Super Reactions to play at once", + type: OptionType.SLIDER, + default: 20, + markers: [5, 10, 20, 40, 60, 80, 100], + stickToMarkers: true, + }, +}, { + superReactionPlayingLimit: { + disabled() { return this.store.unlimitedSuperReactionPlaying; }, + } +}); + +export default definePlugin({ + name: "SuperReactionTweaks", + description: "Customize the limit of Super Reactions playing at once, and super react by default", + authors: [Devs.FieryFlames, Devs.ant0n], + patches: [ + { + find: ",BURST_REACTION_EFFECT_PLAY", + replacement: { + match: /(?<=BURST_REACTION_EFFECT_PLAY:\i=>{.{50,100})(\i\(\i,\i\))>=\d+/, + replace: "!$self.shouldPlayBurstReaction($1)" + } + }, + { + find: ".hasAvailableBurstCurrency)", + replacement: { + match: /(?<=\.useBurstReactionsExperiment.{0,20})useState\(!1\)(?=.+?(\i===\i\.EmojiIntention.REACTION))/, + replace: "useState($self.settings.store.superReactByDefault && $1)" + } + } + ], + settings, + + shouldPlayBurstReaction(playingCount: number) { + if (settings.store.unlimitedSuperReactionPlaying) return true; + if (playingCount <= settings.store.superReactionPlayingLimit) return true; + return false; + } +}); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index aeadcdbfb..3db2e64f8 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -379,6 +379,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "ProffDea", id: 609329952180928513n }, + ant0n: { + name: "ant0n", + id: 145224646868860928n + }, } satisfies Record); // iife so #__PURE__ works correctly From 96126fa39f910960ec10ccc089fe17e91c1a2afe Mon Sep 17 00:00:00 2001 From: V Date: Thu, 9 Nov 2023 07:15:49 +0100 Subject: [PATCH 173/632] remove pipebomb from github actions (#1968) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1bc2129fb..23d9dd272 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "typescript": "^5.0.4", "zip-local": "^0.3.5" }, - "packageManager": "^pnpm@8.1.1", + "packageManager": "pnpm@8.10.2", "pnpm": { "patchedDependencies": { "eslint-plugin-path-alias@1.0.0": "patches/eslint-plugin-path-alias@1.0.0.patch", From ea11f2244fde469ce308f8a4e7224430be62f8f1 Mon Sep 17 00:00:00 2001 From: V Date: Mon, 13 Nov 2023 01:37:15 +0100 Subject: [PATCH 174/632] README: Add sponsors --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index cd54fcdc8..177e65be6 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,14 @@ Visit https://vencord.dev/download https://discord.gg/D9uwnFnqmd +## Sponsors + +| **Thanks a lot to all Vencord [sponsors](https://github.com/sponsors/Vendicated)!!** | +|:--:| +| [![](https://meow.vendicated.dev/sponsors.png)](https://github.com/sponsors/Vendicated) | +| *generated using [github-sponsor-graph](https://github.com/Vendicated/github-sponsor-graph)* | + + ## Star History From 77d08c5c28121823dd5ba85265a9d09b832a2932 Mon Sep 17 00:00:00 2001 From: megumin Date: Sat, 11 Nov 2023 00:28:09 +0000 Subject: [PATCH 175/632] feat: Add Environment variable to disable updater auto-patching (#1971) --- src/main/patchWin32Updater.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/patchWin32Updater.ts b/src/main/patchWin32Updater.ts index ba7a9224e..96717a5ad 100644 --- a/src/main/patchWin32Updater.ts +++ b/src/main/patchWin32Updater.ts @@ -32,6 +32,8 @@ function isNewer($new: string, old: string) { } function patchLatest() { + if (process.env.DISABLE_UPDATER_AUTO_PATCHING) return; + try { const currentAppPath = dirname(process.execPath); const currentVersion = basename(currentAppPath); From fd25b5f2968385741e36b5a6624a80d7ef14b61f Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Sun, 12 Nov 2023 15:33:34 -0500 Subject: [PATCH 176/632] fix(dearrow): support DeArrow thumbnail submissions at 0 seconds (#1979) --- src/plugins/dearrow/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/dearrow/index.tsx b/src/plugins/dearrow/index.tsx index f6b9ef952..52cf1d530 100644 --- a/src/plugins/dearrow/index.tsx +++ b/src/plugins/dearrow/index.tsx @@ -50,7 +50,7 @@ async function embedDidMount(this: Component) { const { titles, thumbnails } = await res.json(); const hasTitle = titles[0]?.votes >= 0; - const hasThumb = thumbnails[0]?.votes >= 0; + const hasThumb = thumbnails[0]?.votes >= 0 && !thumbnails[0].original; if (!hasTitle && !hasThumb) return; @@ -58,12 +58,12 @@ async function embedDidMount(this: Component) { enabled: true }; - if (titles[0]?.votes >= 0) { + if (hasTitle) { embed.dearrow.oldTitle = embed.rawTitle; embed.rawTitle = titles[0].title; } - if (thumbnails[0]?.votes >= 0 && thumbnails[0].timestamp) { + if (hasThumb) { embed.dearrow.oldThumb = embed.thumbnail.proxyURL; embed.thumbnail.proxyURL = `https://dearrow-thumb.ajay.app/api/v1/getThumbnail?videoID=${videoId}&time=${thumbnails[0].timestamp}`; } From 7b248ee30952eccd880a93dfe1209d8da188b809 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 15 Nov 2023 14:50:52 -0300 Subject: [PATCH 177/632] Fix SHC patches --- src/plugins/showHiddenChannels/index.tsx | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/plugins/showHiddenChannels/index.tsx b/src/plugins/showHiddenChannels/index.tsx index 95b16d625..4e275d647 100644 --- a/src/plugins/showHiddenChannels/index.tsx +++ b/src/plugins/showHiddenChannels/index.tsx @@ -68,7 +68,7 @@ export default definePlugin({ patches: [ { // RenderLevel defines if a channel is hidden, collapsed in category, visible, etc - find: ".CannotShow=", + find: '"placeholder-channel-id"', replacement: [ // Remove the special logic for channels we don't have access to { @@ -82,13 +82,8 @@ export default definePlugin({ }, // Make channels we dont have access to be the same level as normal ones { - match: /(?<=renderLevel:(\i\(this,\i\)\?\i\.Show:\i\.WouldShowIfUncollapsed).+?renderLevel:).+?(?=,)/, - replace: (_, renderLevelExpression) => renderLevelExpression - }, - // Make channels we dont have access to be the same level as normal ones - { - match: /(?<=activeJoinedRelevantThreads.+?renderLevel:.+?,threadIds:\i\(this.record.+?renderLevel:)(\i)\..+?(?=,)/, - replace: (_, RenderLevels) => `${RenderLevels}.Show` + match: /(activeJoinedRelevantThreads:.{0,50}VIEW_CHANNEL.+?renderLevel:(.+?),threadIds.+?renderLevel:).+?(?=,threadIds)/g, + replace: (_, rest, defaultRenderLevel) => `${rest}${defaultRenderLevel}` }, // Remove permission checking for getRenderLevel function { From af614465a49cdd70038cf089b195855a7fa1f18c Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 15 Nov 2023 14:54:48 -0300 Subject: [PATCH 178/632] Remove obsolete experiments patch --- src/plugins/experiments/index.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/plugins/experiments/index.tsx b/src/plugins/experiments/index.tsx index 89fcf33e7..f8ea4b04a 100644 --- a/src/plugins/experiments/index.tsx +++ b/src/plugins/experiments/index.tsx @@ -77,15 +77,6 @@ export default definePlugin({ } ] }, - // Fix search history being disabled / broken with isStaff - { - find: '("showNewSearch")', - predicate: () => settings.store.enableIsStaff, - replacement: { - match: /(?<=showNewSearch"\);return)\s?/, - replace: "!1&&" - } - }, { find: 'H1,title:"Experiments"', replacement: { From 3ea6a967153db9150e809f2050b40fa5b4e54a00 Mon Sep 17 00:00:00 2001 From: Jack <30497388+FieryFlames@users.noreply.github.com> Date: Wed, 15 Nov 2023 13:01:50 -0500 Subject: [PATCH 179/632] chore: Fix PinDMs patch (#1981) --- src/plugins/pinDms/index.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/pinDms/index.tsx b/src/plugins/pinDms/index.tsx index ac5957b33..792bdab6a 100644 --- a/src/plugins/pinDms/index.tsx +++ b/src/plugins/pinDms/index.tsx @@ -100,10 +100,10 @@ export default definePlugin({ }, { // Fix getRowHeight's check for whether this is the DMs section - // section === DMS - match: /===\i\.DMS&&0/, - // section -1 === DMS - replace: "-1$&" + // DMS (inlined) === section + match: /(?<=getRowHeight=\(.{2,50}?)1===\i/, + // DMS (inlined) === section - 1 + replace: "$&-1" }, { // Override scrollToChannel to properly account for pinned channels From c080a0eaacd34e813800db4f6b632d62a9633a3b Mon Sep 17 00:00:00 2001 From: Thoth <54485853+xero-lib@users.noreply.github.com> Date: Wed, 15 Nov 2023 12:09:26 -0600 Subject: [PATCH 180/632] shikiCodeblocks: transform lang to lower case to avoid failing detection (#1990) --- src/plugins/shikiCodeblocks.desktop/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/shikiCodeblocks.desktop/index.ts b/src/plugins/shikiCodeblocks.desktop/index.ts index 12c8c858d..f358497d2 100644 --- a/src/plugins/shikiCodeblocks.desktop/index.ts +++ b/src/plugins/shikiCodeblocks.desktop/index.ts @@ -67,7 +67,7 @@ export default definePlugin({ createHighlighter, renderHighlighter: ({ lang, content }: { lang: string; content: string; }) => { return createHighlighter({ - lang, + lang: lang?.toLowerCase(), content, isPreview: false, }); From 6578eb487ea16e04fdf704f19a57268aa8078f11 Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Thu, 16 Nov 2023 01:13:19 +0700 Subject: [PATCH 181/632] MessageLogger: fix attachment ignore (#1989) --- src/plugins/messageLogger/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/messageLogger/index.tsx b/src/plugins/messageLogger/index.tsx index 36f9ab3f9..ac43a9f0c 100644 --- a/src/plugins/messageLogger/index.tsx +++ b/src/plugins/messageLogger/index.tsx @@ -302,6 +302,7 @@ export default definePlugin({ match: /attachments:(\i)\((\i)\)/, replace: "attachments: $1((() => {" + + " if ($self.shouldIgnore($2)) return $2;" + " let old = arguments[1]?.attachments;" + " if (!old) return $2;" + " let new_ = $2.attachments?.map(a => a.id) ?? [];" + From 4a2657f928bdf88514eae707787f2b26d74da9db Mon Sep 17 00:00:00 2001 From: megumin Date: Wed, 15 Nov 2023 18:15:43 +0000 Subject: [PATCH 182/632] fix(channeltags): message author should be clyde (#644) (#1986) --- src/plugins/messageTags/index.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/plugins/messageTags/index.ts b/src/plugins/messageTags/index.ts index d65c388c3..66de9d661 100644 --- a/src/plugins/messageTags/index.ts +++ b/src/plugins/messageTags/index.ts @@ -25,10 +25,6 @@ import definePlugin, { OptionType } from "@utils/types"; const EMOTE = "<:luna:1035316192220553236>"; const DATA_KEY = "MessageTags_TAGS"; const MessageTagsMarker = Symbol("MessageTags"); -const author = { - id: "821472922140803112", - bot: false -}; interface Tag { name: string; @@ -59,14 +55,12 @@ function createTagCommand(tag: Tag) { execute: async (_, ctx) => { if (!await getTag(tag.name)) { sendBotMessage(ctx.channel.id, { - author, content: `${EMOTE} The tag **${tag.name}** does not exist anymore! Please reload ur Discord to fix :)` }); return { content: `/${tag.name}` }; } if (Settings.plugins.MessageTags.clyde) sendBotMessage(ctx.channel.id, { - author, content: `${EMOTE} The tag **${tag.name}** has been sent!` }); return { content: tag.message.replaceAll("\\n", "\n") }; @@ -162,7 +156,6 @@ export default definePlugin({ if (await getTag(name)) return sendBotMessage(ctx.channel.id, { - author, content: `${EMOTE} A Tag with the name **${name}** already exists!` }); @@ -176,7 +169,6 @@ export default definePlugin({ await addTag(tag); sendBotMessage(ctx.channel.id, { - author, content: `${EMOTE} Successfully created the tag **${name}**!` }); break; // end 'create' @@ -186,7 +178,6 @@ export default definePlugin({ if (!await getTag(name)) return sendBotMessage(ctx.channel.id, { - author, content: `${EMOTE} A Tag with the name **${name}** does not exist!` }); @@ -194,14 +185,12 @@ export default definePlugin({ await removeTag(name); sendBotMessage(ctx.channel.id, { - author, content: `${EMOTE} Successfully deleted the tag **${name}**!` }); break; // end 'delete' } case "list": { sendBotMessage(ctx.channel.id, { - author, embeds: [ { // @ts-ignore @@ -224,12 +213,10 @@ export default definePlugin({ if (!tag) return sendBotMessage(ctx.channel.id, { - author, content: `${EMOTE} A Tag with the name **${name}** does not exist!` }); sendBotMessage(ctx.channel.id, { - author, content: tag.message.replaceAll("\\n", "\n") }); break; // end 'preview' @@ -237,7 +224,6 @@ export default definePlugin({ default: { sendBotMessage(ctx.channel.id, { - author, content: "Invalid sub-command" }); break; From 45aa9fbb6d1dadcc1746a5a197a6812b41e3215c Mon Sep 17 00:00:00 2001 From: Justice Almanzar Date: Wed, 15 Nov 2023 18:30:31 +0000 Subject: [PATCH 183/632] Fix hljs find (#1983) --- src/webpack/common/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webpack/common/utils.ts b/src/webpack/common/utils.ts index 04df3c07d..8376925e5 100644 --- a/src/webpack/common/utils.ts +++ b/src/webpack/common/utils.ts @@ -31,7 +31,7 @@ waitFor(["ComponentDispatch", "ComponentDispatcher"], m => ComponentDispatch = m export const RestAPI: t.RestAPI = findByPropsLazy("getAPIBaseURL", "get"); export const moment: typeof import("moment") = findByPropsLazy("parseTwoDigitYear"); -export const hljs: typeof import("highlight.js") = findByPropsLazy("highlight"); +export const hljs: typeof import("highlight.js") = findByPropsLazy("highlight", "registerLanguage"); export const lodash: typeof import("lodash") = findByPropsLazy("debounce", "cloneDeep"); From 77749ed5e1e913d0c5c8ce0995a9fdc12cde7ccb Mon Sep 17 00:00:00 2001 From: adryd Date: Wed, 15 Nov 2023 19:26:35 -0500 Subject: [PATCH 184/632] oneko: update script version (#1994) --- src/plugins/oneko/index.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/plugins/oneko/index.ts b/src/plugins/oneko/index.ts index d95ba2bc9..c5de470e1 100644 --- a/src/plugins/oneko/index.ts +++ b/src/plugins/oneko/index.ts @@ -26,15 +26,13 @@ export default definePlugin({ authors: [Devs.Ven, Devs.adryd], start() { - fetch("https://raw.githubusercontent.com/adryd325/oneko.js/5977144dce83e4d71af1de005d16e38eebeb7b72/oneko.js") + fetch("https://raw.githubusercontent.com/adryd325/oneko.js/8fa8a1864aa71cd7a794d58bc139e755e96a236c/oneko.js") .then(x => x.text()) .then(s => s.replace("./oneko.gif", "https://raw.githubusercontent.com/adryd325/oneko.js/14bab15a755d0e35cd4ae19c931d96d306f99f42/oneko.gif")) .then(eval); }, stop() { - clearInterval(window.onekoInterval); - delete window.onekoInterval; document.getElementById("oneko")?.remove(); } }); From 4a5371a746c03125eefb8d5a35a6037ac56c0439 Mon Sep 17 00:00:00 2001 From: V Date: Thu, 16 Nov 2023 05:40:50 +0100 Subject: [PATCH 185/632] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 177e65be6..8611babd7 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ The cutest Discord client mod -![image](https://github.com/Vendicated/Vencord/assets/45497981/706722b1-32de-4d99-bee9-93993b504334) +| ![image](https://github.com/Vendicated/Vencord/assets/45497981/706722b1-32de-4d99-bee9-93993b504334) | +|:--:| +| A screenshot of vencord showcasing the [vencord-theme](https://github.com/synqat/vencord-theme) | ## Features From 9980c0d04f7fe5ac8002988b4ae29edb1b514b32 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Sat, 18 Nov 2023 22:53:50 -0300 Subject: [PATCH 186/632] IgnoreActivities: fix and improvements --- src/plugins/ignoreActivities/index.tsx | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/plugins/ignoreActivities/index.tsx b/src/plugins/ignoreActivities/index.tsx index 0b42a8b0b..4e747f363 100644 --- a/src/plugins/ignoreActivities/index.tsx +++ b/src/plugins/ignoreActivities/index.tsx @@ -8,7 +8,6 @@ import * as DataStore from "@api/DataStore"; import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; -import { useForceUpdater } from "@utils/react"; import definePlugin from "@utils/types"; import { findStoreLazy } from "@webpack"; import { StatusSettingsStores, Tooltip } from "webpack/common"; @@ -27,14 +26,12 @@ interface IgnoredActivity { const RunningGameStore = findStoreLazy("RunningGameStore"); function ToggleIcon(activity: IgnoredActivity, tooltipText: string, path: string, fill: string) { - const forceUpdate = useForceUpdater(); - return ( {tooltipProps => ( + ) + } +}); + +export default definePlugin({ + name: "ClientTheme", + authors: [Devs.F53], + description: "Recreation of the old client theme experiment. Add a color to your Discord client theme", + settings, + + patches: [ + { + find: "Could not find app-mount", + replacement: { + match: /(?<=Could not find app-mount"\))/, + replace: ",$self.addThemeInitializer()" + } + } + ], + + addThemeInitializer() { + document.addEventListener("DOMContentLoaded", this.themeInitializer = () => { + updateColorVars(settings.store.color); + generateColorOffsets(); + }); + }, + + stop() { + document.removeEventListener("DOMContentLoaded", this.themeInitializer); + document.getElementById("clientThemeVars")?.remove(); + document.getElementById("clientThemeOffsets")?.remove(); + } +}); + +async function generateColorOffsets() { + const variableRegex = /(--primary-[5-9]\d{2}-hsl):.*?(\S*)%;/g; + + const styleLinkNodes = document.querySelectorAll('link[rel="stylesheet"]'); + const variableLightness = {} as Record; + + // Search all stylesheets for color variables + for (const styleLinkNode of styleLinkNodes) { + const cssLink = styleLinkNode.getAttribute("href"); + if (!cssLink) continue; + + const res = await fetch(cssLink); + const cssString = await res.text(); + + // Get lightness values of --primary variables >=500 + let variableMatch = variableRegex.exec(cssString); + while (variableMatch !== null) { + const [, variable, lightness] = variableMatch; + variableLightness[variable] = parseFloat(lightness); + variableMatch = variableRegex.exec(cssString); + } + } + + // Generate offsets + const lightnessOffsets = Object.entries(variableLightness) + .map(([key, lightness]) => { + const lightnessOffset = lightness - variableLightness["--primary-600-hsl"]; + const plusOrMinus = lightnessOffset >= 0 ? "+" : "-"; + return `${key}: var(--theme-h) var(--theme-s) calc(var(--theme-l) ${plusOrMinus} ${Math.abs(lightnessOffset).toFixed(2)}%);`; + }) + .join("\n"); + + const style = document.createElement("style"); + style.setAttribute("id", "clientThemeOffsets"); + style.textContent = `:root:root { + ${lightnessOffsets} + }`; + document.head.appendChild(style); +} + +function updateColorVars(color: string) { + const { hue, saturation, lightness } = hexToHSL(color); + + let style = document.getElementById("clientThemeVars"); + if (!style) { + style = document.createElement("style"); + style.setAttribute("id", "clientThemeVars"); + document.head.appendChild(style); + } + + style.textContent = `:root { + --theme-h: ${hue}; + --theme-s: ${saturation}%; + --theme-l: ${lightness}%; + }`; +} + +// https://css-tricks.com/converting-color-spaces-in-javascript/ +function hexToHSL(hexCode: string) { + // Hex => RGB normalized to 0-1 + const r = parseInt(hexCode.substring(0, 2), 16) / 255; + const g = parseInt(hexCode.substring(2, 4), 16) / 255; + const b = parseInt(hexCode.substring(4, 6), 16) / 255; + + // RGB => HSL + const cMax = Math.max(r, g, b); + const cMin = Math.min(r, g, b); + const delta = cMax - cMin; + + let hue: number, saturation: number, lightness: number; + + lightness = (cMax + cMin) / 2; + + if (delta === 0) { + // If r=g=b then the only thing that matters is lightness + hue = 0; + saturation = 0; + } else { + // Magic + saturation = delta / (1 - Math.abs(2 * lightness - 1)); + + if (cMax === r) + hue = ((g - b) / delta) % 6; + else if (cMax === g) + hue = (b - r) / delta + 2; + else + hue = (r - g) / delta + 4; + hue *= 60; + if (hue < 0) + hue += 360; + } + + // Move saturation and lightness from 0-1 to 0-100 + saturation *= 100; + lightness *= 100; + + return { hue, saturation, lightness }; +} + +// Minimized math just for lightness, lowers lag when changing colors +function hexToLightness(hexCode) { + // Hex => RGB normalized to 0-1 + const r = parseInt(hexCode.substring(0, 2), 16) / 255; + const g = parseInt(hexCode.substring(2, 4), 16) / 255; + const b = parseInt(hexCode.substring(4, 6), 16) / 255; + + const cMax = Math.max(r, g, b); + const cMin = Math.min(r, g, b); + + const lightness = 100 * ((cMax + cMin) / 2); + + return lightness; +} diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 3db2e64f8..7f555d322 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -267,6 +267,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "Dziurwa", id: 1001086404203389018n }, + F53: { + name: "F53", + id: 280411966126948353n + }, AutumnVN: { name: "AutumnVN", id: 393694671383166998n From 074ebae3343170c19ca128ed76b0732375ea36bf Mon Sep 17 00:00:00 2001 From: V Date: Wed, 22 Nov 2023 01:57:00 +0100 Subject: [PATCH 191/632] ClientTheme fixes --- src/plugins/clientTheme/README.md | 7 +++++++ src/plugins/clientTheme/index.tsx | 21 ++++++++++----------- 2 files changed, 17 insertions(+), 11 deletions(-) create mode 100644 src/plugins/clientTheme/README.md diff --git a/src/plugins/clientTheme/README.md b/src/plugins/clientTheme/README.md new file mode 100644 index 000000000..4b40148c5 --- /dev/null +++ b/src/plugins/clientTheme/README.md @@ -0,0 +1,7 @@ +# Classic Client Theme + +Revival of the old client theme experiment (The one that came before the sucky one that we actually got) + +![the ClientTheme theme colour picker](https://user-images.githubusercontent.com/37855219/230238053-e90b7098-373a-459a-bb8c-c24e82f69270.png) + +https://github.com/Vendicated/Vencord/assets/45497981/6c1bcb3b-e0c7-4a02-b0b8-c4c5cd954f38 diff --git a/src/plugins/clientTheme/index.tsx b/src/plugins/clientTheme/index.tsx index 3e07b15fd..d0026c759 100644 --- a/src/plugins/clientTheme/index.tsx +++ b/src/plugins/clientTheme/index.tsx @@ -11,11 +11,12 @@ import { Devs } from "@utils/constants"; import { getTheme, Theme } from "@utils/discord"; import { Margins } from "@utils/margins"; import { classes } from "@utils/misc"; +import { LazyComponent } from "@utils/react"; import definePlugin, { OptionType } from "@utils/types"; -import { findByCodeLazy } from "@webpack"; +import { findByCode } from "@webpack"; import { Button, Forms } from "@webpack/common"; -const ColorPicker = findByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR"); +const ColorPicker = LazyComponent(() => findByCode(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR")); const colorPresets = [ "#1E1514", "#172019", "#13171B", "#1C1C28", "#402D2D", @@ -24,11 +25,7 @@ const colorPresets = [ ]; function onPickColor(color: number) { - let hexColor = color.toString(16); - - while (hexColor.length < 6) { - hexColor = "0" + hexColor; - } + const hexColor = color.toString(16).padStart(6, "0"); settings.store.color = hexColor; updateColorVars(hexColor); @@ -59,7 +56,8 @@ function ThemeSettings() { {lightnessWarning && Selected color is very light} {lightModeWarning && Light mode isn't supported}
- : null} + : null + }
); } @@ -85,7 +83,7 @@ const settings = definePluginSettings({ export default definePlugin({ name: "ClientTheme", - authors: [Devs.F53], + authors: [Devs.F53, Devs.Nuckyz], description: "Recreation of the old client theme experiment. Add a color to your Discord client theme", settings, @@ -113,8 +111,9 @@ export default definePlugin({ } }); +const variableRegex = /(--primary-[5-9]\d{2}-hsl):.*?(\S*)%;/g; + async function generateColorOffsets() { - const variableRegex = /(--primary-[5-9]\d{2}-hsl):.*?(\S*)%;/g; const styleLinkNodes = document.querySelectorAll('link[rel="stylesheet"]'); const variableLightness = {} as Record; @@ -213,7 +212,7 @@ function hexToHSL(hexCode: string) { } // Minimized math just for lightness, lowers lag when changing colors -function hexToLightness(hexCode) { +function hexToLightness(hexCode: string) { // Hex => RGB normalized to 0-1 const r = parseInt(hexCode.substring(0, 2), 16) / 255; const g = parseInt(hexCode.substring(2, 4), 16) / 255; From 371b5b0be8a6a6fab64ecb2d03c760f85820a063 Mon Sep 17 00:00:00 2001 From: V Date: Wed, 22 Nov 2023 06:14:16 +0100 Subject: [PATCH 192/632] allow plugins to specify how soon their start() method is called --- src/Vencord.ts | 16 +++++++++++----- src/plugins/clientTheme/index.tsx | 22 +++++----------------- src/plugins/index.ts | 10 ++++++++-- src/utils/types.ts | 14 ++++++++++++++ 4 files changed, 38 insertions(+), 24 deletions(-) diff --git a/src/Vencord.ts b/src/Vencord.ts index 83c69e738..a106a0b7d 100644 --- a/src/Vencord.ts +++ b/src/Vencord.ts @@ -27,6 +27,8 @@ export { PlainSettings, Settings }; import "./utils/quickCss"; import "./webpack/patchWebpack"; +import { StartAt } from "@utils/types"; + import { get as dsGet } from "./api/DataStore"; import { showNotification } from "./api/Notifications"; import { PlainSettings, Settings } from "./api/Settings"; @@ -79,7 +81,7 @@ async function syncSettings() { async function init() { await onceReady; - startAllPlugins(); + startAllPlugins(StartAt.WebpackReady); syncSettings(); @@ -130,13 +132,17 @@ async function init() { } } +startAllPlugins(StartAt.Init); init(); -if (IS_DISCORD_DESKTOP && Settings.winNativeTitleBar && navigator.platform.toLowerCase().startsWith("win")) { - document.addEventListener("DOMContentLoaded", () => { +document.addEventListener("DOMContentLoaded", () => { + startAllPlugins(StartAt.DOMContentLoaded); + + if (IS_DISCORD_DESKTOP && Settings.winNativeTitleBar && navigator.platform.toLowerCase().startsWith("win")) { document.head.append(Object.assign(document.createElement("style"), { id: "vencord-native-titlebar-style", textContent: "[class*=titleBar]{display: none!important}" })); - }, { once: true }); -} + } +}, { once: true }); + diff --git a/src/plugins/clientTheme/index.tsx b/src/plugins/clientTheme/index.tsx index d0026c759..7cda33e25 100644 --- a/src/plugins/clientTheme/index.tsx +++ b/src/plugins/clientTheme/index.tsx @@ -12,7 +12,7 @@ import { getTheme, Theme } from "@utils/discord"; import { Margins } from "@utils/margins"; import { classes } from "@utils/misc"; import { LazyComponent } from "@utils/react"; -import definePlugin, { OptionType } from "@utils/types"; +import definePlugin, { OptionType, StartAt } from "@utils/types"; import { findByCode } from "@webpack"; import { Button, Forms } from "@webpack/common"; @@ -87,25 +87,13 @@ export default definePlugin({ description: "Recreation of the old client theme experiment. Add a color to your Discord client theme", settings, - patches: [ - { - find: "Could not find app-mount", - replacement: { - match: /(?<=Could not find app-mount"\))/, - replace: ",$self.addThemeInitializer()" - } - } - ], - - addThemeInitializer() { - document.addEventListener("DOMContentLoaded", this.themeInitializer = () => { - updateColorVars(settings.store.color); - generateColorOffsets(); - }); + startAt: StartAt.DOMContentLoaded, + start() { + updateColorVars(settings.store.color); + generateColorOffsets(); }, stop() { - document.removeEventListener("DOMContentLoaded", this.themeInitializer); document.getElementById("clientThemeVars")?.remove(); document.getElementById("clientThemeOffsets")?.remove(); } diff --git a/src/plugins/index.ts b/src/plugins/index.ts index f6d577269..234838606 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -19,7 +19,7 @@ import { registerCommand, unregisterCommand } from "@api/Commands"; import { Settings } from "@api/Settings"; import { Logger } from "@utils/Logger"; -import { Patch, Plugin } from "@utils/types"; +import { Patch, Plugin, StartAt } from "@utils/types"; import { FluxDispatcher } from "@webpack/common"; import { FluxEvents } from "@webpack/types"; @@ -85,9 +85,15 @@ for (const p of pluginsValues) { } } -export const startAllPlugins = traceFunction("startAllPlugins", function startAllPlugins() { +export const startAllPlugins = traceFunction("startAllPlugins", function startAllPlugins(target: StartAt) { + logger.info(`Starting plugins (stage ${target})`); for (const name in Plugins) if (isPluginEnabled(name)) { + const p = Plugins[name]; + + const startAt = p.startAt ?? StartAt.WebpackReady; + if (startAt !== target) continue; + startPlugin(Plugins[name]); } }); diff --git a/src/utils/types.ts b/src/utils/types.ts index b32b127b0..7305cd01a 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -80,6 +80,11 @@ export interface PluginDef { * Whether this plugin should be enabled by default, but can be disabled */ enabledByDefault?: boolean; + /** + * When to call the start() method + * @default StartAt.WebpackReady + */ + startAt?: StartAt, /** * Optionally provide settings that the user can configure in the Plugins tab of settings. * @deprecated Use `settings` instead @@ -117,6 +122,15 @@ export interface PluginDef { tags?: string[]; } +export const enum StartAt { + /** Right away, as soon as Vencord initialised */ + Init = "Init", + /** On the DOMContentLoaded event, so once the document is ready */ + DOMContentLoaded = "DOMContentLoaded", + /** Once Discord's core webpack modules have finished loading, so as soon as things like react and flux are available */ + WebpackReady = "WebpackReady" +} + export const enum OptionType { STRING, NUMBER, From ffe6512693c7828c1d3c3bb36db69d69a93abe3f Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 22 Nov 2023 02:49:08 -0300 Subject: [PATCH 193/632] Improve component finding api and migrate plugins to use them --- src/components/PluginSettings/PluginModal.tsx | 6 +- src/plugins/betterFolders/FolderSideBar.tsx | 6 +- src/plugins/clientTheme/index.tsx | 5 +- src/plugins/customRPC/index.tsx | 6 +- src/plugins/gameActivityToggle/index.tsx | 4 +- src/plugins/messageLinkEmbeds/index.tsx | 10 +-- .../serverProfile/GuildProfileModal.tsx | 6 +- src/plugins/showConnections/VerifiedIcon.tsx | 6 +- src/plugins/showConnections/index.tsx | 6 +- .../components/HiddenChannelLockScreen.tsx | 10 +-- src/plugins/voiceMessages/VoicePreview.tsx | 5 +- src/plugins/whoReacted/index.tsx | 6 +- src/utils/react.tsx | 68 ++++++++++++++----- src/webpack/webpack.ts | 2 +- 14 files changed, 89 insertions(+), 57 deletions(-) diff --git a/src/components/PluginSettings/PluginModal.tsx b/src/components/PluginSettings/PluginModal.tsx index 03789ac6c..7d5750dfa 100644 --- a/src/components/PluginSettings/PluginModal.tsx +++ b/src/components/PluginSettings/PluginModal.tsx @@ -24,9 +24,9 @@ import { proxyLazy } from "@utils/lazy"; import { Margins } from "@utils/margins"; import { classes, isObjectEmpty } from "@utils/misc"; import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize } from "@utils/modal"; -import { LazyComponent } from "@utils/react"; +import { findComponentByCodeLazy } from "@utils/react"; import { OptionType, Plugin } from "@utils/types"; -import { findByCode, findByPropsLazy } from "@webpack"; +import { findByPropsLazy } from "@webpack"; import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common"; import { User } from "discord-types/general"; import { Constructor } from "type-fest"; @@ -42,7 +42,7 @@ import { } from "./components"; import { openContributorModal } from "./ContributorModal"; -const UserSummaryItem = LazyComponent(() => findByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers")); +const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers"); const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar"); const UserRecord: Constructor> = proxyLazy(() => UserStore.getCurrentUser().constructor) as any; diff --git a/src/plugins/betterFolders/FolderSideBar.tsx b/src/plugins/betterFolders/FolderSideBar.tsx index 97959873f..13dd9ac88 100644 --- a/src/plugins/betterFolders/FolderSideBar.tsx +++ b/src/plugins/betterFolders/FolderSideBar.tsx @@ -17,8 +17,8 @@ */ import ErrorBoundary from "@components/ErrorBoundary"; -import { LazyComponent } from "@utils/react"; -import { find, findByPropsLazy, findStoreLazy } from "@webpack"; +import { findComponentByCodeLazy } from "@utils/react"; +import { findByPropsLazy, findStoreLazy } from "@webpack"; import { useStateFromStores } from "@webpack/common"; import type { CSSProperties } from "react"; @@ -26,7 +26,7 @@ import { ExpandedGuildFolderStore, settings } from "."; const ChannelRTCStore = findStoreLazy("ChannelRTCStore"); const Animations = findByPropsLazy("a", "animated", "useTransition"); -const GuildsBar = LazyComponent(() => find(m => m.type?.toString().includes('("guildsnav")'))); +const GuildsBar = findComponentByCodeLazy('("guildsnav")'); export default ErrorBoundary.wrap(guildsBarProps => { const expandedFolders = useStateFromStores([ExpandedGuildFolderStore], () => ExpandedGuildFolderStore.getExpandedFolders()); diff --git a/src/plugins/clientTheme/index.tsx b/src/plugins/clientTheme/index.tsx index 7cda33e25..e5de67e40 100644 --- a/src/plugins/clientTheme/index.tsx +++ b/src/plugins/clientTheme/index.tsx @@ -11,12 +11,11 @@ import { Devs } from "@utils/constants"; import { getTheme, Theme } from "@utils/discord"; import { Margins } from "@utils/margins"; import { classes } from "@utils/misc"; -import { LazyComponent } from "@utils/react"; +import { findComponentByCodeLazy } from "@utils/react"; import definePlugin, { OptionType, StartAt } from "@utils/types"; -import { findByCode } from "@webpack"; import { Button, Forms } from "@webpack/common"; -const ColorPicker = LazyComponent(() => findByCode(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR")); +const ColorPicker = findComponentByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR"); const colorPresets = [ "#1E1514", "#172019", "#13171B", "#1C1C28", "#402D2D", diff --git a/src/plugins/customRPC/index.tsx b/src/plugins/customRPC/index.tsx index feed52fde..64bfd4609 100644 --- a/src/plugins/customRPC/index.tsx +++ b/src/plugins/customRPC/index.tsx @@ -20,12 +20,12 @@ import { definePluginSettings, Settings } from "@api/Settings"; import { Link } from "@components/Link"; import { Devs } from "@utils/constants"; import { isTruthy } from "@utils/guards"; -import { useAwaiter } from "@utils/react"; +import { findComponentByCodeLazy, useAwaiter } from "@utils/react"; import definePlugin, { OptionType } from "@utils/types"; -import { findByCodeLazy, findByPropsLazy } from "@webpack"; +import { findByPropsLazy } from "@webpack"; import { ApplicationAssetUtils, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common"; -const ActivityComponent = findByCodeLazy("onOpenGameProfile"); +const ActivityComponent = findComponentByCodeLazy("onOpenGameProfile"); const ActivityClassName = findByPropsLazy("activity", "buttonColor"); const Colors = findByPropsLazy("profileColors"); diff --git a/src/plugins/gameActivityToggle/index.tsx b/src/plugins/gameActivityToggle/index.tsx index 735f124c5..cc68d55b1 100644 --- a/src/plugins/gameActivityToggle/index.tsx +++ b/src/plugins/gameActivityToggle/index.tsx @@ -19,13 +19,13 @@ import { disableStyle, enableStyle } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; +import { findComponentByCodeLazy } from "@utils/react"; import definePlugin from "@utils/types"; -import { findByCodeLazy } from "@webpack"; import { StatusSettingsStores } from "@webpack/common"; import style from "./style.css?managed"; -const Button = findByCodeLazy("Button.Sizes.NONE,disabled:"); +const Button = findComponentByCodeLazy("Button.Sizes.NONE,disabled:"); function makeIcon(showCurrentGame?: boolean) { return function () { diff --git a/src/plugins/messageLinkEmbeds/index.tsx b/src/plugins/messageLinkEmbeds/index.tsx index e600d737d..d8c3c99f9 100644 --- a/src/plugins/messageLinkEmbeds/index.tsx +++ b/src/plugins/messageLinkEmbeds/index.tsx @@ -22,9 +22,9 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants.js"; import { classes } from "@utils/misc"; import { Queue } from "@utils/Queue"; -import { LazyComponent } from "@utils/react"; +import { findComponentByCodeLazy } from "@utils/react"; import definePlugin, { OptionType } from "@utils/types"; -import { find, findByCode, findByPropsLazy } from "@webpack"; +import { findByPropsLazy } from "@webpack"; import { Button, ChannelStore, @@ -45,9 +45,9 @@ const messageCache = new Map(); -const Embed = LazyComponent(() => findByCode(".inlineMediaEmbed")); -const AutoModEmbed = LazyComponent(() => findByCode(".withFooter]:", "childrenMessageContent:")); -const ChannelMessage = LazyComponent(() => find(m => m.type?.toString()?.includes("renderSimpleAccessories)"))); +const Embed = findComponentByCodeLazy(".inlineMediaEmbed"); +const AutoModEmbed = findComponentByCodeLazy(".withFooter]:", "childrenMessageContent:"); +const ChannelMessage = findComponentByCodeLazy("renderSimpleAccessories)"); const SearchResultClasses = findByPropsLazy("message", "searchResult"); diff --git a/src/plugins/serverProfile/GuildProfileModal.tsx b/src/plugins/serverProfile/GuildProfileModal.tsx index 80f0842bb..9def7bf99 100644 --- a/src/plugins/serverProfile/GuildProfileModal.tsx +++ b/src/plugins/serverProfile/GuildProfileModal.tsx @@ -10,14 +10,14 @@ import { classNameFactory } from "@api/Styles"; import { openImageModal, openUserProfile } from "@utils/discord"; import { classes } from "@utils/misc"; import { ModalRoot, ModalSize, openModal } from "@utils/modal"; -import { LazyComponent, useAwaiter } from "@utils/react"; -import { findByProps, findByPropsLazy } from "@webpack"; +import { findExportedComponentLazy, useAwaiter } from "@utils/react"; +import { findByPropsLazy } from "@webpack"; import { FluxDispatcher, Forms, GuildChannelStore, GuildMemberStore, moment, Parser, PresenceStore, RelationshipStore, ScrollerThin, SnowflakeUtils, TabBar, Timestamp, useEffect, UserStore, UserUtils, useState, useStateFromStores } from "@webpack/common"; import { Guild, User } from "discord-types/general"; const IconUtils = findByPropsLazy("getGuildBannerURL"); const IconClasses = findByPropsLazy("icon", "acronym", "childWrapper"); -const FriendRow = LazyComponent(() => findByProps("FriendRow").FriendRow); +const FriendRow = findExportedComponentLazy("FriendRow"); const cl = classNameFactory("vc-gp-"); diff --git a/src/plugins/showConnections/VerifiedIcon.tsx b/src/plugins/showConnections/VerifiedIcon.tsx index 79e27c455..200050694 100644 --- a/src/plugins/showConnections/VerifiedIcon.tsx +++ b/src/plugins/showConnections/VerifiedIcon.tsx @@ -16,12 +16,12 @@ * along with this program. If not, see . */ -import { LazyComponent } from "@utils/react"; -import { findByCode, findLazy } from "@webpack"; +import { findComponentByCodeLazy } from "@utils/react"; +import { findLazy } from "@webpack"; import { i18n, useToken } from "@webpack/common"; const ColorMap = findLazy(m => m.colors?.INTERACTIVE_MUTED?.css); -const VerifiedIconComponent = LazyComponent(() => findByCode(".CONNECTIONS_ROLE_OFFICIAL_ICON_TOOLTIP")); +const VerifiedIconComponent = findComponentByCodeLazy(".CONNECTIONS_ROLE_OFFICIAL_ICON_TOOLTIP"); export function VerifiedIcon() { const color = useToken(ColorMap.colors.INTERACTIVE_MUTED).hex(); diff --git a/src/plugins/showConnections/index.tsx b/src/plugins/showConnections/index.tsx index 948bdb83b..2d2122d60 100644 --- a/src/plugins/showConnections/index.tsx +++ b/src/plugins/showConnections/index.tsx @@ -24,15 +24,15 @@ import { Flex } from "@components/Flex"; import { CopyIcon, LinkIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; import { copyWithToast } from "@utils/misc"; -import { LazyComponent } from "@utils/react"; +import { findComponentByCodeLazy } from "@utils/react"; import definePlugin, { OptionType } from "@utils/types"; -import { findByCode, findByCodeLazy, findByPropsLazy, findStoreLazy } from "@webpack"; +import { findByCodeLazy, findByPropsLazy, findStoreLazy } from "@webpack"; import { Text, Tooltip, UserProfileStore } from "@webpack/common"; import { User } from "discord-types/general"; import { VerifiedIcon } from "./VerifiedIcon"; -const Section = LazyComponent(() => findByCode(".lastSection]:")); +const Section = findComponentByCodeLazy(".lastSection]: "); const ThemeStore = findStoreLazy("ThemeStore"); const platforms: { get(type: string): ConnectionPlatform; } = findByPropsLazy("isSupported", "getByUrl"); const getTheme: (user: User, displayProfile: any) => any = findByCodeLazy(',"--profile-gradient-primary-color"'); diff --git a/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx b/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx index 26efce1d8..5231fe8e7 100644 --- a/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx +++ b/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx @@ -18,9 +18,9 @@ import { Settings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; -import { LazyComponent } from "@utils/react"; +import { findComponentByCodeLazy, findComponentLazy } from "@utils/react"; import { formatDuration } from "@utils/text"; -import { find, findByCode, findByPropsLazy } from "@webpack"; +import { findByPropsLazy } from "@webpack"; import { EmojiStore, FluxDispatcher, GuildMemberStore, GuildStore, moment, Parser, PermissionsBits, PermissionStore, SnowflakeUtils, Text, Timestamp, Tooltip, useEffect, useState } from "@webpack/common"; import type { Channel } from "discord-types/general"; @@ -81,14 +81,14 @@ const enum ChannelFlags { const ChatScrollClasses = findByPropsLazy("auto", "content", "scrollerBase"); const ChatClasses = findByPropsLazy("chat", "content", "noChat", "chatContent"); -const ChannelBeginHeader = LazyComponent(() => findByCode(".Messages.ROLE_REQUIRED_SINGLE_USER_MESSAGE")); -const TagComponent = LazyComponent(() => find(m => { +const ChannelBeginHeader = findComponentByCodeLazy(".Messages.ROLE_REQUIRED_SINGLE_USER_MESSAGE"); +const TagComponent = findComponentLazy(m => { if (typeof m !== "function") return false; const code = Function.prototype.toString.call(m); // Get the component which doesn't include increasedActivity return code.includes(".Messages.FORUM_TAG_A11Y_FILTER_BY_TAG") && !code.includes("increasedActivityPill"); -})); +}); const EmojiParser = findByPropsLazy("convertSurrogateToName"); const EmojiUtils = findByPropsLazy("getURL", "buildEmojiReactionColorsPlatformed"); diff --git a/src/plugins/voiceMessages/VoicePreview.tsx b/src/plugins/voiceMessages/VoicePreview.tsx index 912c55ae0..d2c62336b 100644 --- a/src/plugins/voiceMessages/VoicePreview.tsx +++ b/src/plugins/voiceMessages/VoicePreview.tsx @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -import { LazyComponent, useTimer } from "@utils/react"; -import { find } from "@webpack"; +import { findComponentByCodeLazy, useTimer } from "@utils/react"; import { cl } from "./utils"; @@ -25,7 +24,7 @@ interface VoiceMessageProps { src: string; waveform: string; } -const VoiceMessage = LazyComponent(() => find(m => m.type?.toString().includes("waveform:"))); +const VoiceMessage = findComponentByCodeLazy("waveform:"); export type VoicePreviewOptions = { src?: string; diff --git a/src/plugins/whoReacted/index.tsx b/src/plugins/whoReacted/index.tsx index fb84c155a..15395a1f3 100644 --- a/src/plugins/whoReacted/index.tsx +++ b/src/plugins/whoReacted/index.tsx @@ -20,14 +20,14 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { sleep } from "@utils/misc"; import { Queue } from "@utils/Queue"; -import { LazyComponent, useForceUpdater } from "@utils/react"; +import { findComponentByCodeLazy, useForceUpdater } from "@utils/react"; import definePlugin from "@utils/types"; -import { findByCode, findByPropsLazy } from "@webpack"; +import { findByPropsLazy } from "@webpack"; import { ChannelStore, FluxDispatcher, React, RestAPI, Tooltip } from "@webpack/common"; import { CustomEmoji } from "@webpack/types"; import { Message, ReactionEmoji, User } from "discord-types/general"; -const UserSummaryItem = LazyComponent(() => findByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers")); +const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers"); const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar"); const queue = new Queue(); diff --git a/src/utils/react.tsx b/src/utils/react.tsx index 0181c95b8..2e3352bcc 100644 --- a/src/utils/react.tsx +++ b/src/utils/react.tsx @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +import { FilterFn, filters, find, findByProps } from "@webpack"; import { React, useEffect, useMemo, useReducer, useState } from "@webpack/common"; import { makeLazy } from "./lazy"; @@ -77,7 +78,6 @@ interface AwaiterOpts { * @param fallbackValue The fallback value that will be used until the promise resolved * @returns [value, error, isPending] */ - export function useAwaiter(factory: () => Promise): AwaiterRes; export function useAwaiter(factory: () => Promise, providedOpts: AwaiterOpts): AwaiterRes; export function useAwaiter(factory: () => Promise, providedOpts?: AwaiterOpts): AwaiterRes { @@ -113,31 +113,16 @@ export function useAwaiter(factory: () => Promise, providedOpts?: AwaiterO return [state.value, state.error, state.pending]; } + /** * Returns a function that can be used to force rerender react components */ - export function useForceUpdater(): () => void; export function useForceUpdater(withDep: true): [unknown, () => void]; export function useForceUpdater(withDep?: true) { const r = useReducer(x => x + 1, 0); return withDep ? r : r[1]; } -/** - * A lazy component. The factory method is called on first render. For example useful - * for const Component = LazyComponent(() => findByDisplayName("...").default) - * @param factory Function returning a Component - * @param attempts How many times to try to get the component before giving up - * @returns Result of factory function - */ - -export function LazyComponent(factory: () => React.ComponentType, attempts = 5) { - const get = makeLazy(factory, attempts); - return (props: T) => { - const Component = get() ?? NoopComponent; - return ; - }; -} interface TimerOpts { interval?: number; @@ -159,3 +144,52 @@ export function useTimer({ interval = 1000, deps = [] }: TimerOpts) { return time; } + +/** + * Finds the component which includes all the given code. Checks for plain components, memos and forwardRefs + */ +export function findComponentByCode(...code: string[]) { + const filter = filters.byCode(...code); + return find(m => { + if (filter(m)) return true; + if (!m.$$typeof) return false; + if (m.type) return filter(m.type); // memos + if (m.render) return filter(m.render); // forwardRefs + return false; + }) ?? NoopComponent; +} + +/** + * Finds the first component that matches the filter, lazily. + */ +export function findComponentLazy(filter: FilterFn) { + return LazyComponent(() => find(filter)); +} + +/** + * Finds the first component that includes all the given code, lazily + */ +export function findComponentByCodeLazy(...code: string[]) { + return LazyComponent(() => findComponentByCode(...code)); +} + +/** + * Finds the first component that is exported by the first prop name, lazily + */ +export function findExportedComponentLazy(...props: string[]) { + return LazyComponent(() => findByProps(...props)?.[props[0]]); +} + +/** + * A lazy component. The factory method is called on first render. + * @param factory Function returning a Component + * @param attempts How many times to try to get the component before giving up + * @returns Result of factory function + */ +export function LazyComponent(factory: () => React.ComponentType, attempts = 5) { + const get = makeLazy(factory, attempts); + return (props: T) => { + const Component = get() ?? NoopComponent; + return ; + }; +} diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index 3ef5ac80a..4fb940561 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -386,7 +386,7 @@ export function findStore(name: string) { } /** - * findByDisplayName but lazy + * findStore but lazy */ export function findStoreLazy(name: string) { return proxyLazy(() => findStore(name)); From b21b6d7e5dae67537864274bd9e9f58c1f827b8f Mon Sep 17 00:00:00 2001 From: V Date: Wed, 22 Nov 2023 06:48:59 +0100 Subject: [PATCH 194/632] Make it possible to destructure lazy webpack finds --- src/plugins/betterFolders/index.tsx | 5 ++--- src/plugins/greetStickerPicker/index.tsx | 5 ++--- src/utils/lazy.ts | 25 +++++++++++++++++++++--- src/webpack/common/stores.ts | 19 +++++++++--------- 4 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/plugins/betterFolders/index.tsx b/src/plugins/betterFolders/index.tsx index 8f40d90ff..68c50bcfd 100644 --- a/src/plugins/betterFolders/index.tsx +++ b/src/plugins/betterFolders/index.tsx @@ -18,9 +18,8 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; -import { proxyLazy } from "@utils/lazy"; import definePlugin, { OptionType } from "@utils/types"; -import { findByProps, findByPropsLazy, findStoreLazy } from "@webpack"; +import { findByPropsLazy, findStoreLazy } from "@webpack"; import { FluxDispatcher, i18n } from "@webpack/common"; import FolderSideBar from "./FolderSideBar"; @@ -31,7 +30,7 @@ enum FolderIconDisplay { MoreThanOneFolderExpanded } -const GuildsTree = proxyLazy(() => findByProps("GuildsTree").GuildsTree); +const { GuildsTree } = findByPropsLazy("GuildsTree"); const SortedGuildStore = findStoreLazy("SortedGuildStore"); export const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore"); const FolderUtils = findByPropsLazy("move", "toggleGuildFolderExpand"); diff --git a/src/plugins/greetStickerPicker/index.tsx b/src/plugins/greetStickerPicker/index.tsx index 2c8f33799..845c7a1b8 100644 --- a/src/plugins/greetStickerPicker/index.tsx +++ b/src/plugins/greetStickerPicker/index.tsx @@ -18,9 +18,8 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; -import { proxyLazy } from "@utils/lazy"; import definePlugin, { OptionType } from "@utils/types"; -import { findByProps, findByPropsLazy } from "@webpack"; +import { findByPropsLazy } from "@webpack"; import { ContextMenu, FluxDispatcher, Menu } from "@webpack/common"; import { Channel, Message } from "discord-types/general"; @@ -51,7 +50,7 @@ const settings = definePluginSettings({ }>(); const MessageActions = findByPropsLazy("sendGreetMessage"); -const WELCOME_STICKERS = proxyLazy(() => findByProps("WELCOME_STICKERS")?.WELCOME_STICKERS); +const { WELCOME_STICKERS } = findByPropsLazy("WELCOME_STICKERS"); function greet(channel: Channel, message: Message, stickers: string[]) { const options = MessageActions.getSendMessageOptionsForReply({ diff --git a/src/utils/lazy.ts b/src/utils/lazy.ts index 4bac45bc2..1c89d5110 100644 --- a/src/utils/lazy.ts +++ b/src/utils/lazy.ts @@ -43,7 +43,6 @@ for (const method of [ "construct", "defineProperty", "deleteProperty", - "get", "getOwnPropertyDescriptor", "getPrototypeOf", "has", @@ -86,7 +85,11 @@ handler.getOwnPropertyDescriptor = (target, p) => { * Note that the example below exists already as an api, see {@link findByPropsLazy} * @example const mod = proxyLazy(() => findByProps("blah")); console.log(mod.blah); */ -export function proxyLazy(factory: () => T, attempts = 5): T { +export function proxyLazy(factory: () => T, attempts = 5, isChild = false): T { + let isSameTick = true; + if (!isChild) + setTimeout(() => isSameTick = false, 0); + let tries = 0; const proxyDummy = Object.assign(function () { }, { [kCACHE]: void 0 as T | undefined, @@ -100,5 +103,21 @@ export function proxyLazy(factory: () => T, attempts = 5): T { } }); - return new Proxy(proxyDummy, handler) as any; + return new Proxy(proxyDummy, { + ...handler, + get(target, p, receiver) { + // if we're still in the same tick, it means the lazy was immediately used. + // thus, we lazy proxy the get access to make things like destructuring work as expected + // meow here will also be a lazy + // `const { meow } = findByPropsLazy("meow");` + if (!isChild && isSameTick) + return proxyLazy( + () => Reflect.get(target[kGET](), p, receiver), + attempts, + true + ); + + return Reflect.get(target[kGET](), p, receiver); + } + }) as any; } diff --git a/src/webpack/common/stores.ts b/src/webpack/common/stores.ts index d10b18664..1b00180fc 100644 --- a/src/webpack/common/stores.ts +++ b/src/webpack/common/stores.ts @@ -16,11 +16,10 @@ * along with this program. If not, see . */ -import { proxyLazy } from "@utils/lazy"; import type * as Stores from "discord-types/stores"; // eslint-disable-next-line path-alias/no-relative -import { filters, findByProps, findByPropsLazy, mapMangledModuleLazy } from "../webpack"; +import { filters, findByPropsLazy, mapMangledModuleLazy } from "../webpack"; import { waitForStore } from "./internal"; import * as t from "./types/stores"; @@ -78,13 +77,15 @@ export const MaskedLinkStore = mapMangledModuleLazy('"MaskedLinkStore"', { * * @example const user = useStateFromStores([UserStore], () => UserStore.getCurrentUser(), null, (old, current) => old.id === current.id); */ -export const useStateFromStores: ( - stores: t.FluxStore[], - mapper: () => T, - idk?: any, - isEqual?: (old: T, newer: T) => boolean -) => T - = proxyLazy(() => findByProps("useStateFromStores").useStateFromStores); +export const { useStateFromStores }: { + useStateFromStores: ( + stores: t.FluxStore[], + mapper: () => T, + idk?: any, + isEqual?: (old: T, newer: T) => boolean + ) => T; +} + = findByPropsLazy("useStateFromStores"); waitForStore("DraftStore", s => DraftStore = s); waitForStore("UserStore", s => UserStore = s); From 7b24c8ac69b194783caaeb79ea04668554c4bec4 Mon Sep 17 00:00:00 2001 From: V Date: Wed, 22 Nov 2023 07:04:17 +0100 Subject: [PATCH 195/632] move new webpack methods to more appropriate file --- src/components/PluginSettings/PluginModal.tsx | 3 +- src/plugins/betterFolders/FolderSideBar.tsx | 3 +- src/plugins/clientTheme/index.tsx | 2 +- src/plugins/customRPC/index.tsx | 4 +-- src/plugins/gameActivityToggle/index.tsx | 2 +- src/plugins/messageLinkEmbeds/index.tsx | 3 +- .../serverProfile/GuildProfileModal.tsx | 4 +-- src/plugins/showConnections/VerifiedIcon.tsx | 3 +- src/plugins/showConnections/index.tsx | 3 +- .../components/HiddenChannelLockScreen.tsx | 3 +- src/plugins/voiceMessages/VoicePreview.tsx | 3 +- src/plugins/whoReacted/index.tsx | 4 +-- src/utils/react.tsx | 36 ------------------- src/webpack/webpack.ts | 36 +++++++++++++++++++ 14 files changed, 52 insertions(+), 57 deletions(-) diff --git a/src/components/PluginSettings/PluginModal.tsx b/src/components/PluginSettings/PluginModal.tsx index 7d5750dfa..34de43c2d 100644 --- a/src/components/PluginSettings/PluginModal.tsx +++ b/src/components/PluginSettings/PluginModal.tsx @@ -24,9 +24,8 @@ import { proxyLazy } from "@utils/lazy"; import { Margins } from "@utils/margins"; import { classes, isObjectEmpty } from "@utils/misc"; import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize } from "@utils/modal"; -import { findComponentByCodeLazy } from "@utils/react"; import { OptionType, Plugin } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common"; import { User } from "discord-types/general"; import { Constructor } from "type-fest"; diff --git a/src/plugins/betterFolders/FolderSideBar.tsx b/src/plugins/betterFolders/FolderSideBar.tsx index 13dd9ac88..53d24ed93 100644 --- a/src/plugins/betterFolders/FolderSideBar.tsx +++ b/src/plugins/betterFolders/FolderSideBar.tsx @@ -17,8 +17,7 @@ */ import ErrorBoundary from "@components/ErrorBoundary"; -import { findComponentByCodeLazy } from "@utils/react"; -import { findByPropsLazy, findStoreLazy } from "@webpack"; +import { findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack"; import { useStateFromStores } from "@webpack/common"; import type { CSSProperties } from "react"; diff --git a/src/plugins/clientTheme/index.tsx b/src/plugins/clientTheme/index.tsx index e5de67e40..7b30863e7 100644 --- a/src/plugins/clientTheme/index.tsx +++ b/src/plugins/clientTheme/index.tsx @@ -11,8 +11,8 @@ import { Devs } from "@utils/constants"; import { getTheme, Theme } from "@utils/discord"; import { Margins } from "@utils/margins"; import { classes } from "@utils/misc"; -import { findComponentByCodeLazy } from "@utils/react"; import definePlugin, { OptionType, StartAt } from "@utils/types"; +import { findComponentByCodeLazy } from "@webpack"; import { Button, Forms } from "@webpack/common"; const ColorPicker = findComponentByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR"); diff --git a/src/plugins/customRPC/index.tsx b/src/plugins/customRPC/index.tsx index 64bfd4609..3653a0776 100644 --- a/src/plugins/customRPC/index.tsx +++ b/src/plugins/customRPC/index.tsx @@ -20,9 +20,9 @@ import { definePluginSettings, Settings } from "@api/Settings"; import { Link } from "@components/Link"; import { Devs } from "@utils/constants"; import { isTruthy } from "@utils/guards"; -import { findComponentByCodeLazy, useAwaiter } from "@utils/react"; +import { useAwaiter } from "@utils/react"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; import { ApplicationAssetUtils, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common"; const ActivityComponent = findComponentByCodeLazy("onOpenGameProfile"); diff --git a/src/plugins/gameActivityToggle/index.tsx b/src/plugins/gameActivityToggle/index.tsx index cc68d55b1..2b84d26f1 100644 --- a/src/plugins/gameActivityToggle/index.tsx +++ b/src/plugins/gameActivityToggle/index.tsx @@ -19,8 +19,8 @@ import { disableStyle, enableStyle } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; -import { findComponentByCodeLazy } from "@utils/react"; import definePlugin from "@utils/types"; +import { findComponentByCodeLazy } from "@webpack"; import { StatusSettingsStores } from "@webpack/common"; import style from "./style.css?managed"; diff --git a/src/plugins/messageLinkEmbeds/index.tsx b/src/plugins/messageLinkEmbeds/index.tsx index d8c3c99f9..762829992 100644 --- a/src/plugins/messageLinkEmbeds/index.tsx +++ b/src/plugins/messageLinkEmbeds/index.tsx @@ -22,9 +22,8 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants.js"; import { classes } from "@utils/misc"; import { Queue } from "@utils/Queue"; -import { findComponentByCodeLazy } from "@utils/react"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; import { Button, ChannelStore, diff --git a/src/plugins/serverProfile/GuildProfileModal.tsx b/src/plugins/serverProfile/GuildProfileModal.tsx index 9def7bf99..97b40b764 100644 --- a/src/plugins/serverProfile/GuildProfileModal.tsx +++ b/src/plugins/serverProfile/GuildProfileModal.tsx @@ -10,8 +10,8 @@ import { classNameFactory } from "@api/Styles"; import { openImageModal, openUserProfile } from "@utils/discord"; import { classes } from "@utils/misc"; import { ModalRoot, ModalSize, openModal } from "@utils/modal"; -import { findExportedComponentLazy, useAwaiter } from "@utils/react"; -import { findByPropsLazy } from "@webpack"; +import { useAwaiter } from "@utils/react"; +import { findByPropsLazy, findExportedComponentLazy } from "@webpack"; import { FluxDispatcher, Forms, GuildChannelStore, GuildMemberStore, moment, Parser, PresenceStore, RelationshipStore, ScrollerThin, SnowflakeUtils, TabBar, Timestamp, useEffect, UserStore, UserUtils, useState, useStateFromStores } from "@webpack/common"; import { Guild, User } from "discord-types/general"; diff --git a/src/plugins/showConnections/VerifiedIcon.tsx b/src/plugins/showConnections/VerifiedIcon.tsx index 200050694..ffdf21e63 100644 --- a/src/plugins/showConnections/VerifiedIcon.tsx +++ b/src/plugins/showConnections/VerifiedIcon.tsx @@ -16,8 +16,7 @@ * along with this program. If not, see . */ -import { findComponentByCodeLazy } from "@utils/react"; -import { findLazy } from "@webpack"; +import { findComponentByCodeLazy, findLazy } from "@webpack"; import { i18n, useToken } from "@webpack/common"; const ColorMap = findLazy(m => m.colors?.INTERACTIVE_MUTED?.css); diff --git a/src/plugins/showConnections/index.tsx b/src/plugins/showConnections/index.tsx index 2d2122d60..67fcc968d 100644 --- a/src/plugins/showConnections/index.tsx +++ b/src/plugins/showConnections/index.tsx @@ -24,9 +24,8 @@ import { Flex } from "@components/Flex"; import { CopyIcon, LinkIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; import { copyWithToast } from "@utils/misc"; -import { findComponentByCodeLazy } from "@utils/react"; import definePlugin, { OptionType } from "@utils/types"; -import { findByCodeLazy, findByPropsLazy, findStoreLazy } from "@webpack"; +import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack"; import { Text, Tooltip, UserProfileStore } from "@webpack/common"; import { User } from "discord-types/general"; diff --git a/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx b/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx index 5231fe8e7..649e87aa8 100644 --- a/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx +++ b/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx @@ -18,9 +18,8 @@ import { Settings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; -import { findComponentByCodeLazy, findComponentLazy } from "@utils/react"; import { formatDuration } from "@utils/text"; -import { findByPropsLazy } from "@webpack"; +import { findByPropsLazy, findComponentByCodeLazy, findComponentLazy } from "@webpack"; import { EmojiStore, FluxDispatcher, GuildMemberStore, GuildStore, moment, Parser, PermissionsBits, PermissionStore, SnowflakeUtils, Text, Timestamp, Tooltip, useEffect, useState } from "@webpack/common"; import type { Channel } from "discord-types/general"; diff --git a/src/plugins/voiceMessages/VoicePreview.tsx b/src/plugins/voiceMessages/VoicePreview.tsx index d2c62336b..0976f7946 100644 --- a/src/plugins/voiceMessages/VoicePreview.tsx +++ b/src/plugins/voiceMessages/VoicePreview.tsx @@ -16,7 +16,8 @@ * along with this program. If not, see . */ -import { findComponentByCodeLazy, useTimer } from "@utils/react"; +import { useTimer } from "@utils/react"; +import { findComponentByCodeLazy } from "@webpack"; import { cl } from "./utils"; diff --git a/src/plugins/whoReacted/index.tsx b/src/plugins/whoReacted/index.tsx index 15395a1f3..4a2bdeeda 100644 --- a/src/plugins/whoReacted/index.tsx +++ b/src/plugins/whoReacted/index.tsx @@ -20,9 +20,9 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { sleep } from "@utils/misc"; import { Queue } from "@utils/Queue"; -import { findComponentByCodeLazy, useForceUpdater } from "@utils/react"; +import { useForceUpdater } from "@utils/react"; import definePlugin from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; import { ChannelStore, FluxDispatcher, React, RestAPI, Tooltip } from "@webpack/common"; import { CustomEmoji } from "@webpack/types"; import { Message, ReactionEmoji, User } from "discord-types/general"; diff --git a/src/utils/react.tsx b/src/utils/react.tsx index 2e3352bcc..2d2bac39d 100644 --- a/src/utils/react.tsx +++ b/src/utils/react.tsx @@ -16,7 +16,6 @@ * along with this program. If not, see . */ -import { FilterFn, filters, find, findByProps } from "@webpack"; import { React, useEffect, useMemo, useReducer, useState } from "@webpack/common"; import { makeLazy } from "./lazy"; @@ -145,41 +144,6 @@ export function useTimer({ interval = 1000, deps = [] }: TimerOpts) { return time; } -/** - * Finds the component which includes all the given code. Checks for plain components, memos and forwardRefs - */ -export function findComponentByCode(...code: string[]) { - const filter = filters.byCode(...code); - return find(m => { - if (filter(m)) return true; - if (!m.$$typeof) return false; - if (m.type) return filter(m.type); // memos - if (m.render) return filter(m.render); // forwardRefs - return false; - }) ?? NoopComponent; -} - -/** - * Finds the first component that matches the filter, lazily. - */ -export function findComponentLazy(filter: FilterFn) { - return LazyComponent(() => find(filter)); -} - -/** - * Finds the first component that includes all the given code, lazily - */ -export function findComponentByCodeLazy(...code: string[]) { - return LazyComponent(() => findComponentByCode(...code)); -} - -/** - * Finds the first component that is exported by the first prop name, lazily - */ -export function findExportedComponentLazy(...props: string[]) { - return LazyComponent(() => findByProps(...props)?.[props[0]]); -} - /** * A lazy component. The factory method is called on first render. * @param factory Function returning a Component diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index 4fb940561..9e3a0aab3 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -18,6 +18,7 @@ import { proxyLazy } from "@utils/lazy"; import { Logger } from "@utils/Logger"; +import { LazyComponent, NoopComponent } from "@utils/react"; import type { WebpackInstance } from "discord-types/other"; import { traceFunction } from "../debug/Tracer"; @@ -392,6 +393,41 @@ export function findStoreLazy(name: string) { return proxyLazy(() => findStore(name)); } +/** + * Finds the component which includes all the given code. Checks for plain components, memos and forwardRefs + */ +export function findComponentByCode(...code: string[]) { + const filter = filters.byCode(...code); + return find(m => { + if (filter(m)) return true; + if (!m.$$typeof) return false; + if (m.type) return filter(m.type); // memos + if (m.render) return filter(m.render); // forwardRefs + return false; + }) ?? NoopComponent; +} + +/** + * Finds the first component that matches the filter, lazily. + */ +export function findComponentLazy(filter: FilterFn) { + return LazyComponent(() => find(filter)); +} + +/** + * Finds the first component that includes all the given code, lazily + */ +export function findComponentByCodeLazy(...code: string[]) { + return LazyComponent(() => findComponentByCode(...code)); +} + +/** + * Finds the first component that is exported by the first prop name, lazily + */ +export function findExportedComponentLazy(...props: string[]) { + return LazyComponent(() => findByProps(...props)?.[props[0]]); +} + /** * Wait for a module that matches the provided filter to be registered, * then call the callback with the module as the first argument From 93a95b6d565d7d6bb501b2776d641f6e25cac542 Mon Sep 17 00:00:00 2001 From: Jack <30497388+FieryFlames@users.noreply.github.com> Date: Wed, 22 Nov 2023 01:23:21 -0500 Subject: [PATCH 196/632] feat(patcher): Grouped replacements (#2009) Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Co-authored-by: V --- src/utils/types.ts | 2 ++ src/webpack/patchWebpack.ts | 33 +++++++++++++++++++++++++++------ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/utils/types.ts b/src/utils/types.ts index 7305cd01a..16867a43c 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -41,6 +41,8 @@ export interface Patch { all?: boolean; /** Do not warn if this patch did no changes */ noWarn?: boolean; + /** Only apply this set of replacements if all of them succeed. Use this if your replacements depend on each other */ + group?: boolean; predicate?(): boolean; } diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index b81415e61..82648f62b 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -204,6 +204,9 @@ function patchFactories(factories: Record()).add(patch.plugin); - logger.warn(`Patch by ${patch.plugin} had no effect (Module id is ${id}): ${replacement.match}`); - if (IS_DEV) { - logger.debug("Function Source:\n", code); + if (newCode === code) { + if (!patch.noWarn) { + (window.explosivePlugins ??= new Set()).add(patch.plugin); + logger.warn(`Patch by ${patch.plugin} had no effect (Module id is ${id}): ${replacement.match}`); + if (IS_DEV) { + logger.debug("Function Source:\n", code); + } + } + + if (patch.group) { + logger.warn(`Undoing patch ${patch.find} by ${patch.plugin} because replacement ${replacement.match} had no effect`); + code = previousCode; + mod = previousMod; + patchedBy.delete(patch.plugin); + break; } } else { code = newCode; @@ -259,9 +272,17 @@ function patchFactories(factories: Record Date: Thu, 23 Nov 2023 02:20:02 +0100 Subject: [PATCH 197/632] fix showConnections & better webpack errors --- src/plugins/showConnections/index.tsx | 2 +- src/webpack/webpack.ts | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/plugins/showConnections/index.tsx b/src/plugins/showConnections/index.tsx index 67fcc968d..d4d59465e 100644 --- a/src/plugins/showConnections/index.tsx +++ b/src/plugins/showConnections/index.tsx @@ -31,7 +31,7 @@ import { User } from "discord-types/general"; import { VerifiedIcon } from "./VerifiedIcon"; -const Section = findComponentByCodeLazy(".lastSection]: "); +const Section = findComponentByCodeLazy(".lastSection", "children:"); const ThemeStore = findStoreLazy("ThemeStore"); const platforms: { get(type: string): ConnectionPlatform; } = findByPropsLazy("isSupported", "getByUrl"); const getTheme: (user: User, displayProfile: any) => any = findByCodeLazy(',"--profile-gradient-primary-color"'); diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index 9e3a0aab3..d8743e308 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -18,7 +18,7 @@ import { proxyLazy } from "@utils/lazy"; import { Logger } from "@utils/Logger"; -import { LazyComponent, NoopComponent } from "@utils/react"; +import { LazyComponent } from "@utils/react"; import type { WebpackInstance } from "discord-types/other"; import { traceFunction } from "../debug/Tracer"; @@ -398,13 +398,18 @@ export function findStoreLazy(name: string) { */ export function findComponentByCode(...code: string[]) { const filter = filters.byCode(...code); - return find(m => { + const res = find(m => { if (filter(m)) return true; if (!m.$$typeof) return false; if (m.type) return filter(m.type); // memos if (m.render) return filter(m.render); // forwardRefs return false; - }) ?? NoopComponent; + }, { isIndirect: true }); + + if (!res) + handleModuleNotFound("findComponentByCode", ...code); + + return res; } /** From 6869705673de48d1f1463b851308c975c010a1f5 Mon Sep 17 00:00:00 2001 From: V Date: Thu, 23 Nov 2023 02:44:04 +0100 Subject: [PATCH 198/632] beef up ConsoleShortcuts --- src/plugins/consoleShortcuts/index.ts | 30 +++++++++++++++++++++++---- src/webpack/webpack.ts | 24 +++++++++++---------- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/src/plugins/consoleShortcuts/index.ts b/src/plugins/consoleShortcuts/index.ts index 1c23d60e7..10853f25a 100644 --- a/src/plugins/consoleShortcuts/index.ts +++ b/src/plugins/consoleShortcuts/index.ts @@ -62,23 +62,27 @@ export default definePlugin({ } let fakeRenderWin: WeakRef | undefined; + const find = newFindWrapper(f => f); return { + ...Vencord.Webpack.Common, wp: Vencord.Webpack, wpc: Webpack.wreq.c, wreq: Webpack.wreq, wpsearch: search, wpex: extract, - wpexs: (code: string) => Vencord.Webpack.extract(Vencord.Webpack.findModuleId(code)!), - find: newFindWrapper(f => f), + wpexs: (code: string) => extract(Webpack.findModuleId(code)!), + find, findAll, findByProps: newFindWrapper(filters.byProps), findAllByProps: (...props: string[]) => findAll(filters.byProps(...props)), findByCode: newFindWrapper(filters.byCode), findAllByCode: (code: string) => findAll(filters.byCode(code)), + findComponentByCode: newFindWrapper(filters.componentByCode), + findAllComponentsByCode: (...code: string[]) => findAll(filters.componentByCode(...code)), + findExportedComponent: (...props: string[]) => find(...props)[props[0]], findStore: newFindWrapper(filters.byStoreName), PluginsApi: Vencord.Plugins, plugins: Vencord.Plugins.plugins, - React, Settings: Vencord.Settings, Api: Vencord.Api, reload: () => location.reload(), @@ -92,7 +96,25 @@ export default definePlugin({ fakeRenderWin = new WeakRef(win); win.focus(); - ReactDOM.render(React.createElement(component, props), win.document.body); + const doc = win.document; + doc.body.style.margin = "1em"; + + if (!win.prepared) { + win.prepared = true; + + [...document.querySelectorAll("style"), ...document.querySelectorAll("link[rel=stylesheet]")].forEach(s => { + const n = s.cloneNode(true) as HTMLStyleElement | HTMLLinkElement; + + if (s.parentElement?.tagName === "HEAD") + doc.head.append(n); + else if (n.id?.startsWith("vencord-") || n.id?.startsWith("vcd-")) + doc.documentElement.append(n); + else + doc.body.append(n); + }); + } + + ReactDOM.render(React.createElement(component, props), doc.body.appendChild(document.createElement("div"))); } }; }, diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index d8743e308..c7be62da0 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -52,7 +52,18 @@ export const filters = { return true; }, byStoreName: (name: string): FilterFn => m => - m.constructor?.displayName === name + m.constructor?.displayName === name, + + componentByCode: (...code: string[]): FilterFn => { + const filter = filters.byCode(...code); + return m => { + if (filter(m)) return true; + if (!m.$$typeof) return false; + if (m.type) return filter(m.type); // memos + if (m.render) return filter(m.render); // forwardRefs + return false; + }; + } }; export const subscriptions = new Map(); @@ -397,18 +408,9 @@ export function findStoreLazy(name: string) { * Finds the component which includes all the given code. Checks for plain components, memos and forwardRefs */ export function findComponentByCode(...code: string[]) { - const filter = filters.byCode(...code); - const res = find(m => { - if (filter(m)) return true; - if (!m.$$typeof) return false; - if (m.type) return filter(m.type); // memos - if (m.render) return filter(m.render); // forwardRefs - return false; - }, { isIndirect: true }); - + const res = find(filters.componentByCode(...code), { isIndirect: true }); if (!res) handleModuleNotFound("findComponentByCode", ...code); - return res; } From 63451bad25c9f246e6d458f0472e2b57d696a581 Mon Sep 17 00:00:00 2001 From: V Date: Thu, 23 Nov 2023 03:11:17 +0100 Subject: [PATCH 199/632] Remove obsolete mapMangledModule ~ modules are no longer mangled --- src/plugins/gifPaste/index.ts | 8 ++-- src/plugins/greetStickerPicker/index.tsx | 4 +- src/plugins/imageZoom/index.tsx | 6 +-- .../components/RolesAndUsersPermissions.tsx | 6 +-- .../spotifyControls/PlayerComponent.tsx | 6 +-- src/utils/modal.tsx | 10 +---- src/webpack/common/menu.ts | 8 +--- src/webpack/common/stores.ts | 6 +-- src/webpack/common/types/menu.d.ts | 6 +-- src/webpack/common/types/utils.d.ts | 21 ++++++++++ src/webpack/common/utils.ts | 14 ++----- src/webpack/webpack.ts | 41 ------------------- 12 files changed, 46 insertions(+), 90 deletions(-) diff --git a/src/plugins/gifPaste/index.ts b/src/plugins/gifPaste/index.ts index 9ce88f9d2..3e864b31a 100644 --- a/src/plugins/gifPaste/index.ts +++ b/src/plugins/gifPaste/index.ts @@ -19,11 +19,9 @@ import { Devs } from "@utils/constants"; import { insertTextIntoChatInputBox } from "@utils/discord"; import definePlugin from "@utils/types"; -import { filters, mapMangledModuleLazy } from "@webpack"; +import { findByPropsLazy } from "@webpack"; -const ExpressionPickerState = mapMangledModuleLazy('name:"expression-picker-last-active-view"', { - close: filters.byCode("activeView:null", "setState") -}); +const { closeExpressionPicker } = findByPropsLazy("closeExpressionPicker"); export default definePlugin({ name: "GifPaste", @@ -41,7 +39,7 @@ export default definePlugin({ handleSelect(gif?: { url: string; }) { if (gif) { insertTextIntoChatInputBox(gif.url + " "); - ExpressionPickerState.close(); + closeExpressionPicker(); } } }); diff --git a/src/plugins/greetStickerPicker/index.tsx b/src/plugins/greetStickerPicker/index.tsx index 845c7a1b8..9623d422e 100644 --- a/src/plugins/greetStickerPicker/index.tsx +++ b/src/plugins/greetStickerPicker/index.tsx @@ -20,7 +20,7 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { findByPropsLazy } from "@webpack"; -import { ContextMenu, FluxDispatcher, Menu } from "@webpack/common"; +import { ContextMenuApi, FluxDispatcher, Menu } from "@webpack/common"; import { Channel, Message } from "discord-types/general"; interface Sticker { @@ -183,6 +183,6 @@ export default definePlugin({ } ) { if (!(props.message as any).deleted) - ContextMenu.open(event, () => ); + ContextMenuApi.openContextMenu(event, () => ); } }); diff --git a/src/plugins/imageZoom/index.tsx b/src/plugins/imageZoom/index.tsx index 75c944ebd..c14754c87 100644 --- a/src/plugins/imageZoom/index.tsx +++ b/src/plugins/imageZoom/index.tsx @@ -23,7 +23,7 @@ import { makeRange } from "@components/PluginSettings/components"; import { Devs } from "@utils/constants"; import { debounce } from "@utils/debounce"; import definePlugin, { OptionType } from "@utils/types"; -import { ContextMenu, Menu, React, ReactDOM } from "@webpack/common"; +import { ContextMenuApi, Menu, React, ReactDOM } from "@webpack/common"; import type { Root } from "react-dom/client"; import { Magnifier, MagnifierProps } from "./components/Magnifier"; @@ -89,7 +89,7 @@ const imageContextMenuPatch: NavContextMenuPatchCallback = children => () => { checked={settings.store.square} action={() => { settings.store.square = !settings.store.square; - ContextMenu.close(); + ContextMenuApi.closeContextMenu(); }} /> () => { checked={settings.store.nearestNeighbour} action={() => { settings.store.nearestNeighbour = !settings.store.nearestNeighbour; - ContextMenu.close(); + ContextMenuApi.closeContextMenu(); }} /> { if ((settings.store as any).unsafeViewAsRole && permission.type === PermissionType.Role) - ContextMenu.open(e, () => ( + ContextMenuApi.openContextMenu(e, () => ( ) => - ContextMenu.open(e, () => ); + ContextMenuApi.openContextMenu(e, () => ); } function Controls() { @@ -277,7 +277,7 @@ function Info({ track }: { track: Track; }) { alt="Album Image" onClick={() => setCoverExpanded(!coverExpanded)} onContextMenu={e => { - ContextMenu.open(e, () => ); + ContextMenuApi.openContextMenu(e, () => ); }} /> )} diff --git a/src/utils/modal.tsx b/src/utils/modal.tsx index 1efb9cdd8..6758a1a1a 100644 --- a/src/utils/modal.tsx +++ b/src/utils/modal.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { filters, findByProps, findByPropsLazy, mapMangledModuleLazy } from "@webpack"; +import { findByProps, findByPropsLazy } from "@webpack"; import type { ComponentType, PropsWithChildren, ReactNode, Ref } from "react"; import { LazyComponent } from "./react"; @@ -49,13 +49,7 @@ export interface ModalOptions { type RenderFunction = (props: ModalProps) => ReactNode; -export const Modals = mapMangledModuleLazy(".closeWithCircleBackground", { - ModalRoot: filters.byCode(".root"), - ModalHeader: filters.byCode(".header"), - ModalContent: filters.byCode(".content"), - ModalFooter: filters.byCode(".footerSeparator"), - ModalCloseButton: filters.byCode(".closeWithCircleBackground"), -}) as { +export const Modals = findByPropsLazy("ModalRoot", "ModalCloseButton") as { ModalRoot: ComponentType Menu = m); -export const ContextMenu: t.ContextMenuApi = mapMangledModuleLazy('type:"CONTEXT_MENU_OPEN"', { - open: filters.byCode("stopPropagation"), - openLazy: m => m.toString().length < 50, - close: filters.byCode("CONTEXT_MENU_CLOSE") -}); +export const ContextMenuApi: t.ContextMenuApi = findByPropsLazy("closeContextMenu", "openContextMenu"); diff --git a/src/webpack/common/stores.ts b/src/webpack/common/stores.ts index 1b00180fc..0c470d6a6 100644 --- a/src/webpack/common/stores.ts +++ b/src/webpack/common/stores.ts @@ -19,7 +19,7 @@ import type * as Stores from "discord-types/stores"; // eslint-disable-next-line path-alias/no-relative -import { filters, findByPropsLazy, mapMangledModuleLazy } from "../webpack"; +import { findByPropsLazy } from "../webpack"; import { waitForStore } from "./internal"; import * as t from "./types/stores"; @@ -62,10 +62,6 @@ export let EmojiStore: t.EmojiStore; export let WindowStore: t.WindowStore; export let DraftStore: t.DraftStore; -export const MaskedLinkStore = mapMangledModuleLazy('"MaskedLinkStore"', { - openUntrustedLink: filters.byCode(".apply(this,arguments)") -}); - /** * React hook that returns stateful data for one or more stores * You might need a custom comparator (4th argument) if your store data is an object diff --git a/src/webpack/common/types/menu.d.ts b/src/webpack/common/types/menu.d.ts index 29f3ffaee..0b8ab5c66 100644 --- a/src/webpack/common/types/menu.d.ts +++ b/src/webpack/common/types/menu.d.ts @@ -75,14 +75,14 @@ export interface Menu { } export interface ContextMenuApi { - close(): void; - open( + closeContextMenu(): void; + openContextMenu( event: UIEvent, render?: Menu["Menu"], options?: { enableSpellCheck?: boolean; }, renderLazy?: () => Promise ): void; - openLazy( + openContextMenuLazy( event: UIEvent, renderLazy?: () => Promise, options?: { enableSpellCheck?: boolean; } diff --git a/src/webpack/common/types/utils.d.ts b/src/webpack/common/types/utils.d.ts index 64a68f2c2..246659146 100644 --- a/src/webpack/common/types/utils.d.ts +++ b/src/webpack/common/types/utils.d.ts @@ -161,3 +161,24 @@ export interface i18n { Messages: Record; } + +export interface Clipboard { + copy(text: string): void; + SUPPORTS_COPY: boolean; +} + +export interface NavigationRouter { + back(): void; + forward(): void; + hasNavigated(): boolean; + getHistory(): { + action: string; + length: 50; + [key: string]: any; + }; + transitionTo(path: string, ...args: unknown[]): void; + transitionToGuild(guildId: string, ...args: unknown[]): void; + replaceWith(...args: unknown[]): void; + getLastRouteChangeSource(): any; + getLastRouteChangeSourceLocationStack(): any; +} diff --git a/src/webpack/common/utils.ts b/src/webpack/common/utils.ts index 8376925e5..2a3d4e677 100644 --- a/src/webpack/common/utils.ts +++ b/src/webpack/common/utils.ts @@ -20,7 +20,7 @@ import { proxyLazy } from "@utils/lazy"; import type { Channel, User } from "discord-types/general"; // eslint-disable-next-line path-alias/no-relative -import { _resolveReady, filters, find, findByPropsLazy, findLazy, mapMangledModuleLazy, waitFor } from "../webpack"; +import { _resolveReady, find, findByPropsLazy, findLazy, waitFor } from "../webpack"; import type * as t from "./types/utils"; export let FluxDispatcher: t.FluxDispatcher; @@ -102,17 +102,9 @@ export const ApplicationAssetUtils = findByPropsLazy("fetchAssetIds", "getAssetI fetchAssetIds: (applicationId: string, e: string[]) => Promise; }; -export const Clipboard = mapMangledModuleLazy('document.queryCommandEnabled("copy")||document.queryCommandSupported("copy")', { - copy: filters.byCode(".copy("), - SUPPORTS_COPY: x => typeof x === "boolean", -}); +export const Clipboard: t.Clipboard = findByPropsLazy("SUPPORTS_COPY", "copy"); -export const NavigationRouter = mapMangledModuleLazy("transitionToGuild - ", { - transitionTo: filters.byCode("transitionTo -"), - transitionToGuild: filters.byCode("transitionToGuild -"), - goBack: filters.byCode("goBack()"), - goForward: filters.byCode("goForward()"), -}); +export const NavigationRouter: t.NavigationRouter = findByPropsLazy("transitionTo", "replaceWith", "transitionToGuild"); waitFor(["dispatch", "subscribe"], m => { FluxDispatcher = m; diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index c7be62da0..b9c67e8fc 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -312,47 +312,6 @@ export const findModuleId = traceFunction("findModuleId", function findModuleId( return null; }); -/** - * Finds a mangled module by the provided code "code" (must be unique and can be anywhere in the module) - * then maps it into an easily usable module via the specified mappers - * @param code Code snippet - * @param mappers Mappers to create the non mangled exports - * @returns Unmangled exports as specified in mappers - * - * @example mapMangledModule("headerIdIsManaged:", { - * openModal: filters.byCode("headerIdIsManaged:"), - * closeModal: filters.byCode("key==") - * }) - */ -export const mapMangledModule = traceFunction("mapMangledModule", function mapMangledModule(code: string, mappers: Record): Record { - const exports = {} as Record; - - const id = findModuleId(code); - if (id === null) - return exports; - - const mod = wreq(id); - outer: - for (const key in mod) { - const member = mod[key]; - for (const newName in mappers) { - // if the current mapper matches this module - if (mappers[newName](member)) { - exports[newName] = member; - continue outer; - } - } - } - return exports; -}); - -/** - * Same as {@link mapMangledModule} but lazy - */ -export function mapMangledModuleLazy(code: string, mappers: Record): Record { - return proxyLazy(() => mapMangledModule(code, mappers)); -} - /** * Find the first module that has the specified properties */ From 9efc0ff5797cfdd346d0a1951db4d3480137f72d Mon Sep 17 00:00:00 2001 From: V Date: Thu, 23 Nov 2023 03:21:58 +0100 Subject: [PATCH 200/632] Remove obsolete nested webpack search --- src/webpack/patchWebpack.ts | 15 ++------ src/webpack/webpack.ts | 68 ------------------------------------- 2 files changed, 3 insertions(+), 80 deletions(-) diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index 82648f62b..0311e171c 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -170,18 +170,9 @@ function patchFactories(factories: Record Date: Thu, 23 Nov 2023 03:41:09 +0100 Subject: [PATCH 201/632] Remove obsolete webpack hacks --- src/webpack/patchWebpack.ts | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index 0311e171c..b992cfdea 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -119,12 +119,9 @@ function patchFactories(factories: Record mod.toString(); - factory.original = originalMod; - } catch { } + factory.toString = () => mod.toString(); + factory.original = originalMod; for (let i = 0; i < patches.length; i++) { const patch = patches[i]; @@ -210,7 +205,6 @@ function patchFactories(factories: Record()).add(patch.plugin); logger.warn(`Patch by ${patch.plugin} had no effect (Module id is ${id}): ${replacement.match}`); if (IS_DEV) { logger.debug("Function Source:\n", code); From 4832a9433fe6575d4a15cd733246cbc1498666c4 Mon Sep 17 00:00:00 2001 From: V Date: Thu, 23 Nov 2023 06:18:34 +0100 Subject: [PATCH 202/632] fix broken webpack finds --- src/plugins/friendInvites/index.ts | 4 ++-- src/plugins/webContextMenus.web/index.ts | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/plugins/friendInvites/index.ts b/src/plugins/friendInvites/index.ts index 92209986d..e5ff447ed 100644 --- a/src/plugins/friendInvites/index.ts +++ b/src/plugins/friendInvites/index.ts @@ -23,7 +23,7 @@ import { findByPropsLazy } from "@webpack"; import { RestAPI, UserStore } from "@webpack/common"; const FriendInvites = findByPropsLazy("createFriendInvite"); -const uuid = findByPropsLazy("v4", "v1"); +const { uuid4 } = findByPropsLazy("uuid4"); export default definePlugin({ name: "FriendInvites", @@ -56,7 +56,7 @@ export default definePlugin({ let invite: any; if (uses === 1) { - const random = uuid.v4(); + const random = uuid4(); const { body: { invite_suggestions } } = await RestAPI.post({ url: "/friend-finder/find-friends", body: { diff --git a/src/plugins/webContextMenus.web/index.ts b/src/plugins/webContextMenus.web/index.ts index bbfaa6747..9c4dd67c4 100644 --- a/src/plugins/webContextMenus.web/index.ts +++ b/src/plugins/webContextMenus.web/index.ts @@ -20,8 +20,8 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { saveFile } from "@utils/web"; -import { findByProps, findLazy } from "@webpack"; -import { Clipboard } from "@webpack/common"; +import { findByProps } from "@webpack"; +import { Clipboard, ComponentDispatch } from "@webpack/common"; async function fetchImage(url: string) { const res = await fetch(url); @@ -30,7 +30,6 @@ async function fetchImage(url: string) { return await res.blob(); } -const MiniDispatcher = findLazy(m => m.emitter?._events?.INSERT_TEXT); const settings = definePluginSettings({ // This needs to be all in one setting because to enable any of these, we need to make Discord use their desktop context @@ -213,7 +212,7 @@ export default definePlugin({ cut() { this.copy(); - MiniDispatcher.dispatch("INSERT_TEXT", { rawText: "" }); + ComponentDispatch.dispatch("INSERT_TEXT", { rawText: "" }); }, async paste() { From 0f74817e255727ad7b1fa6a6d963f42115e29070 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 23 Nov 2023 02:21:59 -0300 Subject: [PATCH 203/632] Fix broken SHC find --- .../showHiddenChannels/components/HiddenChannelLockScreen.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx b/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx index 649e87aa8..87946110d 100644 --- a/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx +++ b/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx @@ -90,7 +90,7 @@ const TagComponent = findComponentLazy(m => { }); const EmojiParser = findByPropsLazy("convertSurrogateToName"); -const EmojiUtils = findByPropsLazy("getURL", "buildEmojiReactionColorsPlatformed"); +const EmojiUtils = findByPropsLazy("getURL", "getEmojiColors"); const ChannelTypesToChannelNames = { [ChannelTypes.GUILD_TEXT]: "text", From f39f16d34bd12a19c4c06b2ac4d7ba4010c1dcef Mon Sep 17 00:00:00 2001 From: V Date: Thu, 23 Nov 2023 06:43:22 +0100 Subject: [PATCH 204/632] fix circular import bricking browser version --- src/utils/lazyReact.tsx | 23 +++++++++++++++++++++++ src/utils/react.tsx | 17 ++--------------- src/webpack/webpack.ts | 5 +---- 3 files changed, 26 insertions(+), 19 deletions(-) create mode 100644 src/utils/lazyReact.tsx diff --git a/src/utils/lazyReact.tsx b/src/utils/lazyReact.tsx new file mode 100644 index 000000000..abd300a97 --- /dev/null +++ b/src/utils/lazyReact.tsx @@ -0,0 +1,23 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { makeLazy } from "./lazy"; + +const NoopComponent = () => null; + +/** + * A lazy component. The factory method is called on first render. + * @param factory Function returning a Component + * @param attempts How many times to try to get the component before giving up + * @returns Result of factory function + */ +export function LazyComponent(factory: () => React.ComponentType, attempts = 5) { + const get = makeLazy(factory, attempts); + return (props: T) => { + const Component = get() ?? NoopComponent; + return ; + }; +} diff --git a/src/utils/react.tsx b/src/utils/react.tsx index 2d2bac39d..f31549f19 100644 --- a/src/utils/react.tsx +++ b/src/utils/react.tsx @@ -18,9 +18,10 @@ import { React, useEffect, useMemo, useReducer, useState } from "@webpack/common"; -import { makeLazy } from "./lazy"; import { checkIntersecting } from "./misc"; +export * from "./lazyReact"; + export const NoopComponent = () => null; /** @@ -143,17 +144,3 @@ export function useTimer({ interval = 1000, deps = [] }: TimerOpts) { return time; } - -/** - * A lazy component. The factory method is called on first render. - * @param factory Function returning a Component - * @param attempts How many times to try to get the component before giving up - * @returns Result of factory function - */ -export function LazyComponent(factory: () => React.ComponentType, attempts = 5) { - const get = makeLazy(factory, attempts); - return (props: T) => { - const Component = get() ?? NoopComponent; - return ; - }; -} diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index 2c55fd367..b4fdb4e10 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -17,8 +17,8 @@ */ import { proxyLazy } from "@utils/lazy"; +import { LazyComponent } from "@utils/lazyReact"; import { Logger } from "@utils/Logger"; -import { LazyComponent } from "@utils/react"; import type { WebpackInstance } from "discord-types/other"; import { traceFunction } from "../debug/Tracer"; @@ -338,9 +338,6 @@ 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); - subscriptions.set(filter, callback); } From 81fb7c6322f4fd5f56ea95b268597f5365af6f39 Mon Sep 17 00:00:00 2001 From: V Date: Thu, 23 Nov 2023 06:45:01 +0100 Subject: [PATCH 205/632] add back code that got lost --- src/webpack/webpack.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index b4fdb4e10..980288e9e 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -338,6 +338,9 @@ 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); + subscriptions.set(filter, callback); } From e14fba28a51981e76f23dad9b9145b49421dac07 Mon Sep 17 00:00:00 2001 From: cat <101748822+capillarys@users.noreply.github.com> Date: Thu, 23 Nov 2023 17:31:25 +0000 Subject: [PATCH 206/632] ClearURLS for x.com links (#2005) --- src/plugins/clearURLs/defaultRules.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/clearURLs/defaultRules.ts b/src/plugins/clearURLs/defaultRules.ts index d6914b905..e7c3ecbeb 100644 --- a/src/plugins/clearURLs/defaultRules.ts +++ b/src/plugins/clearURLs/defaultRules.ts @@ -121,6 +121,9 @@ export const defaultRules = [ "t@*.twitter.com", "s@*.twitter.com", "ref_*@*.twitter.com", + "t@*.x.com", + "s@*.x.com", + "ref_*@*.x.com", "tt_medium", "tt_content", "lr@yandex.*", From fdddfdb05b339a7c677044756b75cdff43f45e16 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 24 Nov 2023 16:32:30 -0300 Subject: [PATCH 207/632] feat(plugins): FixImagesQuality --- src/plugins/fixImagesQuality/index.ts | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/plugins/fixImagesQuality/index.ts diff --git a/src/plugins/fixImagesQuality/index.ts b/src/plugins/fixImagesQuality/index.ts new file mode 100644 index 000000000..e0dfcb6d4 --- /dev/null +++ b/src/plugins/fixImagesQuality/index.ts @@ -0,0 +1,35 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2022 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 { Devs } from "@utils/constants"; +import definePlugin from "@utils/types"; + +export default definePlugin({ + name: "FixImagesQuality", + description: "Fixes the quality of images in the chat being horrible.", + authors: [Devs.Nuckyz], + patches: [ + { + find: "handleImageLoad=", + replacement: { + match: /(?<=getSrc\(\i\){.+?format:)\i/, + replace: "null" + } + } + ] +}); From 3e8e106be7f010a87871a44f75446053aae3b6be Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 24 Nov 2023 16:49:19 -0300 Subject: [PATCH 208/632] Fix broken patches --- src/plugins/fixImagesQuality/index.ts | 20 +++------------ src/plugins/sortFriendRequests/index.tsx | 31 ++++++++++++------------ src/plugins/webContextMenus.web/index.ts | 6 ++--- 3 files changed, 22 insertions(+), 35 deletions(-) diff --git a/src/plugins/fixImagesQuality/index.ts b/src/plugins/fixImagesQuality/index.ts index e0dfcb6d4..d94c8e3b2 100644 --- a/src/plugins/fixImagesQuality/index.ts +++ b/src/plugins/fixImagesQuality/index.ts @@ -1,20 +1,8 @@ /* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2022 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 . -*/ + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; diff --git a/src/plugins/sortFriendRequests/index.tsx b/src/plugins/sortFriendRequests/index.tsx index 3698379c8..c40a18140 100644 --- a/src/plugins/sortFriendRequests/index.tsx +++ b/src/plugins/sortFriendRequests/index.tsx @@ -16,17 +16,27 @@ * along with this program. If not, see . */ +import { definePluginSettings } from "@api/Settings"; import { Flex } from "@components/Flex"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { RelationshipStore } from "@webpack/common"; import { User } from "discord-types/general"; -import { Settings } from "Vencord"; + +const settings = definePluginSettings({ + showDates: { + type: OptionType.BOOLEAN, + description: "Show dates on friend requests", + default: false, + restartNeeded: true + } +}); export default definePlugin({ name: "SortFriendRequests", authors: [Devs.Megu], description: "Sorts friend requests by date of receipt", + settings, patches: [{ find: "getRelationshipCounts(){", @@ -35,13 +45,11 @@ export default definePlugin({ replace: ".sortBy((row) => $self.sortList(row))" } }, { - find: "RelationshipTypes.PENDING_INCOMING?", + find: ".Messages.FRIEND_REQUEST_CANCEL", replacement: { - predicate: () => Settings.plugins.SortFriendRequests.showDates, - match: /(user:(\i),.{10,50}),subText:(\i),(className:\i\.userInfo}\))/, - replace: (_, pre, user, subtext, post) => `${pre}, - subText: $self.makeSubtext(${subtext}, ${user}), - ${post}` + predicate: () => settings.store.showDates, + match: /subText:(\i)(?=,className:\i\.userInfo}\))(?<=user:(\i).+?)/, + replace: (_, subtext, user) => `subText:$self.makeSubtext(${subtext},${user})` } }], @@ -63,14 +71,5 @@ export default definePlugin({ {!isNaN(since.getTime()) && Received — {since.toDateString()}} ); - }, - - options: { - showDates: { - type: OptionType.BOOLEAN, - description: "Show dates on friend requests", - default: false, - restartNeeded: true - } } }); diff --git a/src/plugins/webContextMenus.web/index.ts b/src/plugins/webContextMenus.web/index.ts index 9c4dd67c4..aa575bfd2 100644 --- a/src/plugins/webContextMenus.web/index.ts +++ b/src/plugins/webContextMenus.web/index.ts @@ -120,9 +120,9 @@ export default definePlugin({ find: 'navId:"image-context"', predicate: () => settings.store.addBack, replacement: { - // return IS_DESKTOP ? React.createElement(Menu, ...) - match: /return \i\.\i\?/, - replace: "return true?" + // return IS_DESKTOP && null != ... ? React.createElement(Menu, ...) + match: /return \i\.\i(?=&&null)/, + replace: "return true" } }, From 534565db256e9aaf47f3b3092cbe6f4e0bc982be Mon Sep 17 00:00:00 2001 From: V Date: Sat, 25 Nov 2023 01:32:21 +0100 Subject: [PATCH 209/632] Add webpack find testing (#2016) Co-authored-by: V Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com> --- .github/workflows/reportBrokenPlugins.yml | 2 +- scripts/build/build.mjs | 4 +- scripts/build/buildWeb.mjs | 4 +- scripts/build/common.mjs | 1 + scripts/generateReport.ts | 212 +++++++++++++++--- src/plugins/fakeNitro/index.ts | 9 +- .../components/UserPermissions.tsx | 5 +- src/plugins/spotifyControls/SpotifyStore.ts | 9 +- src/plugins/typingIndicator/index.tsx | 5 +- src/plugins/vencordToolbox/index.tsx | 7 +- src/utils/lazy.ts | 2 +- src/utils/lazyReact.tsx | 6 +- src/webpack/common/internal.tsx | 10 +- src/webpack/common/utils.ts | 44 ++-- src/webpack/webpack.ts | 66 +++++- 15 files changed, 289 insertions(+), 97 deletions(-) diff --git a/.github/workflows/reportBrokenPlugins.yml b/.github/workflows/reportBrokenPlugins.yml index 8bc936183..132022008 100644 --- a/.github/workflows/reportBrokenPlugins.yml +++ b/.github/workflows/reportBrokenPlugins.yml @@ -29,7 +29,7 @@ jobs: sudo apt-get install -y chromium-browser - name: Build web - run: pnpm buildWeb --standalone + run: pnpm buildWeb --standalone --dev - name: Create Report timeout-minutes: 10 diff --git a/scripts/build/build.mjs b/scripts/build/build.mjs index 89cca7e47..a2e0e0024 100755 --- a/scripts/build/build.mjs +++ b/scripts/build/build.mjs @@ -21,11 +21,11 @@ import esbuild from "esbuild"; import { readdir } from "fs/promises"; import { join } from "path"; -import { BUILD_TIMESTAMP, commonOpts, existsAsync, globPlugins, isStandalone, updaterDisabled, VERSION, watch } from "./common.mjs"; +import { BUILD_TIMESTAMP, commonOpts, existsAsync, globPlugins, isDev, isStandalone, updaterDisabled, VERSION, watch } from "./common.mjs"; const defines = { IS_STANDALONE: isStandalone, - IS_DEV: JSON.stringify(watch), + IS_DEV: JSON.stringify(isDev), IS_UPDATER_DISABLED: updaterDisabled, IS_WEB: false, IS_EXTENSION: false, diff --git a/scripts/build/buildWeb.mjs b/scripts/build/buildWeb.mjs index 353f4e060..b4c726064 100644 --- a/scripts/build/buildWeb.mjs +++ b/scripts/build/buildWeb.mjs @@ -23,7 +23,7 @@ import { appendFile, mkdir, readdir, readFile, rm, writeFile } from "fs/promises import { join } from "path"; import Zip from "zip-local"; -import { BUILD_TIMESTAMP, commonOpts, globPlugins, VERSION, watch } from "./common.mjs"; +import { BUILD_TIMESTAMP, commonOpts, globPlugins, isDev, VERSION } from "./common.mjs"; /** * @type {esbuild.BuildOptions} @@ -43,7 +43,7 @@ const commonOptions = { IS_WEB: "true", IS_EXTENSION: "false", IS_STANDALONE: "true", - IS_DEV: JSON.stringify(watch), + IS_DEV: JSON.stringify(isDev), IS_DISCORD_DESKTOP: "false", IS_VESKTOP: "false", IS_UPDATER_DISABLED: "true", diff --git a/scripts/build/common.mjs b/scripts/build/common.mjs index 5488b1b3b..5c34ad038 100644 --- a/scripts/build/common.mjs +++ b/scripts/build/common.mjs @@ -33,6 +33,7 @@ export const VERSION = PackageJSON.version; // https://reproducible-builds.org/docs/source-date-epoch/ export const BUILD_TIMESTAMP = Number(process.env.SOURCE_DATE_EPOCH) || Date.now(); export const watch = process.argv.includes("--watch"); +export const isDev = watch || process.argv.includes("--dev"); export const isStandalone = JSON.stringify(process.argv.includes("--standalone")); export const updaterDisabled = JSON.stringify(process.argv.includes("--disable-updater")); export const gitHash = process.env.VENCORD_HASH || execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim(); diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 4e044c94b..cadf0c2af 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -34,7 +34,7 @@ for (const variable of ["DISCORD_TOKEN", "CHROMIUM_BIN"]) { const CANARY = process.env.USE_CANARY === "true"; const browser = await pup.launch({ - headless: true, + headless: "new", executablePath: process.env.CHROMIUM_BIN }); @@ -58,14 +58,16 @@ const report = { plugin: string; error: string; }[], - otherErrors: [] as string[] + otherErrors: [] as string[], + badWebpackFinds: [] as string[] }; const IGNORED_DISCORD_ERRORS = [ "KeybindStore: Looking for callback action", "Unable to process domain list delta: Client revision number is null", "Downloading the full bad domains file", - /\[GatewaySocket\].{0,110}Cannot access '/ + /\[GatewaySocket\].{0,110}Cannot access '/, + "search for 'name' in undefined" ] as Array; function toCodeBlock(s: string) { @@ -74,7 +76,10 @@ function toCodeBlock(s: string) { } async function printReport() { + console.log(); + console.log("# Vencord Report" + (CANARY ? " (Canary)" : "")); + console.log(); console.log("## Bad Patches"); @@ -87,12 +92,19 @@ async function printReport() { console.log(); + console.log("## Bad Webpack Finds"); + report.badWebpackFinds.forEach(p => console.log("- " + p)); + + console.log(); + console.log("## Bad Starts"); report.badStarts.forEach(p => { console.log(`- ${p.plugin}`); console.log(` - Error: ${toCodeBlock(p.error)}`); }); + console.log(); + report.otherErrors = report.otherErrors.filter(e => !IGNORED_DISCORD_ERRORS.some(regex => e.match(regex))); console.log("## Discord Errors"); @@ -100,8 +112,9 @@ async function printReport() { console.log(`- ${toCodeBlock(e)}`); }); + console.log(); + if (process.env.DISCORD_WEBHOOK) { - // this code was written almost entirely by Copilot xD await fetch(process.env.DISCORD_WEBHOOK, { method: "POST", headers: { @@ -110,7 +123,7 @@ async function printReport() { body: JSON.stringify({ description: "Here's the latest Vencord Report!", username: "Vencord Reporter" + (CANARY ? " (Canary)" : ""), - avatar_url: "https://cdn.discordapp.com/icons/1015060230222131221/f0204a918c6c9c9a43195997e97d8adf.webp", + avatar_url: "https://cdn.discordapp.com/icons/1015060230222131221/6101cff21e241cebb60c4a01563d0c01.webp?size=512", embeds: [ { title: "Bad Patches", @@ -125,6 +138,11 @@ async function printReport() { }).join("\n\n") || "None", color: report.badPatches.length ? 0xff0000 : 0x00ff00 }, + { + title: "Bad Webpack Finds", + description: report.badWebpackFinds.map(toCodeBlock).join("\n") || "None", + color: report.badWebpackFinds.length ? 0xff0000 : 0x00ff00 + }, { title: "Bad Starts", description: report.badStarts.map(p => { @@ -153,29 +171,35 @@ async function printReport() { page.on("console", async e => { const level = e.type(); - const args = e.args(); + const rawArgs = e.args(); - const firstArg = (await args[0]?.jsonValue()); - if (firstArg === "PUPPETEER_TEST_DONE_SIGNAL") { + const firstArg = await rawArgs[0]?.jsonValue(); + if (firstArg === "[PUPPETEER_TEST_DONE_SIGNAL]") { await browser.close(); await printReport(); process.exit(); } - const isVencord = (await args[0]?.jsonValue()) === "[Vencord]"; - const isDebug = (await args[0]?.jsonValue()) === "[PUP_DEBUG]"; + const isVencord = firstArg === "[Vencord]"; + const isDebug = firstArg === "[PUP_DEBUG]"; + const isWebpackFindFail = firstArg === "[PUP_WEBPACK_FIND_FAIL]"; + + if (isWebpackFindFail) { + process.exitCode = 1; + report.badWebpackFinds.push(await rawArgs[1].jsonValue() as string); + } if (isVencord) { - // make ci fail - process.exitCode = 1; + const args = await Promise.all(e.args().map(a => a.jsonValue())); - const jsonArgs = await Promise.all(args.map(a => a.jsonValue())); - const [, tag, message] = jsonArgs; - const cause = await maybeGetError(args[3]); + const [, tag, message] = args as Array; + const cause = await maybeGetError(e.args()[3]); switch (tag) { case "WebpackInterceptor:": - const [, plugin, type, id, regex] = (message as string).match(/Patch by (.+?) (had no effect|errored|found no module) \(Module id is (.+?)\): (.+)/)!; + process.exitCode = 1; + + const [, plugin, type, id, regex] = message.match(/Patch by (.+?) (had no effect|errored|found no module) \(Module id is (.+?)\): (.+)/)!; report.badPatches.push({ plugin, type, @@ -183,17 +207,26 @@ page.on("console", async e => { match: regex.replace(/\[A-Za-z_\$\]\[\\w\$\]\*/g, "\\i"), error: cause }); + break; case "PluginManager:": - const [, name] = (message as string).match(/Failed to start (.+)/)!; + const failedToStartMatch = message.match(/Failed to start (.+)/); + if (!failedToStartMatch) break; + + process.exitCode = 1; + + const [, name] = failedToStartMatch; report.badStarts.push({ plugin: name, error: cause }); + break; } - } else if (isDebug) { - console.error(e.text()); + } + + if (isDebug) { + console.log(e.text()); } else if (level === "error") { const text = await Promise.all( e.args().map(async a => { @@ -206,8 +239,8 @@ page.on("console", async e => { ).then(a => a.join(" ").trim()); - if (text.length && !text.startsWith("Failed to load resource: the server responded with a status of")) { - console.error("Got unexpected error", text); + if (text.length && !text.startsWith("Failed to load resource: the server responded with a status of") && !text.includes("found no module Filter:")) { + console.error("[Unexpected Error]", text); report.otherErrors.push(text); } } @@ -219,17 +252,16 @@ page.on("pageerror", e => console.error("[Page Error]", e)); await page.setBypassCSP(true); function runTime(token: string) { - console.error("[PUP_DEBUG]", "Starting test..."); + console.log("[PUP_DEBUG]", "Starting test..."); try { - // spoof languages to not be suspicious + // Spoof languages to not be suspicious Object.defineProperty(navigator, "languages", { get: function () { return ["en-US", "en"]; }, }); - // Monkey patch Logger to not log with custom css // @ts-ignore Vencord.Util.Logger.prototype._log = function (level, levelColor, args) { @@ -237,7 +269,7 @@ function runTime(token: string) { console[level]("[Vencord]", this.name + ":", ...args); }; - // force enable all plugins and patches + // Force enable all plugins and patches Vencord.Plugins.patches.length = 0; Object.values(Vencord.Plugins.plugins).forEach(p => { // Needs native server to run @@ -247,8 +279,14 @@ function runTime(token: string) { p.patches?.forEach(patch => { patch.plugin = p.name; delete patch.predicate; + if (!Array.isArray(patch.replacement)) patch.replacement = [patch.replacement]; + + patch.replacement.forEach(r => { + delete r.predicate; + }); + Vencord.Plugins.patches.push(patch); }); }); @@ -256,41 +294,141 @@ function runTime(token: string) { Vencord.Webpack.waitFor( "loginToken", m => { - console.error("[PUP_DEBUG]", "Logging in with token..."); + console.log("[PUP_DEBUG]", "Logging in with token..."); m.loginToken(token); } ); - // force load all chunks + // Force load all chunks Vencord.Webpack.onceReady.then(() => setTimeout(async () => { - console.error("[PUP_DEBUG]", "Webpack is ready!"); + console.log("[PUP_DEBUG]", "Webpack is ready!"); const { wreq } = Vencord.Webpack; - console.error("[PUP_DEBUG]", "Loading all chunks..."); - const ids = Function("return" + wreq.u.toString().match(/(?<=\()\{.+?\}/s)![0])(); - for (const id in ids) { + console.log("[PUP_DEBUG]", "Loading all chunks..."); + + let chunks = null as Record | null; + const sym = Symbol("Vencord.chunksExtract"); + + Object.defineProperty(Object.prototype, sym, { + get() { + chunks = this; + }, + set() { }, + configurable: true, + }); + + await (wreq as any).el(sym); + delete Object.prototype[sym]; + + const validChunksEntryPoints = [] as string[]; + const validChunks = [] as string[]; + const invalidChunks = [] as string[]; + + if (!chunks) throw new Error("Failed to get chunks"); + + chunksLoop: + for (const entryPoint in chunks) { + const chunkIds = chunks[entryPoint]; + + for (const id of chunkIds) { + if (!wreq.u(id)) continue; + + const isWasm = await fetch(wreq.p + wreq.u(id)) + .then(r => r.text()) + .then(t => t.includes(".module.wasm") || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push")); + + await new Promise(r => setTimeout(r, 150)); + + if (isWasm) { + invalidChunks.push(id); + continue chunksLoop; + } + + validChunks.push(id); + } + + validChunksEntryPoints.push(entryPoint); + } + + for (const entryPoint of validChunksEntryPoints) { + try { + // Loads all chunks required for an entry point + await (wreq as any).el(entryPoint); + } catch (err) { } + } + + const allChunks = Function("return " + (wreq.u.toString().match(/(?<=\()\{.+?\}/s)?.[0] ?? "null"))() as Record | null; + if (!allChunks) throw new Error("Failed to get all chunks"); + const chunksLeft = Object.keys(allChunks).filter(id => { + return !(validChunks.includes(id) || invalidChunks.includes(id)); + }); + + for (const id of chunksLeft) { const isWasm = await fetch(wreq.p + wreq.u(id)) .then(r => r.text()) .then(t => t.includes(".module.wasm") || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push")); - if (!isWasm) - await wreq.e(id as any); + // Loads a chunk + if (!isWasm) await wreq.e(id as any); await new Promise(r => setTimeout(r, 150)); } - console.error("[PUP_DEBUG]", "Finished loading chunks!"); + + // Make sure every chunk has finished loading + await new Promise(r => setTimeout(r, 1000)); + + for (const entryPoint of validChunksEntryPoints) { + try { + if (wreq.m[entryPoint]) wreq(entryPoint as any); + } catch (err) { + console.error(err); + } + } + + console.log("[PUP_DEBUG]", "Finished loading all chunks!"); for (const patch of Vencord.Plugins.patches) { if (!patch.all) { new Vencord.Util.Logger("WebpackInterceptor").warn(`Patch by ${patch.plugin} found no module (Module id is -): ${patch.find}`); } } - setTimeout(() => console.log("PUPPETEER_TEST_DONE_SIGNAL"), 1000); + + for (const [searchType, args] of Vencord.Webpack.lazyWebpackSearchHistory) { + let method = searchType; + + if (searchType === "findComponent") method = "find"; + if (searchType === "findExportedComponent") method = "findByProps"; + if (searchType === "waitFor" || searchType === "waitForComponent" || searchType === "waitForStore") { + if (typeof args[0] === "string") method = "findByProps"; + else method = "find"; + } + + try { + let result: any; + + if (method === "proxyLazyWebpack" || method === "LazyComponentWebpack") { + const [factory] = args; + result = factory(); + } else { + // @ts-ignore + result = Vencord.Webpack[method](...args); + } + + if (result == null || ("$$get" in result && result.$$get() == null)) throw "a rock at ben shapiro"; + } catch (e) { + let logMessage = searchType; + if (method === "find" || method === "proxyLazyWebpack" || method === "LazyComponentWebpack") logMessage += `(${args[0].toString().slice(0, 147)}...)`; + else logMessage += `(${args.map(arg => `"${arg}"`).join(", ")})`; + + console.log("[PUP_WEBPACK_FIND_FAIL]", logMessage); + } + } + + setTimeout(() => console.log("[PUPPETEER_TEST_DONE_SIGNAL]"), 1000); }, 1000)); } catch (e) { - console.error("[PUP_DEBUG]", "A fatal error occurred"); - console.error("[PUP_DEBUG]", e); + console.log("[PUP_DEBUG]", "A fatal error occurred:", e); process.exit(1); } } diff --git a/src/plugins/fakeNitro/index.ts b/src/plugins/fakeNitro/index.ts index 4d6b7957b..3ac755567 100644 --- a/src/plugins/fakeNitro/index.ts +++ b/src/plugins/fakeNitro/index.ts @@ -21,10 +21,9 @@ import { definePluginSettings, Settings } from "@api/Settings"; import { Devs } from "@utils/constants"; import { ApngBlendOp, ApngDisposeOp, importApngJs } from "@utils/dependencies"; import { getCurrentGuild } from "@utils/discord"; -import { proxyLazy } from "@utils/lazy"; import { Logger } from "@utils/Logger"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy, findStoreLazy } from "@webpack"; +import { findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack"; import { ChannelStore, EmojiStore, FluxDispatcher, lodash, Parser, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common"; import type { Message } from "discord-types/general"; import { applyPalette, GIFEncoder, quantize } from "gifenc"; @@ -48,9 +47,9 @@ function searchProtoClassField(localName: string, protoClass: any) { return fieldGetter?.(); } -const PreloadedUserSettingsActionCreators = proxyLazy(() => UserSettingsActionCreators.PreloadedUserSettingsActionCreators); -const AppearanceSettingsActionCreators = proxyLazy(() => searchProtoClassField("appearance", PreloadedUserSettingsActionCreators.ProtoClass)); -const ClientThemeSettingsActionsCreators = proxyLazy(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators)); +const PreloadedUserSettingsActionCreators = proxyLazyWebpack(() => UserSettingsActionCreators.PreloadedUserSettingsActionCreators); +const AppearanceSettingsActionCreators = proxyLazyWebpack(() => searchProtoClassField("appearance", PreloadedUserSettingsActionCreators.ProtoClass)); +const ClientThemeSettingsActionsCreators = proxyLazyWebpack(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators)); const USE_EXTERNAL_EMOJIS = 1n << 18n; const USE_EXTERNAL_STICKERS = 1n << 37n; diff --git a/src/plugins/permissionsViewer/components/UserPermissions.tsx b/src/plugins/permissionsViewer/components/UserPermissions.tsx index aeb976645..b75bafdcb 100644 --- a/src/plugins/permissionsViewer/components/UserPermissions.tsx +++ b/src/plugins/permissionsViewer/components/UserPermissions.tsx @@ -18,9 +18,8 @@ import ErrorBoundary from "@components/ErrorBoundary"; import ExpandableHeader from "@components/ExpandableHeader"; -import { proxyLazy } from "@utils/lazy"; import { classes } from "@utils/misc"; -import { filters, findBulk } from "@webpack"; +import { filters, findBulk, proxyLazyWebpack } from "@webpack"; import { i18n, PermissionsBits, Text, Tooltip, useMemo, UserStore } from "@webpack/common"; import type { Guild, GuildMember } from "discord-types/general"; @@ -36,7 +35,7 @@ interface UserPermission { type UserPermissions = Array; -const Classes = proxyLazy(() => { +const Classes = proxyLazyWebpack(() => { const modules = findBulk( filters.byProps("roles", "rolePill", "rolePillBorder"), filters.byProps("roleCircle", "dotBorderBase", "dotBorderColor"), diff --git a/src/plugins/spotifyControls/SpotifyStore.ts b/src/plugins/spotifyControls/SpotifyStore.ts index f940d8d57..b3cd0b282 100644 --- a/src/plugins/spotifyControls/SpotifyStore.ts +++ b/src/plugins/spotifyControls/SpotifyStore.ts @@ -17,8 +17,7 @@ */ import { Settings } from "@api/Settings"; -import { proxyLazy } from "@utils/lazy"; -import { findByPropsLazy } from "@webpack"; +import { findByProps, proxyLazyWebpack } from "@webpack"; import { Flux, FluxDispatcher } from "@webpack/common"; export interface Track { @@ -66,12 +65,12 @@ interface Device { type Repeat = "off" | "track" | "context"; // Don't wanna run before Flux and Dispatcher are ready! -export const SpotifyStore = proxyLazy(() => { +export const SpotifyStore = proxyLazyWebpack(() => { // For some reason ts hates extends Flux.Store const { Store } = Flux; - const SpotifySocket = findByPropsLazy("getActiveSocketAndDevice"); - const SpotifyUtils = findByPropsLazy("SpotifyAPI"); + const SpotifySocket = findByProps("getActiveSocketAndDevice"); + const SpotifyUtils = findByProps("SpotifyAPI"); const API_BASE = "https://api.spotify.com/v1/me/player"; diff --git a/src/plugins/typingIndicator/index.tsx b/src/plugins/typingIndicator/index.tsx index 86bfbb4f2..e35eb9b6e 100644 --- a/src/plugins/typingIndicator/index.tsx +++ b/src/plugins/typingIndicator/index.tsx @@ -19,14 +19,13 @@ import { definePluginSettings, Settings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; -import { LazyComponent } from "@utils/react"; import definePlugin, { OptionType } from "@utils/types"; -import { find, findStoreLazy } from "@webpack"; +import { find, findStoreLazy, LazyComponentWebpack } from "@webpack"; import { ChannelStore, GuildMemberStore, i18n, RelationshipStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common"; import { buildSeveralUsers } from "../typingTweaks"; -const ThreeDots = LazyComponent(() => { +const ThreeDots = LazyComponentWebpack(() => { // This doesn't really need to explicitly find Dots' own module, but it's fine const res = find(m => m.Dots && !m.Menu); diff --git a/src/plugins/vencordToolbox/index.tsx b/src/plugins/vencordToolbox/index.tsx index cd266c6f2..bb63a86b8 100644 --- a/src/plugins/vencordToolbox/index.tsx +++ b/src/plugins/vencordToolbox/index.tsx @@ -22,15 +22,14 @@ import { openNotificationLogModal } from "@api/Notifications/notificationLog"; import { Settings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; -import { LazyComponent } from "@utils/react"; import definePlugin from "@utils/types"; -import { filters, find } from "@webpack"; +import { filters, find, LazyComponentWebpack } from "@webpack"; import { Menu, Popout, useState } from "@webpack/common"; import type { ReactNode } from "react"; -const HeaderBarIcon = LazyComponent(() => { +const HeaderBarIcon = LazyComponentWebpack(() => { const filter = filters.byCode(".HEADER_BAR_BADGE"); - return find(m => m.Icon && filter(m.Icon)).Icon; + return find(m => m.Icon && filter(m.Icon))?.Icon; }); function VencordPopout(onClose: () => void) { diff --git a/src/utils/lazy.ts b/src/utils/lazy.ts index 1c89d5110..32336fb40 100644 --- a/src/utils/lazy.ts +++ b/src/utils/lazy.ts @@ -76,7 +76,7 @@ handler.getOwnPropertyDescriptor = (target, p) => { }; /** - * Wraps the result of {@see makeLazy} in a Proxy you can consume as if it wasn't lazy. + * Wraps the result of {@link makeLazy} in a Proxy you can consume as if it wasn't lazy. * On first property access, the lazy is evaluated * @param factory lazy factory * @param attempts how many times to try to evaluate the lazy before giving up diff --git a/src/utils/lazyReact.tsx b/src/utils/lazyReact.tsx index abd300a97..e45ca0792 100644 --- a/src/utils/lazyReact.tsx +++ b/src/utils/lazyReact.tsx @@ -16,8 +16,12 @@ const NoopComponent = () => null; */ export function LazyComponent(factory: () => React.ComponentType, attempts = 5) { const get = makeLazy(factory, attempts); - return (props: T) => { + const LazyComponent = (props: T) => { const Component = get() ?? NoopComponent; return ; }; + + LazyComponent.$$get = get; + + return LazyComponent; } diff --git a/src/webpack/common/internal.tsx b/src/webpack/common/internal.tsx index 66c52de00..9a89af362 100644 --- a/src/webpack/common/internal.tsx +++ b/src/webpack/common/internal.tsx @@ -19,9 +19,11 @@ import { LazyComponent } from "@utils/react"; // eslint-disable-next-line path-alias/no-relative -import { FilterFn, filters, waitFor } from "../webpack"; +import { FilterFn, filters, lazyWebpackSearchHistory, waitFor } from "../webpack"; export function waitForComponent = React.ComponentType & Record>(name: string, filter: FilterFn | string | string[]): T { + if (IS_DEV) lazyWebpackSearchHistory.push(["waitForComponent", Array.isArray(filter) ? filter : [filter]]); + let myValue: T = function () { throw new Error(`Vencord could not find the ${name} Component`); } as any; @@ -30,11 +32,13 @@ export function waitForComponent = React.Comp waitFor(filter, (v: any) => { myValue = v; Object.assign(lazyComponent, v); - }); + }, { isIndirect: true }); return lazyComponent; } export function waitForStore(name: string, cb: (v: any) => void) { - waitFor(filters.byStoreName(name), cb); + if (IS_DEV) lazyWebpackSearchHistory.push(["waitForStore", [name]]); + + waitFor(filters.byStoreName(name), cb, { isIndirect: true }); } diff --git a/src/webpack/common/utils.ts b/src/webpack/common/utils.ts index 2a3d4e677..cef4d51d6 100644 --- a/src/webpack/common/utils.ts +++ b/src/webpack/common/utils.ts @@ -16,14 +16,23 @@ * along with this program. If not, see . */ -import { proxyLazy } from "@utils/lazy"; import type { Channel, User } from "discord-types/general"; // eslint-disable-next-line path-alias/no-relative -import { _resolveReady, find, findByPropsLazy, findLazy, waitFor } from "../webpack"; +import { _resolveReady, findByPropsLazy, findLazy, waitFor } from "../webpack"; import type * as t from "./types/utils"; export let FluxDispatcher: t.FluxDispatcher; + +waitFor(["dispatch", "subscribe"], m => { + FluxDispatcher = m; + const cb = () => { + m.unsubscribe("CONNECTION_OPEN", cb); + _resolveReady(); + }; + m.subscribe("CONNECTION_OPEN", cb); +}); + export let ComponentDispatch; waitFor(["ComponentDispatch", "ComponentDispatcher"], m => ComponentDispatch = m.ComponentDispatch); @@ -41,7 +50,9 @@ export let SnowflakeUtils: t.SnowflakeUtils; waitFor(["fromTimestamp", "extractTimestamp"], m => SnowflakeUtils = m); export let Parser: t.Parser; +waitFor("parseTopic", m => Parser = m); export let Alerts: t.Alerts; +waitFor(["show", "close"], m => Alerts = m); const ToastType = { MESSAGE: 0, @@ -82,6 +93,13 @@ export const Toasts = { } }; +// This is the same module but this is easier +waitFor("showToast", m => { + Toasts.show = m.showToast; + Toasts.pop = m.popToast; +}); + + /** * Show a simple toast. If you need more options, use Toasts.show manually */ @@ -106,26 +124,8 @@ export const Clipboard: t.Clipboard = findByPropsLazy("SUPPORTS_COPY", "copy"); export const NavigationRouter: t.NavigationRouter = findByPropsLazy("transitionTo", "replaceWith", "transitionToGuild"); -waitFor(["dispatch", "subscribe"], m => { - FluxDispatcher = m; - const cb = () => { - m.unsubscribe("CONNECTION_OPEN", cb); - _resolveReady(); - }; - m.subscribe("CONNECTION_OPEN", cb); -}); - - -// This is the same module but this is easier -waitFor("showToast", m => { - Toasts.show = m.showToast; - Toasts.pop = m.popToast; -}); - -waitFor(["show", "close"], m => Alerts = m); -waitFor("parseTopic", m => Parser = m); - export let SettingsRouter: any; waitFor(["open", "saveAccountChanges"], m => SettingsRouter = m); -export const PermissionsBits: t.PermissionsBits = proxyLazy(() => find(m => typeof m.Permissions?.ADMINISTRATOR === "bigint").Permissions); +const { Permissions } = findLazy(m => typeof m.Permissions?.ADMINISTRATOR === "bigint") as { Permissions: t.PermissionsBits; }; +export { Permissions as PermissionsBits }; diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index 980288e9e..4bdec7075 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -127,13 +127,6 @@ export const find = traceFunction("find", function find(filter: FilterFn, { isIn return isWaitFor ? [null, null] : null; }); -/** - * find but lazy - */ -export function findLazy(filter: FilterFn) { - return proxyLazy(() => find(filter)); -} - export function findAll(filter: FilterFn) { if (typeof filter !== "function") throw new Error("Invalid filter. Expected a function got " + typeof filter); @@ -244,6 +237,49 @@ export const findModuleId = traceFunction("findModuleId", function findModuleId( return null; }); +export const lazyWebpackSearchHistory = [] as Array<["find" | "findByProps" | "findByCode" | "findStore" | "findComponent" | "findComponentByCode" | "findExportedComponent" | "waitFor" | "waitForComponent" | "waitForStore" | "proxyLazyWebpack" | "LazyComponentWebpack", any[]]>; + +/** + * This is just a wrapper around {@link proxyLazy} to make our reporter test for your webpack finds. + * + * Wraps the result of {@link makeLazy} in a Proxy you can consume as if it wasn't lazy. + * On first property access, the lazy is evaluated + * @param factory lazy factory + * @param attempts how many times to try to evaluate the lazy before giving up + * @returns Proxy + * + * Note that the example below exists already as an api, see {@link findByPropsLazy} + * @example const mod = proxyLazy(() => findByProps("blah")); console.log(mod.blah); + */ +export function proxyLazyWebpack(factory: () => any, attempts?: number) { + if (IS_DEV) lazyWebpackSearchHistory.push(["proxyLazyWebpack", [factory]]); + + return proxyLazy(factory, attempts); +} + +/** + * This is just a wrapper around {@link LazyComponent} to make our reporter test for your webpack finds. + * + * A lazy component. The factory method is called on first render. + * @param factory Function returning a Component + * @param attempts How many times to try to get the component before giving up + * @returns Result of factory function + */ +export function LazyComponentWebpack(factory: () => any, attempts?: number) { + if (IS_DEV) lazyWebpackSearchHistory.push(["LazyComponentWebpack", [factory]]); + + return LazyComponent(factory, attempts); +} + +/** + * find but lazy + */ +export function findLazy(filter: FilterFn) { + if (IS_DEV) lazyWebpackSearchHistory.push(["find", [filter]]); + + return proxyLazy(() => find(filter)); +} + /** * Find the first module that has the specified properties */ @@ -258,6 +294,8 @@ export function findByProps(...props: string[]) { * findByProps but lazy */ export function findByPropsLazy(...props: string[]) { + if (IS_DEV) lazyWebpackSearchHistory.push(["findByProps", props]); + return proxyLazy(() => findByProps(...props)); } @@ -275,6 +313,8 @@ export function findByCode(...code: string[]) { * findByCode but lazy */ export function findByCodeLazy(...code: string[]) { + if (IS_DEV) lazyWebpackSearchHistory.push(["findByCode", code]); + return proxyLazy(() => findByCode(...code)); } @@ -292,6 +332,8 @@ export function findStore(name: string) { * findStore but lazy */ export function findStoreLazy(name: string) { + if (IS_DEV) lazyWebpackSearchHistory.push(["findStore", [name]]); + return proxyLazy(() => findStore(name)); } @@ -309,6 +351,8 @@ export function findComponentByCode(...code: string[]) { * Finds the first component that matches the filter, lazily. */ export function findComponentLazy(filter: FilterFn) { + if (IS_DEV) lazyWebpackSearchHistory.push(["findComponent", [filter]]); + return LazyComponent(() => find(filter)); } @@ -316,6 +360,8 @@ export function findComponentLazy(filter: FilterFn) { * Finds the first component that includes all the given code, lazily */ export function findComponentByCodeLazy(...code: string[]) { + if (IS_DEV) lazyWebpackSearchHistory.push(["findComponentByCode", code]); + return LazyComponent(() => findComponentByCode(...code)); } @@ -323,6 +369,8 @@ export function findComponentByCodeLazy(...code: string[ * Finds the first component that is exported by the first prop name, lazily */ export function findExportedComponentLazy(...props: string[]) { + if (IS_DEV) lazyWebpackSearchHistory.push(["findExportedComponent", props]); + return LazyComponent(() => findByProps(...props)?.[props[0]]); } @@ -330,7 +378,9 @@ export function findExportedComponentLazy(...props: stri * Wait for a module that matches the provided filter to be registered, * then call the callback with the module as the first argument */ -export function waitFor(filter: string | string[] | FilterFn, callback: CallbackFn) { +export function waitFor(filter: string | string[] | FilterFn, callback: CallbackFn, { isIndirect = false }: { isIndirect?: boolean; } = {}) { + if (IS_DEV && !isIndirect) lazyWebpackSearchHistory.push(["waitFor", Array.isArray(filter) ? filter : [filter]]); + if (typeof filter === "string") filter = filters.byProps(filter); else if (Array.isArray(filter)) From 598ffe6368bf454b65c522d4dfbca8a1afc9a05c Mon Sep 17 00:00:00 2001 From: V Date: Sat, 25 Nov 2023 01:51:50 +0100 Subject: [PATCH 210/632] reporter: remove sleeps --- scripts/generateReport.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index cadf0c2af..3cbab55e6 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -338,8 +338,6 @@ function runTime(token: string) { .then(r => r.text()) .then(t => t.includes(".module.wasm") || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push")); - await new Promise(r => setTimeout(r, 150)); - if (isWasm) { invalidChunks.push(id); continue chunksLoop; @@ -371,8 +369,6 @@ function runTime(token: string) { // Loads a chunk if (!isWasm) await wreq.e(id as any); - - await new Promise(r => setTimeout(r, 150)); } // Make sure every chunk has finished loading From 68fca7854179222ba29cb7428f34874f3576fe05 Mon Sep 17 00:00:00 2001 From: V Date: Sat, 25 Nov 2023 01:56:29 +0100 Subject: [PATCH 211/632] reporter: test dev instead of main --- .github/workflows/reportBrokenPlugins.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/reportBrokenPlugins.yml b/.github/workflows/reportBrokenPlugins.yml index 132022008..4b09463e0 100644 --- a/.github/workflows/reportBrokenPlugins.yml +++ b/.github/workflows/reportBrokenPlugins.yml @@ -12,6 +12,12 @@ jobs: steps: - uses: actions/checkout@v3 + if: ${{ github.event_name == 'schedule' }} + with: + ref: dev + + - uses: actions/checkout@v3 + if: ${{ github.event_name == 'workflow_dispatch' }} - uses: pnpm/action-setup@v2 # Install pnpm using packageManager key in package.json From 7c3b247d84c01882830e754e0e2995c36e4a2a33 Mon Sep 17 00:00:00 2001 From: V Date: Sat, 25 Nov 2023 02:43:29 +0100 Subject: [PATCH 212/632] fix WebContextMenus --- src/plugins/webContextMenus.web/index.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/webContextMenus.web/index.ts b/src/plugins/webContextMenus.web/index.ts index aa575bfd2..cf50bb86e 100644 --- a/src/plugins/webContextMenus.web/index.ts +++ b/src/plugins/webContextMenus.web/index.ts @@ -118,10 +118,11 @@ export default definePlugin({ // Add back image context menu { find: 'navId:"image-context"', + all: true, predicate: () => settings.store.addBack, replacement: { - // return IS_DESKTOP && null != ... ? React.createElement(Menu, ...) - match: /return \i\.\i(?=&&null)/, + // return IS_DESKTOP ? React.createElement(Menu, ...) + match: /return \i\.\i(?=\?|&&)/, replace: "return true" } }, From 604cf002116e4172058b2f1c8a6df4a187cf61ab Mon Sep 17 00:00:00 2001 From: V Date: Sat, 25 Nov 2023 02:51:19 +0100 Subject: [PATCH 213/632] reporter: fix markdown output --- scripts/generateReport.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 3cbab55e6..540976714 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -226,7 +226,7 @@ page.on("console", async e => { } if (isDebug) { - console.log(e.text()); + console.error(e.text()); } else if (level === "error") { const text = await Promise.all( e.args().map(async a => { From 6bbf562ab6b92ad7cc267cfa53aec6a88b7c006d Mon Sep 17 00:00:00 2001 From: V Date: Sat, 25 Nov 2023 02:51:58 +0100 Subject: [PATCH 214/632] bump to v1.6.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 23d9dd272..4d0dd262a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.6.3", + "version": "1.6.4", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { From ec16fd874189411479890e00d93770001f9d09ab Mon Sep 17 00:00:00 2001 From: V Date: Sat, 25 Nov 2023 02:55:59 +0100 Subject: [PATCH 215/632] fix ci --- .github/workflows/test.yml | 2 ++ src/utils/lazyReact.tsx | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 46d564184..a756681c2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,9 +3,11 @@ on: push: branches: - main + - dev pull_request: branches: - main + - dev jobs: test: runs-on: ubuntu-latest diff --git a/src/utils/lazyReact.tsx b/src/utils/lazyReact.tsx index e45ca0792..da36d4e79 100644 --- a/src/utils/lazyReact.tsx +++ b/src/utils/lazyReact.tsx @@ -4,6 +4,8 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ +import { ComponentType } from "react"; + import { makeLazy } from "./lazy"; const NoopComponent = () => null; @@ -23,5 +25,5 @@ export function LazyComponent(factory: () => React.Compo LazyComponent.$$get = get; - return LazyComponent; + return LazyComponent as ComponentType; } From 6573c4757cc1b2e3847c037d254cbdd11653b579 Mon Sep 17 00:00:00 2001 From: Korbo Date: Tue, 28 Nov 2023 12:32:29 -0600 Subject: [PATCH 216/632] fix: strikethrough in SilentMessageToggle's disabled SVG (#2004) --- src/plugins/silentMessageToggle/index.tsx | 19 +++++++++---------- src/utils/constants.ts | 4 ++++ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/plugins/silentMessageToggle/index.tsx b/src/plugins/silentMessageToggle/index.tsx index a1ee8de89..b7b33826d 100644 --- a/src/plugins/silentMessageToggle/index.tsx +++ b/src/plugins/silentMessageToggle/index.tsx @@ -80,16 +80,15 @@ function SilentMessageToggle(chatBoxProps: { style={{ padding: "0 6px" }} >
- - - - - {!enabled && } - + + + {!enabled && <> + + + + + + }
diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 7f555d322..0ff7da72a 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -387,6 +387,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "ant0n", id: 145224646868860928n }, + Korbo: { + name: "Korbo", + id: 455856406420258827n + }, } satisfies Record); // iife so #__PURE__ works correctly From 1b179f3c6d2b109251a9abcedfa90666ba0989dd Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 24 Nov 2023 23:14:18 -0300 Subject: [PATCH 217/632] Simplify some components finds; Make undo of patch groups more clear --- src/plugins/typingIndicator/index.tsx | 9 ++------- src/plugins/vencordToolbox/index.tsx | 7 ++----- src/webpack/patchWebpack.ts | 4 ++-- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/plugins/typingIndicator/index.tsx b/src/plugins/typingIndicator/index.tsx index e35eb9b6e..c5cf5a9d0 100644 --- a/src/plugins/typingIndicator/index.tsx +++ b/src/plugins/typingIndicator/index.tsx @@ -20,17 +20,12 @@ import { definePluginSettings, Settings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { find, findStoreLazy, LazyComponentWebpack } from "@webpack"; +import { findExportedComponentLazy, findStoreLazy } from "@webpack"; import { ChannelStore, GuildMemberStore, i18n, RelationshipStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common"; import { buildSeveralUsers } from "../typingTweaks"; -const ThreeDots = LazyComponentWebpack(() => { - // This doesn't really need to explicitly find Dots' own module, but it's fine - const res = find(m => m.Dots && !m.Menu); - - return res?.Dots; -}); +const ThreeDots = findExportedComponentLazy("Dots", "AnimatedDots"); const TypingStore = findStoreLazy("TypingStore"); const UserGuildSettingsStore = findStoreLazy("UserGuildSettingsStore"); diff --git a/src/plugins/vencordToolbox/index.tsx b/src/plugins/vencordToolbox/index.tsx index bb63a86b8..2b0ce14e6 100644 --- a/src/plugins/vencordToolbox/index.tsx +++ b/src/plugins/vencordToolbox/index.tsx @@ -23,14 +23,11 @@ import { Settings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { filters, find, LazyComponentWebpack } from "@webpack"; +import { findExportedComponentLazy } from "@webpack"; import { Menu, Popout, useState } from "@webpack/common"; import type { ReactNode } from "react"; -const HeaderBarIcon = LazyComponentWebpack(() => { - const filter = filters.byCode(".HEADER_BAR_BADGE"); - return find(m => m.Icon && filter(m.Icon))?.Icon; -}); +const HeaderBarIcon = findExportedComponentLazy("Icon", "Divider"); function VencordPopout(onClose: () => void) { const pluginEntries = [] as ReactNode[]; diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index b992cfdea..76a9fb0f5 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -212,7 +212,7 @@ function patchFactories(factories: Record Date: Mon, 27 Nov 2023 02:56:57 -0300 Subject: [PATCH 218/632] Utility function for loading Discord chunks (#2017) --- scripts/generateReport.ts | 8 ++- src/utils/lazyReact.tsx | 2 +- src/webpack/patchWebpack.ts | 8 +-- src/webpack/webpack.ts | 127 ++++++++++++++++++++++++++++++------ 4 files changed, 118 insertions(+), 27 deletions(-) diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 540976714..719a84568 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -406,15 +406,21 @@ function runTime(token: string) { if (method === "proxyLazyWebpack" || method === "LazyComponentWebpack") { const [factory] = args; result = factory(); + } else if (method === "extractAndLoadChunks") { + const [code, matcher] = args; + + const module = Vencord.Webpack.findModuleFactory(...code); + if (module) result = module.toString().match(matcher); } else { // @ts-ignore result = Vencord.Webpack[method](...args); } - if (result == null || ("$$get" in result && result.$$get() == null)) throw "a rock at ben shapiro"; + if (result == null || ("$$vencordInternal" in result && result.$$vencordInternal() == null)) throw "a rock at ben shapiro"; } catch (e) { let logMessage = searchType; if (method === "find" || method === "proxyLazyWebpack" || method === "LazyComponentWebpack") logMessage += `(${args[0].toString().slice(0, 147)}...)`; + else if (method === "extractAndLoadChunks") logMessage += `([${args[0].map(arg => `"${arg}"`).join(", ")}], ${args[1].toString()})`; else logMessage += `(${args.map(arg => `"${arg}"`).join(", ")})`; console.log("[PUP_WEBPACK_FIND_FAIL]", logMessage); diff --git a/src/utils/lazyReact.tsx b/src/utils/lazyReact.tsx index da36d4e79..4896a0581 100644 --- a/src/utils/lazyReact.tsx +++ b/src/utils/lazyReact.tsx @@ -23,7 +23,7 @@ export function LazyComponent(factory: () => React.Compo return ; }; - LazyComponent.$$get = get; + LazyComponent.$$vencordInternal = get; return LazyComponent as ComponentType; } diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index 76a9fb0f5..f131471bf 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -152,11 +152,9 @@ function patchFactories(factories: Record(); export const listeners = new Set(); -export type CallbackFn = (mod: any, id: number) => void; +export type CallbackFn = (mod: any, id: string) => void; export function _initWebpack(instance: typeof window.webpackChunkdiscord_app) { if (cache !== void 0) throw "no."; @@ -111,12 +112,12 @@ export const find = traceFunction("find", function find(filter: FilterFn, { isIn if (!mod?.exports) continue; if (filter(mod.exports)) { - return isWaitFor ? [mod.exports, Number(key)] : mod.exports; + return isWaitFor ? [mod.exports, key] : mod.exports; } if (mod.exports.default && filter(mod.exports.default)) { const found = mod.exports.default; - return isWaitFor ? [found, Number(key)] : found; + return isWaitFor ? [found, key] : found; } } @@ -214,18 +215,21 @@ export const findBulk = traceFunction("findBulk", function findBulk(...filterFns }); /** - * Find the id of a module by its code - * @param code Code - * @returns number or null + * Find the id of the first module factory that includes all the given code + * @returns string or null */ -export const findModuleId = traceFunction("findModuleId", function findModuleId(code: string) { +export const findModuleId = traceFunction("findModuleId", function findModuleId(...code: string[]) { + outer: for (const id in wreq.m) { - if (wreq.m[id].toString().includes(code)) { - return Number(id); + const str = wreq.m[id].toString(); + + for (const c of code) { + if (!str.includes(c)) continue outer; } + return id; } - const err = new Error("Didn't find module with code:\n" + code); + const err = new Error("Didn't find module with code(s):\n" + code.join("\n")); if (IS_DEV) { if (!devToolsOpen) // Strict behaviour in DevBuilds to fail early and make sure the issue is found @@ -237,7 +241,18 @@ export const findModuleId = traceFunction("findModuleId", function findModuleId( return null; }); -export const lazyWebpackSearchHistory = [] as Array<["find" | "findByProps" | "findByCode" | "findStore" | "findComponent" | "findComponentByCode" | "findExportedComponent" | "waitFor" | "waitForComponent" | "waitForStore" | "proxyLazyWebpack" | "LazyComponentWebpack", any[]]>; +/** + * Find the first module factory that includes all the given code + * @returns The module factory or null + */ +export function findModuleFactory(...code: string[]) { + const id = findModuleId(...code); + if (!id) return null; + + return wreq.m[id]; +} + +export const lazyWebpackSearchHistory = [] as Array<["find" | "findByProps" | "findByCode" | "findStore" | "findComponent" | "findComponentByCode" | "findExportedComponent" | "waitFor" | "waitForComponent" | "waitForStore" | "proxyLazyWebpack" | "LazyComponentWebpack" | "extractAndLoadChunks", any[]]>; /** * This is just a wrapper around {@link proxyLazy} to make our reporter test for your webpack finds. @@ -272,7 +287,7 @@ export function LazyComponentWebpack(factory: () => any, } /** - * find but lazy + * Find the first module that matches the filter, lazily */ export function findLazy(filter: FilterFn) { if (IS_DEV) lazyWebpackSearchHistory.push(["find", [filter]]); @@ -291,7 +306,7 @@ export function findByProps(...props: string[]) { } /** - * findByProps but lazy + * Find the first module that has the specified properties, lazily */ export function findByPropsLazy(...props: string[]) { if (IS_DEV) lazyWebpackSearchHistory.push(["findByProps", props]); @@ -300,7 +315,7 @@ export function findByPropsLazy(...props: string[]) { } /** - * Find a function by its code + * Find the first function that includes all the given code */ export function findByCode(...code: string[]) { const res = find(filters.byCode(...code), { isIndirect: true }); @@ -310,7 +325,7 @@ export function findByCode(...code: string[]) { } /** - * findByCode but lazy + * Find the first function that includes all the given code, lazily */ export function findByCodeLazy(...code: string[]) { if (IS_DEV) lazyWebpackSearchHistory.push(["findByCode", code]); @@ -329,7 +344,7 @@ export function findStore(name: string) { } /** - * findStore but lazy + * Find a store by its displayName, lazily */ export function findStoreLazy(name: string) { if (IS_DEV) lazyWebpackSearchHistory.push(["findStore", [name]]); @@ -353,7 +368,13 @@ export function findComponentByCode(...code: string[]) { export function findComponentLazy(filter: FilterFn) { if (IS_DEV) lazyWebpackSearchHistory.push(["findComponent", [filter]]); - return LazyComponent(() => find(filter)); + + return LazyComponent(() => { + const res = find(filter, { isIndirect: true }); + if (!res) + handleModuleNotFound("findComponent", filter); + return res; + }); } /** @@ -362,7 +383,12 @@ export function findComponentLazy(filter: FilterFn) { export function findComponentByCodeLazy(...code: string[]) { if (IS_DEV) lazyWebpackSearchHistory.push(["findComponentByCode", code]); - return LazyComponent(() => findComponentByCode(...code)); + return LazyComponent(() => { + const res = find(filters.componentByCode(...code), { isIndirect: true }); + if (!res) + handleModuleNotFound("findComponentByCode", ...code); + return res; + }); } /** @@ -371,7 +397,68 @@ export function findComponentByCodeLazy(...code: string[ export function findExportedComponentLazy(...props: string[]) { if (IS_DEV) lazyWebpackSearchHistory.push(["findExportedComponent", props]); - return LazyComponent(() => findByProps(...props)?.[props[0]]); + return LazyComponent(() => { + const res = find(filters.byProps(...props), { isIndirect: true }); + if (!res) + handleModuleNotFound("findExportedComponent", ...props); + return res[props[0]]; + }); +} + +/** + * Extract and load chunks using their entry point + * @param code An array of all the code the module factory containing the entry point (as of using it to load chunks) must include + * @param matcher A RegExp that returns the entry point id as the first capture group. Defaults to a matcher that captures the first entry point found in the module factory + */ +export async function extractAndLoadChunks(code: string[], matcher: RegExp = /\.el\("(.+?)"\)(?<=(\i)\.el.+?)\.then\(\2\.bind\(\2,"\1"\)\)/) { + const module = findModuleFactory(...code); + if (!module) { + const err = new Error("extractAndLoadChunks: Couldn't find module factory"); + logger.warn(err, "Code:", code, "Matcher:", matcher); + + return; + } + + const match = module.toString().match(canonicalizeMatch(matcher)); + if (!match) { + const err = new Error("extractAndLoadChunks: Couldn't find entry point id in module factory code"); + logger.warn(err, "Code:", code, "Matcher:", matcher); + + // Strict behaviour in DevBuilds to fail early and make sure the issue is found + if (IS_DEV && !devToolsOpen) + throw err; + + return; + } + + const [, id] = match; + if (!id || !Number(id)) { + const err = new Error("extractAndLoadChunks: Matcher didn't return a capturing group with the entry point, or the entry point returned wasn't a number"); + logger.warn(err, "Code:", code, "Matcher:", matcher); + + // Strict behaviour in DevBuilds to fail early and make sure the issue is found + if (IS_DEV && !devToolsOpen) + throw err; + + return; + } + + await (wreq as any).el(id); + return wreq(id as any); +} + +/** + * This is just a wrapper around {@link extractAndLoadChunks} to make our reporter test for your webpack finds. + * + * Extract and load chunks using their entry point + * @param code An array of all the code the module factory containing the entry point (as of using it to load chunks) must include + * @param matcher A RegExp that returns the entry point id as the first capture group. Defaults to a matcher that captures the first entry point found in the module factory + * @returns A function that loads the chunks on first call + */ +export function extractAndLoadChunksLazy(code: string[], matcher: RegExp = /\.el\("(.+?)"\)(?<=(\i)\.el.+?)\.then\(\2\.bind\(\2,"\1"\)\)/) { + if (IS_DEV) lazyWebpackSearchHistory.push(["extractAndLoadChunks", [code, matcher]]); + + return () => extractAndLoadChunks(code, matcher); } /** @@ -433,7 +520,7 @@ export function search(...filters: Array) { * so putting breakpoints or similar will have no effect. * @param id The id of the module to extract */ -export function extract(id: number) { +export function extract(id: string | number) { const mod = wreq.m[id] as Function; if (!mod) return null; From f814eeb74c3a562b0ad632c808f63dfddd53f59b Mon Sep 17 00:00:00 2001 From: V Date: Mon, 27 Nov 2023 16:05:25 +0100 Subject: [PATCH 219/632] VoiceMessages: fix preview being blank --- src/plugins/voiceMessages/VoicePreview.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/voiceMessages/VoicePreview.tsx b/src/plugins/voiceMessages/VoicePreview.tsx index 0976f7946..9c77d8329 100644 --- a/src/plugins/voiceMessages/VoicePreview.tsx +++ b/src/plugins/voiceMessages/VoicePreview.tsx @@ -25,7 +25,7 @@ interface VoiceMessageProps { src: string; waveform: string; } -const VoiceMessage = findComponentByCodeLazy("waveform:"); +const VoiceMessage = findComponentByCodeLazy("waveform:", "onVolumeChange"); export type VoicePreviewOptions = { src?: string; From 597a74ff6c740e0ff26ca53a56f175b6d06e2e47 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Tue, 28 Nov 2023 17:05:11 -0300 Subject: [PATCH 220/632] ClientTheme: make color picker finder more specific --- src/plugins/clientTheme/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/clientTheme/index.tsx b/src/plugins/clientTheme/index.tsx index 7b30863e7..d75929961 100644 --- a/src/plugins/clientTheme/index.tsx +++ b/src/plugins/clientTheme/index.tsx @@ -15,7 +15,7 @@ import definePlugin, { OptionType, StartAt } from "@utils/types"; import { findComponentByCodeLazy } from "@webpack"; import { Button, Forms } from "@webpack/common"; -const ColorPicker = findComponentByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR"); +const ColorPicker = findComponentByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); const colorPresets = [ "#1E1514", "#172019", "#13171B", "#1C1C28", "#402D2D", From 9b6308a8355910f5911c1202e6a409636cb9e553 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Tue, 28 Nov 2023 22:12:00 -0300 Subject: [PATCH 221/632] Fix a console shortcut and suppressExperimentalWarnings on more scripts --- package.json | 4 ++-- src/plugins/consoleShortcuts/index.ts | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 4d0dd262a..d035dcb6a 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "doc": "docs" }, "scripts": { - "build": "node scripts/build/build.mjs", + "build": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/build.mjs", "buildWeb": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/buildWeb.mjs", "generatePluginJson": "tsx scripts/generatePluginList.ts", "inject": "node scripts/runInstaller.mjs", @@ -28,7 +28,7 @@ "testWeb": "pnpm lint && pnpm buildWeb && pnpm testTsc", "testTsc": "tsc --noEmit", "uninject": "node scripts/runInstaller.mjs", - "watch": "node scripts/build/build.mjs --watch" + "watch": "node --require=./scripts/suppressExperimentalWarnings.js scripts/build/build.mjs --watch" }, "dependencies": { "@sapphi-red/web-noise-suppressor": "0.3.3", diff --git a/src/plugins/consoleShortcuts/index.ts b/src/plugins/consoleShortcuts/index.ts index 10853f25a..e25e7cb30 100644 --- a/src/plugins/consoleShortcuts/index.ts +++ b/src/plugins/consoleShortcuts/index.ts @@ -63,6 +63,7 @@ export default definePlugin({ let fakeRenderWin: WeakRef | undefined; const find = newFindWrapper(f => f); + const findByProps = newFindWrapper(filters.byProps); return { ...Vencord.Webpack.Common, wp: Vencord.Webpack, @@ -73,13 +74,13 @@ export default definePlugin({ wpexs: (code: string) => extract(Webpack.findModuleId(code)!), find, findAll, - findByProps: newFindWrapper(filters.byProps), + findByProps, findAllByProps: (...props: string[]) => findAll(filters.byProps(...props)), findByCode: newFindWrapper(filters.byCode), findAllByCode: (code: string) => findAll(filters.byCode(code)), findComponentByCode: newFindWrapper(filters.componentByCode), findAllComponentsByCode: (...code: string[]) => findAll(filters.componentByCode(...code)), - findExportedComponent: (...props: string[]) => find(...props)[props[0]], + findExportedComponent: (...props: string[]) => findByProps(...props)[props[0]], findStore: newFindWrapper(filters.byStoreName), PluginsApi: Vencord.Plugins, plugins: Vencord.Plugins.plugins, From 091d29bf5e89f1a41f5528c520bb6cfac654b3e4 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 29 Nov 2023 16:14:05 -0300 Subject: [PATCH 222/632] CrashHandler: attempt to prevent more crashes --- src/plugins/crashHandler/index.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/plugins/crashHandler/index.ts b/src/plugins/crashHandler/index.ts index a1ba01c3d..202cac04a 100644 --- a/src/plugins/crashHandler/index.ts +++ b/src/plugins/crashHandler/index.ts @@ -24,11 +24,14 @@ import { closeAllModals } from "@utils/modal"; import definePlugin, { OptionType } from "@utils/types"; import { maybePromptToUpdate } from "@utils/updater"; import { findByPropsLazy } from "@webpack"; -import { FluxDispatcher, NavigationRouter } from "@webpack/common"; +import { FluxDispatcher, NavigationRouter, SelectedChannelStore } from "@webpack/common"; import type { ReactElement } from "react"; const CrashHandlerLogger = new Logger("CrashHandler"); const ModalStack = findByPropsLazy("pushLazy", "popAll"); +const DraftManager = findByPropsLazy("clearDraft", "saveDraft"); +const { DraftType } = findByPropsLazy("DraftType"); +const { closeExpressionPicker } = findByPropsLazy("closeExpressionPicker", "openExpressionPicker"); const settings = definePluginSettings({ attemptToPreventCrashes: { @@ -115,6 +118,20 @@ export default definePlugin({ } catch { } } + try { + const channelId = SelectedChannelStore.getChannelId(); + + DraftManager.clearDraft(channelId, DraftType.ChannelMessage); + DraftManager.clearDraft(channelId, DraftType.FirstThreadMessage); + } catch (err) { + CrashHandlerLogger.debug("Failed to clear drafts.", err); + } + try { + closeExpressionPicker(); + } + catch (err) { + CrashHandlerLogger.debug("Failed to close expression picker.", err); + } try { FluxDispatcher.dispatch({ type: "CONTEXT_MENU_CLOSE" }); } catch (err) { From 9945219de70fb70f43577c7df2c231d5b82a23de Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 29 Nov 2023 23:14:52 -0300 Subject: [PATCH 223/632] openInviteModal utility Co-authored-by: AutumnVN --- src/api/Commands/commandHelpers.ts | 4 ++-- src/plugins/greetStickerPicker/index.tsx | 2 +- src/plugins/spotifyShareCommands/index.ts | 4 ++-- src/plugins/voiceMessages/index.tsx | 4 ++-- src/utils/discord.tsx | 17 +++++++++++++++-- 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/api/Commands/commandHelpers.ts b/src/api/Commands/commandHelpers.ts index 2fd189032..ebcc4e2fa 100644 --- a/src/api/Commands/commandHelpers.ts +++ b/src/api/Commands/commandHelpers.ts @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +import { MessageActions } from "@utils/discord"; import { mergeDefaults } from "@utils/misc"; import { findByPropsLazy } from "@webpack"; import { SnowflakeUtils } from "@webpack/common"; @@ -24,7 +25,6 @@ import type { PartialDeep } from "type-fest"; import { Argument } from "./types"; -const MessageCreator = findByPropsLazy("createBotMessage"); const MessageSender = findByPropsLazy("receiveMessage"); export function generateId() { @@ -38,7 +38,7 @@ export function generateId() { * @returns {Message} */ export function sendBotMessage(channelId: string, message: PartialDeep): Message { - const botMessage = MessageCreator.createBotMessage({ channelId, content: "", embeds: [] }); + const botMessage = MessageActions.createBotMessage({ channelId, content: "", embeds: [] }); MessageSender.receiveMessage(channelId, mergeDefaults(message, botMessage)); diff --git a/src/plugins/greetStickerPicker/index.tsx b/src/plugins/greetStickerPicker/index.tsx index 9623d422e..c2104af4e 100644 --- a/src/plugins/greetStickerPicker/index.tsx +++ b/src/plugins/greetStickerPicker/index.tsx @@ -18,6 +18,7 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; +import { MessageActions } from "@utils/discord"; import definePlugin, { OptionType } from "@utils/types"; import { findByPropsLazy } from "@webpack"; import { ContextMenuApi, FluxDispatcher, Menu } from "@webpack/common"; @@ -49,7 +50,6 @@ const settings = definePluginSettings({ unholyMultiGreetEnabled?: boolean; }>(); -const MessageActions = findByPropsLazy("sendGreetMessage"); const { WELCOME_STICKERS } = findByPropsLazy("WELCOME_STICKERS"); function greet(channel: Channel, message: Message, stickers: string[]) { diff --git a/src/plugins/spotifyShareCommands/index.ts b/src/plugins/spotifyShareCommands/index.ts index 7634e9d57..3569dd288 100644 --- a/src/plugins/spotifyShareCommands/index.ts +++ b/src/plugins/spotifyShareCommands/index.ts @@ -18,6 +18,7 @@ import { ApplicationCommandInputType, sendBotMessage } from "@api/Commands"; import { Devs } from "@utils/constants"; +import { MessageActions } from "@utils/discord"; import definePlugin from "@utils/types"; import { findByPropsLazy } from "@webpack"; import { FluxDispatcher } from "@webpack/common"; @@ -53,7 +54,6 @@ interface Track { } const Spotify = findByPropsLazy("getPlayerState"); -const MessageCreator = findByPropsLazy("getSendMessageOptionsForReply", "sendMessage"); const PendingReplyStore = findByPropsLazy("getPendingReply"); function sendMessage(channelId, message) { @@ -65,7 +65,7 @@ function sendMessage(channelId, message) { ...message }; const reply = PendingReplyStore.getPendingReply(channelId); - MessageCreator.sendMessage(channelId, message, void 0, MessageCreator.getSendMessageOptionsForReply(reply)) + MessageActions.sendMessage(channelId, message, void 0, MessageActions.getSendMessageOptionsForReply(reply)) .then(() => { if (reply) { FluxDispatcher.dispatch({ type: "DELETE_PENDING_REPLY", channelId }); diff --git a/src/plugins/voiceMessages/index.tsx b/src/plugins/voiceMessages/index.tsx index 7c8a06943..17e10a4b8 100644 --- a/src/plugins/voiceMessages/index.tsx +++ b/src/plugins/voiceMessages/index.tsx @@ -21,6 +21,7 @@ import "./styles.css"; import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; import { Microphone } from "@components/Icons"; import { Devs } from "@utils/constants"; +import { MessageActions } from "@utils/discord"; import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModal } from "@utils/modal"; import { useAwaiter } from "@utils/react"; import definePlugin from "@utils/types"; @@ -36,7 +37,6 @@ import { VoicePreview } from "./VoicePreview"; import { VoiceRecorderWeb } from "./WebRecorder"; const CloudUtils = findByPropsLazy("CloudUpload"); -const MessageCreator = findByPropsLazy("getSendMessageOptionsForReply", "sendMessage"); const PendingReplyStore = findStoreLazy("PendingReplyStore"); const OptionClasses = findByPropsLazy("optionName", "optionIcon", "optionLabel"); @@ -100,7 +100,7 @@ function sendAudio(blob: Blob, meta: AudioMetadata) { waveform: meta.waveform, duration_secs: meta.duration, }], - message_reference: reply ? MessageCreator.getSendMessageOptionsForReply(reply)?.messageReference : null, + message_reference: reply ? MessageActions.getSendMessageOptionsForReply(reply)?.messageReference : null, } }); }); diff --git a/src/utils/discord.tsx b/src/utils/discord.tsx index 96193f219..41e18f9d3 100644 --- a/src/utils/discord.tsx +++ b/src/utils/discord.tsx @@ -23,8 +23,21 @@ import { Guild, Message, User } from "discord-types/general"; import { ImageModal, ModalRoot, ModalSize, openModal } from "./modal"; -const MessageActions = findByPropsLazy("editMessage", "sendMessage"); -const UserProfileActions = findByPropsLazy("openUserProfileModal", "closeUserProfileModal"); +export const MessageActions = findByPropsLazy("editMessage", "sendMessage"); +export const UserProfileActions = findByPropsLazy("openUserProfileModal", "closeUserProfileModal"); +export const InviteActions = findByPropsLazy("resolveInvite"); + +export async function openInviteModal(code: string) { + const { invite } = await InviteActions.resolveInvite(code, "Desktop Modal"); + if (!invite) throw new Error("Invalid invite: " + code); + + FluxDispatcher.dispatch({ + type: "INVITE_MODAL_OPEN", + invite, + code, + context: "APP" + }); +} export function getCurrentChannel() { return ChannelStore.getChannel(SelectedChannelStore.getChannelId()); From 8ef1882d4375c18b485d5afa0432b938e04d27e6 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 30 Nov 2023 00:43:23 -0300 Subject: [PATCH 224/632] use findBulk on CrashHandler and cleaups --- src/plugins/crashHandler/index.ts | 23 ++++++++++++++----- .../components/UserPermissions.tsx | 10 ++++---- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/plugins/crashHandler/index.ts b/src/plugins/crashHandler/index.ts index 202cac04a..9d38b7d17 100644 --- a/src/plugins/crashHandler/index.ts +++ b/src/plugins/crashHandler/index.ts @@ -23,15 +23,26 @@ import { Logger } from "@utils/Logger"; import { closeAllModals } from "@utils/modal"; import definePlugin, { OptionType } from "@utils/types"; import { maybePromptToUpdate } from "@utils/updater"; -import { findByPropsLazy } from "@webpack"; +import { filters, findBulk, proxyLazyWebpack } from "@webpack"; import { FluxDispatcher, NavigationRouter, SelectedChannelStore } from "@webpack/common"; import type { ReactElement } from "react"; const CrashHandlerLogger = new Logger("CrashHandler"); -const ModalStack = findByPropsLazy("pushLazy", "popAll"); -const DraftManager = findByPropsLazy("clearDraft", "saveDraft"); -const { DraftType } = findByPropsLazy("DraftType"); -const { closeExpressionPicker } = findByPropsLazy("closeExpressionPicker", "openExpressionPicker"); +const { ModalStack, DraftManager, DraftType, closeExpressionPicker } = proxyLazyWebpack(() => { + const modules = findBulk( + filters.byProps("pushLazy", "popAll"), + filters.byProps("clearDraft", "saveDraft"), + filters.byProps("DraftType"), + filters.byProps("closeExpressionPicker", "openExpressionPicker"), + ); + + return { + ModalStack: modules[0], + DraftManager: modules[1], + DraftType: modules[2]?.DraftType, + closeExpressionPicker: modules[3]?.closeExpressionPicker, + }; +}); const settings = definePluginSettings({ attemptToPreventCrashes: { @@ -138,7 +149,7 @@ export default definePlugin({ CrashHandlerLogger.debug("Failed to close open context menu.", err); } try { - ModalStack?.popAll(); + ModalStack.popAll(); } catch (err) { CrashHandlerLogger.debug("Failed to close old modals.", err); } diff --git a/src/plugins/permissionsViewer/components/UserPermissions.tsx b/src/plugins/permissionsViewer/components/UserPermissions.tsx index b75bafdcb..3c6767713 100644 --- a/src/plugins/permissionsViewer/components/UserPermissions.tsx +++ b/src/plugins/permissionsViewer/components/UserPermissions.tsx @@ -35,15 +35,13 @@ interface UserPermission { type UserPermissions = Array; -const Classes = proxyLazyWebpack(() => { - const modules = findBulk( +const Classes = proxyLazyWebpack(() => + Object.assign({}, ...findBulk( filters.byProps("roles", "rolePill", "rolePillBorder"), filters.byProps("roleCircle", "dotBorderBase", "dotBorderColor"), filters.byProps("roleNameOverflow", "root", "roleName", "roleRemoveButton") - ); - - return Object.assign({}, ...modules); -}) as Record<"roles" | "rolePill" | "rolePillBorder" | "desaturateUserColors" | "flex" | "alignCenter" | "justifyCenter" | "svg" | "background" | "dot" | "dotBorderColor" | "roleCircle" | "dotBorderBase" | "flex" | "alignCenter" | "justifyCenter" | "wrap" | "root" | "role" | "roleRemoveButton" | "roleDot" | "roleFlowerStar" | "roleRemoveIcon" | "roleRemoveIconFocused" | "roleVerifiedIcon" | "roleName" | "roleNameOverflow" | "actionButton" | "overflowButton" | "addButton" | "addButtonIcon" | "overflowRolesPopout" | "overflowRolesPopoutArrowWrapper" | "overflowRolesPopoutArrow" | "popoutBottom" | "popoutTop" | "overflowRolesPopoutHeader" | "overflowRolesPopoutHeaderIcon" | "overflowRolesPopoutHeaderText" | "roleIcon", string>; + )) +) as Record<"roles" | "rolePill" | "rolePillBorder" | "desaturateUserColors" | "flex" | "alignCenter" | "justifyCenter" | "svg" | "background" | "dot" | "dotBorderColor" | "roleCircle" | "dotBorderBase" | "flex" | "alignCenter" | "justifyCenter" | "wrap" | "root" | "role" | "roleRemoveButton" | "roleDot" | "roleFlowerStar" | "roleRemoveIcon" | "roleRemoveIconFocused" | "roleVerifiedIcon" | "roleName" | "roleNameOverflow" | "actionButton" | "overflowButton" | "addButton" | "addButtonIcon" | "overflowRolesPopout" | "overflowRolesPopoutArrowWrapper" | "overflowRolesPopoutArrow" | "popoutBottom" | "popoutTop" | "overflowRolesPopoutHeader" | "overflowRolesPopoutHeaderIcon" | "overflowRolesPopoutHeaderText" | "roleIcon", string>; function UserPermissionsComponent({ guild, guildMember, showBorder }: { guild: Guild; guildMember: GuildMember; showBorder: boolean; }) { const stns = settings.use(["permissionsSortOrder"]); From b47a5f569e3684857048207f0da0d7f6de80c78a Mon Sep 17 00:00:00 2001 From: Jack <30497388+FieryFlames@users.noreply.github.com> Date: Thu, 30 Nov 2023 00:10:50 -0500 Subject: [PATCH 225/632] feat: Add Decor plugin (#910) --- package.json | 3 +- pnpm-lock.yaml | 21 +- src/components/Icons.tsx | 35 +++ src/plugins/decor/README.md | 17 ++ src/plugins/decor/index.tsx | 168 +++++++++++ src/plugins/decor/lib/api.ts | 83 ++++++ src/plugins/decor/lib/constants.ts | 16 ++ .../decor/lib/stores/AuthorizationStore.tsx | 102 +++++++ .../lib/stores/CurrentUserDecorationsStore.ts | 56 ++++ .../decor/lib/stores/UsersDecorationsStore.ts | 118 ++++++++ src/plugins/decor/lib/utils/decoration.ts | 17 ++ .../DecorDecorationGridDecoration.tsx | 35 +++ .../decor/ui/components/DecorSection.tsx | 59 ++++ .../ui/components/DecorationContextMenu.tsx | 47 +++ .../ui/components/DecorationGridCreate.tsx | 30 ++ .../ui/components/DecorationGridNone.tsx | 30 ++ src/plugins/decor/ui/components/Grid.tsx | 28 ++ .../decor/ui/components/SectionedGridList.tsx | 38 +++ src/plugins/decor/ui/components/index.ts | 33 +++ src/plugins/decor/ui/index.ts | 13 + .../decor/ui/modals/ChangeDecorationModal.tsx | 270 ++++++++++++++++++ .../decor/ui/modals/CreateDecorationModal.tsx | 163 +++++++++++ src/plugins/decor/ui/styles.css | 80 ++++++ src/utils/cloud.tsx | 5 +- src/utils/discord.tsx | 24 +- src/webpack/common/components.ts | 4 +- src/webpack/common/types/components.d.ts | 1 + src/webpack/common/utils.ts | 10 +- tsconfig.json | 1 + 29 files changed, 1493 insertions(+), 14 deletions(-) create mode 100644 src/plugins/decor/README.md create mode 100644 src/plugins/decor/index.tsx create mode 100644 src/plugins/decor/lib/api.ts create mode 100644 src/plugins/decor/lib/constants.ts create mode 100644 src/plugins/decor/lib/stores/AuthorizationStore.tsx create mode 100644 src/plugins/decor/lib/stores/CurrentUserDecorationsStore.ts create mode 100644 src/plugins/decor/lib/stores/UsersDecorationsStore.ts create mode 100644 src/plugins/decor/lib/utils/decoration.ts create mode 100644 src/plugins/decor/ui/components/DecorDecorationGridDecoration.tsx create mode 100644 src/plugins/decor/ui/components/DecorSection.tsx create mode 100644 src/plugins/decor/ui/components/DecorationContextMenu.tsx create mode 100644 src/plugins/decor/ui/components/DecorationGridCreate.tsx create mode 100644 src/plugins/decor/ui/components/DecorationGridNone.tsx create mode 100644 src/plugins/decor/ui/components/Grid.tsx create mode 100644 src/plugins/decor/ui/components/SectionedGridList.tsx create mode 100644 src/plugins/decor/ui/components/index.ts create mode 100644 src/plugins/decor/ui/index.ts create mode 100644 src/plugins/decor/ui/modals/ChangeDecorationModal.tsx create mode 100644 src/plugins/decor/ui/modals/CreateDecorationModal.tsx create mode 100644 src/plugins/decor/ui/styles.css diff --git a/package.json b/package.json index d035dcb6a..af4720937 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,8 @@ "tsx": "^3.12.7", "type-fest": "^3.9.0", "typescript": "^5.0.4", - "zip-local": "^0.3.5" + "zip-local": "^0.3.5", + "zustand": "^3.7.2" }, "packageManager": "pnpm@8.10.2", "pnpm": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index be7befab3..43866f50b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,9 +1,5 @@ lockfileVersion: '6.0' -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - patchedDependencies: eslint-plugin-path-alias@1.0.0: hash: m6sma4g6bh67km3q6igf6uxaja @@ -123,6 +119,9 @@ devDependencies: zip-local: specifier: ^0.3.5 version: 0.3.5 + zustand: + specifier: ^3.7.2 + version: 3.7.2 packages: @@ -3450,8 +3449,22 @@ packages: q: 1.5.1 dev: true + /zustand@3.7.2: + resolution: {integrity: sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==} + engines: {node: '>=12.7.0'} + peerDependencies: + react: '>=16.8' + peerDependenciesMeta: + react: + optional: true + dev: true + github.com/mattdesl/gifenc/64842fca317b112a8590f8fef2bf3825da8f6fe3: resolution: {tarball: https://codeload.github.com/mattdesl/gifenc/tar.gz/64842fca317b112a8590f8fef2bf3825da8f6fe3} name: gifenc version: 1.0.3 dev: false + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false diff --git a/src/components/Icons.tsx b/src/components/Icons.tsx index 93b1323e7..2eb83d4ef 100644 --- a/src/components/Icons.tsx +++ b/src/components/Icons.tsx @@ -255,3 +255,38 @@ export function DeleteIcon(props: IconProps) { ); } + +export function PlusIcon(props: IconProps) { + return ( + + + + ); +} + +export function NoEntrySignIcon(props: IconProps) { + return ( + + + + + ); +} diff --git a/src/plugins/decor/README.md b/src/plugins/decor/README.md new file mode 100644 index 000000000..467a61457 --- /dev/null +++ b/src/plugins/decor/README.md @@ -0,0 +1,17 @@ +# Decor + +Custom avatar decorations! + +![Custom decorations in chat](https://github.com/Vendicated/Vencord/assets/30497388/b0c4c4c8-8723-42a8-b50f-195ad4e26136) + +Create and use your own custom avatar decorations, or pick your favorite from the presets. + +You'll be able to see the custom avatar decorations of other users of this plugin, and they'll be able to see your custom avatar decoration. + +You can select and manage your custom avatar decorations under the "Profiles" page in settings, or in the plugin settings. + +![Custom decorations management](https://github.com/Vendicated/Vencord/assets/30497388/74fe8a9e-a2a2-4b29-bc10-9eaa58208ad4) + +Review the [guidelines](https://github.com/decor-discord/.github/blob/main/GUIDELINES.md) before creating your own custom avatar decoration. + +Join the [Discord server](https://discord.gg/dXp2SdxDcP) for support and notifications on your decoration's review. diff --git a/src/plugins/decor/index.tsx b/src/plugins/decor/index.tsx new file mode 100644 index 000000000..4dd7aa0c9 --- /dev/null +++ b/src/plugins/decor/index.tsx @@ -0,0 +1,168 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated, FieryFlames and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import "./ui/styles.css"; + +import { definePluginSettings } from "@api/Settings"; +import ErrorBoundary from "@components/ErrorBoundary"; +import { Link } from "@components/Link"; +import { Devs } from "@utils/constants"; +import { Margins } from "@utils/margins"; +import { classes } from "@utils/misc"; +import { closeAllModals } from "@utils/modal"; +import definePlugin, { OptionType } from "@utils/types"; +import { findByPropsLazy } from "@webpack"; +import { FluxDispatcher, Forms, UserStore } from "@webpack/common"; + +import { CDN_URL, RAW_SKU_ID, SKU_ID } from "./lib/constants"; +import { useAuthorizationStore } from "./lib/stores/AuthorizationStore"; +import { useCurrentUserDecorationsStore } from "./lib/stores/CurrentUserDecorationsStore"; +import { useUserDecorAvatarDecoration, useUsersDecorationsStore } from "./lib/stores/UsersDecorationsStore"; +import { setDecorationGridDecoration, setDecorationGridItem } from "./ui/components"; +import DecorSection from "./ui/components/DecorSection"; + +const { isAnimatedAvatarDecoration } = findByPropsLazy("isAnimatedAvatarDecoration"); +export interface AvatarDecoration { + asset: string; + skuId: string; +} + +const settings = definePluginSettings({ + changeDecoration: { + type: OptionType.COMPONENT, + description: "Change your avatar decoration", + component() { + return
+ + + You can also access Decor decorations from the { + e.preventDefault(); + closeAllModals(); + FluxDispatcher.dispatch({ type: "USER_SETTINGS_MODAL_SET_SECTION", section: "Profile Customization" }); + }} + >Profiles page. + +
; + } + } +}); +export default definePlugin({ + name: "Decor", + description: "Create and use your own custom avatar decorations, or pick your favorite from the presets.", + authors: [Devs.FieryFlames], + patches: [ + // Patch MediaResolver to return correct URL for Decor avatar decorations + { + find: "getAvatarDecorationURL:", + replacement: { + match: /(?<=function \i\(\i\){)(?=let{avatarDecoration)/, + replace: "const vcDecorDecoration=$self.getDecorAvatarDecorationURL(arguments[0]);if(vcDecorDecoration)return vcDecorDecoration;" + } + }, + // Patch profile customization settings to include Decor section + { + find: "DefaultCustomizationSections", + replacement: { + match: /(?<={user:\i},"decoration"\),)/, + replace: "$self.DecorSection()," + } + }, + // Decoration modal module + { + find: ".decorationGridItem", + replacement: [ + { + match: /(?<==)\i=>{let{children.{20,100}decorationGridItem/, + replace: "$self.DecorationGridItem=$&" + }, + { + match: /(?<==)\i=>{let{user:\i,avatarDecoration.{300,600}decorationGridItemChurned/, + replace: "$self.DecorationGridDecoration=$&" + }, + // Remove NEW label from decor avatar decorations + { + match: /(?<=\.Section\.PREMIUM_PURCHASE&&\i;if\()(?<=avatarDecoration:(\i).+?)/, + replace: "$1.skuId===$self.SKU_ID||" + } + ] + }, + { + find: "isAvatarDecorationAnimating:", + group: true, + replacement: [ + // Add Decor avatar decoration hook to avatar decoration hook + { + match: /(?<=TryItOut:\i}\),)(?<=user:(\i).+?)/, + replace: "vcDecorAvatarDecoration=$self.useUserDecorAvatarDecoration($1)," + }, + // Use added hook + { + match: /(?<={avatarDecoration:).{1,20}?(?=,)(?<=avatarDecorationOverride:(\i).+?)/, + replace: "$1??vcDecorAvatarDecoration??($&)" + }, + // Make memo depend on added hook + { + match: /(?<=size:\i}\),\[)/, + replace: "vcDecorAvatarDecoration," + } + ] + }, + // Current user area, at bottom of channels/dm list + { + find: "renderAvatarWithPopout(){", + replacement: [ + // Use Decor avatar decoration hook + { + match: /(?<=getAvatarDecorationURL\)\({avatarDecoration:)(\i).avatarDecoration(?=,)/, + replace: "$self.useUserDecorAvatarDecoration($1)??$&" + } + ] + } + ], + settings, + + flux: { + CONNECTION_OPEN: () => { + useAuthorizationStore.getState().init(); + useCurrentUserDecorationsStore.getState().clear(); + useUsersDecorationsStore.getState().fetch(UserStore.getCurrentUser().id, true); + }, + USER_PROFILE_MODAL_OPEN: data => { + useUsersDecorationsStore.getState().fetch(data.userId, true); + }, + }, + + set DecorationGridItem(e: any) { + setDecorationGridItem(e); + }, + + set DecorationGridDecoration(e: any) { + setDecorationGridDecoration(e); + }, + + SKU_ID, + + useUserDecorAvatarDecoration, + + async start() { + useUsersDecorationsStore.getState().fetch(UserStore.getCurrentUser().id, true); + }, + + 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(); + } else if (avatarDecoration?.skuId === RAW_SKU_ID) { + return avatarDecoration.asset; + } + }, + + DecorSection: ErrorBoundary.wrap(DecorSection) +}); diff --git a/src/plugins/decor/lib/api.ts b/src/plugins/decor/lib/api.ts new file mode 100644 index 000000000..3719cf245 --- /dev/null +++ b/src/plugins/decor/lib/api.ts @@ -0,0 +1,83 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { API_URL } from "./constants"; +import { useAuthorizationStore } from "./stores/AuthorizationStore"; + +export interface Preset { + id: string; + name: string; + description: string | null; + decorations: Decoration[]; + authorIds: string[]; +} + +export interface Decoration { + hash: string; + animated: boolean; + alt: string | null; + authorId: string | null; + reviewed: boolean | null; + presetId: string | null; +} + +export interface NewDecoration { + file: File; + alt: string | null; +} + +export async function fetchApi(url: RequestInfo, options?: RequestInit) { + const res = await fetch(url, { + ...options, + headers: { + ...options?.headers, + Authorization: `Bearer ${useAuthorizationStore.getState().token}` + } + }); + + if (res.ok) return res; + else throw new Error(await res.text()); +} + +export const getUsersDecorations = async (ids?: string[]): Promise> => { + if (ids?.length === 0) return {}; + + const url = new URL(API_URL + "/users"); + if (ids && ids.length !== 0) url.searchParams.set("ids", JSON.stringify(ids)); + + return await fetch(url).then(c => c.json()); +}; + +export const getUserDecorations = async (id: string = "@me"): Promise => + fetchApi(API_URL + `/users/${id}/decorations`).then(c => c.json()); + +export const getUserDecoration = async (id: string = "@me"): Promise => + fetchApi(API_URL + `/users/${id}/decoration`).then(c => c.json()); + +export const setUserDecoration = async (decoration: Decoration | NewDecoration | null, id: string = "@me"): Promise => { + const formData = new FormData(); + + if (!decoration) { + formData.append("hash", "null"); + } else if ("hash" in decoration) { + formData.append("hash", decoration.hash); + } else if ("file" in decoration) { + formData.append("image", decoration.file); + formData.append("alt", decoration.alt ?? "null"); + } + + return fetchApi(API_URL + `/users/${id}/decoration`, { method: "PUT", body: formData }).then(c => + decoration && "file" in decoration ? c.json() : c.text() + ); +}; + +export const getDecoration = async (hash: string): Promise => fetch(API_URL + `/decorations/${hash}`).then(c => c.json()); + +export const deleteDecoration = async (hash: string): Promise => { + await fetchApi(API_URL + `/decorations/${hash}`, { method: "DELETE" }); +}; + +export const getPresets = async (): Promise => fetch(API_URL + "/decorations/presets").then(c => c.json()); diff --git a/src/plugins/decor/lib/constants.ts b/src/plugins/decor/lib/constants.ts new file mode 100644 index 000000000..ce0b59798 --- /dev/null +++ b/src/plugins/decor/lib/constants.ts @@ -0,0 +1,16 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +export const BASE_URL = "https://decor.fieryflames.dev"; +export const API_URL = BASE_URL + "/api"; +export const AUTHORIZE_URL = API_URL + "/authorize"; +export const CDN_URL = "https://ugc.decor.fieryflames.dev"; +export const CLIENT_ID = "1096966363416899624"; +export const SKU_ID = "100101099111114"; // decor in ascii numbers +export const RAW_SKU_ID = "11497119"; // raw in ascii numbers +export const GUILD_ID = "1096357702931841148"; +export const INVITE_KEY = "dXp2SdxDcP"; +export const DECORATION_FETCH_COOLDOWN = 1000 * 60 * 60 * 4; // 4 hours diff --git a/src/plugins/decor/lib/stores/AuthorizationStore.tsx b/src/plugins/decor/lib/stores/AuthorizationStore.tsx new file mode 100644 index 000000000..e31b1f43c --- /dev/null +++ b/src/plugins/decor/lib/stores/AuthorizationStore.tsx @@ -0,0 +1,102 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { DataStore } from "@api/index"; +import { proxyLazy } from "@utils/lazy"; +import { Logger } from "@utils/Logger"; +import { openModal } from "@utils/modal"; +import { OAuth2AuthorizeModal, showToast, Toasts, UserStore, zustandCreate, zustandPersist } from "@webpack/common"; +import type { StateStorage } from "zustand/middleware"; + +import { AUTHORIZE_URL, CLIENT_ID } from "../constants"; + +interface AuthorizationState { + token: string | null; + tokens: Record; + init: () => void; + authorize: () => Promise; + setToken: (token: string) => void; + remove: (id: string) => void; + isAuthorized: () => boolean; +} + +const indexedDBStorage: StateStorage = { + async getItem(name: string): Promise { + return DataStore.get(name).then(v => v ?? null); + }, + async setItem(name: string, value: string): Promise { + await DataStore.set(name, value); + }, + async removeItem(name: string): Promise { + await DataStore.del(name); + }, +}; + +// TODO: Move switching accounts subscription inside the store? +export const useAuthorizationStore = proxyLazy(() => zustandCreate( + zustandPersist( + (set, get) => ({ + token: null, + tokens: {}, + init: () => { set({ token: get().tokens[UserStore.getCurrentUser().id] ?? null }); }, + setToken: (token: string) => set({ token, tokens: { ...get().tokens, [UserStore.getCurrentUser().id]: token } }), + remove: (id: string) => { + const { tokens, init } = get(); + const newTokens = { ...tokens }; + delete newTokens[id]; + set({ tokens: newTokens }); + + init(); + }, + async authorize() { + return new Promise((resolve, reject) => openModal(props => + { + try { + const url = new URL(response.location); + url.searchParams.append("client", "vencord"); + + const req = await fetch(url); + + if (req?.ok) { + const token = await req.text(); + get().setToken(token); + } else { + throw new Error("Request not OK"); + } + resolve(void 0); + } catch (e) { + if (e instanceof Error) { + showToast(`Failed to authorize: ${e.message}`, Toasts.Type.FAILURE); + new Logger("Decor").error("Failed to authorize", e); + reject(e); + } + } + }} + />, { + onCloseCallback() { + reject(new Error("Authorization cancelled")); + }, + } + )); + }, + isAuthorized: () => !!get().token, + }), + { + name: "decor-auth", + getStorage: () => indexedDBStorage, + partialize: state => ({ tokens: state.tokens }), + onRehydrateStorage: () => state => state?.init() + } + ) +)); diff --git a/src/plugins/decor/lib/stores/CurrentUserDecorationsStore.ts b/src/plugins/decor/lib/stores/CurrentUserDecorationsStore.ts new file mode 100644 index 000000000..1485a7438 --- /dev/null +++ b/src/plugins/decor/lib/stores/CurrentUserDecorationsStore.ts @@ -0,0 +1,56 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { proxyLazy } from "@utils/lazy"; +import { UserStore, zustandCreate } from "@webpack/common"; + +import { Decoration, deleteDecoration, getUserDecoration, getUserDecorations, NewDecoration, setUserDecoration } from "../api"; +import { decorationToAsset } from "../utils/decoration"; +import { useUsersDecorationsStore } from "./UsersDecorationsStore"; + +interface UserDecorationsState { + decorations: Decoration[]; + selectedDecoration: Decoration | null; + fetch: () => Promise; + delete: (decoration: Decoration | string) => Promise; + create: (decoration: NewDecoration) => Promise; + select: (decoration: Decoration | null) => Promise; + clear: () => void; +} + +export const useCurrentUserDecorationsStore = proxyLazy(() => zustandCreate((set, get) => ({ + decorations: [], + selectedDecoration: null, + async fetch() { + const decorations = await getUserDecorations(); + const selectedDecoration = await getUserDecoration(); + + set({ decorations, selectedDecoration }); + }, + async create(newDecoration: NewDecoration) { + const decoration = (await setUserDecoration(newDecoration)) as Decoration; + set({ decorations: [...get().decorations, decoration] }); + }, + async delete(decoration: Decoration | string) { + const hash = typeof decoration === "object" ? decoration.hash : decoration; + await deleteDecoration(hash); + + const { selectedDecoration, decorations } = get(); + const newState = { + decorations: decorations.filter(d => d.hash !== hash), + selectedDecoration: selectedDecoration?.hash === hash ? null : selectedDecoration + }; + + set(newState); + }, + async select(decoration: Decoration | null) { + if (get().selectedDecoration === decoration) return; + set({ selectedDecoration: decoration }); + setUserDecoration(decoration); + useUsersDecorationsStore.getState().set(UserStore.getCurrentUser().id, decoration ? decorationToAsset(decoration) : null); + }, + clear: () => set({ decorations: [], selectedDecoration: null }) +}))); diff --git a/src/plugins/decor/lib/stores/UsersDecorationsStore.ts b/src/plugins/decor/lib/stores/UsersDecorationsStore.ts new file mode 100644 index 000000000..7295a3b17 --- /dev/null +++ b/src/plugins/decor/lib/stores/UsersDecorationsStore.ts @@ -0,0 +1,118 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { debounce } from "@utils/debounce"; +import { proxyLazy } from "@utils/lazy"; +import { useEffect, useState, zustandCreate } from "@webpack/common"; +import { User } from "discord-types/general"; + +import { AvatarDecoration } from "../../"; +import { getUsersDecorations } from "../api"; +import { DECORATION_FETCH_COOLDOWN, SKU_ID } from "../constants"; + +interface UserDecorationData { + asset: string | null; + fetchedAt: Date; +} + +interface UsersDecorationsState { + usersDecorations: Map; + fetchQueue: Set; + bulkFetch: () => Promise; + fetch: (userId: string, force?: boolean) => Promise; + fetchMany: (userIds: string[]) => Promise; + get: (userId: string) => UserDecorationData | undefined; + getAsset: (userId: string) => string | null | undefined; + has: (userId: string) => boolean; + set: (userId: string, decoration: string | null) => void; +} + +export const useUsersDecorationsStore = proxyLazy(() => zustandCreate((set, get) => ({ + usersDecorations: new Map(), + fetchQueue: new Set(), + bulkFetch: debounce(async () => { + const { fetchQueue, usersDecorations } = get(); + + if (fetchQueue.size === 0) return; + + set({ fetchQueue: new Set() }); + + const fetchIds = Array.from(fetchQueue); + const fetchedUsersDecorations = await getUsersDecorations(fetchIds); + + const newUsersDecorations = new Map(usersDecorations); + + const now = new Date(); + for (const fetchId of fetchIds) { + const newDecoration = fetchedUsersDecorations[fetchId] ?? null; + newUsersDecorations.set(fetchId, { asset: newDecoration, fetchedAt: now }); + } + + set({ usersDecorations: newUsersDecorations }); + }), + async fetch(userId: string, force: boolean = false) { + const { usersDecorations, fetchQueue, bulkFetch } = get(); + + const { fetchedAt } = usersDecorations.get(userId) ?? {}; + if (fetchedAt) { + if (!force && Date.now() - fetchedAt.getTime() < DECORATION_FETCH_COOLDOWN) return; + } + + set({ fetchQueue: new Set(fetchQueue).add(userId) }); + bulkFetch(); + }, + async fetchMany(userIds) { + if (!userIds.length) return; + const { usersDecorations, fetchQueue, bulkFetch } = get(); + + const newFetchQueue = new Set(fetchQueue); + + const now = Date.now(); + for (const userId of userIds) { + const { fetchedAt } = usersDecorations.get(userId) ?? {}; + if (fetchedAt) { + if (now - fetchedAt.getTime() < DECORATION_FETCH_COOLDOWN) continue; + } + newFetchQueue.add(userId); + } + + set({ fetchQueue: newFetchQueue }); + bulkFetch(); + }, + get(userId: string) { return get().usersDecorations.get(userId); }, + getAsset(userId: string) { return get().usersDecorations.get(userId)?.asset; }, + has(userId: string) { return get().usersDecorations.has(userId); }, + set(userId: string, decoration: string | null) { + const { usersDecorations } = get(); + const newUsersDecorations = new Map(usersDecorations); + + newUsersDecorations.set(userId, { asset: decoration, fetchedAt: new Date() }); + set({ usersDecorations: newUsersDecorations }); + } +}))); + +export function useUserDecorAvatarDecoration(user?: User): AvatarDecoration | null | undefined { + const [decorAvatarDecoration, setDecorAvatarDecoration] = useState(user ? useUsersDecorationsStore.getState().getAsset(user.id) ?? null : null); + + useEffect(() => { + const destructor = useUsersDecorationsStore.subscribe( + state => { + if (!user) return; + const newDecorAvatarDecoration = state.getAsset(user.id); + if (!newDecorAvatarDecoration) return; + if (decorAvatarDecoration !== newDecorAvatarDecoration) setDecorAvatarDecoration(newDecorAvatarDecoration); + } + ); + + if (user) { + const { fetch: fetchUserDecorAvatarDecoration } = useUsersDecorationsStore.getState(); + fetchUserDecorAvatarDecoration(user.id); + } + return destructor; + }, []); + + return decorAvatarDecoration ? { asset: decorAvatarDecoration, skuId: SKU_ID } : null; +} diff --git a/src/plugins/decor/lib/utils/decoration.ts b/src/plugins/decor/lib/utils/decoration.ts new file mode 100644 index 000000000..176507ef8 --- /dev/null +++ b/src/plugins/decor/lib/utils/decoration.ts @@ -0,0 +1,17 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { AvatarDecoration } from "../../"; +import { Decoration } from "../api"; +import { SKU_ID } from "../constants"; + +export function decorationToAsset(decoration: Decoration) { + return `${decoration.animated ? "a_" : ""}${decoration.hash}`; +} + +export function decorationToAvatarDecoration(decoration: Decoration): AvatarDecoration { + return { asset: decorationToAsset(decoration), skuId: SKU_ID }; +} diff --git a/src/plugins/decor/ui/components/DecorDecorationGridDecoration.tsx b/src/plugins/decor/ui/components/DecorDecorationGridDecoration.tsx new file mode 100644 index 000000000..deaeef630 --- /dev/null +++ b/src/plugins/decor/ui/components/DecorDecorationGridDecoration.tsx @@ -0,0 +1,35 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { ContextMenuApi } from "@webpack/common"; +import type { HTMLProps } from "react"; + +import { Decoration } from "../../lib/api"; +import { decorationToAvatarDecoration } from "../../lib/utils/decoration"; +import { DecorationGridDecoration } from "."; +import DecorationContextMenu from "./DecorationContextMenu"; + +interface DecorDecorationGridDecorationProps extends HTMLProps { + decoration: Decoration; + isSelected: boolean; + onSelect: () => void; +} + +export default function DecorDecorationGridDecoration(props: DecorDecorationGridDecorationProps) { + const { decoration } = props; + + return { + ContextMenuApi.openContextMenu(e, () => ( + + )); + }} + avatarDecoration={decorationToAvatarDecoration(decoration)} + />; +} diff --git a/src/plugins/decor/ui/components/DecorSection.tsx b/src/plugins/decor/ui/components/DecorSection.tsx new file mode 100644 index 000000000..f11a87a53 --- /dev/null +++ b/src/plugins/decor/ui/components/DecorSection.tsx @@ -0,0 +1,59 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { Flex } from "@components/Flex"; +import { findByCodeLazy } from "@webpack"; +import { Button, useEffect } from "@webpack/common"; + +import { useAuthorizationStore } from "../../lib/stores/AuthorizationStore"; +import { useCurrentUserDecorationsStore } from "../../lib/stores/CurrentUserDecorationsStore"; +import { cl } from "../"; +import { openChangeDecorationModal } from "../modals/ChangeDecorationModal"; + +const CustomizationSection = findByCodeLazy(".customizationSectionBackground"); + +interface DecorSectionProps { + hideTitle?: boolean; + hideDivider?: boolean; + noMargin?: boolean; +} + +export default function DecorSection({ hideTitle = false, hideDivider = false, noMargin = false }: DecorSectionProps) { + const authorization = useAuthorizationStore(); + const { selectedDecoration, select: selectDecoration, fetch: fetchDecorations } = useCurrentUserDecorationsStore(); + + useEffect(() => { + if (authorization.isAuthorized()) fetchDecorations(); + }, [authorization.token]); + + return + + + {selectedDecoration && authorization.isAuthorized() && } + + ; +} diff --git a/src/plugins/decor/ui/components/DecorationContextMenu.tsx b/src/plugins/decor/ui/components/DecorationContextMenu.tsx new file mode 100644 index 000000000..7451bb229 --- /dev/null +++ b/src/plugins/decor/ui/components/DecorationContextMenu.tsx @@ -0,0 +1,47 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { CopyIcon, DeleteIcon } from "@components/Icons"; +import { Alerts, Clipboard, ContextMenuApi, Menu, UserStore } from "webpack/common"; + +import { Decoration } from "../../lib/api"; +import { useCurrentUserDecorationsStore } from "../../lib/stores/CurrentUserDecorationsStore"; +import { cl } from "../"; + +export default function DecorationContextMenu({ decoration }: { decoration: Decoration; }) { + const { delete: deleteDecoration } = useCurrentUserDecorationsStore(); + + return + Clipboard.copy(decoration.hash)} + /> + {decoration.authorId === UserStore.getCurrentUser().id && + Alerts.show({ + title: "Delete Decoration", + body: `Are you sure you want to delete ${decoration.alt}?`, + confirmText: "Delete", + confirmColor: cl("danger-btn"), + cancelText: "Cancel", + onConfirm() { + deleteDecoration(decoration); + } + })} + /> + } + ; +} diff --git a/src/plugins/decor/ui/components/DecorationGridCreate.tsx b/src/plugins/decor/ui/components/DecorationGridCreate.tsx new file mode 100644 index 000000000..7699b23d9 --- /dev/null +++ b/src/plugins/decor/ui/components/DecorationGridCreate.tsx @@ -0,0 +1,30 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { PlusIcon } from "@components/Icons"; +import { i18n, Text } from "@webpack/common"; +import { HTMLProps } from "react"; + +import { DecorationGridItem } from "."; + +type DecorationGridCreateProps = HTMLProps & { + onSelect: () => void; +}; + +export default function DecorationGridCreate(props: DecorationGridCreateProps) { + return + + + {i18n.Messages.CREATE} + + ; +} diff --git a/src/plugins/decor/ui/components/DecorationGridNone.tsx b/src/plugins/decor/ui/components/DecorationGridNone.tsx new file mode 100644 index 000000000..b6114c674 --- /dev/null +++ b/src/plugins/decor/ui/components/DecorationGridNone.tsx @@ -0,0 +1,30 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { NoEntrySignIcon } from "@components/Icons"; +import { i18n, Text } from "@webpack/common"; +import { HTMLProps } from "react"; + +import { DecorationGridItem } from "."; + +type DecorationGridNoneProps = HTMLProps & { + isSelected: boolean; + onSelect: () => void; +}; + +export default function DecorationGridNone(props: DecorationGridNoneProps) { + return + + + {i18n.Messages.NONE} + + ; +} diff --git a/src/plugins/decor/ui/components/Grid.tsx b/src/plugins/decor/ui/components/Grid.tsx new file mode 100644 index 000000000..401802481 --- /dev/null +++ b/src/plugins/decor/ui/components/Grid.tsx @@ -0,0 +1,28 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { React } from "@webpack/common"; + +import { cl } from "../"; + +export interface GridProps { + renderItem: (item: ItemT) => JSX.Element; + getItemKey: (item: ItemT) => string; + itemKeyPrefix?: string; + items: Array; +} + +export default function Grid({ renderItem, getItemKey, itemKeyPrefix: ikp, items }: GridProps) { + return
+ {items.map(item => + + {renderItem(item)} + + )} +
; +} diff --git a/src/plugins/decor/ui/components/SectionedGridList.tsx b/src/plugins/decor/ui/components/SectionedGridList.tsx new file mode 100644 index 000000000..9a6ec1b8d --- /dev/null +++ b/src/plugins/decor/ui/components/SectionedGridList.tsx @@ -0,0 +1,38 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { classes } from "@utils/misc"; +import { findByPropsLazy } from "@webpack"; +import { React } from "@webpack/common"; + +import { cl } from "../"; +import Grid, { GridProps } from "./Grid"; + +const ScrollerClasses = findByPropsLazy("managedReactiveScroller"); + +type Section = SectionT & { + items: Array; +}; + +interface SectionedGridListProps> extends Omit, "items"> { + renderSectionHeader: (section: SectionU) => JSX.Element; + getSectionKey: (section: SectionU) => string; + sections: SectionU[]; +} + +export default function SectionedGridList(props: SectionedGridListProps) { + return
+ {props.sections.map(section =>
+ {props.renderSectionHeader(section)} + +
)} +
; +} diff --git a/src/plugins/decor/ui/components/index.ts b/src/plugins/decor/ui/components/index.ts new file mode 100644 index 000000000..8f39a10ee --- /dev/null +++ b/src/plugins/decor/ui/components/index.ts @@ -0,0 +1,33 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { findComponentByCode, LazyComponentWebpack } from "@webpack"; +import { React } from "@webpack/common"; +import type { ComponentType, HTMLProps, PropsWithChildren } from "react"; + +import { AvatarDecoration } from "../.."; + +type DecorationGridItemComponent = ComponentType> & { + onSelect: () => void, + isSelected: boolean, +}>; + +export let DecorationGridItem: DecorationGridItemComponent; +export const setDecorationGridItem = v => DecorationGridItem = v; + +export const AvatarDecorationModalPreview = LazyComponentWebpack(() => { + const component = findComponentByCode("AvatarDecorationModalPreview"); + return React.memo(component); +}); + +type DecorationGridDecorationComponent = React.ComponentType & { + avatarDecoration: AvatarDecoration; + onSelect: () => void, + isSelected: boolean, +}>; + +export let DecorationGridDecoration: DecorationGridDecorationComponent; +export const setDecorationGridDecoration = v => DecorationGridDecoration = v; diff --git a/src/plugins/decor/ui/index.ts b/src/plugins/decor/ui/index.ts new file mode 100644 index 000000000..52b169d77 --- /dev/null +++ b/src/plugins/decor/ui/index.ts @@ -0,0 +1,13 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { classNameFactory } from "@api/Styles"; +import { extractAndLoadChunksLazy } from "@webpack"; + +export const cl = classNameFactory("vc-decor-"); + +export const requireAvatarDecorationModal = extractAndLoadChunksLazy(["openAvatarDecorationModal:"]); +export const requireCreateStickerModal = extractAndLoadChunksLazy(["stickerInspected]:"]); diff --git a/src/plugins/decor/ui/modals/ChangeDecorationModal.tsx b/src/plugins/decor/ui/modals/ChangeDecorationModal.tsx new file mode 100644 index 000000000..bed007174 --- /dev/null +++ b/src/plugins/decor/ui/modals/ChangeDecorationModal.tsx @@ -0,0 +1,270 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { Flex } from "@components/Flex"; +import { openInviteModal } from "@utils/discord"; +import { Margins } from "@utils/margins"; +import { classes } from "@utils/misc"; +import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal"; +import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; +import { Alerts, Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, Parser, Text, Tooltip, useEffect, UserStore, UserUtils, useState } from "@webpack/common"; +import { User } from "discord-types/general"; + +import { Decoration, getPresets, Preset } from "../../lib/api"; +import { GUILD_ID, INVITE_KEY } from "../../lib/constants"; +import { useAuthorizationStore } from "../../lib/stores/AuthorizationStore"; +import { useCurrentUserDecorationsStore } from "../../lib/stores/CurrentUserDecorationsStore"; +import { decorationToAvatarDecoration } from "../../lib/utils/decoration"; +import { cl, requireAvatarDecorationModal } from "../"; +import { AvatarDecorationModalPreview } from "../components"; +import DecorationGridCreate from "../components/DecorationGridCreate"; +import DecorationGridNone from "../components/DecorationGridNone"; +import DecorDecorationGridDecoration from "../components/DecorDecorationGridDecoration"; +import SectionedGridList from "../components/SectionedGridList"; +import { openCreateDecorationModal } from "./CreateDecorationModal"; + +const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers"); +const DecorationModalStyles = findByPropsLazy("modalFooterShopButton"); + +function usePresets() { + const [presets, setPresets] = useState([]); + useEffect(() => { getPresets().then(setPresets); }, []); + return presets; +} + +interface Section { + title: string; + subtitle?: string; + sectionKey: string; + items: ("none" | "create" | Decoration)[]; + authorIds?: string[]; +} + +function SectionHeader({ section }: { section: Section; }) { + const hasSubtitle = typeof section.subtitle !== "undefined"; + const hasAuthorIds = typeof section.authorIds !== "undefined"; + + const [authors, setAuthors] = useState([]); + + useEffect(() => { + (async () => { + if (!section.authorIds) return; + + for (const authorId of section.authorIds) { + const author = UserStore.getUser(authorId) ?? await UserUtils.getUser(authorId); + setAuthors(authors => [...authors, author]); + } + })(); + }, [section.authorIds]); + + return
+ + {section.title} + {hasAuthorIds && + } + + {hasSubtitle && + + {section.subtitle} + + } +
; +} + +export default function ChangeDecorationModal(props: any) { + // undefined = not trying, null = none, Decoration = selected + const [tryingDecoration, setTryingDecoration] = useState(undefined); + const isTryingDecoration = typeof tryingDecoration !== "undefined"; + + const avatarDecorationOverride = tryingDecoration != null ? decorationToAvatarDecoration(tryingDecoration) : tryingDecoration; + + const { + decorations, + selectedDecoration, + fetch: fetchUserDecorations, + select: selectDecoration + } = useCurrentUserDecorationsStore(); + + useEffect(() => { + fetchUserDecorations(); + }, []); + + const activeSelectedDecoration = isTryingDecoration ? tryingDecoration : selectedDecoration; + const activeDecorationHasAuthor = typeof activeSelectedDecoration?.authorId !== "undefined"; + const hasDecorationPendingReview = decorations.some(d => d.reviewed === false); + + const presets = usePresets(); + const presetDecorations = presets.flatMap(preset => preset.decorations); + + const activeDecorationPreset = presets.find(preset => preset.id === activeSelectedDecoration?.presetId); + const isActiveDecorationPreset = typeof activeDecorationPreset !== "undefined"; + + const ownDecorations = decorations.filter(d => !presetDecorations.some(p => p.hash === d.hash)); + + const data = [ + { + title: "Your Decorations", + sectionKey: "ownDecorations", + items: ["none", ...ownDecorations, "create"] + }, + ...presets.map(preset => ({ + title: preset.name, + subtitle: preset.description || undefined, + sectionKey: `preset-${preset.id}`, + items: preset.decorations, + authorIds: preset.authorIds + })) + ] as Section[]; + + return + + + Change Decoration + + + + + { + if (typeof item === "string") { + switch (item) { + case "none": + return setTryingDecoration(null)} + />; + case "create": + return + {tooltipProps => { }} + />} + ; + } + } else { + return + {tooltipProps => ( + setTryingDecoration(item) : () => { }} + isSelected={activeSelectedDecoration?.hash === item.hash} + decoration={item} + /> + )} + ; + } + }} + getItemKey={item => typeof item === "string" ? item : item.hash} + getSectionKey={section => section.sectionKey} + renderSectionHeader={section => } + sections={data} + /> +
+ + {isActiveDecorationPreset && Part of the {activeDecorationPreset.name} Preset} + {typeof activeSelectedDecoration === "object" && + + {activeSelectedDecoration?.alt} + + } + {activeDecorationHasAuthor && Created by {Parser.parse(`<@${activeSelectedDecoration.authorId}>`)}} +
+
+ +
+ + +
+
+ + + {tooltipProps => } + +
+
+
; +} + +export const openChangeDecorationModal = () => + requireAvatarDecorationModal().then(() => openModal(props => )); diff --git a/src/plugins/decor/ui/modals/CreateDecorationModal.tsx b/src/plugins/decor/ui/modals/CreateDecorationModal.tsx new file mode 100644 index 000000000..a5937b0dd --- /dev/null +++ b/src/plugins/decor/ui/modals/CreateDecorationModal.tsx @@ -0,0 +1,163 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { Link } from "@components/Link"; +import { openInviteModal } from "@utils/discord"; +import { Margins } from "@utils/margins"; +import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal"; +import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; +import { Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, Text, TextInput, useEffect, useMemo, UserStore, useState } from "@webpack/common"; + +import { GUILD_ID, INVITE_KEY, RAW_SKU_ID } from "../../lib/constants"; +import { useCurrentUserDecorationsStore } from "../../lib/stores/CurrentUserDecorationsStore"; +import { cl, requireAvatarDecorationModal, requireCreateStickerModal } from "../"; +import { AvatarDecorationModalPreview } from "../components"; + + +const DecorationModalStyles = findByPropsLazy("modalFooterShopButton"); + +const FileUpload = findComponentByCodeLazy("fileUploadInput,"); + +function useObjectURL(object: Blob | MediaSource | null) { + const [url, setUrl] = useState(null); + + useEffect(() => { + if (!object) return; + + const objectUrl = URL.createObjectURL(object); + setUrl(objectUrl); + + return () => { + URL.revokeObjectURL(objectUrl); + setUrl(null); + }; + }, [object]); + + return url; +} + +export default function CreateDecorationModal(props) { + const [name, setName] = useState(""); + const [file, setFile] = useState(null); + const [submitting, setSubmitting] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + if (error) setError(null); + }, [file]); + + const { create: createDecoration } = useCurrentUserDecorationsStore(); + + const fileUrl = useObjectURL(file); + + const decoration = useMemo(() => fileUrl ? { asset: fileUrl, skuId: RAW_SKU_ID } : null, [fileUrl]); + + return + + + Create Decoration + + + + +
+
+ {error !== null && {error.message}} + + + + File should be APNG or PNG. + + + + + + This name will be used when referring to this decoration. + + +
+
+ +
+
+ + Make sure your decoration does not violate + the guidelines + before creating your decoration. +
You can receive updates on your decoration's review by joining { + e.preventDefault(); + if (!GuildStore.getGuild(GUILD_ID)) { + const inviteAccepted = await openInviteModal(INVITE_KEY); + if (inviteAccepted) { + closeAllModals(); + FluxDispatcher.dispatch({ type: "LAYER_POP_ALL" }); + } + } else { + closeAllModals(); + FluxDispatcher.dispatch({ type: "LAYER_POP_ALL" }); + NavigationRouter.transitionToGuild(GUILD_ID); + } + }} + > + Decor's Discord server + . +
+
+ + + + +
; +} + +export const openCreateDecorationModal = () => + Promise.all([requireAvatarDecorationModal(), requireCreateStickerModal()]) + .then(() => openModal(props => )); diff --git a/src/plugins/decor/ui/styles.css b/src/plugins/decor/ui/styles.css new file mode 100644 index 000000000..ff10c82fa --- /dev/null +++ b/src/plugins/decor/ui/styles.css @@ -0,0 +1,80 @@ +.vc-decor-danger-btn { + color: var(--white-500); + background-color: var(--button-danger-background); +} + +.vc-decor-change-decoration-modal-content { + position: relative; + display: flex; + border-radius: 5px 5px 0 0; + padding: 0 16px; + gap: 4px +} + +.vc-decor-change-decoration-modal-preview { + display: flex; + flex-direction: column; + margin-top: 24px; + gap: 8px; + max-width: 280px; +} + +.vc-decor-change-decoration-modal-decoration { + width: 80px; + height: 80px; +} + +.vc-decor-change-decoration-modal-footer { + justify-content: space-between; +} + +.vc-decor-change-decoration-modal-footer-btn-container { + display: flex; + flex-direction: row-reverse; +} + +.vc-decor-create-decoration-modal-content { + display: flex; + flex-direction: column; + gap: 20px; + padding: 0 16px; +} + +.vc-decor-create-decoration-modal-form-preview-container { + display: flex; + gap: 16px; +} + +.vc-decor-modal-header { + padding: 16px; +} + +.vc-decor-modal-footer { + padding: 16px; +} + +.vc-decor-create-decoration-modal-form { + display: flex; + flex-direction: column; + flex-grow: 1; + gap: 16px; +} + +.vc-decor-sectioned-grid-list-container { + display: flex; + flex-direction: column; + overflow: hidden scroll; + max-height: 512px; + width: 352px; /* ((80 + 8 (grid gap)) * desired columns) (scrolled takes the extra 8 padding off conveniently) */ + gap: 12px; +} + +.vc-decor-sectioned-grid-list-grid { + display: flex; + flex-wrap: wrap; + gap: 8px +} + +.vc-decor-section-remove-margin { + margin-bottom: 0; +} diff --git a/src/utils/cloud.tsx b/src/utils/cloud.tsx index 029306227..f56c78dc5 100644 --- a/src/utils/cloud.tsx +++ b/src/utils/cloud.tsx @@ -19,8 +19,7 @@ import * as DataStore from "@api/DataStore"; import { showNotification } from "@api/Notifications"; import { Settings } from "@api/Settings"; -import { findByProps } from "@webpack"; -import { UserStore } from "@webpack/common"; +import { OAuth2AuthorizeModal, UserStore } from "@webpack/common"; import { Logger } from "./Logger"; import { openModal } from "./modal"; @@ -91,8 +90,6 @@ export async function authorizeCloud() { return; } - const { OAuth2AuthorizeModal } = findByProps("OAuth2AuthorizeModal"); - openModal((props: any) => (r => { + let onClose: () => void, onAccept: () => void; + let inviteAccepted = false; + + FluxDispatcher.subscribe("INVITE_ACCEPT", onAccept = () => { + inviteAccepted = true; + }); + + FluxDispatcher.subscribe("INVITE_MODAL_CLOSE", onClose = () => { + FluxDispatcher.unsubscribe("INVITE_MODAL_CLOSE", onClose); + FluxDispatcher.unsubscribe("INVITE_ACCEPT", onAccept); + r(inviteAccepted); + }); + }); } export function getCurrentChannel() { diff --git a/src/webpack/common/components.ts b/src/webpack/common/components.ts index e44b1c9f6..d7bb5d759 100644 --- a/src/webpack/common/components.ts +++ b/src/webpack/common/components.ts @@ -17,7 +17,7 @@ */ // eslint-disable-next-line path-alias/no-relative -import { filters, waitFor } from "@webpack"; +import { filters, findByPropsLazy, waitFor } from "@webpack"; import { waitForComponent } from "./internal"; import * as t from "./types/components"; @@ -55,6 +55,8 @@ export const MaskedLink = waitForComponent("MaskedLink", m => m?.t export const Timestamp = waitForComponent("Timestamp", filters.byCode(".Messages.MESSAGE_EDITED_TIMESTAMP_A11Y_LABEL.format")); export const Flex = waitForComponent("Flex", ["Justify", "Align", "Wrap"]); +export const { OAuth2AuthorizeModal } = findByPropsLazy("OAuth2AuthorizeModal"); + waitFor(["FormItem", "Button"], m => { ({ useToken, Card, Button, FormSwitch: Switch, Tooltip, TextInput, TextArea, Text, Select, SearchableSelect, Slider, ButtonLooks, TabBar, Popout, Dialog, Paginator, ScrollerThin, Clickable, Avatar } = m); Forms = m; diff --git a/src/webpack/common/types/components.d.ts b/src/webpack/common/types/components.d.ts index 5d5424fe2..b9bc434c6 100644 --- a/src/webpack/common/types/components.d.ts +++ b/src/webpack/common/types/components.d.ts @@ -126,6 +126,7 @@ export type Button = ComponentType; focusProps?: any; + submitting?: boolean; submittingStartedLabel?: string; submittingFinishedLabel?: string; diff --git a/src/webpack/common/utils.ts b/src/webpack/common/utils.ts index cef4d51d6..f5d2a9666 100644 --- a/src/webpack/common/utils.ts +++ b/src/webpack/common/utils.ts @@ -19,7 +19,7 @@ import type { Channel, User } from "discord-types/general"; // eslint-disable-next-line path-alias/no-relative -import { _resolveReady, findByPropsLazy, findLazy, waitFor } from "../webpack"; +import { _resolveReady, filters, findByCodeLazy, findByPropsLazy, findLazy, waitFor } from "../webpack"; import type * as t from "./types/utils"; export let FluxDispatcher: t.FluxDispatcher; @@ -127,5 +127,9 @@ export const NavigationRouter: t.NavigationRouter = findByPropsLazy("transitionT export let SettingsRouter: any; waitFor(["open", "saveAccountChanges"], m => SettingsRouter = m); -const { Permissions } = findLazy(m => typeof m.Permissions?.ADMINISTRATOR === "bigint") as { Permissions: t.PermissionsBits; }; -export { Permissions as PermissionsBits }; +export const { Permissions: PermissionsBits } = findLazy(m => typeof m.Permissions?.ADMINISTRATOR === "bigint") as { Permissions: t.PermissionsBits; }; + +export const zustandCreate: typeof import("zustand").default = findByCodeLazy("will be removed in v4"); + +const persistFilter = filters.byCode("[zustand persist middleware]"); +export const { persist: zustandPersist }: typeof import("zustand/middleware") = findLazy(m => m.persist && persistFilter(m.persist)); diff --git a/tsconfig.json b/tsconfig.json index db5407455..4563f3f86 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,7 @@ "compilerOptions": { "allowSyntheticDefaultImports": true, "esModuleInterop": true, + "skipLibCheck": true, "lib": [ "DOM", "DOM.Iterable", From 66dbe7ef07c69f8c9963c947c5f1eac03b0e1222 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 30 Nov 2023 02:26:18 -0300 Subject: [PATCH 226/632] Fix reporter testing for extractAndLoadChunks --- scripts/generateReport.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 719a84568..c1a4f711e 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -410,7 +410,7 @@ function runTime(token: string) { const [code, matcher] = args; const module = Vencord.Webpack.findModuleFactory(...code); - if (module) result = module.toString().match(matcher); + if (module) result = module.toString().match(Vencord.Util.canonicalizeMatch(matcher)); } else { // @ts-ignore result = Vencord.Webpack[method](...args); From 8e1546be000748ac647d5a1950e9c5750a016b27 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 30 Nov 2023 02:38:12 -0300 Subject: [PATCH 227/632] Include ignored Discord errors in summary --- scripts/generateReport.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index c1a4f711e..a75a5985f 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -105,7 +105,14 @@ async function printReport() { console.log(); - report.otherErrors = report.otherErrors.filter(e => !IGNORED_DISCORD_ERRORS.some(regex => e.match(regex))); + const ignoredErrors = [] as string[]; + report.otherErrors = report.otherErrors.filter(e => { + if (IGNORED_DISCORD_ERRORS.some(regex => e.match(regex))) { + ignoredErrors.push(e); + return false; + } + return true; + }); console.log("## Discord Errors"); report.otherErrors.forEach(e => { @@ -114,6 +121,13 @@ async function printReport() { console.log(); + console.log("## Ignored Discord Errors"); + ignoredErrors.forEach(e => { + console.log(`- ${toCodeBlock(e)}`); + }); + + console.log(); + if (process.env.DISCORD_WEBHOOK) { await fetch(process.env.DISCORD_WEBHOOK, { method: "POST", From fccdd3dc08fb45fb9dad79d2dc8a0ac5ddb120f9 Mon Sep 17 00:00:00 2001 From: V Date: Thu, 30 Nov 2023 17:28:53 +0100 Subject: [PATCH 228/632] migrate to new badge api we used to store badges on the discord cdn. since discord is now making it harder to use their cdn for such purposes (due to expiring links), we are forced to stop using it thus, badges are now stored on our server, accessible via https://badges.vencord.dev. The full list of badges is now at https://badges.vencord.dev/badges.json --- src/plugins/_api/badges.tsx | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/src/plugins/_api/badges.tsx b/src/plugins/_api/badges.tsx index 11e843db4..16b244a19 100644 --- a/src/plugins/_api/badges.tsx +++ b/src/plugins/_api/badges.tsx @@ -22,14 +22,13 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Flex } from "@components/Flex"; import { Heart } from "@components/Heart"; import { Devs } from "@utils/constants"; -import { Logger } from "@utils/Logger"; import { Margins } from "@utils/margins"; import { isPluginDev } from "@utils/misc"; import { closeModal, Modals, openModal } from "@utils/modal"; import definePlugin from "@utils/types"; import { Forms, Toasts } from "@webpack/common"; -const CONTRIBUTOR_BADGE = "https://cdn.discordapp.com/attachments/1033680203433660458/1092089947126780035/favicon.png"; +const CONTRIBUTOR_BADGE = "https://vencord.dev/assets/favicon.png"; const ContributorBadge: ProfileBadge = { description: "Vencord Contributor", @@ -45,7 +44,7 @@ const ContributorBadge: ProfileBadge = { link: "https://github.com/Vendicated/Vencord" }; -let DonorBadges = {} as Record[]>; +let DonorBadges = {} as Record>>; async function loadBadges(noCache = false) { DonorBadges = {}; @@ -54,19 +53,8 @@ async function loadBadges(noCache = false) { if (noCache) init.cache = "no-cache"; - const badges = await fetch("https://gist.githubusercontent.com/Vendicated/51a3dd775f6920429ec6e9b735ca7f01/raw/badges.csv", init) - .then(r => r.text()); - - const lines = badges.trim().split("\n"); - if (lines.shift() !== "id,tooltip,image") { - new Logger("BadgeAPI").error("Invalid badges.csv file!"); - return; - } - - for (const line of lines) { - const [id, description, image] = line.split(","); - (DonorBadges[id] ??= []).push({ image, description }); - } + DonorBadges = await fetch("https://badges.vencord.dev/badges.json", init) + .then(r => r.json()); } export default definePlugin({ @@ -127,7 +115,8 @@ export default definePlugin({ getDonorBadges(userId: string) { return DonorBadges[userId]?.map(badge => ({ - ...badge, + image: badge.badge, + description: badge.tooltip, position: BadgePosition.START, props: { style: { From 3e7d9462961ba2d1291cb68b9d5618c46c568451 Mon Sep 17 00:00:00 2001 From: megumin Date: Thu, 30 Nov 2023 19:32:48 +0000 Subject: [PATCH 229/632] fix(SpotifyControls): Requests double-sending when using Spotify Connect (#2023) --- src/plugins/spotifyControls/index.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/plugins/spotifyControls/index.tsx b/src/plugins/spotifyControls/index.tsx index 829f7850a..cfb352efe 100644 --- a/src/plugins/spotifyControls/index.tsx +++ b/src/plugins/spotifyControls/index.tsx @@ -55,13 +55,19 @@ export default definePlugin({ replace: "return [$self.renderPlayer(),$1]" } }, - // Adds POST and a Marker to the SpotifyAPI (so we can easily find it) { find: ".PLAYER_DEVICES", - replacement: { + replacement: [{ + // Adds POST and a Marker to the SpotifyAPI (so we can easily find it) match: /get:(\i)\.bind\(null,(\i\.\i)\.get\)/, replace: "post:$1.bind(null,$2.post),$&" - } + }, + { + // Spotify Connect API returns status 202 instead of 204 when skipping tracks. + // Discord rejects 202 which causes the request to send twice. This patch prevents this. + match: /202===\i\.status/, + replace: "false", + }] }, // Discord doesn't give you the repeat kind, only a boolean { From 80016180b612718e7a73b6a24169e8ccfec5e166 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 1 Dec 2023 16:39:15 -0300 Subject: [PATCH 230/632] FixImagesQuality: no longer make gifs play when autoplay is off --- src/plugins/fixImagesQuality/index.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/plugins/fixImagesQuality/index.ts b/src/plugins/fixImagesQuality/index.ts index d94c8e3b2..4acd9a921 100644 --- a/src/plugins/fixImagesQuality/index.ts +++ b/src/plugins/fixImagesQuality/index.ts @@ -14,10 +14,12 @@ export default definePlugin({ patches: [ { find: "handleImageLoad=", - replacement: { - match: /(?<=getSrc\(\i\){.+?format:)\i/, - replace: "null" - } + replacement: [ + { + match: /(?<=getSrc\(\i\){.+?return )\i\.SUPPORTS_WEBP.+?:(?=\i&&\(\i="png"\))/, + replace: "" + } + ] } ] }); From 9dd00fb766f6fdf74b037a76a16b43a0edf8866a Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Fri, 1 Dec 2023 23:06:18 -0300 Subject: [PATCH 231/632] Fix SuperReactionTweaks patch --- src/plugins/superReactionTweaks/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/superReactionTweaks/index.ts b/src/plugins/superReactionTweaks/index.ts index 2652eef07..0e58eb0a8 100644 --- a/src/plugins/superReactionTweaks/index.ts +++ b/src/plugins/superReactionTweaks/index.ts @@ -46,10 +46,10 @@ export default definePlugin({ } }, { - find: ".hasAvailableBurstCurrency)", + find: ".trackEmojiSearchEmpty,200", replacement: { - match: /(?<=\.useBurstReactionsExperiment.{0,20})useState\(!1\)(?=.+?(\i===\i\.EmojiIntention.REACTION))/, - replace: "useState($self.settings.store.superReactByDefault && $1)" + match: /(\.trackEmojiSearchEmpty,200(?=.+?isBurstReaction:(\i).+?(\i===\i\.EmojiIntention.REACTION)).+?\[\2,\i\]=\i\.useState\().+?\)/, + replace: (_, rest, isBurstReactionVariable, isReactionIntention) => `${rest}$self.settings.store.superReactByDefault&&${isReactionIntention})` } } ], From 08036f7af28dba4978abed93f9a7b6af5cf979cb Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 6 Dec 2023 01:37:42 -0300 Subject: [PATCH 232/632] convert non lazy finds to test with reporter --- src/plugins/muteNewGuild/index.tsx | 6 ++++-- src/plugins/webContextMenus.web/index.ts | 6 +++--- src/utils/modal.tsx | 4 ++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/plugins/muteNewGuild/index.tsx b/src/plugins/muteNewGuild/index.tsx index 7586830bb..08c558a95 100644 --- a/src/plugins/muteNewGuild/index.tsx +++ b/src/plugins/muteNewGuild/index.tsx @@ -19,7 +19,9 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByProps } from "@webpack"; +import { findByPropsLazy } from "@webpack"; + +const { updateGuildNotificationSettings } = findByPropsLazy("updateGuildNotificationSettings"); const settings = definePluginSettings({ guild: { @@ -63,7 +65,7 @@ export default definePlugin({ handleMute(guildId: string | null) { if (guildId === "@me" || guildId === "null" || guildId == null) return; - findByProps("updateGuildNotificationSettings").updateGuildNotificationSettings(guildId, + updateGuildNotificationSettings(guildId, { muted: settings.store.guild, suppress_everyone: settings.store.everyone, diff --git a/src/plugins/webContextMenus.web/index.ts b/src/plugins/webContextMenus.web/index.ts index cf50bb86e..eb076dfd0 100644 --- a/src/plugins/webContextMenus.web/index.ts +++ b/src/plugins/webContextMenus.web/index.ts @@ -20,9 +20,11 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { saveFile } from "@utils/web"; -import { findByProps } from "@webpack"; +import { findByPropsLazy } from "@webpack"; import { Clipboard, ComponentDispatch } from "@webpack/common"; +const ctxMenuCallbacks = findByPropsLazy("contextMenuCallbackNative"); + async function fetchImage(url: string) { const res = await fetch(url); if (res.status !== 200) return; @@ -55,7 +57,6 @@ export default definePlugin({ start() { if (settings.store.addBack) { - const ctxMenuCallbacks = findByProps("contextMenuCallbackNative"); window.removeEventListener("contextmenu", ctxMenuCallbacks.contextMenuCallbackWeb); window.addEventListener("contextmenu", ctxMenuCallbacks.contextMenuCallbackNative); this.changedListeners = true; @@ -64,7 +65,6 @@ export default definePlugin({ stop() { if (this.changedListeners) { - const ctxMenuCallbacks = findByProps("contextMenuCallbackNative"); window.removeEventListener("contextmenu", ctxMenuCallbacks.contextMenuCallbackNative); window.addEventListener("contextmenu", ctxMenuCallbacks.contextMenuCallbackWeb); } diff --git a/src/utils/modal.tsx b/src/utils/modal.tsx index 6758a1a1a..b4d0f59fb 100644 --- a/src/utils/modal.tsx +++ b/src/utils/modal.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { findByProps, findByPropsLazy } from "@webpack"; +import { findByPropsLazy, findExportedComponentLazy } from "@webpack"; import type { ComponentType, PropsWithChildren, ReactNode, Ref } from "react"; import { LazyComponent } from "./react"; @@ -118,7 +118,7 @@ export type ImageModal = ComponentType<{ shouldHideMediaOptions?: boolean; }>; -export const ImageModal = LazyComponent(() => findByProps("ImageModal").ImageModal as ImageModal); +export const ImageModal = findExportedComponentLazy("ImageModal") as ImageModal; export const ModalRoot = LazyComponent(() => Modals.ModalRoot); export const ModalHeader = LazyComponent(() => Modals.ModalHeader); From cf7028331c152da10d64e810238157bf6b267ec4 Mon Sep 17 00:00:00 2001 From: Syncx <47534062+Syncxv@users.noreply.github.com> Date: Thu, 7 Dec 2023 02:55:29 +0530 Subject: [PATCH 233/632] Native Folder Support (#2031) --- scripts/build/build.mjs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/build/build.mjs b/scripts/build/build.mjs index a2e0e0024..0c2a930a0 100755 --- a/scripts/build/build.mjs +++ b/scripts/build/build.mjs @@ -76,7 +76,11 @@ const globNativesPlugin = { if (!await existsAsync(dirPath)) continue; const plugins = await readdir(dirPath); for (const p of plugins) { - if (!await existsAsync(join(dirPath, p, "native.ts"))) continue; + const nativePath = join(dirPath, p, "native.ts"); + const indexNativePath = join(dirPath, p, "native/index.ts"); + + if (!(await existsAsync(nativePath)) && !(await existsAsync(indexNativePath))) + continue; const nameParts = p.split("."); const namePartsWithoutTarget = nameParts.length === 1 ? nameParts : nameParts.slice(0, -1); From 3453d0c97c880a9f12d757d1fb07c72efc077e2a Mon Sep 17 00:00:00 2001 From: Carter Date: Wed, 6 Dec 2023 14:25:53 -0700 Subject: [PATCH 234/632] feat(clearUrls): moar twitter rules (#2029) --- src/plugins/clearURLs/defaultRules.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/plugins/clearURLs/defaultRules.ts b/src/plugins/clearURLs/defaultRules.ts index e7c3ecbeb..0633b717d 100644 --- a/src/plugins/clearURLs/defaultRules.ts +++ b/src/plugins/clearURLs/defaultRules.ts @@ -124,6 +124,18 @@ export const defaultRules = [ "t@*.x.com", "s@*.x.com", "ref_*@*.x.com", + "t@*.fixupx.com", + "s@*.fixupx.com", + "ref_*@*.fixupx.com", + "t@*.fxtwitter.com", + "s@*.fxtwitter.com", + "ref_*@*.fxtwitter.com", + "t@*.twittpr.com", + "s@*.twittpr.com", + "ref_*@*.twittpr.com", + "t@*.fixvx.com", + "s@*.fixvx.com", + "ref_*@*.fixvx.com", "tt_medium", "tt_content", "lr@yandex.*", From c0b6d8f1c4a6c103f0a5ff4af2fad246f8d035b6 Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Thu, 7 Dec 2023 04:27:06 +0700 Subject: [PATCH 235/632] devCompanion: add findComponentByCode (#2026) --- src/plugins/devCompanion.dev/index.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/devCompanion.dev/index.tsx b/src/plugins/devCompanion.dev/index.tsx index 2fa01f257..25fd563e4 100644 --- a/src/plugins/devCompanion.dev/index.tsx +++ b/src/plugins/devCompanion.dev/index.tsx @@ -215,6 +215,9 @@ function initWs(isManual = false) { case "ModuleId": results = Object.keys(search(parsedArgs[0])); break; + case "ComponentByCode": + results = findAll(filters.componentByCode(...parsedArgs)); + break; default: return reply("Unknown Find Type " + type); } From 9a89f7b3b228a3a0ed9c4a0ac42d03e87a6aa3c8 Mon Sep 17 00:00:00 2001 From: nya <24845294+nyakowint@users.noreply.github.com> Date: Wed, 6 Dec 2023 16:31:22 -0500 Subject: [PATCH 236/632] feat(plugin): XSOverlay (#1901) Co-authored-by: V --- src/plugins/iLoveSpam/index.ts | 2 +- src/plugins/xsOverlay.desktop/README.md | 15 ++ src/plugins/xsOverlay.desktop/index.ts | 288 ++++++++++++++++++++++++ src/plugins/xsOverlay.desktop/native.ts | 16 ++ src/utils/constants.ts | 4 +- 5 files changed, 322 insertions(+), 3 deletions(-) create mode 100644 src/plugins/xsOverlay.desktop/README.md create mode 100644 src/plugins/xsOverlay.desktop/index.ts create mode 100644 src/plugins/xsOverlay.desktop/native.ts diff --git a/src/plugins/iLoveSpam/index.ts b/src/plugins/iLoveSpam/index.ts index 8556de426..bb0b20539 100644 --- a/src/plugins/iLoveSpam/index.ts +++ b/src/plugins/iLoveSpam/index.ts @@ -22,7 +22,7 @@ import definePlugin from "@utils/types"; export default definePlugin({ name: "iLoveSpam", description: "Do not hide messages from 'likely spammers'", - authors: [Devs.botato, Devs.Animal], + authors: [Devs.botato, Devs.Nyako], patches: [ { find: "hasFlag:{writable", diff --git a/src/plugins/xsOverlay.desktop/README.md b/src/plugins/xsOverlay.desktop/README.md new file mode 100644 index 000000000..477e30bf3 --- /dev/null +++ b/src/plugins/xsOverlay.desktop/README.md @@ -0,0 +1,15 @@ +# XSOverlay Notifier + +Sends Discord messages to [XSOverlay](https://store.steampowered.com/app/1173510/XSOverlay/) for easier viewing while using VR. + +## Preview + +![](https://github.com/Vendicated/Vencord/assets/24845294/205d2055-bb4a-44e4-b7e3-265391bccd40) + +![](https://github.com/Vendicated/Vencord/assets/24845294/f15eff61-2d52-4620-bcab-808ecb1606d2) + +## Usage +- Enable this plugin +- Set plugin settings as desired +- Open XSOverlay +- get ping spammed diff --git a/src/plugins/xsOverlay.desktop/index.ts b/src/plugins/xsOverlay.desktop/index.ts new file mode 100644 index 000000000..5461696ec --- /dev/null +++ b/src/plugins/xsOverlay.desktop/index.ts @@ -0,0 +1,288 @@ +/* + * 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 { makeRange } from "@components/PluginSettings/components"; +import { Devs } from "@utils/constants"; +import { Logger } from "@utils/Logger"; +import definePlugin, { OptionType, PluginNative } from "@utils/types"; +import { findByPropsLazy } from "@webpack"; +import { ChannelStore, GuildStore, UserStore } from "@webpack/common"; +import type { Channel, Embed, GuildMember, MessageAttachment, User } from "discord-types/general"; + +const enum ChannelTypes { + DM = 1, + GROUP_DM = 3 +} + +interface Message { + guild_id: string, + attachments: MessageAttachment[], + author: User, + channel_id: string, + components: any[], + content: string, + edited_timestamp: string, + embeds: Embed[], + sticker_items?: Sticker[], + flags: number, + id: string, + member: GuildMember, + mention_everyone: boolean, + mention_roles: string[], + mentions: Mention[], + nonce: string, + pinned: false, + referenced_message: any, + timestamp: string, + tts: boolean, + type: number; +} + +interface Mention { + avatar: string, + avatar_decoration_data: any, + discriminator: string, + global_name: string, + id: string, + public_flags: number, + username: string; +} + +interface Sticker { + t: "Sticker"; + description: string; + format_type: number; + guild_id: string; + id: string; + name: string; + tags: string; + type: number; +} + +interface Call { + channel_id: string, + guild_id: string, + message_id: string, + region: string, + ringing: string[]; +} + +const MuteStore = findByPropsLazy("isSuppressEveryoneEnabled"); +const XSLog = new Logger("XSOverlay"); + +const settings = definePluginSettings({ + ignoreBots: { + type: OptionType.BOOLEAN, + description: "Ignore messages from bots", + default: false + }, + pingColor: { + type: OptionType.STRING, + description: "User mention color", + default: "#7289da" + }, + channelPingColor: { + type: OptionType.STRING, + description: "Channel mention color", + default: "#8a2be2" + }, + soundPath: { + type: OptionType.STRING, + description: "Notification sound (default/warning/error)", + default: "default" + }, + timeout: { + type: OptionType.NUMBER, + description: "Notif duration (secs)", + default: 1.0, + }, + opacity: { + type: OptionType.SLIDER, + description: "Notif opacity", + default: 1, + markers: makeRange(0, 1, 0.1) + }, + volume: { + type: OptionType.SLIDER, + description: "Volume", + default: 0.2, + markers: makeRange(0, 1, 0.1) + }, +}); + +const Native = VencordNative.pluginHelpers.XsOverlay as PluginNative; + +export default definePlugin({ + name: "XSOverlay", + description: "Forwards discord notifications to XSOverlay, for easy viewing in VR", + authors: [Devs.Nyako], + tags: ["vr", "notify"], + settings, + flux: { + CALL_UPDATE({ call }: { call: Call; }) { + if (call?.ringing?.includes(UserStore.getCurrentUser().id)) { + const channel = ChannelStore.getChannel(call.channel_id); + sendOtherNotif("Incoming call", `${channel.name} is calling you...`); + } + }, + MESSAGE_CREATE({ message, optimistic }: { message: Message; optimistic: boolean; }) { + // Apparently without this try/catch, discord's socket connection dies if any part of this errors + try { + if (optimistic) return; + const channel = ChannelStore.getChannel(message.channel_id); + if (!shouldNotify(message, channel)) return; + + const pingColor = settings.store.pingColor.replaceAll("#", "").trim(); + const channelPingColor = settings.store.channelPingColor.replaceAll("#", "").trim(); + let finalMsg = message.content; + let titleString = ""; + + if (channel.guild_id) { + const guild = GuildStore.getGuild(channel.guild_id); + titleString = `${message.author.username} (${guild.name}, #${channel.name})`; + } + + + switch (channel.type) { + case ChannelTypes.DM: + titleString = message.author.username.trim(); + break; + case ChannelTypes.GROUP_DM: + const channelName = channel.name.trim() ?? channel.rawRecipients.map(e => e.username).join(", "); + titleString = `${message.author.username} (${channelName})`; + break; + } + + if (message.referenced_message) { + titleString += " (reply)"; + } + + if (message.embeds.length > 0) { + finalMsg += " [embed] "; + if (message.content === "") { + finalMsg = "sent message embed(s)"; + } + } + + if (message.sticker_items) { + finalMsg += " [sticker] "; + if (message.content === "") { + finalMsg = "sent a sticker"; + } + } + + const images = message.attachments.filter(e => + typeof e?.content_type === "string" + && e?.content_type.startsWith("image") + ); + + + images.forEach(img => { + finalMsg += ` [image: ${img.filename}] `; + }); + + message.attachments.filter(a => a && !a.content_type?.startsWith("image")).forEach(a => { + finalMsg += ` [attachment: ${a.filename}] `; + }); + + // make mentions readable + if (message.mentions.length > 0) { + finalMsg = finalMsg.replace(/<@!?(\d{17,20})>/g, (_, id) => `@${UserStore.getUser(id)?.username || "unknown-user"}`); + } + + if (message.mention_roles.length > 0) { + for (const roleId of message.mention_roles) { + const role = GuildStore.getGuild(channel.guild_id).roles[roleId]; + if (!role) continue; + const roleColor = role.colorString ?? `#${pingColor}`; + finalMsg = finalMsg.replace(`<@&${roleId}>`, `@${role.name}`); + } + } + + // make emotes and channel mentions readable + const emoteMatches = finalMsg.match(new RegExp("()", "g")); + const channelMatches = finalMsg.match(new RegExp("<(#\\d+)>", "g")); + + if (emoteMatches) { + for (const eMatch of emoteMatches) { + finalMsg = finalMsg.replace(new RegExp(`${eMatch}`, "g"), `:${eMatch.split(":")[1]}:`); + } + } + + if (channelMatches) { + for (const cMatch of channelMatches) { + let channelId = cMatch.split("<#")[1]; + channelId = channelId.substring(0, channelId.length - 1); + finalMsg = finalMsg.replace(new RegExp(`${cMatch}`, "g"), `#${ChannelStore.getChannel(channelId).name}`); + } + } + + sendMsgNotif(titleString, finalMsg, message); + } catch (err) { + XSLog.error(`Failed to catch MESSAGE_CREATE: ${err}`); + } + } + } +}); + +function sendMsgNotif(titleString: string, content: string, message: Message) { + fetch(`https://cdn.discordapp.com/avatars/${message.author.id}/${message.author.avatar}.png?size=128`).then(response => response.arrayBuffer()).then(result => { + const msgData = { + messageType: 1, + index: 0, + timeout: settings.store.timeout, + height: calculateHeight(cleanMessage(content)), + opacity: settings.store.opacity, + volume: settings.store.volume, + audioPath: settings.store.soundPath, + title: titleString, + content: content, + useBase64Icon: true, + icon: result, + sourceApp: "Vencord" + }; + Native.sendToOverlay(msgData); + }); +} + +function sendOtherNotif(content: string, titleString: string) { + const msgData = { + messageType: 1, + index: 0, + timeout: settings.store.timeout, + height: calculateHeight(cleanMessage(content)), + opacity: settings.store.opacity, + volume: settings.store.volume, + audioPath: settings.store.soundPath, + title: titleString, + content: content, + useBase64Icon: false, + icon: null, + sourceApp: "Vencord" + }; + Native.sendToOverlay(msgData); +} + +function shouldNotify(message: Message, channel: Channel) { + const currentUser = UserStore.getCurrentUser(); + if (message.author.id === currentUser.id) return false; + if (message.author.bot && settings.store.ignoreBots) return false; + if (MuteStore.allowAllMessages(channel) || message.mention_everyone && !MuteStore.isSuppressEveryoneEnabled(message.guild_id)) return true; + + return message.mentions.some(m => m.id === currentUser.id); +} + +function calculateHeight(content: string) { + if (content.length <= 100) return 100; + if (content.length <= 200) return 150; + if (content.length <= 300) return 200; + return 250; +} + +function cleanMessage(content: string) { + return content.replace(new RegExp("<[^>]*>", "g"), ""); +} diff --git a/src/plugins/xsOverlay.desktop/native.ts b/src/plugins/xsOverlay.desktop/native.ts new file mode 100644 index 000000000..82809383a --- /dev/null +++ b/src/plugins/xsOverlay.desktop/native.ts @@ -0,0 +1,16 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { createSocket, Socket } from "dgram"; + +let xsoSocket: Socket; + +export function sendToOverlay(_, data: any) { + data.icon = Buffer.from(data.icon).toString("base64"); + const json = JSON.stringify(data); + xsoSocket ??= createSocket("udp4"); + xsoSocket.send(json, 42069, "127.0.0.1"); +} diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 0ff7da72a..daa4a74dc 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -78,8 +78,8 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "Samu", id: 702973430449832038n, }, - Animal: { - name: "Animal", + Nyako: { + name: "nyako", id: 118437263754395652n }, MaiKokain: { From 613b2dc5f61fcdd6b03d1e66da89f7c8062d118b Mon Sep 17 00:00:00 2001 From: fres621 <126067139+fres621@users.noreply.github.com> Date: Wed, 6 Dec 2023 21:54:23 +0000 Subject: [PATCH 237/632] Fix QuickMention not working in DMs (#2028) Co-authored-by: V --- src/plugins/quickMention/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/quickMention/index.tsx b/src/plugins/quickMention/index.tsx index 9720c7d33..df86e9b70 100644 --- a/src/plugins/quickMention/index.tsx +++ b/src/plugins/quickMention/index.tsx @@ -31,7 +31,7 @@ export default definePlugin({ start() { addButton("QuickMention", msg => { const channel = ChannelStore.getChannel(msg.channel_id); - if (!PermissionStore.can(PermissionsBits.SEND_MESSAGES, channel)) return null; + if (channel.guild_id && !PermissionStore.can(PermissionsBits.SEND_MESSAGES, channel)) return null; return { label: "Quick Mention", From 9faa1331da67b76a50697d61c00f72f5601b8b4a Mon Sep 17 00:00:00 2001 From: philipbry <81459908+philipbry@users.noreply.github.com> Date: Wed, 6 Dec 2023 23:15:34 +0100 Subject: [PATCH 238/632] new plugin: notificationVolume (#1992) Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Co-authored-by: V --- src/plugins/notificationVolume/README.md | 3 ++ src/plugins/notificationVolume/index.ts | 35 ++++++++++++++++++++++++ src/utils/constants.ts | 4 +++ 3 files changed, 42 insertions(+) create mode 100644 src/plugins/notificationVolume/README.md create mode 100644 src/plugins/notificationVolume/index.ts diff --git a/src/plugins/notificationVolume/README.md b/src/plugins/notificationVolume/README.md new file mode 100644 index 000000000..05e5af4e2 --- /dev/null +++ b/src/plugins/notificationVolume/README.md @@ -0,0 +1,3 @@ +# NotificationVolume + +Set a separate volume for notifications and in-app sounds (e.g. messages, call sound, mute/unmute) helping your ears stay healthy for many years to come. diff --git a/src/plugins/notificationVolume/index.ts b/src/plugins/notificationVolume/index.ts new file mode 100644 index 000000000..50eabee73 --- /dev/null +++ b/src/plugins/notificationVolume/index.ts @@ -0,0 +1,35 @@ +/* + * 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({ + notificationVolume: { + type: OptionType.SLIDER, + description: "Notification volume", + markers: [0, 25, 50, 75, 100], + default: 100, + stickToMarkers: false + } +}); + +export default definePlugin({ + name: "NotificationVolume", + description: "Save your ears and set a separate volume for notifications and in-app sounds", + authors: [Devs.philipbry], + settings, + patches: [ + { + find: "_ensureAudio(){", + replacement: { + match: /onloadeddata=\(\)=>\{.\.volume=/, + replace: "$&$self.settings.store.notificationVolume/100*" + }, + }, + ], +}); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index daa4a74dc..1c477b124 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -387,6 +387,10 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "ant0n", id: 145224646868860928n }, + philipbry: { + name: "philipbry", + id: 554994003318276106n + }, Korbo: { name: "Korbo", id: 455856406420258827n From e4942397dc0a536fcdc08f060977188d1376268b Mon Sep 17 00:00:00 2001 From: Nickyux <30734036+nmsturcke@users.noreply.github.com> Date: Wed, 6 Dec 2023 23:18:03 +0100 Subject: [PATCH 239/632] MessageClickActions: Ignore Ephemeral & Deleted Messages (#1972) Co-authored-by: V --- src/plugins/messageClickActions/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/plugins/messageClickActions/index.ts b/src/plugins/messageClickActions/index.ts index 08cee4c9f..052c33f61 100644 --- a/src/plugins/messageClickActions/index.ts +++ b/src/plugins/messageClickActions/index.ts @@ -72,6 +72,7 @@ export default definePlugin({ if (event.detail < 2) return; if (settings.store.requireModifier && !event.ctrlKey && !event.shiftKey) return; if (channel.guild_id && !PermissionStore.can(PermissionsBits.SEND_MESSAGES, channel)) return; + if (msg.deleted === true) return; if (isMe) { if (!settings.store.enableDoubleClickToEdit || EditStore.isEditing(channel.id, msg.id)) return; @@ -81,6 +82,9 @@ export default definePlugin({ } else { if (!settings.store.enableDoubleClickToReply) return; + const EPHEMERAL = 64; + if (msg.hasFlag(EPHEMERAL)) return; + FluxDispatcher.dispatch({ type: "CREATE_PENDING_REPLY", channel, From 920252956f60377b8aa593c217830391dfa344ff Mon Sep 17 00:00:00 2001 From: AutumnVN Date: Thu, 7 Dec 2023 05:42:40 +0700 Subject: [PATCH 240/632] shikiCodeBlocks: support file preview (#1977) --- src/plugins/shikiCodeblocks.desktop/index.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/plugins/shikiCodeblocks.desktop/index.ts b/src/plugins/shikiCodeblocks.desktop/index.ts index f358497d2..ef1b5d3df 100644 --- a/src/plugins/shikiCodeblocks.desktop/index.ts +++ b/src/plugins/shikiCodeblocks.desktop/index.ts @@ -42,6 +42,13 @@ export default definePlugin({ match: /codeBlock:\{react\((\i),(\i),(\i)\)\{/, replace: "$&return $self.renderHighlighter($1,$2,$3);" } + }, + { + find: ".PREVIEW_NUM_LINES", + replacement: { + match: /(?<=function \i\((\i)\)\{)(?=let\{text:\i,language:)/, + replace: "return $self.renderHighlighter({lang:$1.language,content:$1.text});" + } } ], start: async () => { From fd9c675942e727570776b4018c26674d21ecc6e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Han=20Seung=20Min=20-=20=ED=95=9C=EC=8A=B9=EB=AF=BC?= Date: Thu, 7 Dec 2023 08:25:13 +0900 Subject: [PATCH 241/632] fix: patch helper overflow (#2007) --- src/components/VencordSettings/PatchHelperTab.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/VencordSettings/PatchHelperTab.tsx b/src/components/VencordSettings/PatchHelperTab.tsx index 0b869a518..35f46ef50 100644 --- a/src/components/VencordSettings/PatchHelperTab.tsx +++ b/src/components/VencordSettings/PatchHelperTab.tsx @@ -108,7 +108,7 @@ function ReplacementComponent({ module, match, replacement, setReplacementError function renderDiff() { return diff?.map(p => { const color = p.added ? "lime" : p.removed ? "red" : "grey"; - return
{p.value}
; + return
{p.value}
; }); } From 6ee50d30f6b0886aa03a2829618205f0f53542db Mon Sep 17 00:00:00 2001 From: Ajay Ramachandran Date: Wed, 6 Dec 2023 18:26:08 -0500 Subject: [PATCH 242/632] fix(dearrow): remove > from DeArrow titles (#1999) --- src/plugins/dearrow/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/dearrow/index.tsx b/src/plugins/dearrow/index.tsx index 52cf1d530..80eb84aa7 100644 --- a/src/plugins/dearrow/index.tsx +++ b/src/plugins/dearrow/index.tsx @@ -60,7 +60,7 @@ async function embedDidMount(this: Component) { if (hasTitle) { embed.dearrow.oldTitle = embed.rawTitle; - embed.rawTitle = titles[0].title; + embed.rawTitle = titles[0].title.replace(/ >(\S)/g, " $1"); } if (hasThumb) { From 34cbb22efe82484ca7fa5e655411679f9c82422a Mon Sep 17 00:00:00 2001 From: Damien Erambert Date: Wed, 6 Dec 2023 15:30:41 -0800 Subject: [PATCH 243/632] feat: add dropdown to choose vibrancy value on macOS (#1941) Co-authored-by: V --- src/api/Settings.ts | 20 ++++- src/components/VencordSettings/VencordTab.tsx | 79 +++++++++++++++++-- src/main/patcher.ts | 10 ++- 3 files changed, 100 insertions(+), 9 deletions(-) diff --git a/src/api/Settings.ts b/src/api/Settings.ts index 368f88f7a..004a8988b 100644 --- a/src/api/Settings.ts +++ b/src/api/Settings.ts @@ -38,7 +38,21 @@ export interface Settings { frameless: boolean; transparent: boolean; winCtrlQ: boolean; - macosTranslucency: boolean; + macosVibrancyStyle: + | "content" + | "fullscreen-ui" + | "header" + | "hud" + | "menu" + | "popover" + | "selection" + | "sidebar" + | "titlebar" + | "tooltip" + | "under-page" + | "window" + | undefined; + macosTranslucency: boolean | undefined; disableMinSize: boolean; winNativeTitleBar: boolean; plugins: { @@ -74,7 +88,9 @@ const DefaultSettings: Settings = { frameless: false, transparent: false, winCtrlQ: false, - macosTranslucency: false, + // Replaced by macosVibrancyStyle + macosTranslucency: undefined, + macosVibrancyStyle: undefined, disableMinSize: false, winNativeTitleBar: false, plugins: {}, diff --git a/src/components/VencordSettings/VencordTab.tsx b/src/components/VencordSettings/VencordTab.tsx index a8e9ea5b0..07d777eb3 100644 --- a/src/components/VencordSettings/VencordTab.tsx +++ b/src/components/VencordSettings/VencordTab.tsx @@ -48,6 +48,15 @@ function VencordSettings() { const isWindows = navigator.platform.toLowerCase().startsWith("win"); const isMac = navigator.platform.toLowerCase().startsWith("mac"); + const needsVibrancySettings = IS_DISCORD_DESKTOP && isMac; + + // One-time migration of the old setting to the new one if necessary. + React.useEffect(() => { + if (settings.macosTranslucency === true && !settings.macosVibrancyStyle) { + settings.macosVibrancyStyle = "sidebar"; + settings.macosTranslucency = undefined; + } + }, []); const Switches: Array; @@ -89,11 +98,6 @@ function VencordSettings() { title: "Disable minimum window size", note: "Requires a full restart" }, - IS_DISCORD_DESKTOP && isMac && { - key: "macosTranslucency", - title: "Enable translucent window", - note: "Requires a full restart" - } ]; return ( @@ -152,6 +156,71 @@ function VencordSettings() { + {needsVibrancySettings && <> + Window vibrancy style (requires restart) +