From 75fa0ff708f343e78642b16e31a0ef71f62ec3ec Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 2 May 2024 23:18:12 -0300 Subject: [PATCH] Immediate finds --- scripts/generateReport.ts | 57 +- src/api/ChatButtons.tsx | 5 +- src/api/Commands/commandHelpers.ts | 4 +- src/api/Notices.ts | 5 +- src/components/CodeBlock.tsx | 4 +- src/components/PluginSettings/PluginModal.tsx | 6 +- src/components/PluginSettings/index.tsx | 6 +- src/components/Switch.tsx | 4 +- src/components/VencordSettings/ThemesTab.tsx | 13 +- src/plugins/anonymiseFileNames/index.tsx | 6 +- src/plugins/arRPC.web/index.tsx | 4 +- src/plugins/betterFolders/FolderSideBar.tsx | 8 +- src/plugins/betterFolders/index.tsx | 10 +- src/plugins/betterNotes/index.tsx | 4 +- src/plugins/betterRoleContext/index.tsx | 4 +- src/plugins/betterSessions/index.tsx | 12 +- src/plugins/betterSettings/index.tsx | 4 +- .../biggerStreamPreview/webpack/stores.ts | 6 +- src/plugins/clientTheme/index.tsx | 10 +- src/plugins/consoleShortcuts/index.ts | 12 +- src/plugins/crashHandler/index.ts | 21 +- src/plugins/customRPC/index.tsx | 8 +- src/plugins/decor/index.tsx | 4 +- .../decor/ui/components/DecorSection.tsx | 4 +- .../decor/ui/components/SectionedGridList.tsx | 4 +- src/plugins/decor/ui/components/index.ts | 5 +- src/plugins/decor/ui/index.ts | 4 +- .../decor/ui/modals/ChangeDecorationModal.tsx | 4 +- .../decor/ui/modals/CreateDecorationModal.tsx | 6 +- src/plugins/devCompanion.dev/index.tsx | 12 +- src/plugins/emoteCloner/index.tsx | 6 +- src/plugins/experiments/index.tsx | 4 +- src/plugins/fakeNitro/index.tsx | 14 +- src/plugins/favGifSearch/index.tsx | 6 +- src/plugins/friendInvites/index.ts | 6 +- src/plugins/friendsSince/index.tsx | 12 +- src/plugins/gameActivityToggle/index.tsx | 4 +- src/plugins/gifPaste/index.ts | 4 +- src/plugins/greetStickerPicker/index.tsx | 4 +- src/plugins/ignoreActivities/index.tsx | 4 +- .../imageZoom/components/Magnifier.tsx | 2 +- src/plugins/implicitRelationships/index.ts | 4 +- src/plugins/lastfm/index.tsx | 4 +- src/plugins/memberCount/index.tsx | 6 +- src/plugins/messageClickActions/index.ts | 6 +- src/plugins/messageLinkEmbeds/index.tsx | 12 +- src/plugins/messageLogger/index.tsx | 4 +- src/plugins/moreUserTags/index.tsx | 6 +- src/plugins/mutualGroupDMs/index.tsx | 10 +- src/plugins/newGuildSettings/index.tsx | 10 +- src/plugins/noBlockedMessages/index.ts | 4 +- src/plugins/noPendingCount/index.ts | 4 +- src/plugins/pauseInvitesForever/index.tsx | 6 +- .../components/UserPermissions.tsx | 15 +- src/plugins/petpet/index.ts | 4 +- .../pinDms/components/CreateCategoryModal.tsx | 6 +- src/plugins/pinDms/index.tsx | 6 +- src/plugins/pinDms/settings.ts | 94 +++ src/plugins/platformIndicators/index.tsx | 6 +- src/plugins/previewMessage/index.tsx | 4 +- .../components/PronounsChatComponent.tsx | 4 +- src/plugins/quickReply/index.ts | 4 +- src/plugins/resurrectHome/index.tsx | 4 +- src/plugins/revealAllSpoilers/index.ts | 6 +- src/plugins/reviewDB/auth.tsx | 4 +- .../reviewDB/components/MessageButton.tsx | 4 +- .../reviewDB/components/ReviewComponent.tsx | 274 ++++---- .../reviewDB/components/ReviewsView.tsx | 10 +- src/plugins/searchReply/index.tsx | 4 +- .../serverProfile/GuildProfileModal.tsx | 6 +- .../previewExample.tsx | 2 +- src/plugins/showConnections/VerifiedIcon.tsx | 6 +- src/plugins/showConnections/index.tsx | 12 +- .../components/HiddenChannelLockScreen.tsx | 14 +- src/plugins/showHiddenChannels/index.tsx | 4 +- src/plugins/spotifyControls/SpotifyStore.ts | 10 +- src/plugins/spotifyShareCommands/index.ts | 6 +- .../startupTimings/StartupTimingPage.tsx | 4 +- src/plugins/typingIndicator/index.tsx | 10 +- .../components/VoiceChannelSection.tsx | 6 +- src/plugins/userVoiceShow/index.tsx | 4 +- src/plugins/vcNarrator/index.tsx | 4 +- src/plugins/vencordToolbox/index.tsx | 4 +- src/plugins/voiceMessages/VoicePreview.tsx | 4 +- src/plugins/voiceMessages/index.tsx | 8 +- src/plugins/webContextMenus.web/index.ts | 4 +- src/plugins/webKeybinds.web/index.ts | 4 +- src/plugins/whoReacted/index.tsx | 7 +- src/plugins/xsOverlay.desktop/index.ts | 4 +- src/utils/discord.tsx | 4 +- src/utils/index.ts | 1 + src/utils/lazy.ts | 138 ++-- src/utils/lazyReact.tsx | 18 +- src/utils/modal.tsx | 31 +- src/utils/proxyInner.ts | 91 +++ src/webpack/common/classes.ts | 6 +- src/webpack/common/components.ts | 15 +- src/webpack/common/internal.tsx | 44 -- src/webpack/common/menu.ts | 8 +- src/webpack/common/react.ts | 6 +- src/webpack/common/settingsStores.ts | 8 +- src/webpack/common/stores.ts | 58 +- src/webpack/common/types/components.d.ts | 10 +- src/webpack/common/utils.ts | 65 +- src/webpack/patchWebpack.ts | 12 +- src/webpack/webpack.ts | 535 --------------- src/webpack/webpack.tsx | 609 ++++++++++++++++++ 107 files changed, 1400 insertions(+), 1229 deletions(-) create mode 100644 src/plugins/pinDms/settings.ts create mode 100644 src/utils/proxyInner.ts delete mode 100644 src/webpack/common/internal.tsx delete mode 100644 src/webpack/webpack.ts create mode 100644 src/webpack/webpack.tsx diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index 912f38eda..8108e5436 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -400,7 +400,7 @@ async function runtime(token: string) { } Vencord.Webpack.waitFor( - "loginToken", + Vencord.Webpack.filters.byProps("loginToken"), m => { console.log("[PUP_DEBUG]", "Logging in with token..."); m.loginToken(token); @@ -471,39 +471,62 @@ async function runtime(token: string) { } } - for (const [searchType, args] of Vencord.Webpack.lazyWebpackSearchHistory) { - let method = searchType; + for (const [searchType, args] of [...Vencord.Webpack.webpackSearchHistory]) { + let method = searchType as string; - if (searchType === "findComponent") method = "find"; - if (searchType === "findExportedComponent") method = "findByProps"; - if (searchType === "waitFor" || searchType === "waitForComponent") { - if (typeof args[0] === "string") method = "findByProps"; - else method = "find"; - } - if (searchType === "waitForStore") method = "findStore"; + if (searchType === "waitFor") method = "cacheFind"; try { let result: any; - if (method === "proxyLazyWebpack" || method === "LazyComponentWebpack") { + if (method === "webpackDependantLazy" || method === "webpackDependantLazyComponent") { const [factory] = args; result = factory(); + if (result != null && "$$vencordGetter" in result) result = result.$$vencordGetter(); } else if (method === "extractAndLoadChunks") { const [code, matcher] = args; const module = Vencord.Webpack.findModuleFactory(...code); - if (module) result = module.toString().match(canonicalizeMatch(matcher)); + if (module) result = module.toString().match(Vencord.Util.canonicalizeMatch(matcher)); } else { - // @ts-ignore result = Vencord.Webpack[method](...args); + + // If the result is our Proxy or ComponentWrapper, this means the search failed + if (result != null && result[Vencord.Util.proxyInnerGet] != null) result = undefined; + if (result != null && "$$vencordGetter" in result) result = undefined; } - if (result == null || ("$$vencordInternal" in result && result.$$vencordInternal() == null)) throw "a rock at ben shapiro"; + if (result == null) throw "find failed"; } 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(", ")})`; + + let filterName = ""; + let parsedArgs = args; + if ("$$vencordProps" in args[0]) { + if ( + searchType === "find" || + searchType === "findComponent" || + searchType === "waitFor" + ) { + filterName = args[0].$$vencordProps[0]; + } + + parsedArgs = args[0].$$vencordProps.slice(1); + } + + if ( + parsedArgs === args && searchType === "waitFor" || + searchType === "find" || + searchType === "findComponent" || + searchType === "webpackDependantLazy" || + searchType === "webpackDependantLazyComponent" + ) { + logMessage += `(${parsedArgs[0].toString().slice(0, 147)}...)`; + } else if (searchType === "extractAndLoadChunks") { + logMessage += `([${parsedArgs[0].map((arg: any) => `"${arg}"`).join(", ")}], ${parsedArgs[1].toString()})`; + } else { + logMessage += `(${filterName.length ? `${filterName}(` : ""}${parsedArgs.map(arg => `"${arg}"`).join(", ")})${filterName.length ? ")" : ""}`; + } console.log("[PUP_WEBPACK_FIND_FAIL]", logMessage); } diff --git a/src/api/ChatButtons.tsx b/src/api/ChatButtons.tsx index fcb76fffc..b44ee29ad 100644 --- a/src/api/ChatButtons.tsx +++ b/src/api/ChatButtons.tsx @@ -8,13 +8,12 @@ import "./ChatButton.css"; import ErrorBoundary from "@components/ErrorBoundary"; import { Logger } from "@utils/Logger"; -import { waitFor } from "@webpack"; +import { findByProps } from "@webpack"; import { Button, ButtonLooks, ButtonWrapperClasses, Tooltip } from "@webpack/common"; import { Channel } from "discord-types/general"; import { HTMLProps, MouseEventHandler, ReactNode } from "react"; -let ChannelTextAreaClasses: Record<"button" | "buttonContainer", string>; -waitFor(["buttonContainer", "channelTextArea"], m => ChannelTextAreaClasses = m); +const ChannelTextAreaClasses = findByProps>("buttonContainer", "channelTextArea"); export interface ChatBarProps { channel: Channel; diff --git a/src/api/Commands/commandHelpers.ts b/src/api/Commands/commandHelpers.ts index dc5ecfd67..759bb0e3c 100644 --- a/src/api/Commands/commandHelpers.ts +++ b/src/api/Commands/commandHelpers.ts @@ -17,14 +17,14 @@ */ import { mergeDefaults } from "@utils/misc"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { MessageActions, SnowflakeUtils } from "@webpack/common"; import { Message } from "discord-types/general"; import type { PartialDeep } from "type-fest"; import { Argument } from "./types"; -const MessageCreator = findByPropsLazy("createBotMessage"); +const MessageCreator = findByProps("createBotMessage"); export function generateId() { return `-${SnowflakeUtils.fromTimestamp(Date.now())}`; diff --git a/src/api/Notices.ts b/src/api/Notices.ts index 6d20087a7..7652d55a6 100644 --- a/src/api/Notices.ts +++ b/src/api/Notices.ts @@ -16,10 +16,9 @@ * along with this program. If not, see . */ -import { waitFor } from "@webpack"; +import { find } from "@webpack"; -let NoticesModule: any; -waitFor(m => m.show && m.dismiss && !m.suppressAll, m => NoticesModule = m); +const NoticesModule = find(m => m.show && m.dismiss && !m.suppressAll, m => m); export const noticesQueue = [] as any[]; export let currentNotice: any = null; diff --git a/src/components/CodeBlock.tsx b/src/components/CodeBlock.tsx index 41c5ef0c1..03ec96fc1 100644 --- a/src/components/CodeBlock.tsx +++ b/src/components/CodeBlock.tsx @@ -4,10 +4,10 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { Parser } from "@webpack/common"; -const CodeContainerClasses = findByPropsLazy("markup", "codeContainer"); +const CodeContainerClasses = findByProps("markup", "codeContainer"); /** * Renders code in a Discord codeblock diff --git a/src/components/PluginSettings/PluginModal.tsx b/src/components/PluginSettings/PluginModal.tsx index 34de43c2d..f62da7632 100644 --- a/src/components/PluginSettings/PluginModal.tsx +++ b/src/components/PluginSettings/PluginModal.tsx @@ -25,7 +25,7 @@ import { Margins } from "@utils/margins"; import { classes, isObjectEmpty } from "@utils/misc"; import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize } from "@utils/modal"; import { OptionType, Plugin } from "@utils/types"; -import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; +import { findByProps,findComponentByCode } 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"; @@ -41,8 +41,8 @@ import { } from "./components"; import { openContributorModal } from "./ContributorModal"; -const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers"); -const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar"); +const UserSummaryItem = findComponentByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers"); +const AvatarStyles = findByProps("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar"); const UserRecord: Constructor> = proxyLazy(() => UserStore.getCurrentUser().constructor) as any; interface PluginModalProps extends ModalProps { diff --git a/src/components/PluginSettings/index.tsx b/src/components/PluginSettings/index.tsx index 33a472c1a..eff1b4359 100644 --- a/src/components/PluginSettings/index.tsx +++ b/src/components/PluginSettings/index.tsx @@ -33,7 +33,7 @@ import { classes, isObjectEmpty } from "@utils/misc"; import { openModalLazy } from "@utils/modal"; import { useAwaiter } from "@utils/react"; import { Plugin } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { Alerts, Button, Card, Forms, lodash, Parser, React, Select, Text, TextInput, Toasts, Tooltip } from "@webpack/common"; import Plugins from "~plugins"; @@ -44,8 +44,8 @@ import { startDependenciesRecursive, startPlugin, stopPlugin } from "../../plugi const cl = classNameFactory("vc-plugins-"); const logger = new Logger("PluginSettings", "#a6d189"); -const InputStyles = findByPropsLazy("inputDefault", "inputWrapper"); -const ButtonClasses = findByPropsLazy("button", "disabled", "enabled"); +const InputStyles = findByProps("inputDefault", "inputWrapper"); +const ButtonClasses = findByProps("button", "disabled", "enabled"); function showErrorToast(message: string) { diff --git a/src/components/Switch.tsx b/src/components/Switch.tsx index 10904e141..4e72054dd 100644 --- a/src/components/Switch.tsx +++ b/src/components/Switch.tsx @@ -19,7 +19,7 @@ import "./Switch.css"; import { classes } from "@utils/misc"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; interface SwitchProps { checked: boolean; @@ -29,7 +29,7 @@ interface SwitchProps { const SWITCH_ON = "var(--green-360)"; const SWITCH_OFF = "var(--primary-400)"; -const SwitchClasses = findByPropsLazy("slider", "input", "container"); +const SwitchClasses = findByProps("slider", "input", "container"); export function Switch({ checked, onChange, disabled }: SwitchProps) { return ( diff --git a/src/components/VencordSettings/ThemesTab.tsx b/src/components/VencordSettings/ThemesTab.tsx index 2eb91cb82..8c5943fcc 100644 --- a/src/components/VencordSettings/ThemesTab.tsx +++ b/src/components/VencordSettings/ThemesTab.tsx @@ -29,23 +29,22 @@ import { classes } from "@utils/misc"; import { openModal } from "@utils/modal"; import { showItemInFolder } from "@utils/native"; import { useAwaiter } from "@utils/react"; -import { findByPropsLazy, findLazy } from "@webpack"; +import { find, findComponent } from "@webpack"; import { Button, Card, Forms, React, showToast, TabBar, TextArea, useEffect, useRef, useState } from "@webpack/common"; -import type { ComponentType, Ref, SyntheticEvent } from "react"; +import type { Ref, SyntheticEvent } from "react"; import { AddonCard } from "./AddonCard"; import { SettingsTab, wrapTab } from "./shared"; -type FileInput = ComponentType<{ +type FileInputProps = { ref: Ref; onChange: (e: SyntheticEvent) => void; multiple?: boolean; filters?: { name?: string; extensions: string[]; }[]; -}>; +}; -const InviteActions = findByPropsLazy("resolveInvite"); -const FileInput: FileInput = findLazy(m => m.prototype?.activateUploadDialogue && m.prototype.setRef); -const TextAreaProps = findLazy(m => typeof m.textarea === "string"); +const FileInput = findComponent(m => m.prototype?.activateUploadDialogue && m.prototype.setRef); +const TextAreaProps = find(m => typeof m.textarea === "string"); const cl = classNameFactory("vc-settings-theme-"); diff --git a/src/plugins/anonymiseFileNames/index.tsx b/src/plugins/anonymiseFileNames/index.tsx index b424b7a59..de8cb0fb2 100644 --- a/src/plugins/anonymiseFileNames/index.tsx +++ b/src/plugins/anonymiseFileNames/index.tsx @@ -21,12 +21,12 @@ import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByCodeLazy, findByPropsLazy } from "@webpack"; +import { findByCode, findByProps } from "@webpack"; type AnonUpload = Upload & { anonymise?: boolean; }; -const ActionBarIcon = findByCodeLazy(".actionBarIcon)"); -const UploadDraft = findByPropsLazy("popFirstFile", "update"); +const ActionBarIcon = findByCode(".actionBarIcon)"); +const UploadDraft = findByProps("popFirstFile", "update"); const enum Methods { Random, diff --git a/src/plugins/arRPC.web/index.tsx b/src/plugins/arRPC.web/index.tsx index 423dce9b5..1a21e757f 100644 --- a/src/plugins/arRPC.web/index.tsx +++ b/src/plugins/arRPC.web/index.tsx @@ -20,10 +20,10 @@ import { popNotice, showNotice } from "@api/Notices"; import { Link } from "@components/Link"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { ApplicationAssetUtils, FluxDispatcher, Forms, Toasts } from "@webpack/common"; -const RpcUtils = findByPropsLazy("fetchApplicationsRPC", "getRemoteIconURL"); +const RpcUtils = findByProps("fetchApplicationsRPC", "getRemoteIconURL"); async function lookupAsset(applicationId: string, key: string): Promise { return (await ApplicationAssetUtils.fetchAssetIds(applicationId, [key]))[0]; diff --git a/src/plugins/betterFolders/FolderSideBar.tsx b/src/plugins/betterFolders/FolderSideBar.tsx index 53d24ed93..05f7833b0 100644 --- a/src/plugins/betterFolders/FolderSideBar.tsx +++ b/src/plugins/betterFolders/FolderSideBar.tsx @@ -17,15 +17,15 @@ */ import ErrorBoundary from "@components/ErrorBoundary"; -import { findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack"; +import { findByProps, findComponentByCode, findStore } 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 = findComponentByCodeLazy('("guildsnav")'); +const ChannelRTCStore = findStore("ChannelRTCStore"); +const Animations = findByProps("a", "animated", "useTransition"); +const GuildsBar = findComponentByCode('("guildsnav")'); export default ErrorBoundary.wrap(guildsBarProps => { const expandedFolders = useStateFromStores([ExpandedGuildFolderStore], () => ExpandedGuildFolderStore.getExpandedFolders()); diff --git a/src/plugins/betterFolders/index.tsx b/src/plugins/betterFolders/index.tsx index 70e4070cd..81d9c122d 100644 --- a/src/plugins/betterFolders/index.tsx +++ b/src/plugins/betterFolders/index.tsx @@ -19,7 +19,7 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy, findStoreLazy } from "@webpack"; +import { findByProps, findStore } from "@webpack"; import { FluxDispatcher, i18n } from "@webpack/common"; import FolderSideBar from "./FolderSideBar"; @@ -30,10 +30,10 @@ enum FolderIconDisplay { MoreThanOneFolderExpanded } -const { GuildsTree } = findByPropsLazy("GuildsTree"); -const SortedGuildStore = findStoreLazy("SortedGuildStore"); -export const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore"); -const FolderUtils = findByPropsLazy("move", "toggleGuildFolderExpand"); +const { GuildsTree } = findByProps("GuildsTree"); +const SortedGuildStore = findStore("SortedGuildStore"); +export const ExpandedGuildFolderStore = findStore("ExpandedGuildFolderStore"); +const FolderUtils = findByProps("move", "toggleGuildFolderExpand"); let lastGuildId = null as string | null; let dispatchingFoldersClose = false; diff --git a/src/plugins/betterNotes/index.tsx b/src/plugins/betterNotes/index.tsx index 2183d98e2..76c971018 100644 --- a/src/plugins/betterNotes/index.tsx +++ b/src/plugins/betterNotes/index.tsx @@ -20,9 +20,9 @@ import { Settings } from "@api/Settings"; import { Devs } from "@utils/constants"; import { canonicalizeMatch } from "@utils/patches"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; -const UserPopoutSectionCssClasses = findByPropsLazy("section", "lastSection"); +const UserPopoutSectionCssClasses = findByProps("section", "lastSection"); export default definePlugin({ name: "BetterNotesBox", diff --git a/src/plugins/betterRoleContext/index.tsx b/src/plugins/betterRoleContext/index.tsx index 3db3494f9..d52a127b0 100644 --- a/src/plugins/betterRoleContext/index.tsx +++ b/src/plugins/betterRoleContext/index.tsx @@ -7,10 +7,10 @@ import { Devs } from "@utils/constants"; import { getCurrentGuild } from "@utils/discord"; import definePlugin from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { Clipboard, GuildStore, Menu, PermissionStore, TextAndImagesSettingsStores } from "@webpack/common"; -const GuildSettingsActions = findByPropsLazy("open", "selectRole", "updateGuild"); +const GuildSettingsActions = findByProps("open", "selectRole", "updateGuild"); function PencilIcon() { return ( diff --git a/src/plugins/betterSessions/index.tsx b/src/plugins/betterSessions/index.tsx index 539508f80..7fdaff971 100644 --- a/src/plugins/betterSessions/index.tsx +++ b/src/plugins/betterSessions/index.tsx @@ -21,20 +21,20 @@ import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy, findExportedComponentLazy, findStoreLazy } from "@webpack"; +import { findByProps, findExportedComponent, findStore } from "@webpack"; import { React, RestAPI, Tooltip } from "@webpack/common"; import { RenameButton } from "./components/RenameButton"; import { Session, SessionInfo } from "./types"; import { fetchNamesFromDataStore, getDefaultName, GetOsColor, GetPlatformIcon, savedSessionsCache, saveSessionsToDataStore } from "./utils"; -const AuthSessionsStore = findStoreLazy("AuthSessionsStore"); -const UserSettingsModal = findByPropsLazy("saveAccountChanges", "open"); +const AuthSessionsStore = findStore("AuthSessionsStore"); +const UserSettingsModal = findByProps("saveAccountChanges", "open"); -const TimestampClasses = findByPropsLazy("timestampTooltip", "blockquoteContainer"); -const SessionIconClasses = findByPropsLazy("sessionIcon"); +const TimestampClasses = findByProps("timestampTooltip", "blockquoteContainer"); +const SessionIconClasses = findByProps("sessionIcon"); -const BlobMask = findExportedComponentLazy("BlobMask"); +const BlobMask = findExportedComponent("BlobMask"); const settings = definePluginSettings({ backgroundCheck: { diff --git a/src/plugins/betterSettings/index.tsx b/src/plugins/betterSettings/index.tsx index 7d81c6f5c..ed246ae06 100644 --- a/src/plugins/betterSettings/index.tsx +++ b/src/plugins/betterSettings/index.tsx @@ -9,14 +9,14 @@ import { classNameFactory } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { ComponentDispatch, FocusLock, i18n, Menu, useEffect, useRef } from "@webpack/common"; import type { HTMLAttributes, ReactElement } from "react"; type SettingsEntry = { section: string, label: string; }; const cl = classNameFactory(""); -const Classes = findByPropsLazy("animating", "baseLayer", "bg", "layer", "layers"); +const Classes = findByProps("animating", "baseLayer", "bg", "layer", "layers"); const settings = definePluginSettings({ disableFade: { diff --git a/src/plugins/biggerStreamPreview/webpack/stores.ts b/src/plugins/biggerStreamPreview/webpack/stores.ts index ba5227baa..805c8622e 100644 --- a/src/plugins/biggerStreamPreview/webpack/stores.ts +++ b/src/plugins/biggerStreamPreview/webpack/stores.ts @@ -16,9 +16,9 @@ * along with this program. If not, see . */ -import { findStoreLazy } from "@webpack"; +import { findStore } from "@webpack"; import * as t from "./types/stores"; -export const ApplicationStreamPreviewStore: t.ApplicationStreamPreviewStore = findStoreLazy("ApplicationStreamPreviewStore"); -export const ApplicationStreamingStore: t.ApplicationStreamingStore = findStoreLazy("ApplicationStreamingStore"); +export const ApplicationStreamPreviewStore: t.ApplicationStreamPreviewStore = findStore("ApplicationStreamPreviewStore"); +export const ApplicationStreamingStore: t.ApplicationStreamingStore = findStore("ApplicationStreamingStore"); diff --git a/src/plugins/clientTheme/index.tsx b/src/plugins/clientTheme/index.tsx index 4e07daf42..34e0e74f9 100644 --- a/src/plugins/clientTheme/index.tsx +++ b/src/plugins/clientTheme/index.tsx @@ -11,10 +11,10 @@ import { Devs } from "@utils/constants"; import { Margins } from "@utils/margins"; import { classes } from "@utils/misc"; import definePlugin, { OptionType, StartAt } from "@utils/types"; -import { findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack"; +import { findByProps, findComponentByCode, findStore } from "@webpack"; import { Button, Forms, useStateFromStores } from "@webpack/common"; -const ColorPicker = findComponentByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); +const ColorPicker = findComponentByCode(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); const colorPresets = [ "#1E1514", "#172019", "#13171B", "#1C1C28", "#402D2D", @@ -30,14 +30,14 @@ function onPickColor(color: number) { updateColorVars(hexColor); } -const { saveClientTheme } = findByPropsLazy("saveClientTheme"); +const { saveClientTheme } = findByProps("saveClientTheme"); function setTheme(theme: string) { saveClientTheme({ theme }); } -const ThemeStore = findStoreLazy("ThemeStore"); -const NitroThemeStore = findStoreLazy("ClientThemesBackgroundStore"); +const ThemeStore = findStore("ThemeStore"); +const NitroThemeStore = findStore("ClientThemesBackgroundStore"); function ThemeSettings() { const theme = useStateFromStores([ThemeStore], () => ThemeStore.theme); diff --git a/src/plugins/consoleShortcuts/index.ts b/src/plugins/consoleShortcuts/index.ts index e25e7cb30..5db4d4478 100644 --- a/src/plugins/consoleShortcuts/index.ts +++ b/src/plugins/consoleShortcuts/index.ts @@ -21,7 +21,7 @@ import { relaunch } from "@utils/native"; import { canonicalizeMatch, canonicalizeReplace, canonicalizeReplacement } from "@utils/patches"; import definePlugin from "@utils/types"; import * as Webpack from "@webpack"; -import { extract, filters, findAll, search } from "@webpack"; +import { cacheFindAll, extract, filters, search } from "@webpack"; import { React, ReactDOM } from "@webpack/common"; import type { ComponentType } from "react"; @@ -42,7 +42,7 @@ export default definePlugin({ const cacheKey = String(filterProps); if (cache.has(cacheKey)) return cache.get(cacheKey); - const matches = findAll(filterFactory(...filterProps)); + const matches = cacheFindAll(filterFactory(...filterProps)); const result = (() => { switch (matches.length) { @@ -73,13 +73,13 @@ export default definePlugin({ wpex: extract, wpexs: (code: string) => extract(Webpack.findModuleId(code)!), find, - findAll, + findAll: cacheFindAll, findByProps, - findAllByProps: (...props: string[]) => findAll(filters.byProps(...props)), + findAllByProps: (...props: string[]) => cacheFindAll(filters.byProps(...props)), findByCode: newFindWrapper(filters.byCode), - findAllByCode: (code: string) => findAll(filters.byCode(code)), + findAllByCode: (code: string) => cacheFindAll(filters.byCode(code)), findComponentByCode: newFindWrapper(filters.componentByCode), - findAllComponentsByCode: (...code: string[]) => findAll(filters.componentByCode(...code)), + findAllComponentsByCode: (...code: string[]) => cacheFindAll(filters.componentByCode(...code)), findExportedComponent: (...props: string[]) => findByProps(...props)[props[0]], findStore: newFindWrapper(filters.byStoreName), PluginsApi: Vencord.Plugins, diff --git a/src/plugins/crashHandler/index.ts b/src/plugins/crashHandler/index.ts index f8c76d7f7..d1995e1c0 100644 --- a/src/plugins/crashHandler/index.ts +++ b/src/plugins/crashHandler/index.ts @@ -23,25 +23,14 @@ import { Logger } from "@utils/Logger"; import { closeAllModals } from "@utils/modal"; import definePlugin, { OptionType } from "@utils/types"; import { maybePromptToUpdate } from "@utils/updater"; -import { filters, findBulk, proxyLazyWebpack } from "@webpack"; +import { findByProps } from "@webpack"; import { FluxDispatcher, NavigationRouter, SelectedChannelStore } from "@webpack/common"; const CrashHandlerLogger = new Logger("CrashHandler"); -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 ModalStack = findByProps("pushLazy", "popAll"); +const DraftManager = findByProps("clearDraft", "saveDraft"); +const { DraftType } = findByProps("DraftType"); +const { closeExpressionPicker } = findByProps("closeExpressionPicker", "openExpressionPicker"); const settings = definePluginSettings({ attemptToPreventCrashes: { diff --git a/src/plugins/customRPC/index.tsx b/src/plugins/customRPC/index.tsx index 334372e38..5eef82e43 100644 --- a/src/plugins/customRPC/index.tsx +++ b/src/plugins/customRPC/index.tsx @@ -22,12 +22,12 @@ import { Devs } from "@utils/constants"; import { isTruthy } from "@utils/guards"; import { useAwaiter } from "@utils/react"; import definePlugin, { OptionType } from "@utils/types"; -import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy } from "@webpack"; +import { findByCode, findByProps, findComponentByCode } from "@webpack"; import { ApplicationAssetUtils, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common"; -const useProfileThemeStyle = findByCodeLazy("profileThemeStyle:", "--profile-gradient-primary-color"); -const ActivityComponent = findComponentByCodeLazy("onOpenGameProfile"); -const ActivityClassName = findByPropsLazy("activity", "buttonColor"); +const useProfileThemeStyle = findByCode("profileThemeStyle:", "--profile-gradient-primary-color"); +const ActivityComponent = findComponentByCode("onOpenGameProfile"); +const ActivityClassName = findByProps("activity", "buttonColor"); 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)\//, ""); diff --git a/src/plugins/decor/index.tsx b/src/plugins/decor/index.tsx index 5b2b6d0f0..b01d5d05c 100644 --- a/src/plugins/decor/index.tsx +++ b/src/plugins/decor/index.tsx @@ -9,7 +9,7 @@ import "./ui/styles.css"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { UserStore } from "@webpack/common"; import { CDN_URL, RAW_SKU_ID, SKU_ID } from "./lib/constants"; @@ -20,7 +20,7 @@ import { settings } from "./settings"; import { setDecorationGridDecoration, setDecorationGridItem } from "./ui/components"; import DecorSection from "./ui/components/DecorSection"; -const { isAnimatedAvatarDecoration } = findByPropsLazy("isAnimatedAvatarDecoration"); +const { isAnimatedAvatarDecoration } = findByProps("isAnimatedAvatarDecoration"); export interface AvatarDecoration { asset: string; skuId: string; diff --git a/src/plugins/decor/ui/components/DecorSection.tsx b/src/plugins/decor/ui/components/DecorSection.tsx index f11a87a53..8e454ea03 100644 --- a/src/plugins/decor/ui/components/DecorSection.tsx +++ b/src/plugins/decor/ui/components/DecorSection.tsx @@ -5,7 +5,7 @@ */ import { Flex } from "@components/Flex"; -import { findByCodeLazy } from "@webpack"; +import { findByCode } from "@webpack"; import { Button, useEffect } from "@webpack/common"; import { useAuthorizationStore } from "../../lib/stores/AuthorizationStore"; @@ -13,7 +13,7 @@ import { useCurrentUserDecorationsStore } from "../../lib/stores/CurrentUserDeco import { cl } from "../"; import { openChangeDecorationModal } from "../modals/ChangeDecorationModal"; -const CustomizationSection = findByCodeLazy(".customizationSectionBackground"); +const CustomizationSection = findByCode(".customizationSectionBackground"); interface DecorSectionProps { hideTitle?: boolean; diff --git a/src/plugins/decor/ui/components/SectionedGridList.tsx b/src/plugins/decor/ui/components/SectionedGridList.tsx index 9a6ec1b8d..315448c64 100644 --- a/src/plugins/decor/ui/components/SectionedGridList.tsx +++ b/src/plugins/decor/ui/components/SectionedGridList.tsx @@ -5,13 +5,13 @@ */ import { classes } from "@utils/misc"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { React } from "@webpack/common"; import { cl } from "../"; import Grid, { GridProps } from "./Grid"; -const ScrollerClasses = findByPropsLazy("managedReactiveScroller"); +const ScrollerClasses = findByProps("managedReactiveScroller"); type Section = SectionT & { items: Array; diff --git a/src/plugins/decor/ui/components/index.ts b/src/plugins/decor/ui/components/index.ts index 8fe41fc92..8bc7f296d 100644 --- a/src/plugins/decor/ui/components/index.ts +++ b/src/plugins/decor/ui/components/index.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { findComponentByCode, LazyComponentWebpack } from "@webpack"; +import { filters, findComponent } from "@webpack"; import { React } from "@webpack/common"; import type { ComponentType, HTMLProps, PropsWithChildren } from "react"; @@ -18,8 +18,7 @@ type DecorationGridItemComponent = ComponentType DecorationGridItem = v; -export const AvatarDecorationModalPreview = LazyComponentWebpack(() => { - const component = findComponentByCode(".shopPreviewBanner"); +export const AvatarDecorationModalPreview = findComponent(filters.componentByCode(".shopPreviewBanner"), component => { return React.memo(component); }); diff --git a/src/plugins/decor/ui/index.ts b/src/plugins/decor/ui/index.ts index 0ead602e2..b3fd3f246 100644 --- a/src/plugins/decor/ui/index.ts +++ b/src/plugins/decor/ui/index.ts @@ -5,10 +5,10 @@ */ import { classNameFactory } from "@api/Styles"; -import { extractAndLoadChunksLazy, findByPropsLazy } from "@webpack"; +import { extractAndLoadChunksLazy, findByProps } from "@webpack"; export const cl = classNameFactory("vc-decor-"); -export const DecorationModalStyles = findByPropsLazy("modalFooterShopButton"); +export const DecorationModalStyles = findByProps("modalFooterShopButton"); 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 index 5fbe165ce..2e8e6a75c 100644 --- a/src/plugins/decor/ui/modals/ChangeDecorationModal.tsx +++ b/src/plugins/decor/ui/modals/ChangeDecorationModal.tsx @@ -10,7 +10,7 @@ import { openInviteModal } from "@utils/discord"; import { Margins } from "@utils/margins"; import { classes } from "@utils/misc"; import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; -import { findComponentByCodeLazy } from "@webpack"; +import { findComponentByCode } 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"; @@ -29,7 +29,7 @@ import SectionedGridList from "../components/SectionedGridList"; import { openCreateDecorationModal } from "./CreateDecorationModal"; import { openGuidelinesModal } from "./GuidelinesModal"; -const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers"); +const UserSummaryItem = findComponentByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers"); function usePresets() { const [presets, setPresets] = useState([]); diff --git a/src/plugins/decor/ui/modals/CreateDecorationModal.tsx b/src/plugins/decor/ui/modals/CreateDecorationModal.tsx index 0dcf855ef..8bd0ce9bf 100644 --- a/src/plugins/decor/ui/modals/CreateDecorationModal.tsx +++ b/src/plugins/decor/ui/modals/CreateDecorationModal.tsx @@ -9,7 +9,7 @@ import { Link } from "@components/Link"; import { openInviteModal } from "@utils/discord"; import { Margins } from "@utils/margins"; import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; -import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; +import { findByProps, findComponentByCode } 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"; @@ -17,9 +17,9 @@ import { useCurrentUserDecorationsStore } from "../../lib/stores/CurrentUserDeco import { cl, DecorationModalStyles, requireAvatarDecorationModal, requireCreateStickerModal } from "../"; import { AvatarDecorationModalPreview } from "../components"; -const FileUpload = findComponentByCodeLazy("fileUploadInput,"); +const FileUpload = findComponentByCode("fileUploadInput,"); -const { default: HelpMessage, HelpMessageTypes } = findByPropsLazy("HelpMessageTypes"); +const { default: HelpMessage, HelpMessageTypes } = findByProps("HelpMessageTypes"); function useObjectURL(object: Blob | MediaSource | null) { const [url, setUrl] = useState(null); diff --git a/src/plugins/devCompanion.dev/index.tsx b/src/plugins/devCompanion.dev/index.tsx index 25fd563e4..9caddee45 100644 --- a/src/plugins/devCompanion.dev/index.tsx +++ b/src/plugins/devCompanion.dev/index.tsx @@ -22,7 +22,7 @@ import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches"; import definePlugin, { OptionType } from "@utils/types"; -import { filters, findAll, search } from "@webpack"; +import { cacheFind, filters, search } from "@webpack"; const PORT = 8485; const NAV_ID = "dev-companion-reconnect"; @@ -201,22 +201,22 @@ function initWs(isManual = false) { let results: any[]; switch (type.replace("find", "").replace("Lazy", "")) { case "": - results = findAll(parsedArgs[0]); + results = cacheFind(parsedArgs[0]); break; case "ByProps": - results = findAll(filters.byProps(...parsedArgs)); + results = cacheFind(filters.byProps(...parsedArgs)); break; case "Store": - results = findAll(filters.byStoreName(parsedArgs[0])); + results = cacheFind(filters.byStoreName(parsedArgs[0])); break; case "ByCode": - results = findAll(filters.byCode(...parsedArgs)); + results = cacheFind(filters.byCode(...parsedArgs)); break; case "ModuleId": results = Object.keys(search(parsedArgs[0])); break; case "ComponentByCode": - results = findAll(filters.componentByCode(...parsedArgs)); + results = cacheFind(filters.componentByCode(...parsedArgs)); break; default: return reply("Unknown Find Type " + type); diff --git a/src/plugins/emoteCloner/index.tsx b/src/plugins/emoteCloner/index.tsx index cd9890a80..2c95317b2 100644 --- a/src/plugins/emoteCloner/index.tsx +++ b/src/plugins/emoteCloner/index.tsx @@ -23,12 +23,12 @@ import { Logger } from "@utils/Logger"; import { Margins } from "@utils/margins"; import { ModalContent, ModalHeader, ModalRoot, openModalLazy } from "@utils/modal"; import definePlugin from "@utils/types"; -import { findByPropsLazy, findStoreLazy } from "@webpack"; +import { findByProps, findStore } from "@webpack"; import { EmojiStore, FluxDispatcher, Forms, GuildStore, Menu, PermissionsBits, PermissionStore, React, RestAPI, Toasts, Tooltip, UserStore } from "@webpack/common"; import { Promisable } from "type-fest"; -const StickersStore = findStoreLazy("StickersStore"); -const EmojiManager = findByPropsLazy("fetchEmoji", "uploadEmoji", "deleteEmoji"); +const StickersStore = findStore("StickersStore"); +const EmojiManager = findByProps("fetchEmoji", "uploadEmoji", "deleteEmoji"); interface Sticker { t: "Sticker"; diff --git a/src/plugins/experiments/index.tsx b/src/plugins/experiments/index.tsx index 344638828..c10a41a5b 100644 --- a/src/plugins/experiments/index.tsx +++ b/src/plugins/experiments/index.tsx @@ -22,10 +22,10 @@ import { ErrorCard } from "@components/ErrorCard"; import { Devs } from "@utils/constants"; import { Margins } from "@utils/margins"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { Forms, React } from "@webpack/common"; -const KbdStyles = findByPropsLazy("key", "removeBuildOverride"); +const KbdStyles = findByProps("key", "removeBuildOverride"); const settings = definePluginSettings({ enableIsStaff: { diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx index adf1f70fb..1e26b3a2e 100644 --- a/src/plugins/fakeNitro/index.tsx +++ b/src/plugins/fakeNitro/index.tsx @@ -23,7 +23,7 @@ import { ApngBlendOp, ApngDisposeOp, importApngJs } from "@utils/dependencies"; import { getCurrentGuild } from "@utils/discord"; import { Logger } from "@utils/Logger"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack"; +import { findByProps, findStore, webpackDependantLazy } from "@webpack"; import { Alerts, ChannelStore, EmojiStore, FluxDispatcher, Forms, IconUtils, lodash, Parser, PermissionsBits, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common"; import type { CustomEmoji } from "@webpack/types"; import type { Message } from "discord-types/general"; @@ -31,14 +31,14 @@ import { applyPalette, GIFEncoder, quantize } from "gifenc"; import type { ReactElement, ReactNode } from "react"; const DRAFT_TYPE = 0; -const StickerStore = findStoreLazy("StickersStore") as { +const StickerStore = findStore("StickersStore") as { getPremiumPacks(): StickerPack[]; getAllGuildStickers(): Map; getStickerById(id: string): Sticker | undefined; }; -const UserSettingsProtoStore = findStoreLazy("UserSettingsProtoStore"); -const ProtoUtils = findByPropsLazy("BINARY_READ_OPTIONS"); +const UserSettingsProtoStore = findStore("UserSettingsProtoStore"); +const ProtoUtils = findByProps("BINARY_READ_OPTIONS"); function searchProtoClassField(localName: string, protoClass: any) { const field = protoClass?.fields?.find((field: any) => field.localName === localName); @@ -48,9 +48,9 @@ function searchProtoClassField(localName: string, protoClass: any) { return fieldGetter?.(); } -const PreloadedUserSettingsActionCreators = proxyLazyWebpack(() => UserSettingsActionCreators.PreloadedUserSettingsActionCreators); -const AppearanceSettingsActionCreators = proxyLazyWebpack(() => searchProtoClassField("appearance", PreloadedUserSettingsActionCreators.ProtoClass)); -const ClientThemeSettingsActionsCreators = proxyLazyWebpack(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators)); +const PreloadedUserSettingsActionCreators = webpackDependantLazy(() => UserSettingsActionCreators.PreloadedUserSettingsActionCreators); +const AppearanceSettingsActionCreators = webpackDependantLazy(() => searchProtoClassField("appearance", PreloadedUserSettingsActionCreators.ProtoClass)); +const ClientThemeSettingsActionsCreators = webpackDependantLazy(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators)); const enum EmojiIntentions { diff --git a/src/plugins/favGifSearch/index.tsx b/src/plugins/favGifSearch/index.tsx index d71f56795..f1af5b043 100644 --- a/src/plugins/favGifSearch/index.tsx +++ b/src/plugins/favGifSearch/index.tsx @@ -20,7 +20,7 @@ import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { useCallback, useEffect, useRef, useState } from "@webpack/common"; interface SearchBarComponentProps { @@ -35,7 +35,7 @@ interface SearchBarComponentProps { } type TSearchBarComponent = - React.FC & { Sizes: Record<"SMALL" | "MEDIUM" | "LARGE", string>; }; + React.ComponentType & { Sizes: Record<"SMALL" | "MEDIUM" | "LARGE", string>; }; interface Gif { format: number; @@ -60,7 +60,7 @@ interface Instance { } -const containerClasses: { searchBar: string; } = findByPropsLazy("searchBar", "searchBarFullRow"); +const containerClasses: { searchBar: string; } = findByProps("searchBar", "searchBarFullRow"); export const settings = definePluginSettings({ searchOption: { diff --git a/src/plugins/friendInvites/index.ts b/src/plugins/friendInvites/index.ts index e5ff447ed..eb293b663 100644 --- a/src/plugins/friendInvites/index.ts +++ b/src/plugins/friendInvites/index.ts @@ -19,11 +19,11 @@ import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, sendBotMessage } from "@api/Commands"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { RestAPI, UserStore } from "@webpack/common"; -const FriendInvites = findByPropsLazy("createFriendInvite"); -const { uuid4 } = findByPropsLazy("uuid4"); +const FriendInvites = findByProps("createFriendInvite"); +const { uuid4 } = findByProps("uuid4"); export default definePlugin({ name: "FriendInvites", diff --git a/src/plugins/friendsSince/index.tsx b/src/plugins/friendsSince/index.tsx index fb431b52b..1c19eeeca 100644 --- a/src/plugins/friendsSince/index.tsx +++ b/src/plugins/friendsSince/index.tsx @@ -8,14 +8,14 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { getCurrentChannel } from "@utils/discord"; import definePlugin from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { Heading, React, RelationshipStore, Text } from "@webpack/common"; -const container = findByPropsLazy("memberSinceWrapper"); -const { getCreatedAtDate } = findByPropsLazy("getCreatedAtDate"); -const clydeMoreInfo = findByPropsLazy("clydeMoreInfo"); -const locale = findByPropsLazy("getLocale"); -const lastSection = findByPropsLazy("lastSection"); +const container = findByProps("memberSinceWrapper"); +const { getCreatedAtDate } = findByProps("getCreatedAtDate"); +const clydeMoreInfo = findByProps("clydeMoreInfo"); +const locale = findByProps("getLocale"); +const lastSection = findByProps("lastSection"); export default definePlugin({ name: "FriendsSince", diff --git a/src/plugins/gameActivityToggle/index.tsx b/src/plugins/gameActivityToggle/index.tsx index 51feb9165..6ecdf4e1f 100644 --- a/src/plugins/gameActivityToggle/index.tsx +++ b/src/plugins/gameActivityToggle/index.tsx @@ -21,12 +21,12 @@ import { disableStyle, enableStyle } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findComponentByCodeLazy } from "@webpack"; +import { findComponentByCode } from "@webpack"; import { StatusSettingsStores } from "@webpack/common"; import style from "./style.css?managed"; -const Button = findComponentByCodeLazy("Button.Sizes.NONE,disabled:"); +const Button = findComponentByCode("Button.Sizes.NONE,disabled:"); function makeIcon(showCurrentGame?: boolean) { const { oldIcon } = settings.use(["oldIcon"]); diff --git a/src/plugins/gifPaste/index.ts b/src/plugins/gifPaste/index.ts index 5553bf847..fd1eb0dae 100644 --- a/src/plugins/gifPaste/index.ts +++ b/src/plugins/gifPaste/index.ts @@ -19,9 +19,9 @@ import { Devs } from "@utils/constants"; import { insertTextIntoChatInputBox } from "@utils/discord"; import definePlugin from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; -const { closeExpressionPicker } = findByPropsLazy("closeExpressionPicker"); +const { closeExpressionPicker } = findByProps("closeExpressionPicker"); export default definePlugin({ name: "GifPaste", diff --git a/src/plugins/greetStickerPicker/index.tsx b/src/plugins/greetStickerPicker/index.tsx index 73bb5125e..608e97d07 100644 --- a/src/plugins/greetStickerPicker/index.tsx +++ b/src/plugins/greetStickerPicker/index.tsx @@ -19,7 +19,7 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { ContextMenuApi, FluxDispatcher, Menu, MessageActions } from "@webpack/common"; import { Channel, Message } from "discord-types/general"; @@ -49,7 +49,7 @@ const settings = definePluginSettings({ unholyMultiGreetEnabled?: boolean; }>(); -const { WELCOME_STICKERS } = findByPropsLazy("WELCOME_STICKERS"); +const { WELCOME_STICKERS } = findByProps("WELCOME_STICKERS"); function greet(channel: Channel, message: Message, stickers: string[]) { const options = MessageActions.getSendMessageOptionsForReply({ diff --git a/src/plugins/ignoreActivities/index.tsx b/src/plugins/ignoreActivities/index.tsx index e2262129d..adddb0adf 100644 --- a/src/plugins/ignoreActivities/index.tsx +++ b/src/plugins/ignoreActivities/index.tsx @@ -11,7 +11,7 @@ import { Flex } from "@components/Flex"; import { Devs } from "@utils/constants"; import { Margins } from "@utils/margins"; import definePlugin, { OptionType } from "@utils/types"; -import { findStoreLazy } from "@webpack"; +import { findStore } from "@webpack"; import { Button, Forms, showToast, StatusSettingsStores, TextInput, Toasts, Tooltip, useEffect, useState } from "webpack/common"; const enum ActivitiesTypes { @@ -25,7 +25,7 @@ interface IgnoredActivity { type: ActivitiesTypes; } -const RunningGameStore = findStoreLazy("RunningGameStore"); +const RunningGameStore = findStore("RunningGameStore"); function ToggleIcon(activity: IgnoredActivity, tooltipText: string, path: string, fill: string) { return ( diff --git a/src/plugins/imageZoom/components/Magnifier.tsx b/src/plugins/imageZoom/components/Magnifier.tsx index 816717350..f2bd79688 100644 --- a/src/plugins/imageZoom/components/Magnifier.tsx +++ b/src/plugins/imageZoom/components/Magnifier.tsx @@ -36,7 +36,7 @@ export interface MagnifierProps { const cl = classNameFactory("vc-imgzoom-"); -export const Magnifier: React.FC = ({ instance, size: initialSize, zoom: initalZoom }) => { +export const Magnifier: React.ComponentType = ({ instance, size: initialSize, zoom: initalZoom }) => { const [ready, setReady] = useState(false); const [lensPosition, setLensPosition] = useState({ x: 0, y: 0 }); diff --git a/src/plugins/implicitRelationships/index.ts b/src/plugins/implicitRelationships/index.ts index be17e0fe4..07ddc855e 100644 --- a/src/plugins/implicitRelationships/index.ts +++ b/src/plugins/implicitRelationships/index.ts @@ -19,11 +19,11 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByProps, findStoreLazy } from "@webpack"; +import { findByProps, findStore } from "@webpack"; import { ChannelStore, FluxDispatcher, GuildStore, RelationshipStore, SnowflakeUtils, UserStore } from "@webpack/common"; import { Settings } from "Vencord"; -const UserAffinitiesStore = findStoreLazy("UserAffinitiesStore"); +const UserAffinitiesStore = findStore("UserAffinitiesStore"); interface UserAffinity { user_id: string; diff --git a/src/plugins/lastfm/index.tsx b/src/plugins/lastfm/index.tsx index 5dfec8a32..01aae2c16 100644 --- a/src/plugins/lastfm/index.tsx +++ b/src/plugins/lastfm/index.tsx @@ -21,7 +21,7 @@ import { Link } from "@components/Link"; import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { ApplicationAssetUtils, FluxDispatcher, Forms } from "@webpack/common"; interface ActivityAssets { @@ -85,7 +85,7 @@ const placeholderId = "2a96cbd8b46e442fc41c2b86b821562f"; const logger = new Logger("LastFMRichPresence"); -const presenceStore = findByPropsLazy("getLocalPresence"); +const presenceStore = findByProps("getLocalPresence"); async function getApplicationAsset(key: string): Promise { return (await ApplicationAssetUtils.fetchAssetIds(applicationId, [key]))[0]; diff --git a/src/plugins/memberCount/index.tsx b/src/plugins/memberCount/index.tsx index 92e9a2057..98e672b61 100644 --- a/src/plugins/memberCount/index.tsx +++ b/src/plugins/memberCount/index.tsx @@ -23,13 +23,13 @@ import { classNameFactory } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findStoreLazy } from "@webpack"; +import { findStore } from "@webpack"; import { FluxStore } from "@webpack/types"; import { MemberCount } from "./MemberCount"; -export const GuildMemberCountStore = findStoreLazy("GuildMemberCountStore") as FluxStore & { getMemberCount(guildId: string): number | null; }; -export const ChannelMemberStore = findStoreLazy("ChannelMemberStore") as FluxStore & { +export const GuildMemberCountStore = findStore("GuildMemberCountStore") as FluxStore & { getMemberCount(guildId: string): number | null; }; +export const ChannelMemberStore = findStore("ChannelMemberStore") as FluxStore & { getProps(guildId: string, channelId: string): { groups: { count: number; id: string; }[]; }; }; diff --git a/src/plugins/messageClickActions/index.ts b/src/plugins/messageClickActions/index.ts index 052c33f61..f254428e9 100644 --- a/src/plugins/messageClickActions/index.ts +++ b/src/plugins/messageClickActions/index.ts @@ -20,7 +20,7 @@ import { addClickListener, removeClickListener } from "@api/MessageEvents"; import { definePluginSettings, Settings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { FluxDispatcher, PermissionsBits, PermissionStore, UserStore } from "@webpack/common"; let isDeletePressed = false; @@ -60,8 +60,8 @@ export default definePlugin({ settings, start() { - const MessageActions = findByPropsLazy("deleteMessage", "startEditMessage"); - const EditStore = findByPropsLazy("isEditing", "isEditingAny"); + const MessageActions = findByProps("deleteMessage", "startEditMessage"); + const EditStore = findByProps("isEditing", "isEditingAny"); document.addEventListener("keydown", keydown); document.addEventListener("keyup", keyup); diff --git a/src/plugins/messageLinkEmbeds/index.tsx b/src/plugins/messageLinkEmbeds/index.tsx index 2a5f88282..29da765e2 100644 --- a/src/plugins/messageLinkEmbeds/index.tsx +++ b/src/plugins/messageLinkEmbeds/index.tsx @@ -23,7 +23,7 @@ import { Devs } from "@utils/constants.js"; import { classes } from "@utils/misc"; import { Queue } from "@utils/Queue"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; +import { findByProps,findComponentByCode } from "@webpack"; import { Button, ChannelStore, @@ -46,12 +46,12 @@ const messageCache = new Map(); -const Embed = findComponentByCodeLazy(".inlineMediaEmbed"); -const AutoModEmbed = findComponentByCodeLazy(".withFooter]:", "childrenMessageContent:"); -const ChannelMessage = findComponentByCodeLazy("renderSimpleAccessories)"); +const Embed = findComponentByCode(".inlineMediaEmbed"); +const AutoModEmbed = findComponentByCode(".withFooter]:", "childrenMessageContent:"); +const ChannelMessage = findComponentByCode("renderSimpleAccessories)"); -const SearchResultClasses = findByPropsLazy("message", "searchResult"); -const EmbedClasses = findByPropsLazy("embedAuthorIcon", "embedAuthor", "embedAuthor"); +const SearchResultClasses = findByProps("message", "searchResult"); +const EmbedClasses = findByProps("embedAuthorIcon", "embedAuthor", "embedAuthor"); const messageLinkRegex = /(? m.Types?.[0] === "BOT") as RC<{ type?: number, className?: string, useRemSizes?: boolean; }> & { Types: Record; }; +const Tag = find(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(); diff --git a/src/plugins/mutualGroupDMs/index.tsx b/src/plugins/mutualGroupDMs/index.tsx index 1753fefbc..aed1876ad 100644 --- a/src/plugins/mutualGroupDMs/index.tsx +++ b/src/plugins/mutualGroupDMs/index.tsx @@ -19,15 +19,15 @@ import { Devs } from "@utils/constants"; import { isNonNullish } from "@utils/guards"; import definePlugin from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { Avatar, ChannelStore, Clickable, IconUtils, RelationshipStore, ScrollerThin, UserStore } from "@webpack/common"; import { Channel, User } from "discord-types/general"; -const SelectedChannelActionCreators = findByPropsLazy("selectPrivateChannel"); -const UserUtils = findByPropsLazy("getGlobalName"); +const SelectedChannelActionCreators = findByProps("selectPrivateChannel"); +const UserUtils = findByProps("getGlobalName"); -const ProfileListClasses = findByPropsLazy("emptyIconFriends", "emptyIconGuilds"); -const GuildLabelClasses = findByPropsLazy("guildNick", "guildAvatarWithoutIcon"); +const ProfileListClasses = findByProps("emptyIconFriends", "emptyIconGuilds"); +const GuildLabelClasses = findByProps("guildNick", "guildAvatarWithoutIcon"); function getGroupDMName(channel: Channel) { return channel.name || diff --git a/src/plugins/newGuildSettings/index.tsx b/src/plugins/newGuildSettings/index.tsx index ff6f1c261..39fda260c 100644 --- a/src/plugins/newGuildSettings/index.tsx +++ b/src/plugins/newGuildSettings/index.tsx @@ -16,14 +16,14 @@ * along with this program. If not, see . */ -import { definePluginSettings,migratePluginSettings } from "@api/Settings"; +import { definePluginSettings, migratePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; -const { updateGuildNotificationSettings } = findByPropsLazy("updateGuildNotificationSettings"); -const { toggleShowAllChannels } = findByPropsLazy("toggleShowAllChannels"); -const { isOptInEnabledForGuild } = findByPropsLazy("isOptInEnabledForGuild"); +const { updateGuildNotificationSettings } = findByProps("updateGuildNotificationSettings"); +const { toggleShowAllChannels } = findByProps("toggleShowAllChannels"); +const { isOptInEnabledForGuild } = findByProps("isOptInEnabledForGuild"); const settings = definePluginSettings({ guild: { diff --git a/src/plugins/noBlockedMessages/index.ts b/src/plugins/noBlockedMessages/index.ts index 427f725ca..5a3f9797e 100644 --- a/src/plugins/noBlockedMessages/index.ts +++ b/src/plugins/noBlockedMessages/index.ts @@ -19,9 +19,9 @@ import { Settings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; -const RelationshipStore = findByPropsLazy("getRelationships", "isBlocked"); +const RelationshipStore = findByProps("getRelationships", "isBlocked"); export default definePlugin({ name: "NoBlockedMessages", diff --git a/src/plugins/noPendingCount/index.ts b/src/plugins/noPendingCount/index.ts index 29458df9d..7fd988b98 100644 --- a/src/plugins/noPendingCount/index.ts +++ b/src/plugins/noPendingCount/index.ts @@ -19,9 +19,9 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; -const MessageRequestStore = findByPropsLazy("getMessageRequestsCount"); +const MessageRequestStore = findByProps("getMessageRequestsCount"); const settings = definePluginSettings({ hideFriendRequestsCount: { diff --git a/src/plugins/pauseInvitesForever/index.tsx b/src/plugins/pauseInvitesForever/index.tsx index 81f18fd6e..74cbca7dc 100644 --- a/src/plugins/pauseInvitesForever/index.tsx +++ b/src/plugins/pauseInvitesForever/index.tsx @@ -18,11 +18,11 @@ import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { GuildStore, RestAPI } from "@webpack/common"; -const Messages = findByPropsLazy("GUILD_INVITE_DISABLE_ACTION_SHEET_DESCRIPTION"); -const { InvitesDisabledExperiment } = findByPropsLazy("InvitesDisabledExperiment"); +const Messages = findByProps("GUILD_INVITE_DISABLE_ACTION_SHEET_DESCRIPTION"); +const { InvitesDisabledExperiment } = findByProps("InvitesDisabledExperiment"); export default definePlugin({ name: "PauseInvitesForever", diff --git a/src/plugins/permissionsViewer/components/UserPermissions.tsx b/src/plugins/permissionsViewer/components/UserPermissions.tsx index bcd6bdf07..a8f5cf667 100644 --- a/src/plugins/permissionsViewer/components/UserPermissions.tsx +++ b/src/plugins/permissionsViewer/components/UserPermissions.tsx @@ -18,8 +18,9 @@ import ErrorBoundary from "@components/ErrorBoundary"; import ExpandableHeader from "@components/ExpandableHeader"; +import { proxyLazy } from "@utils/lazy"; import { classes } from "@utils/misc"; -import { filters, findBulk, proxyLazyWebpack } from "@webpack"; +import { findByProps } from "@webpack"; import { i18n, PermissionsBits, Text, Tooltip, useMemo, UserStore } from "@webpack/common"; import type { Guild, GuildMember } from "discord-types/general"; @@ -35,13 +36,11 @@ interface UserPermission { type UserPermissions = Array; -const Classes = proxyLazyWebpack(() => - Object.assign({}, ...findBulk( - filters.byProps("roles", "rolePill", "rolePillBorder"), - filters.byProps("roleCircle", "dotBorderBase", "dotBorderColor"), - filters.byProps("roleNameOverflow", "root", "roleName", "roleRemoveButton") - )) -) 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>; +const RoleClasses1 = findByProps("roles", "rolePill", "rolePillBorder"); +const RoleClasses2 = findByProps("roleCircle", "dotBorderBase", "dotBorderColor"); +const RoleClasses3 = findByProps("roleNameOverflow", "root", "roleName", "roleRemoveButton"); + +const Classes = proxyLazy(() => Object.assign({}, RoleClasses1, RoleClasses2, RoleClasses3)); function UserPermissionsComponent({ guild, guildMember, showBorder }: { guild: Guild; guildMember: GuildMember; showBorder: boolean; }) { const stns = settings.use(["permissionsSortOrder"]); diff --git a/src/plugins/petpet/index.ts b/src/plugins/petpet/index.ts index 3f9743255..b6fb5d5fc 100644 --- a/src/plugins/petpet/index.ts +++ b/src/plugins/petpet/index.ts @@ -20,7 +20,7 @@ import { ApplicationCommandInputType, ApplicationCommandOptionType, Argument, Co import { Devs } from "@utils/constants"; import { makeLazy } from "@utils/lazy"; import definePlugin from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { UploadHandler, UserUtils } from "@webpack/common"; import { applyPalette, GIFEncoder, quantize } from "gifenc"; @@ -36,7 +36,7 @@ const getFrames = makeLazy(() => Promise.all( )) ); -const UploadStore = findByPropsLazy("getUploads"); +const UploadStore = findByProps("getUploads"); function loadImage(source: File | string) { const isFile = source instanceof File; diff --git a/src/plugins/pinDms/components/CreateCategoryModal.tsx b/src/plugins/pinDms/components/CreateCategoryModal.tsx index 06e1c3568..ea73ec724 100644 --- a/src/plugins/pinDms/components/CreateCategoryModal.tsx +++ b/src/plugins/pinDms/components/CreateCategoryModal.tsx @@ -6,7 +6,7 @@ import { classNameFactory } from "@api/Styles"; import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModalLazy } from "@utils/modal"; -import { extractAndLoadChunksLazy, findComponentByCodeLazy } from "@webpack"; +import { extractAndLoadChunksLazy, findComponentByCode } from "@webpack"; import { Button, Forms, Text, TextInput, Toasts, useEffect, useState } from "@webpack/common"; import { DEFAULT_COLOR, SWATCHES } from "../constants"; @@ -30,8 +30,8 @@ interface ColorPickerWithSwatchesProps { renderCustomButton?: () => React.ReactNode; } -const ColorPicker = findComponentByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); -const ColorPickerWithSwatches = findComponentByCodeLazy("presets,", "customColor:"); +const ColorPicker = findComponentByCode(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); +const ColorPickerWithSwatches = findComponentByCode("presets,", "customColor:"); export const requireSettingsMenu = extractAndLoadChunksLazy(['name:"UserSettings"'], /createPromise:.{0,20}Promise\.all\((\[\i\.\i\(".+?"\).+?\])\).then\(\i\.bind\(\i,"(.+?)"\)\).{0,50}"UserSettings"/); diff --git a/src/plugins/pinDms/index.tsx b/src/plugins/pinDms/index.tsx index 010b5506c..26bfe894b 100644 --- a/src/plugins/pinDms/index.tsx +++ b/src/plugins/pinDms/index.tsx @@ -11,7 +11,7 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { classes } from "@utils/misc"; import definePlugin, { OptionType, StartAt } from "@utils/types"; -import { findByPropsLazy, findStoreLazy } from "@webpack"; +import { findByProps, findStore } from "@webpack"; import { ContextMenuApi, FluxDispatcher, Menu, React } from "@webpack/common"; import { Channel } from "discord-types/general"; @@ -27,9 +27,9 @@ interface ChannelComponentProps { } -const headerClasses = findByPropsLazy("privateChannelsHeaderContainer"); +const headerClasses = findByProps("privateChannelsHeaderContainer"); -export const PrivateChannelSortStore = findStoreLazy("PrivateChannelSortStore") as { getPrivateChannelIds: () => string[]; }; +export const PrivateChannelSortStore = findStore("PrivateChannelSortStore") as { getPrivateChannelIds: () => string[]; }; export let instance: any; export const forceUpdate = () => instance?.props?._forceUpdate?.(); diff --git a/src/plugins/pinDms/settings.ts b/src/plugins/pinDms/settings.ts new file mode 100644 index 000000000..386b662d1 --- /dev/null +++ b/src/plugins/pinDms/settings.ts @@ -0,0 +1,94 @@ +/* + * 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 { definePluginSettings, Settings, useSettings } from "@api/Settings"; +import { OptionType } from "@utils/types"; +import { findStore } from "@webpack"; + +export const enum PinOrder { + LastMessage, + Custom +} + +export const settings = definePluginSettings({ + pinOrder: { + type: OptionType.SELECT, + description: "Which order should pinned DMs be displayed in?", + options: [ + { label: "Most recent message", value: PinOrder.LastMessage, default: true }, + { label: "Custom (right click channels to reorder)", value: PinOrder.Custom } + ] + } +}); + +const PrivateChannelSortStore = findStore("PrivateChannelSortStore"); + +export let snapshotArray: string[]; +let snapshot: Set | undefined; + +const getArray = () => (Settings.plugins.PinDMs.pinnedDMs || void 0)?.split(",") as string[] | undefined; +const save = (pins: string[]) => { + snapshot = void 0; + Settings.plugins.PinDMs.pinnedDMs = pins.join(","); +}; +const takeSnapshot = () => { + snapshotArray = getArray() ?? []; + return snapshot = new Set(snapshotArray); +}; +const requireSnapshot = () => snapshot ?? takeSnapshot(); + +export function usePinnedDms() { + useSettings(["plugins.PinDMs.pinnedDMs"]); + + return requireSnapshot(); +} + +export function isPinned(id: string) { + return requireSnapshot().has(id); +} + +export function togglePin(id: string) { + const snapshot = requireSnapshot(); + if (!snapshot.delete(id)) { + snapshot.add(id); + } + + save([...snapshot]); +} + +export function sortedSnapshot() { + requireSnapshot(); + if (settings.store.pinOrder === PinOrder.LastMessage) + return PrivateChannelSortStore.getPrivateChannelIds().filter(isPinned); + + return snapshotArray; +} + +export function getPinAt(idx: number) { + return sortedSnapshot()[idx]; +} + +export function movePin(id: string, direction: -1 | 1) { + const pins = getArray()!; + const a = pins.indexOf(id); + const b = a + direction; + + [pins[a], pins[b]] = [pins[b], pins[a]]; + + save(pins); +} diff --git a/src/plugins/platformIndicators/index.tsx b/src/plugins/platformIndicators/index.tsx index 9fae9adfa..7293d7cff 100644 --- a/src/plugins/platformIndicators/index.tsx +++ b/src/plugins/platformIndicators/index.tsx @@ -23,11 +23,11 @@ import { Settings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy, findStoreLazy } from "@webpack"; +import { findByProps, findStore } from "@webpack"; import { PresenceStore, Tooltip, UserStore } from "@webpack/common"; import { User } from "discord-types/general"; -const SessionsStore = findStoreLazy("SessionsStore"); +const SessionsStore = findStore("SessionsStore"); function Icon(path: string, opts?: { viewBox?: string; width?: number; height?: number; }) { return ({ color, tooltip, small }: { color: string; tooltip: string; small: boolean; }) => ( @@ -55,7 +55,7 @@ const Icons = { }; type Platform = keyof typeof Icons; -const StatusUtils = findByPropsLazy("useStatusFillColor", "StatusTypes"); +const StatusUtils = findByProps("useStatusFillColor", "StatusTypes"); const PlatformIcon = ({ platform, status, small }: { platform: Platform, status: string; small: boolean; }) => { const tooltip = platform[0].toUpperCase() + platform.slice(1); diff --git a/src/plugins/previewMessage/index.tsx b/src/plugins/previewMessage/index.tsx index fe6b227a5..132a8fde0 100644 --- a/src/plugins/previewMessage/index.tsx +++ b/src/plugins/previewMessage/index.tsx @@ -20,11 +20,11 @@ import { addChatBarButton, ChatBarButton, removeChatBarButton } from "@api/ChatB import { generateId, sendBotMessage } from "@api/Commands"; import { Devs } from "@utils/constants"; import definePlugin, { StartAt } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { DraftStore, DraftType, SelectedChannelStore, UserStore, useStateFromStores } from "@webpack/common"; import { MessageAttachment } from "discord-types/general"; -const UploadStore = findByPropsLazy("getUploads"); +const UploadStore = findByProps("getUploads"); const getDraft = (channelId: string) => DraftStore.getDraft(channelId, DraftType.ChannelMessage); diff --git a/src/plugins/pronoundb/components/PronounsChatComponent.tsx b/src/plugins/pronoundb/components/PronounsChatComponent.tsx index 64fac18ba..5164f0d0e 100644 --- a/src/plugins/pronoundb/components/PronounsChatComponent.tsx +++ b/src/plugins/pronoundb/components/PronounsChatComponent.tsx @@ -18,14 +18,14 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { classes } from "@utils/misc"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { UserStore } from "@webpack/common"; import { Message } from "discord-types/general"; import { useFormattedPronouns } from "../pronoundbUtils"; import { settings } from "../settings"; -const styles: Record = findByPropsLazy("timestampInline"); +const styles: Record = findByProps("timestampInline"); const AUTO_MODERATION_ACTION = 24; diff --git a/src/plugins/quickReply/index.ts b/src/plugins/quickReply/index.ts index 620e1a33f..468bb40a5 100644 --- a/src/plugins/quickReply/index.ts +++ b/src/plugins/quickReply/index.ts @@ -19,11 +19,11 @@ import { definePluginSettings, Settings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { ChannelStore, FluxDispatcher as Dispatcher, MessageStore, PermissionsBits, PermissionStore, SelectedChannelStore, UserStore } from "@webpack/common"; import { Message } from "discord-types/general"; -const Kangaroo = findByPropsLazy("jumpToMessage"); +const Kangaroo = findByProps("jumpToMessage"); const isMac = navigator.platform.includes("Mac"); // bruh let replyIdx = -1; diff --git a/src/plugins/resurrectHome/index.tsx b/src/plugins/resurrectHome/index.tsx index 2042ed9c7..596c33a4c 100644 --- a/src/plugins/resurrectHome/index.tsx +++ b/src/plugins/resurrectHome/index.tsx @@ -21,10 +21,10 @@ import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { Button, Menu, Tooltip, useEffect, useState } from "@webpack/common"; -const ChannelRowClasses = findByPropsLazy("modeConnected", "modeLocked", "icon"); +const ChannelRowClasses = findByProps("modeConnected", "modeLocked", "icon"); let currentShouldViewServerHome = false; const shouldViewServerHomeStates = new Set>>(); diff --git a/src/plugins/revealAllSpoilers/index.ts b/src/plugins/revealAllSpoilers/index.ts index e728181aa..896571b09 100644 --- a/src/plugins/revealAllSpoilers/index.ts +++ b/src/plugins/revealAllSpoilers/index.ts @@ -18,10 +18,10 @@ import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; -const SpoilerClasses = findByPropsLazy("spoilerContent"); -const MessagesClasses = findByPropsLazy("messagesWrapper", "messages"); +const SpoilerClasses = findByProps("spoilerContent"); +const MessagesClasses = findByProps("messagesWrapper", "messages"); export default definePlugin({ name: "RevealAllSpoilers", diff --git a/src/plugins/reviewDB/auth.tsx b/src/plugins/reviewDB/auth.tsx index 4cd81f2ea..7f3898b4e 100644 --- a/src/plugins/reviewDB/auth.tsx +++ b/src/plugins/reviewDB/auth.tsx @@ -7,14 +7,14 @@ import { DataStore } from "@api/index"; import { Logger } from "@utils/Logger"; import { openModal } from "@utils/modal"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { showToast, Toasts, UserStore } from "@webpack/common"; import { ReviewDBAuth } from "./entities"; const DATA_STORE_KEY = "rdb-auth"; -const { OAuth2AuthorizeModal } = findByPropsLazy("OAuth2AuthorizeModal"); +const { OAuth2AuthorizeModal } = findByProps("OAuth2AuthorizeModal"); export let Auth: ReviewDBAuth = {}; diff --git a/src/plugins/reviewDB/components/MessageButton.tsx b/src/plugins/reviewDB/components/MessageButton.tsx index 9b0b4be1a..5c84303c7 100644 --- a/src/plugins/reviewDB/components/MessageButton.tsx +++ b/src/plugins/reviewDB/components/MessageButton.tsx @@ -18,10 +18,10 @@ import { DeleteIcon } from "@components/Icons"; import { classes } from "@utils/misc"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { Tooltip } from "@webpack/common"; -const iconClasses = findByPropsLazy("button", "wrapper", "disabled", "separator"); +const iconClasses = findByProps("button", "wrapper", "disabled", "separator"); export function DeleteButton({ onClick }: { onClick(): void; }) { return ( diff --git a/src/plugins/reviewDB/components/ReviewComponent.tsx b/src/plugins/reviewDB/components/ReviewComponent.tsx index 20b298ccb..1526543ad 100644 --- a/src/plugins/reviewDB/components/ReviewComponent.tsx +++ b/src/plugins/reviewDB/components/ReviewComponent.tsx @@ -18,8 +18,7 @@ import { openUserProfile } from "@utils/discord"; import { classes } from "@utils/misc"; -import { LazyComponent } from "@utils/react"; -import { filters, findBulk } from "@webpack"; +import { findByProps } from "@webpack"; import { Alerts, Parser, Timestamp, useState } from "@webpack/common"; import { Auth, getToken } from "../auth"; @@ -31,161 +30,150 @@ import { openBlockModal } from "./BlockedUserModal"; import { BlockButton, DeleteButton, ReportButton } from "./MessageButton"; import ReviewBadge from "./ReviewBadge"; -export default LazyComponent(() => { - // this is terrible, blame mantika - const p = filters.byProps; - const [ - { cozyMessage, buttons, message, buttonsInner, groupStart }, - { container, isHeader }, - { avatar, clickable, username, wrapper, cozy }, - buttonClasses, - botTag - ] = findBulk( - p("cozyMessage"), - p("container", "isHeader"), - p("avatar", "zalgo"), - p("button", "wrapper", "selected"), - p("botTag", "botTagRegular") - ); +const { cozyMessage, message, groupStart, buttons, buttonsInner } = findByProps("cozyMessage"); +const { container, isHeader } = findByProps("container", "isHeader"); +const { wrapper, cozy, avatar, clickable, username } = findByProps("avatar", "zalgo"); +const buttonClasses = findByProps("button", "wrapper", "selected"); +const botTag = findByProps("botTag", "botTagRegular"); - const dateFormat = new Intl.DateTimeFormat(); +const dateFormat = new Intl.DateTimeFormat(); - return function ReviewComponent({ review, refetch, profileId }: { review: Review; refetch(): void; profileId: string; }) { - const [showAll, setShowAll] = useState(false); +export default function ReviewComponent({ review, refetch, profileId }: { review: Review; refetch(): void; profileId: string; }) { + const [showAll, setShowAll] = useState(false); - function openModal() { - openUserProfile(review.sender.discordID); - } + function openModal() { + openUserProfile(review.sender.discordID); + } - function delReview() { - Alerts.show({ - title: "Are you sure?", - body: "Do you really want to delete this review?", - confirmText: "Delete", - cancelText: "Nevermind", - onConfirm: async () => { - if (!(await getToken())) { - return showToast("You must be logged in to delete reviews."); - } else { - deleteReview(review.id).then(res => { - if (res) { - refetch(); - } - }); - } + function delReview() { + Alerts.show({ + title: "Are you sure?", + body: "Do you really want to delete this review?", + confirmText: "Delete", + cancelText: "Nevermind", + onConfirm: async () => { + if (!(await getToken())) { + return showToast("You must be logged in to delete reviews."); + } else { + deleteReview(review.id).then(res => { + if (res) { + refetch(); + } + }); } - }); - } + } + }); + } - function reportRev() { - Alerts.show({ - title: "Are you sure?", - body: "Do you really you want to report this review?", - confirmText: "Report", - cancelText: "Nevermind", - // confirmColor: "red", this just adds a class name and breaks the submit button guh - onConfirm: async () => { - if (!(await getToken())) { - return showToast("You must be logged in to report reviews."); - } else { - reportReview(review.id); - } + function reportRev() { + Alerts.show({ + title: "Are you sure?", + body: "Do you really you want to report this review?", + confirmText: "Report", + cancelText: "Nevermind", + // confirmColor: "red", this just adds a class name and breaks the submit button guh + onConfirm: async () => { + if (!(await getToken())) { + return showToast("You must be logged in to report reviews."); + } else { + reportReview(review.id); } - }); - } + } + }); + } - const isAuthorBlocked = Auth?.user?.blockedUsers?.includes(review.sender.discordID) ?? false; + const isAuthorBlocked = Auth?.user?.blockedUsers?.includes(review.sender.discordID) ?? false; - function blockReviewSender() { - if (isAuthorBlocked) - return unblockUser(review.sender.discordID); + function blockReviewSender() { + if (isAuthorBlocked) + return unblockUser(review.sender.discordID); - Alerts.show({ - title: "Are you sure?", - body: "Do you really you want to block this user? They will be unable to leave further reviews on your profile. You can unblock users in the plugin settings.", - confirmText: "Block", - cancelText: "Nevermind", - // confirmColor: "red", this just adds a class name and breaks the submit button guh - onConfirm: async () => { - if (!(await getToken())) { - return showToast("You must be logged in to block users."); - } else { - blockUser(review.sender.discordID); - } + Alerts.show({ + title: "Are you sure?", + body: "Do you really you want to block this user? They will be unable to leave further reviews on your profile. You can unblock users in the plugin settings.", + confirmText: "Block", + cancelText: "Nevermind", + // confirmColor: "red", this just adds a class name and breaks the submit button guh + onConfirm: async () => { + if (!(await getToken())) { + return showToast("You must be logged in to block users."); + } else { + blockUser(review.sender.discordID); } - }); - } + } + }); + } - return ( -
+ return ( +
- -
+ +
+ openModal()} + > + {review.sender.username} + + + {review.type === ReviewType.System && ( openModal()} - > - {review.sender.username} - - - {review.type === ReviewType.System && ( - - - System - + className={classes(botTag.botTagVerified, botTag.botTagRegular, botTag.botTag, botTag.px, botTag.rem)} + style={{ marginLeft: "4px" }}> + + System - )} -
- {isAuthorBlocked && ( - openBlockModal()} - /> - )} - {review.sender.badges.map(badge => )} - - { - !settings.store.hideTimestamps && review.type !== ReviewType.System && ( - - {dateFormat.format(review.timestamp * 1000)} - ) - } - -
- {(review.comment.length > 200 && !showAll) - ? [Parser.parseGuildEventDescription(review.comment.substring(0, 200)), "...",
, ( setShowAll(true)}>Read more)] - : Parser.parseGuildEventDescription(review.comment)} -
- - {review.id !== 0 && ( -
-
- {canReportReview(review) && } - {canBlockReviewAuthor(profileId, review) && } - {canDeleteReview(profileId, review) && } -
-
+ )}
- ); - }; -}); + {isAuthorBlocked && ( + openBlockModal()} + /> + )} + {review.sender.badges.map(badge => )} + + { + !settings.store.hideTimestamps && review.type !== ReviewType.System && ( + + {dateFormat.format(review.timestamp * 1000)} + ) + } + +
+ {(review.comment.length > 200 && !showAll) + ? [Parser.parseGuildEventDescription(review.comment.substring(0, 200)), "...",
, ( setShowAll(true)}>Read more)] + : Parser.parseGuildEventDescription(review.comment)} +
+ + {review.id !== 0 && ( +
+
+ {canReportReview(review) && } + {canBlockReviewAuthor(profileId, review) && } + {canDeleteReview(profileId, review) && } +
+
+ )} +
+ ); +} + diff --git a/src/plugins/reviewDB/components/ReviewsView.tsx b/src/plugins/reviewDB/components/ReviewsView.tsx index a705bc80a..d4829f0ad 100644 --- a/src/plugins/reviewDB/components/ReviewsView.tsx +++ b/src/plugins/reviewDB/components/ReviewsView.tsx @@ -17,7 +17,7 @@ */ import { useAwaiter, useForceUpdater } from "@utils/react"; -import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; +import { findByProps, findComponentByCode } from "@webpack"; import { Forms, React, RelationshipStore, useRef, UserStore } from "@webpack/common"; import { Auth, authorize } from "../auth"; @@ -28,11 +28,11 @@ import { cl, showToast } from "../utils"; import ReviewComponent from "./ReviewComponent"; -const { Editor, Transforms } = findByPropsLazy("Editor", "Transforms"); -const { ChatInputTypes } = findByPropsLazy("ChatInputTypes"); +const { Editor, Transforms } = findByProps("Editor", "Transforms"); +const { ChatInputTypes } = findByProps("ChatInputTypes"); -const InputComponent = findComponentByCodeLazy("default.CHANNEL_TEXT_AREA", "input"); -const { createChannelRecordFromServer } = findByPropsLazy("createChannelRecordFromServer"); +const InputComponent = findComponentByCode("default.CHANNEL_TEXT_AREA", "input"); +const { createChannelRecordFromServer } = findByProps("createChannelRecordFromServer"); interface UserProps { discordId: string; diff --git a/src/plugins/searchReply/index.tsx b/src/plugins/searchReply/index.tsx index 35b197874..354eba80b 100644 --- a/src/plugins/searchReply/index.tsx +++ b/src/plugins/searchReply/index.tsx @@ -20,12 +20,12 @@ import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/Co import { ReplyIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { ChannelStore, i18n, Menu, PermissionsBits, PermissionStore, SelectedChannelStore } from "@webpack/common"; import { Message } from "discord-types/general"; -const messageUtils = findByPropsLazy("replyToMessage"); +const messageUtils = findByProps("replyToMessage"); const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { message }: { message: Message; }) => { // make sure the message is in the selected channel diff --git a/src/plugins/serverProfile/GuildProfileModal.tsx b/src/plugins/serverProfile/GuildProfileModal.tsx index 8e6f60518..0621df8db 100644 --- a/src/plugins/serverProfile/GuildProfileModal.tsx +++ b/src/plugins/serverProfile/GuildProfileModal.tsx @@ -11,12 +11,12 @@ import { openImageModal, openUserProfile } from "@utils/discord"; import { classes } from "@utils/misc"; import { ModalRoot, ModalSize, openModal } from "@utils/modal"; import { useAwaiter } from "@utils/react"; -import { findByPropsLazy, findExportedComponentLazy } from "@webpack"; +import { findByProps,findExportedComponent } from "@webpack"; import { FluxDispatcher, Forms, GuildChannelStore, GuildMemberStore, GuildStore, IconUtils, Parser, PresenceStore, RelationshipStore, ScrollerThin, SnowflakeUtils, TabBar, Timestamp, useEffect, UserStore, UserUtils, useState, useStateFromStores } from "@webpack/common"; import { Guild, User } from "discord-types/general"; -const IconClasses = findByPropsLazy("icon", "acronym", "childWrapper"); -const FriendRow = findExportedComponentLazy("FriendRow"); +const IconClasses = findByProps("icon", "acronym", "childWrapper"); +const FriendRow = findExportedComponent("FriendRow"); const cl = classNameFactory("vc-gp-"); diff --git a/src/plugins/shikiCodeblocks.desktop/previewExample.tsx b/src/plugins/shikiCodeblocks.desktop/previewExample.tsx index 508153b4b..48853ec8c 100644 --- a/src/plugins/shikiCodeblocks.desktop/previewExample.tsx +++ b/src/plugins/shikiCodeblocks.desktop/previewExample.tsx @@ -4,7 +4,7 @@ import React from "react"; const handleClick = async () => console.log((await import("@webpack/common")).Clipboard.copy("\u200b")); -export const Example: React.FC<{ +export const Example: React.ComponentType<{ real: boolean, shigged?: number, }> = ({ real, shigged }) => <> diff --git a/src/plugins/showConnections/VerifiedIcon.tsx b/src/plugins/showConnections/VerifiedIcon.tsx index ffdf21e63..ec435b438 100644 --- a/src/plugins/showConnections/VerifiedIcon.tsx +++ b/src/plugins/showConnections/VerifiedIcon.tsx @@ -16,11 +16,11 @@ * along with this program. If not, see . */ -import { findComponentByCodeLazy, findLazy } from "@webpack"; +import { find, findComponentByCode } from "@webpack"; import { i18n, useToken } from "@webpack/common"; -const ColorMap = findLazy(m => m.colors?.INTERACTIVE_MUTED?.css); -const VerifiedIconComponent = findComponentByCodeLazy(".CONNECTIONS_ROLE_OFFICIAL_ICON_TOOLTIP"); +const ColorMap = find(m => m.colors?.INTERACTIVE_MUTED?.css); +const VerifiedIconComponent = findComponentByCode(".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 d70c09315..cc26725a3 100644 --- a/src/plugins/showConnections/index.tsx +++ b/src/plugins/showConnections/index.tsx @@ -25,17 +25,17 @@ import { CopyIcon, LinkIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; import { copyWithToast } from "@utils/misc"; import definePlugin, { OptionType } from "@utils/types"; -import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack"; +import { findByCode, findByProps, findComponentByCode, findStore } from "@webpack"; import { Text, Tooltip, UserProfileStore } from "@webpack/common"; import { User } from "discord-types/general"; import { VerifiedIcon } from "./VerifiedIcon"; -const Section = findComponentByCodeLazy(".lastSection", "children:"); -const ThemeStore = findStoreLazy("ThemeStore"); -const platformHooks: { useLegacyPlatformType(platform: string): string; } = findByPropsLazy("useLegacyPlatformType"); -const platforms: { get(type: string): ConnectionPlatform; } = findByPropsLazy("isSupported", "getByUrl"); -const getProfileThemeProps = findByCodeLazy(".getPreviewThemeColors", "primaryColor:"); +const Section = findComponentByCode(".lastSection", "children:"); +const ThemeStore = findStore("ThemeStore"); +const platformHooks: { useLegacyPlatformType(platform: string): string; } = findByProps("useLegacyPlatformType"); +const platforms: { get(type: string): ConnectionPlatform; } = findByProps("isSupported", "getByUrl"); +const getProfileThemeProps = findByCode(".getPreviewThemeColors", "primaryColor:"); const enum Spacing { COMPACT, diff --git a/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx b/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx index a8f5735e7..e7d7fdb6b 100644 --- a/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx +++ b/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx @@ -19,7 +19,7 @@ import { Settings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { formatDuration } from "@utils/text"; -import { findByPropsLazy, findComponentByCodeLazy, findComponentLazy } from "@webpack"; +import { findByProps,findComponent, findComponentByCode } from "@webpack"; import { EmojiStore, FluxDispatcher, GuildMemberStore, GuildStore, Parser, PermissionsBits, PermissionStore, SnowflakeUtils, Text, Timestamp, Tooltip, useEffect, useState } from "@webpack/common"; import type { Channel } from "discord-types/general"; @@ -78,10 +78,10 @@ const enum ChannelFlags { } -const ChatScrollClasses = findByPropsLazy("auto", "content", "scrollerBase"); -const ChatClasses = findByPropsLazy("chat", "content", "noChat", "chatContent"); -const ChannelBeginHeader = findComponentByCodeLazy(".Messages.ROLE_REQUIRED_SINGLE_USER_MESSAGE"); -const TagComponent = findComponentLazy(m => { +const ChatScrollClasses = findByProps("auto", "content", "scrollerBase"); +const ChatClasses = findByProps("chat", "content", "noChat", "chatContent"); +const ChannelBeginHeader = findComponentByCode(".Messages.ROLE_REQUIRED_SINGLE_USER_MESSAGE"); +const TagComponent = findComponent(m => { if (typeof m !== "function") return false; const code = Function.prototype.toString.call(m); @@ -89,8 +89,8 @@ const TagComponent = findComponentLazy(m => { return code.includes(".Messages.FORUM_TAG_A11Y_FILTER_BY_TAG") && !code.includes("increasedActivityPill"); }); -const EmojiParser = findByPropsLazy("convertSurrogateToName"); -const EmojiUtils = findByPropsLazy("getURL", "getEmojiColors"); +const EmojiParser = findByProps("convertSurrogateToName"); +const EmojiUtils = findByProps("getURL", "getEmojiColors"); const ChannelTypesToChannelNames = { [ChannelTypes.GUILD_TEXT]: "text", diff --git a/src/plugins/showHiddenChannels/index.tsx b/src/plugins/showHiddenChannels/index.tsx index 09aa2302a..be9ea3aff 100644 --- a/src/plugins/showHiddenChannels/index.tsx +++ b/src/plugins/showHiddenChannels/index.tsx @@ -23,13 +23,13 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import { canonicalizeMatch } from "@utils/patches"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { ChannelStore, PermissionsBits, PermissionStore, Tooltip } from "@webpack/common"; import type { Channel, Role } from "discord-types/general"; import HiddenChannelLockScreen from "./components/HiddenChannelLockScreen"; -const ChannelListClasses = findByPropsLazy("modeMuted", "modeSelected", "unread", "icon"); +const ChannelListClasses = findByProps("modeMuted", "modeSelected", "unread", "icon"); const enum ShowMode { LockIcon, diff --git a/src/plugins/spotifyControls/SpotifyStore.ts b/src/plugins/spotifyControls/SpotifyStore.ts index b3cd0b282..cb441f370 100644 --- a/src/plugins/spotifyControls/SpotifyStore.ts +++ b/src/plugins/spotifyControls/SpotifyStore.ts @@ -17,7 +17,7 @@ */ import { Settings } from "@api/Settings"; -import { findByProps, proxyLazyWebpack } from "@webpack"; +import { findByProps, webpackDependantLazy } from "@webpack"; import { Flux, FluxDispatcher } from "@webpack/common"; export interface Track { @@ -64,14 +64,14 @@ interface Device { type Repeat = "off" | "track" | "context"; +const SpotifySocket = findByProps("getActiveSocketAndDevice"); +const SpotifyUtils = findByProps("SpotifyAPI"); + // Don't wanna run before Flux and Dispatcher are ready! -export const SpotifyStore = proxyLazyWebpack(() => { +export const SpotifyStore = webpackDependantLazy(() => { // For some reason ts hates extends Flux.Store const { Store } = Flux; - const SpotifySocket = findByProps("getActiveSocketAndDevice"); - const SpotifyUtils = findByProps("SpotifyAPI"); - const API_BASE = "https://api.spotify.com/v1/me/player"; class SpotifyStore extends Store { diff --git a/src/plugins/spotifyShareCommands/index.ts b/src/plugins/spotifyShareCommands/index.ts index a3b82dc20..bdb7dfa7f 100644 --- a/src/plugins/spotifyShareCommands/index.ts +++ b/src/plugins/spotifyShareCommands/index.ts @@ -19,7 +19,7 @@ import { ApplicationCommandInputType, sendBotMessage } from "@api/Commands"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { FluxDispatcher, MessageActions } from "@webpack/common"; interface Album { @@ -52,8 +52,8 @@ interface Track { name: string; } -const Spotify = findByPropsLazy("getPlayerState"); -const PendingReplyStore = findByPropsLazy("getPendingReply"); +const Spotify = findByProps("getPlayerState"); +const PendingReplyStore = findByProps("getPendingReply"); function sendMessage(channelId, message) { message = { diff --git a/src/plugins/startupTimings/StartupTimingPage.tsx b/src/plugins/startupTimings/StartupTimingPage.tsx index c8cf51da2..d70e44ff2 100644 --- a/src/plugins/startupTimings/StartupTimingPage.tsx +++ b/src/plugins/startupTimings/StartupTimingPage.tsx @@ -18,7 +18,7 @@ import ErrorBoundary from "@components/ErrorBoundary"; import { Flex } from "@components/Flex"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { Forms, React } from "@webpack/common"; interface AppStartPerformance { @@ -45,7 +45,7 @@ interface Log { delta?: number; } -const AppStartPerformance = findByPropsLazy("markWithDelta", "markAndLog", "markAt") as AppStartPerformance; +const AppStartPerformance = findByProps("markWithDelta", "markAndLog", "markAt") as AppStartPerformance; interface TimerItemProps extends Log { instance: { diff --git a/src/plugins/typingIndicator/index.tsx b/src/plugins/typingIndicator/index.tsx index 6df3f4b80..858f18699 100644 --- a/src/plugins/typingIndicator/index.tsx +++ b/src/plugins/typingIndicator/index.tsx @@ -22,16 +22,16 @@ import { definePluginSettings, Settings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findComponentByCodeLazy, findExportedComponentLazy, findStoreLazy } from "@webpack"; +import { findComponentByCode, findExportedComponent, findStore } from "@webpack"; import { ChannelStore, GuildMemberStore, i18n, RelationshipStore, SelectedChannelStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common"; import { buildSeveralUsers } from "../typingTweaks"; -const ThreeDots = findExportedComponentLazy("Dots", "AnimatedDots"); -const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers"); +const ThreeDots = findExportedComponent("Dots", "AnimatedDots"); +const UserSummaryItem = findComponentByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers"); -const TypingStore = findStoreLazy("TypingStore"); -const UserGuildSettingsStore = findStoreLazy("UserGuildSettingsStore"); +const TypingStore = findStore("TypingStore"); +const UserGuildSettingsStore = findStore("UserGuildSettingsStore"); const enum IndicatorMode { Dots = 1 << 0, diff --git a/src/plugins/userVoiceShow/components/VoiceChannelSection.tsx b/src/plugins/userVoiceShow/components/VoiceChannelSection.tsx index c1bcbd657..c7f9bdd8a 100644 --- a/src/plugins/userVoiceShow/components/VoiceChannelSection.tsx +++ b/src/plugins/userVoiceShow/components/VoiceChannelSection.tsx @@ -18,12 +18,12 @@ import "./VoiceChannelSection.css"; -import { findByCodeLazy, findByPropsLazy } from "@webpack"; +import { findByCode,findByProps } from "@webpack"; import { Button, Forms, PermissionStore, Toasts } from "@webpack/common"; import { Channel } from "discord-types/general"; -const ChannelActions = findByPropsLazy("selectChannel", "selectVoiceChannel"); -const UserPopoutSection = findByCodeLazy(".lastSection", "children:"); +const ChannelActions = findByProps("selectChannel", "selectVoiceChannel"); +const UserPopoutSection = findByCode(".lastSection", "children:"); const CONNECT = 1n << 20n; diff --git a/src/plugins/userVoiceShow/index.tsx b/src/plugins/userVoiceShow/index.tsx index feba28316..090a4fe4e 100644 --- a/src/plugins/userVoiceShow/index.tsx +++ b/src/plugins/userVoiceShow/index.tsx @@ -20,13 +20,13 @@ import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findStoreLazy } from "@webpack"; +import { findStore } from "@webpack"; import { ChannelStore, GuildStore, UserStore } from "@webpack/common"; import { User } from "discord-types/general"; import { VoiceChannelSection } from "./components/VoiceChannelSection"; -const VoiceStateStore = findStoreLazy("VoiceStateStore"); +const VoiceStateStore = findStore("VoiceStateStore"); const settings = definePluginSettings({ showInUserProfileModal: { diff --git a/src/plugins/vcNarrator/index.tsx b/src/plugins/vcNarrator/index.tsx index ac629e749..be6c093a5 100644 --- a/src/plugins/vcNarrator/index.tsx +++ b/src/plugins/vcNarrator/index.tsx @@ -23,7 +23,7 @@ import { Logger } from "@utils/Logger"; import { Margins } from "@utils/margins"; import { wordsToTitle } from "@utils/text"; import definePlugin, { OptionType, PluginOptionsItem } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { Button, ChannelStore, Forms, GuildMemberStore, SelectedChannelStore, SelectedGuildStore, useMemo, UserStore } from "@webpack/common"; interface VoiceState { @@ -36,7 +36,7 @@ interface VoiceState { selfMute: boolean; } -const VoiceStateStore = findByPropsLazy("getVoiceStatesForChannel", "getCurrentClientVoiceChannelId"); +const VoiceStateStore = findByProps("getVoiceStatesForChannel", "getCurrentClientVoiceChannelId"); // Mute/Deaf for other people than you is commented out, because otherwise someone can spam it and it will be annoying // Filtering out events is not as simple as just dropping duplicates, as otherwise mute, unmute, mute would diff --git a/src/plugins/vencordToolbox/index.tsx b/src/plugins/vencordToolbox/index.tsx index 00805fbd3..b894845e5 100644 --- a/src/plugins/vencordToolbox/index.tsx +++ b/src/plugins/vencordToolbox/index.tsx @@ -23,11 +23,11 @@ import { Settings, useSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { findExportedComponentLazy } from "@webpack"; +import { findExportedComponent } from "@webpack"; import { Menu, Popout, useState } from "@webpack/common"; import type { ReactNode } from "react"; -const HeaderBarIcon = findExportedComponentLazy("Icon", "Divider"); +const HeaderBarIcon = findExportedComponent("Icon", "Divider"); function VencordPopout(onClose: () => void) { const { useQuickCss } = useSettings(["useQuickCss"]); diff --git a/src/plugins/voiceMessages/VoicePreview.tsx b/src/plugins/voiceMessages/VoicePreview.tsx index 9c77d8329..24cf6359a 100644 --- a/src/plugins/voiceMessages/VoicePreview.tsx +++ b/src/plugins/voiceMessages/VoicePreview.tsx @@ -17,7 +17,7 @@ */ import { useTimer } from "@utils/react"; -import { findComponentByCodeLazy } from "@webpack"; +import { findComponentByCode } from "@webpack"; import { cl } from "./utils"; @@ -25,7 +25,7 @@ interface VoiceMessageProps { src: string; waveform: string; } -const VoiceMessage = findComponentByCodeLazy("waveform:", "onVolumeChange"); +const VoiceMessage = findComponentByCode("waveform:", "onVolumeChange"); export type VoicePreviewOptions = { src?: string; diff --git a/src/plugins/voiceMessages/index.tsx b/src/plugins/voiceMessages/index.tsx index 2f232f341..c67ccc333 100644 --- a/src/plugins/voiceMessages/index.tsx +++ b/src/plugins/voiceMessages/index.tsx @@ -27,7 +27,7 @@ import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModa import { useAwaiter } from "@utils/react"; import definePlugin from "@utils/types"; import { chooseFile } from "@utils/web"; -import { findByPropsLazy, findStoreLazy } from "@webpack"; +import { findByProps, findStore } from "@webpack"; import { Button, Card, FluxDispatcher, Forms, lodash, Menu, MessageActions, PermissionsBits, PermissionStore, RestAPI, SelectedChannelStore, showToast, SnowflakeUtils, Toasts, useEffect, useState } from "@webpack/common"; import { ComponentType } from "react"; @@ -37,9 +37,9 @@ import { cl } from "./utils"; import { VoicePreview } from "./VoicePreview"; import { VoiceRecorderWeb } from "./WebRecorder"; -const CloudUtils = findByPropsLazy("CloudUpload"); -const PendingReplyStore = findStoreLazy("PendingReplyStore"); -const OptionClasses = findByPropsLazy("optionName", "optionIcon", "optionLabel"); +const CloudUtils = findByProps("CloudUpload"); +const PendingReplyStore = findStore("PendingReplyStore"); +const OptionClasses = findByProps("optionName", "optionIcon", "optionLabel"); export type VoiceRecorder = ComponentType<{ setAudioBlob(blob: Blob): void; diff --git a/src/plugins/webContextMenus.web/index.ts b/src/plugins/webContextMenus.web/index.ts index ac4689036..e68592379 100644 --- a/src/plugins/webContextMenus.web/index.ts +++ b/src/plugins/webContextMenus.web/index.ts @@ -20,10 +20,10 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { saveFile } from "@utils/web"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { Clipboard, ComponentDispatch } from "@webpack/common"; -const ctxMenuCallbacks = findByPropsLazy("contextMenuCallbackNative"); +const ctxMenuCallbacks = findByProps("contextMenuCallbackNative"); async function fetchImage(url: string) { const res = await fetch(url); diff --git a/src/plugins/webKeybinds.web/index.ts b/src/plugins/webKeybinds.web/index.ts index 12d485aac..d229dcd99 100644 --- a/src/plugins/webKeybinds.web/index.ts +++ b/src/plugins/webKeybinds.web/index.ts @@ -18,10 +18,10 @@ import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import { ComponentDispatch, FluxDispatcher, NavigationRouter, SelectedGuildStore, SettingsRouter } from "@webpack/common"; -const KeyBinds = findByPropsLazy("JUMP_TO_GUILD", "SERVER_NEXT"); +const KeyBinds = findByProps("JUMP_TO_GUILD", "SERVER_NEXT"); export default definePlugin({ name: "WebKeybinds", diff --git a/src/plugins/whoReacted/index.tsx b/src/plugins/whoReacted/index.tsx index b3728c215..a4f3248ba 100644 --- a/src/plugins/whoReacted/index.tsx +++ b/src/plugins/whoReacted/index.tsx @@ -22,14 +22,15 @@ import { sleep } from "@utils/misc"; import { Queue } from "@utils/Queue"; import { useForceUpdater } from "@utils/react"; import definePlugin from "@utils/types"; -import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; +import { findByProps, findComponentByCode } 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 = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers"); -const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar"); +const UserSummaryItem = findComponentByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers"); +const AvatarStyles = findByProps("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar"); let Scroll: any = null; + const queue = new Queue(); let reactions: Record; diff --git a/src/plugins/xsOverlay.desktop/index.ts b/src/plugins/xsOverlay.desktop/index.ts index 763f6a782..fbda6861c 100644 --- a/src/plugins/xsOverlay.desktop/index.ts +++ b/src/plugins/xsOverlay.desktop/index.ts @@ -9,7 +9,7 @@ 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 { findByProps } from "@webpack"; import { ChannelStore, GuildStore, UserStore } from "@webpack/common"; import type { Channel, Embed, GuildMember, MessageAttachment, User } from "discord-types/general"; @@ -71,7 +71,7 @@ interface Call { ringing: string[]; } -const MuteStore = findByPropsLazy("isSuppressEveryoneEnabled"); +const MuteStore = findByProps("isSuppressEveryoneEnabled"); const XSLog = new Logger("XSOverlay"); const settings = definePluginSettings({ diff --git a/src/utils/discord.tsx b/src/utils/discord.tsx index 74e1aefe8..0cbf3ce72 100644 --- a/src/utils/discord.tsx +++ b/src/utils/discord.tsx @@ -20,7 +20,7 @@ import { MessageObject } from "@api/MessageEvents"; import { ChannelStore, ComponentDispatch, FluxDispatcher, GuildStore, InviteActions, MaskedLink, MessageActions, ModalImageClasses, PrivateChannelsStore, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common"; import { Guild, Message, User } from "discord-types/general"; -import { ImageModal, ModalRoot, ModalSize, openModal } from "./modal"; +import { ImageModal, ImageModalProps, ModalRoot, ModalSize, openModal } from "./modal"; /** * Open the invite modal @@ -108,7 +108,7 @@ export function sendMessage( return MessageActions.sendMessage(channelId, messageData, waitForChannelReady, extra); } -export function openImageModal(url: string, props?: Partial>): string { +export function openImageModal(url: string, props?: Partial): string { return openModal(modalProps => ( . -*/ + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ export function makeLazy(factory: () => T, attempts = 5): () => T { let tries = 0; @@ -22,8 +10,9 @@ export function makeLazy(factory: () => T, attempts = 5): () => T { return () => { if (!cache && attempts > tries++) { cache = factory(); - if (!cache && attempts === tries) - console.error("Lazy factory failed:", factory); + if (!cache && attempts === tries) { + console.error(`Lazy factory failed:\n${factory}`); + } } return cache; }; @@ -33,94 +22,81 @@ export function makeLazy(factory: () => T, attempts = 5): () => T { // will always return the function default for them. const unconfigurable = ["arguments", "caller", "prototype"]; -const handler: ProxyHandler = {}; +const handler: ProxyHandler = { + ...Object.fromEntries(Object.getOwnPropertyNames(Reflect).map(propName => + [propName, (target: any, ...args: any[]) => Reflect[propName](target[proxyLazyGet](), ...args)] + )), + ownKeys: target => { + const keys = Reflect.ownKeys(target[proxyLazyGet]()); + for (const key of unconfigurable) { + if (!keys.includes(key)) keys.push(key); + } + return keys; + }, + getOwnPropertyDescriptor: (target, p) => { + if (typeof p === "string" && unconfigurable.includes(p)) + return Reflect.getOwnPropertyDescriptor(target, p); -const kGET = Symbol.for("vencord.lazy.get"); -const kCACHE = Symbol.for("vencord.lazy.cached"); - -for (const method of [ - "apply", - "construct", - "defineProperty", - "deleteProperty", - "getOwnPropertyDescriptor", - "getPrototypeOf", - "has", - "isExtensible", - "ownKeys", - "preventExtensions", - "set", - "setPrototypeOf" -]) { - handler[method] = - (target: any, ...args: any[]) => Reflect[method](target[kGET](), ...args); -} - -handler.ownKeys = target => { - const v = target[kGET](); - const keys = Reflect.ownKeys(v); - for (const key of unconfigurable) { - if (!keys.includes(key)) keys.push(key); + const descriptor = Reflect.getOwnPropertyDescriptor(target[proxyLazyGet](), p); + if (descriptor) Object.defineProperty(target, p, descriptor); + return descriptor; } - return keys; }; -handler.getOwnPropertyDescriptor = (target, p) => { - if (typeof p === "string" && unconfigurable.includes(p)) - return Reflect.getOwnPropertyDescriptor(target, p); - - const descriptor = Reflect.getOwnPropertyDescriptor(target[kGET](), p); - - if (descriptor) Object.defineProperty(target, p, descriptor); - return descriptor; -}; +const proxyLazyGet = Symbol.for("vencord.lazy.get"); +const proxyLazyCache = Symbol.for("vencord.lazy.cached"); /** - * 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); + * Wraps the result of factory in a Proxy you can consume as if it wasn't lazy. + * On first property access, the factory is evaluated + * @param factory Factory returning the result + * @param attempts How many times to try to evaluate the factory before giving up + * @returns Result of factory function */ -export function proxyLazy(factory: () => T, attempts = 5, isChild = false): T { - let isSameTick = true; - if (!isChild) - setTimeout(() => isSameTick = false, 0); +export function proxyLazy(factory: () => T, attempts = 5, isChild = false): T { + const get = makeLazy(factory, attempts); - let tries = 0; + let isSameTick = true; + if (!isChild) setTimeout(() => isSameTick = false, 0); + + let failed = false; const proxyDummy = Object.assign(function () { }, { - [kCACHE]: void 0 as T | undefined, - [kGET]() { - if (!proxyDummy[kCACHE] && attempts > tries++) { - proxyDummy[kCACHE] = factory(); - if (!proxyDummy[kCACHE] && attempts === tries) - console.error("Lazy factory failed:", factory); + [proxyLazyGet]() { + if (!proxyDummy[proxyLazyCache] && !failed) { + proxyDummy[proxyLazyCache] = get(); + + if (!proxyDummy[proxyLazyCache]) { + failed = true; + throw new Error(`proxyLazy factory failed:\n${factory}`); + } } - return proxyDummy[kCACHE]; - } + + return proxyDummy[proxyLazyCache]; + }, + [proxyLazyCache]: void 0 as T | undefined }); return new Proxy(proxyDummy, { ...handler, get(target, p, receiver) { - // if we're still in the same tick, it means the lazy was immediately used. + // 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) + if (!isChild && isSameTick) { return proxyLazy( - () => Reflect.get(target[kGET](), p, receiver), + () => Reflect.get(target[proxyLazyGet](), p, receiver), attempts, true ); - const lazyTarget = target[kGET](); + } + + const lazyTarget = target[proxyLazyGet](); if (typeof lazyTarget === "object" || typeof lazyTarget === "function") { return Reflect.get(lazyTarget, p, receiver); } + throw new Error("proxyLazy called on a primitive value"); } - }) as any; + }) as T; } diff --git a/src/utils/lazyReact.tsx b/src/utils/lazyReact.tsx index 4896a0581..0e929aa98 100644 --- a/src/utils/lazyReact.tsx +++ b/src/utils/lazyReact.tsx @@ -8,22 +8,32 @@ import { ComponentType } from "react"; import { makeLazy } from "./lazy"; -const NoopComponent = () => null; +export const NoopComponent = () => null; /** * A lazy component. The factory method is called on first render. - * @param factory Function returning a Component + * @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); + + let failed = false; const LazyComponent = (props: T) => { - const Component = get() ?? NoopComponent; + const Component = get() ?? (() => { + if (!failed) { + failed = true; + console.error(`LazyComponent factory failed:\n${factory}`); + } + + return NoopComponent; + })(); + return ; }; - LazyComponent.$$vencordInternal = get; + LazyComponent.$$vencordGetter = get; return LazyComponent as ComponentType; } diff --git a/src/utils/modal.tsx b/src/utils/modal.tsx index b4d0f59fb..4c9962563 100644 --- a/src/utils/modal.tsx +++ b/src/utils/modal.tsx @@ -16,10 +16,9 @@ * along with this program. If not, see . */ -import { findByPropsLazy, findExportedComponentLazy } from "@webpack"; +import { filters, find, findByProps, findExportedComponent } from "@webpack"; import type { ComponentType, PropsWithChildren, ReactNode, Ref } from "react"; -import { LazyComponent } from "./react"; export const enum ModalSize { SMALL = "small", @@ -49,7 +48,7 @@ export interface ModalOptions { type RenderFunction = (props: ModalProps) => ReactNode; -export const Modals = findByPropsLazy("ModalRoot", "ModalCloseButton") as { +type Modals = { ModalRoot: ComponentType; }; -export type ImageModal = ComponentType<{ + +export let ModalRoot: Modals["ModalRoot"]; +export let ModalHeader: Modals["ModalHeader"]; +export let ModalContent: Modals["ModalContent"]; +export let ModalFooter: Modals["ModalFooter"]; +export let ModalCloseButton: Modals["ModalCloseButton"]; + +export const Modals = find(filters.byProps("ModalRoot", "ModalCloseButton"), m => { + ({ ModalRoot, ModalHeader, ModalContent, ModalFooter, ModalCloseButton } = m); + return m; +}); + +export type ImageModalProps = { className?: string; src: string; placeholder: string; @@ -116,17 +127,11 @@ export type ImageModal = ComponentType<{ shouldAnimate?: boolean; onClose?(): void; shouldHideMediaOptions?: boolean; -}>; +}; -export const ImageModal = findExportedComponentLazy("ImageModal") as ImageModal; +export const ImageModal = findExportedComponent("ImageModal"); -export const ModalRoot = LazyComponent(() => Modals.ModalRoot); -export const ModalHeader = LazyComponent(() => Modals.ModalHeader); -export const ModalContent = LazyComponent(() => Modals.ModalContent); -export const ModalFooter = LazyComponent(() => Modals.ModalFooter); -export const ModalCloseButton = LazyComponent(() => Modals.ModalCloseButton); - -const ModalAPI = findByPropsLazy("openModalLazy"); +const ModalAPI = findByProps("openModalLazy"); /** * Wait for the render promise to resolve, then open a modal with it. diff --git a/src/utils/proxyInner.ts b/src/utils/proxyInner.ts new file mode 100644 index 000000000..7f818d0d5 --- /dev/null +++ b/src/utils/proxyInner.ts @@ -0,0 +1,91 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +export const proxyInnerGet = Symbol.for("vencord.proxyInner.get"); +export const proxyInnerValue = Symbol.for("vencord.proxyInner.innerValue"); + +// Proxies demand that these properties be unmodified, so proxyInner +// will always return the function default for them. +const unconfigurable = ["arguments", "caller", "prototype"]; + +const handler: ProxyHandler = { + ...Object.fromEntries(Object.getOwnPropertyNames(Reflect).map(propName => + [propName, (target: any, ...args: any[]) => Reflect[propName](target[proxyInnerGet](), ...args)] + )), + ownKeys: target => { + const keys = Reflect.ownKeys(target[proxyInnerGet]()); + for (const key of unconfigurable) { + if (!keys.includes(key)) keys.push(key); + } + return keys; + }, + getOwnPropertyDescriptor: (target, p) => { + if (typeof p === "string" && unconfigurable.includes(p)) + return Reflect.getOwnPropertyDescriptor(target, p); + + const descriptor = Reflect.getOwnPropertyDescriptor(target[proxyInnerGet](), p); + if (descriptor) Object.defineProperty(target, p, descriptor); + return descriptor; + } +}; + +/** + * A proxy which has an inner value that can be set later. + * When a property is accessed, the proxy looks for the property value in its inner value, and errors if it's not set. + * @param err The error to throw when the inner value is not set + * @returns A proxy which will act like the inner value when accessed + */ +export function proxyInner(err = new Error("Proxy inner value is undefined, setInnerValue was never called."), isChild = false): [proxy: T, setInnerValue: (innerValue: T) => void] { + let isSameTick = true; + if (!isChild) setTimeout(() => isSameTick = false, 0); + + const proxyDummy = Object.assign(function () { }, { + [proxyInnerGet]: function () { + if (proxyDummy[proxyInnerValue] == null) { + throw err; + } + + return proxyDummy[proxyInnerValue]; + }, + [proxyInnerValue]: void 0 as T | undefined + }); + + const recursiveSetInnerValues = [] as Array<(innerValue: T) => void>; + + function setInnerValue(innerValue: T) { + proxyDummy[proxyInnerValue] = innerValue; + recursiveSetInnerValues.forEach(setInnerValue => setInnerValue(innerValue)); + } + + return [new Proxy(proxyDummy, { + ...handler, + get(target, p, receiver) { + if (p === proxyInnerValue) return target[proxyInnerValue]; + if (p === proxyInnerGet) return target[proxyInnerGet]; + + // If we're still in the same tick, it means the proxy was immediately used. + // thus, we proxy the get access to make things like destructuring work as expected + // meow here will also be a proxy + // `const { meow } = findByProps("meow");` + if (!isChild && isSameTick) { + const [recursiveProxy, recursiveSetInnerValue] = proxyInner(err, true); + recursiveSetInnerValues.push((innerValue: T) => { + recursiveSetInnerValue(Reflect.get(innerValue as object, p, receiver)); + }); + + return recursiveProxy; + } + + const innerTarget = target[proxyInnerGet](); + if (typeof innerTarget === "object" || typeof innerTarget === "function") { + return Reflect.get(innerTarget, p, receiver); + } + + throw new Error("proxyInner called on a primitive value"); + + } + }) as T, setInnerValue]; +} diff --git a/src/webpack/common/classes.ts b/src/webpack/common/classes.ts index ca3d75f57..7a9eee949 100644 --- a/src/webpack/common/classes.ts +++ b/src/webpack/common/classes.ts @@ -16,9 +16,9 @@ * along with this program. If not, see . */ -import { findByPropsLazy, findLazy } from "@webpack"; +import { find, findByProps } from "@webpack"; import * as t from "./types/classes"; -export const ModalImageClasses: t.ImageModalClasses = findLazy(m => m.image && m.modal && !m.applicationIcon); -export const ButtonWrapperClasses: t.ButtonWrapperClasses = findByPropsLazy("buttonWrapper", "buttonContent"); +export const ModalImageClasses: t.ImageModalClasses = find(m => m.image && m.modal && !m.applicationIcon); +export const ButtonWrapperClasses: t.ButtonWrapperClasses = findByProps("buttonWrapper", "buttonContent"); diff --git a/src/webpack/common/components.ts b/src/webpack/common/components.ts index 46f843ce6..cdf12d997 100644 --- a/src/webpack/common/components.ts +++ b/src/webpack/common/components.ts @@ -17,9 +17,8 @@ */ // eslint-disable-next-line path-alias/no-relative -import { filters, findByPropsLazy, waitFor } from "@webpack"; +import { filters, findComponent, findExportedComponent, waitFor } from "@webpack"; -import { waitForComponent } from "./internal"; import * as t from "./types/components"; export let Forms = {} as { @@ -53,13 +52,14 @@ export let FocusLock: t.FocusLock; /** css colour resolver stuff, no clue what exactly this does, just copied usage from Discord */ export let useToken: t.useToken; -export const MaskedLink = waitForComponent("MaskedLink", filters.componentByCode("MASKED_LINK)")); -export const Timestamp = waitForComponent("Timestamp", filters.byCode(".Messages.MESSAGE_EDITED_TIMESTAMP_A11Y_LABEL.format")); -export const Flex = waitForComponent("Flex", ["Justify", "Align", "Wrap"]); +export const MaskedLink = findComponent(filters.componentByCode("MASKED_LINK)")); +export const Timestamp = findComponent(filters.componentByCode(".Messages.MESSAGE_EDITED_TIMESTAMP_A11Y_LABEL.format")); +export const Flex = findComponent(filters.byProps("Justify", "Align", "Wrap")); -export const { OAuth2AuthorizeModal } = findByPropsLazy("OAuth2AuthorizeModal"); +export const OAuth2AuthorizeModal = findExportedComponent("OAuth2AuthorizeModal"); -waitFor(["FormItem", "Button"], m => { +waitFor(filters.byProps("FormItem", "Button"), m => { + Forms = m; ({ useToken, Card, @@ -83,5 +83,4 @@ waitFor(["FormItem", "Button"], m => { FocusLock, Heading } = m); - Forms = m; }); diff --git a/src/webpack/common/internal.tsx b/src/webpack/common/internal.tsx deleted file mode 100644 index 9a89af362..000000000 --- a/src/webpack/common/internal.tsx +++ /dev/null @@ -1,44 +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 { LazyComponent } from "@utils/react"; - -// eslint-disable-next-line path-alias/no-relative -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; - - const lazyComponent = LazyComponent(() => myValue) as T; - waitFor(filter, (v: any) => { - myValue = v; - Object.assign(lazyComponent, v); - }, { isIndirect: true }); - - return lazyComponent; -} - -export function waitForStore(name: string, cb: (v: any) => void) { - if (IS_DEV) lazyWebpackSearchHistory.push(["waitForStore", [name]]); - - waitFor(filters.byStoreName(name), cb, { isIndirect: true }); -} diff --git a/src/webpack/common/menu.ts b/src/webpack/common/menu.ts index 9a4a3a799..a722cf242 100644 --- a/src/webpack/common/menu.ts +++ b/src/webpack/common/menu.ts @@ -17,12 +17,10 @@ */ // eslint-disable-next-line path-alias/no-relative -import { findByPropsLazy, waitFor } from "../webpack"; +import { findByProps } from "../webpack"; import type * as t from "./types/menu"; -export let Menu = {} as t.Menu; +export const Menu = findByProps("MenuItem", "MenuSliderControl"); -waitFor(["MenuItem", "MenuSliderControl"], m => Menu = m); - -export const ContextMenuApi: t.ContextMenuApi = findByPropsLazy("closeContextMenu", "openContextMenu"); +export const ContextMenuApi = findByProps("closeContextMenu", "openContextMenu"); diff --git a/src/webpack/common/react.ts b/src/webpack/common/react.ts index 17196132d..f71517f2c 100644 --- a/src/webpack/common/react.ts +++ b/src/webpack/common/react.ts @@ -17,7 +17,7 @@ */ // eslint-disable-next-line path-alias/no-relative -import { findByPropsLazy, waitFor } from "../webpack"; +import { filters, findByProps,waitFor } from "../webpack"; export let React: typeof import("react"); export let useState: typeof React.useState; @@ -27,9 +27,9 @@ export let useRef: typeof React.useRef; export let useReducer: typeof React.useReducer; export let useCallback: typeof React.useCallback; -export const ReactDOM: typeof import("react-dom") & typeof import("react-dom/client") = findByPropsLazy("createPortal", "render"); +export const ReactDOM = findByProps("createPortal", "render"); -waitFor("useState", m => { +waitFor(filters.byProps("useState"), m => { React = m; ({ useEffect, useState, useMemo, useRef, useReducer, useCallback } = React); }); diff --git a/src/webpack/common/settingsStores.ts b/src/webpack/common/settingsStores.ts index 4a48efda6..e4da26b31 100644 --- a/src/webpack/common/settingsStores.ts +++ b/src/webpack/common/settingsStores.ts @@ -4,12 +4,12 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { findByPropsLazy } from "@webpack"; +import { findByProps } from "@webpack"; import * as t from "./types/settingsStores"; -export const TextAndImagesSettingsStores = findByPropsLazy("MessageDisplayCompact") as Record; -export const StatusSettingsStores = findByPropsLazy("ShowCurrentGame") as Record; +export const TextAndImagesSettingsStores = findByProps("MessageDisplayCompact") as Record; +export const StatusSettingsStores = findByProps("ShowCurrentGame") as Record; -export const UserSettingsActionCreators = findByPropsLazy("PreloadedUserSettingsActionCreators"); +export const UserSettingsActionCreators = findByProps("PreloadedUserSettingsActionCreators"); diff --git a/src/webpack/common/stores.ts b/src/webpack/common/stores.ts index 2f9786bc5..5ab4651e8 100644 --- a/src/webpack/common/stores.ts +++ b/src/webpack/common/stores.ts @@ -19,11 +19,10 @@ import type * as Stores from "discord-types/stores"; // eslint-disable-next-line path-alias/no-relative -import { findByPropsLazy } from "../webpack"; -import { waitForStore } from "./internal"; +import { findByProps, findStore } from "../webpack"; import * as t from "./types/stores"; -export const Flux: t.Flux = findByPropsLazy("connectStores"); +export const Flux: t.Flux = findByProps("connectStores"); export type GenericStore = t.FluxStore & Record; @@ -34,32 +33,32 @@ export enum DraftType { ApplicationLauncherCommand = 3 } -export let MessageStore: Omit & { +export const MessageStore = findStore("MessageStore") as Omit & { getMessages(chanId: string): any; }; // this is not actually a FluxStore -export const PrivateChannelsStore = findByPropsLazy("openPrivateChannel"); -export let PermissionStore: GenericStore; -export let GuildChannelStore: GenericStore; -export let ReadStateStore: GenericStore; -export let PresenceStore: GenericStore; +export const PrivateChannelsStore = findByProps("openPrivateChannel"); +export const PermissionStore: GenericStore = findStore("PermissionStore"); +export const GuildChannelStore: GenericStore = findStore("GuildChannelStore"); +export const ReadStateStore: GenericStore = findStore("ReadStateStore"); +export const PresenceStore: GenericStore = findStore("PresenceStore"); -export let GuildStore: t.GuildStore; -export let UserStore: Stores.UserStore & t.FluxStore; -export let UserProfileStore: GenericStore; -export let SelectedChannelStore: Stores.SelectedChannelStore & t.FluxStore; -export let SelectedGuildStore: t.FluxStore & Record; -export let ChannelStore: Stores.ChannelStore & t.FluxStore; -export let GuildMemberStore: Stores.GuildMemberStore & t.FluxStore; -export let RelationshipStore: Stores.RelationshipStore & t.FluxStore & { +export const GuildStore: t.GuildStore = findStore("GuildStore"); +export const UserStore: Stores.UserStore & t.FluxStore = findStore("UserStore"); +export const UserProfileStore: GenericStore = findStore("UserProfileStore"); +export const SelectedChannelStore: Stores.SelectedChannelStore & t.FluxStore = findStore("SelectedChannelStore"); +export const SelectedGuildStore: t.FluxStore & Record = findStore("SelectedGuildStore"); +export const ChannelStore: Stores.ChannelStore & t.FluxStore = findStore("ChannelStore"); +export const GuildMemberStore: Stores.GuildMemberStore & t.FluxStore = findStore("GuildMemberStore"); +export const RelationshipStore = findStore("RelationshipStore") as Stores.RelationshipStore & t.FluxStore & { /** Get the date (as a string) that the relationship was created */ getSince(userId: string): string; }; -export let EmojiStore: t.EmojiStore; -export let WindowStore: t.WindowStore; -export let DraftStore: t.DraftStore; +export const EmojiStore: t.EmojiStore = findStore("EmojiStore"); +export const WindowStore: t.WindowStore = findStore("WindowStore"); +export const DraftStore: t.DraftStore = findStore("DraftStore"); /** * React hook that returns stateful data for one or more stores @@ -80,21 +79,4 @@ export const { useStateFromStores }: { isEqual?: (old: T, newer: T) => boolean ) => T; } - = findByPropsLazy("useStateFromStores"); - -waitForStore("DraftStore", s => DraftStore = s); -waitForStore("UserStore", s => UserStore = s); -waitForStore("UserProfileStore", m => UserProfileStore = m); -waitForStore("ChannelStore", m => ChannelStore = m); -waitForStore("SelectedChannelStore", m => SelectedChannelStore = m); -waitForStore("SelectedGuildStore", m => SelectedGuildStore = m); -waitForStore("GuildStore", m => GuildStore = m); -waitForStore("GuildMemberStore", m => GuildMemberStore = m); -waitForStore("RelationshipStore", m => RelationshipStore = m); -waitForStore("PermissionStore", m => PermissionStore = m); -waitForStore("PresenceStore", m => PresenceStore = m); -waitForStore("ReadStateStore", m => ReadStateStore = m); -waitForStore("GuildChannelStore", m => GuildChannelStore = m); -waitForStore("MessageStore", m => MessageStore = m); -waitForStore("WindowStore", m => WindowStore = m); -waitForStore("EmojiStore", m => EmojiStore = m); + = findByProps("useStateFromStores"); diff --git a/src/webpack/common/types/components.d.ts b/src/webpack/common/types/components.d.ts index c51264370..c772c8994 100644 --- a/src/webpack/common/types/components.d.ts +++ b/src/webpack/common/types/components.d.ts @@ -153,7 +153,7 @@ export type Switch = ComponentType>; -export type Timestamp = ComponentType>; +}>; export type TextInput = ComponentType>; // TODO - type maybe idk probably not that useful other than the constants -export type Flex = ComponentType> & { +export type FlexProps = PropsWithChildren & { Align: Record<"START" | "END" | "CENTER" | "STRETCH" | "BASELINE", string>; Direction: Record<"VERTICAL" | "HORIZONTAL" | "HORIZONTAL_REVERSE", string>; Justify: Record<"START" | "END" | "CENTER" | "BETWEEN" | "AROUND", string>; @@ -399,7 +399,7 @@ export type Paginator = ComponentType<{ hideMaxPage?: boolean; }>; -export type MaskedLink = ComponentType>; +}>; export type ScrollerThin = ComponentType { - FluxDispatcher = m; +export const FluxDispatcher = find(filters.byProps("dispatch", "subscribe"), m => { // Non import call to avoid circular dependency Vencord.Plugins.subscribeAllPluginsFluxEvents(m); @@ -34,31 +31,25 @@ waitFor(["dispatch", "subscribe"], m => { _resolveReady(); }; m.subscribe("CONNECTION_OPEN", cb); + + return m; }); -export let ComponentDispatch; -waitFor(["ComponentDispatch", "ComponentDispatcher"], m => ComponentDispatch = m.ComponentDispatch); +export const { ComponentDispatch } = findByProps("ComponentDispatch", "ComponentDispatcher"); +export const RestAPI = find(filters.byProps("getAPIBaseURL"), m => m.HTTP ?? m); +export const moment = findByProps("parseTwoDigitYear"); -export const RestAPI: t.RestAPI = proxyLazyWebpack(() => { - const mod = findByProps("getAPIBaseURL"); - return mod.HTTP ?? mod; -}); -export const moment: typeof import("moment") = findByPropsLazy("parseTwoDigitYear"); +export const hljs = findByProps("highlight", "registerLanguage"); -export const hljs: typeof import("highlight.js") = findByPropsLazy("highlight", "registerLanguage"); +export const lodash = findByProps("debounce", "cloneDeep"); -export const lodash: typeof import("lodash") = findByPropsLazy("debounce", "cloneDeep"); +export const i18n = find(m => m.Messages?.["en-US"]); -export const i18n: t.i18n = findLazy(m => m.Messages?.["en-US"]); +export const SnowflakeUtils = findByProps("fromTimestamp", "extractTimestamp"); -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); +export const Parser = findByProps("parseTopic"); +export const Alerts = findByProps("show", "close"); const ToastType = { MESSAGE: 0, @@ -100,12 +91,11 @@ export const Toasts = { }; // This is the same module but this is easier -waitFor("showToast", m => { +waitFor(filters.byCode("showToast"), m => { Toasts.show = m.showToast; Toasts.pop = m.popToast; }); - /** * Show a simple toast. If you need more options, use Toasts.show manually */ @@ -117,31 +107,30 @@ export function showToast(message: string, type = ToastType.MESSAGE) { }); } -export const UserUtils = findByPropsLazy("getUser", "fetchCurrentUser") as { getUser: (id: string) => Promise; }; -export const UploadHandler = findByPropsLazy("showUploadFileSizeExceededError", "promptToUpload") as { +export const UserUtils = findByProps("getUser", "fetchCurrentUser") as { getUser: (id: string) => Promise; }; +export const UploadHandler = findByProps("showUploadFileSizeExceededError", "promptToUpload") as { promptToUpload: (files: File[], channel: Channel, draftType: Number) => void; }; -export const ApplicationAssetUtils = findByPropsLazy("fetchAssetIds", "getAssetImage") as { +export const ApplicationAssetUtils = findByProps("fetchAssetIds", "getAssetImage") as { fetchAssetIds: (applicationId: string, e: string[]) => Promise; }; -export const Clipboard: t.Clipboard = findByPropsLazy("SUPPORTS_COPY", "copy"); +export const Clipboard = findByProps("SUPPORTS_COPY", "copy"); -export const NavigationRouter: t.NavigationRouter = findByPropsLazy("transitionTo", "replaceWith", "transitionToGuild"); +export const NavigationRouter = findByProps("transitionTo", "replaceWith", "transitionToGuild"); -export let SettingsRouter: any; -waitFor(["open", "saveAccountChanges"], m => SettingsRouter = m); +export const SettingsRouter = findByProps("open", "saveAccountChanges"); -export const { Permissions: PermissionsBits } = findLazy(m => typeof m.Permissions?.ADMINISTRATOR === "bigint") as { Permissions: t.PermissionsBits; }; +export const { Permissions: PermissionsBits } = find(m => typeof m.Permissions?.ADMINISTRATOR === "bigint") as { Permissions: t.PermissionsBits; }; -export const zustandCreate: typeof import("zustand").default = findByCodeLazy("will be removed in v4"); +export const zustandCreate = findByCode("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)); +export const { persist: zustandPersist } = find(m => m.persist && persistFilter(m.persist)); -export const MessageActions = findByPropsLazy("editMessage", "sendMessage"); -export const UserProfileActions = findByPropsLazy("openUserProfileModal", "closeUserProfileModal"); -export const InviteActions = findByPropsLazy("resolveInvite"); +export const MessageActions = findByProps("editMessage", "sendMessage"); +export const UserProfileActions = findByProps("openUserProfileModal", "closeUserProfileModal"); +export const InviteActions = findByProps("resolveInvite"); -export const IconUtils: t.IconUtils = findByPropsLazy("getGuildBannerURL", "getUserAvatarURL"); +export const IconUtils = findByProps("getGuildBannerURL", "getUserAvatarURL"); diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index d2b569e8d..913db621b 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -24,7 +24,7 @@ import { WebpackInstance } from "discord-types/other"; import { traceFunction } from "../debug/Tracer"; import { patches } from "../plugins"; -import { _initWebpack, beforeInitListeners, factoryListeners, moduleListeners, subscriptions, wreq } from "."; +import { _initWebpack, beforeInitListeners, factoryListeners, moduleListeners, waitForSubscriptions, wreq } from "."; const logger = new Logger("WebpackInterceptor", "#8caaee"); const initCallbackRegex = canonicalizeMatch(/{return \i\(".+?"\)}/); @@ -216,14 +216,14 @@ function patchFactories(factories: Record. -*/ - -import { proxyLazy } from "@utils/lazy"; -import { LazyComponent } from "@utils/lazyReact"; -import { Logger } from "@utils/Logger"; -import { canonicalizeMatch } from "@utils/patches"; -import type { WebpackInstance } from "discord-types/other"; - -import { traceFunction } from "../debug/Tracer"; - -const logger = new Logger("Webpack"); - -export let _resolveReady: () => void; -/** - * Fired once a gateway connection to Discord has been established. - * This indicates that the core webpack modules have been initialised - */ -export const onceReady = new Promise(r => _resolveReady = r); - -export let wreq: WebpackInstance; -export let cache: WebpackInstance["c"]; - -export type FilterFn = (mod: any) => boolean; - -export const filters = { - byProps: (...props: string[]): FilterFn => - props.length === 1 - ? m => m[props[0]] !== void 0 - : m => props.every(p => m[p] !== void 0), - - byCode: (...code: string[]): FilterFn => m => { - if (typeof m !== "function") return false; - const s = Function.prototype.toString.call(m); - for (const c of code) { - if (!s.includes(c)) return false; - } - return true; - }, - byStoreName: (name: string): FilterFn => m => - 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 && m.type.render) return filter(m.type.render); // memo + forwardRef - if (m.type) return filter(m.type); // memos - if (m.render) return filter(m.render); // forwardRefs - return false; - }; - } -}; - -export type CallbackFn = (mod: any, id: string) => void; - -export const subscriptions = new Map(); -export const moduleListeners = new Set(); -export const factoryListeners = new Set<(factory: (module: any, exports: any, require: WebpackInstance) => void) => void>(); -export const beforeInitListeners = new Set<(wreq: WebpackInstance) => void>(); - -export function _initWebpack(webpackRequire: WebpackInstance) { - wreq = webpackRequire; - cache = webpackRequire.c; -} - -let devToolsOpen = false; -if (IS_DEV && IS_DISCORD_DESKTOP) { - // At this point in time, DiscordNative has not been exposed yet, so setImmediate is needed - setTimeout(() => { - DiscordNative/* just to make sure */?.window.setDevtoolsCallbacks(() => devToolsOpen = true, () => devToolsOpen = false); - }, 0); -} - -export function handleModuleNotFound(method: string, ...filter: unknown[]) { - const err = new Error(`webpack.${method} found no module`); - logger.error(err, "Filter:", filter); - - // Strict behaviour in DevBuilds to fail early and make sure the issue is found - if (IS_DEV && !devToolsOpen) - throw err; -} - -/** - * Find the first module that matches the filter - */ -export const find = traceFunction("find", function find(filter: FilterFn, { isIndirect = false, isWaitFor = false }: { isIndirect?: boolean; isWaitFor?: boolean; } = {}) { - if (typeof filter !== "function") - throw new Error("Invalid filter. Expected a function got " + typeof filter); - - for (const key in cache) { - const mod = cache[key]; - if (!mod?.exports || mod.exports === window) continue; - - if (filter(mod.exports)) { - return isWaitFor ? [mod.exports, key] : mod.exports; - } - - if (mod.exports.default && mod.exports.default !== window && filter(mod.exports.default)) { - const found = mod.exports.default; - return isWaitFor ? [found, key] : found; - } - } - - if (!isIndirect) { - handleModuleNotFound("find", filter); - } - - return isWaitFor ? [null, null] : null; -}); - -export function findAll(filter: FilterFn) { - if (typeof filter !== "function") - throw new Error("Invalid filter. Expected a function got " + typeof filter); - - const ret = [] as any[]; - for (const key in cache) { - const mod = cache[key]; - if (!mod?.exports) continue; - - if (filter(mod.exports)) - ret.push(mod.exports); - - if (mod.exports.default && filter(mod.exports.default)) - ret.push(mod.exports.default); - } - - return ret; -} - -/** - * Same as {@link find} but in bulk - * @param filterFns Array of filters. Please note that this array will be modified in place, so if you still - * need it afterwards, pass a copy. - * @returns Array of results in the same order as the passed filters - */ -export const findBulk = traceFunction("findBulk", function findBulk(...filterFns: FilterFn[]) { - if (!Array.isArray(filterFns)) - throw new Error("Invalid filters. Expected function[] got " + typeof filterFns); - - const { length } = filterFns; - - if (length === 0) - throw new Error("Expected at least two filters."); - - if (length === 1) { - if (IS_DEV) { - throw new Error("bulk called with only one filter. Use find"); - } - return find(filterFns[0]); - } - - const filters = filterFns as Array; - - let found = 0; - const results = Array(length); - - outer: - for (const key in cache) { - const mod = cache[key]; - if (!mod?.exports) continue; - - for (let j = 0; j < length; j++) { - const filter = filters[j]; - // Already done - if (filter === undefined) continue; - - if (filter(mod.exports)) { - results[j] = mod.exports; - filters[j] = undefined; - if (++found === length) break outer; - break; - } - - if (mod.exports.default && filter(mod.exports.default)) { - results[j] = mod.exports.default; - filters[j] = undefined; - if (++found === length) break outer; - break; - } - } - } - - if (found !== length) { - const err = new Error(`Got ${length} filters, but only found ${found} modules!`); - if (IS_DEV) { - if (!devToolsOpen) - // Strict behaviour in DevBuilds to fail early and make sure the issue is found - throw err; - } else { - logger.warn(err); - } - } - - return results; -}); - -/** - * 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[]) { - outer: - for (const id in wreq.m) { - 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(s):\n" + code.join("\n")); - if (IS_DEV) { - if (!devToolsOpen) - // Strict behaviour in DevBuilds to fail early and make sure the issue is found - throw err; - } else { - logger.warn(err); - } - - return null; -}); - -/** - * 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. - * - * 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 the first module that matches the filter, lazily - */ -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 - */ -export function findByProps(...props: string[]) { - const res = find(filters.byProps(...props), { isIndirect: true }); - if (!res) - handleModuleNotFound("findByProps", ...props); - return res; -} - -/** - * Find the first module that has the specified properties, lazily - */ -export function findByPropsLazy(...props: string[]) { - if (IS_DEV) lazyWebpackSearchHistory.push(["findByProps", props]); - - return proxyLazy(() => findByProps(...props)); -} - -/** - * Find the first function that includes all the given code - */ -export function findByCode(...code: string[]) { - const res = find(filters.byCode(...code), { isIndirect: true }); - if (!res) - handleModuleNotFound("findByCode", ...code); - return res; -} - -/** - * Find the first function that includes all the given code, lazily - */ -export function findByCodeLazy(...code: string[]) { - if (IS_DEV) lazyWebpackSearchHistory.push(["findByCode", code]); - - return proxyLazy(() => findByCode(...code)); -} - -/** - * Find a store by its displayName - */ -export function findStore(name: string) { - const res = find(filters.byStoreName(name), { isIndirect: true }); - if (!res) - handleModuleNotFound("findStore", name); - return res; -} - -/** - * Find a store by its displayName, lazily - */ -export function findStoreLazy(name: string) { - if (IS_DEV) lazyWebpackSearchHistory.push(["findStore", [name]]); - - 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 res = find(filters.componentByCode(...code), { isIndirect: true }); - if (!res) - handleModuleNotFound("findComponentByCode", ...code); - return res; -} - -/** - * Finds the first component that matches the filter, lazily. - */ -export function findComponentLazy(filter: FilterFn) { - if (IS_DEV) lazyWebpackSearchHistory.push(["findComponent", [filter]]); - - - return LazyComponent(() => { - const res = find(filter, { isIndirect: true }); - if (!res) - handleModuleNotFound("findComponent", filter); - return res; - }); -} - -/** - * 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(() => { - const res = find(filters.componentByCode(...code), { isIndirect: true }); - if (!res) - handleModuleNotFound("findComponentByCode", ...code); - return res; - }); -} - -/** - * 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(() => { - const res = find(filters.byProps(...props), { isIndirect: true }); - if (!res) - handleModuleNotFound("findExportedComponent", ...props); - return res[props[0]]; - }); -} - -const DefaultExtractAndLoadChunksRegex = /(?:Promise\.all\((\[\i\.\i\(".+?"\).+?\])\)|Promise\.resolve\(\)).then\(\i\.bind\(\i,"(.+?)"\)\)/; - -/** - * Extract and load chunks using their entry point - * @param code An array of all the code the module factory containing the lazy chunk loading must include - * @param matcher A RegExp that returns the chunk ids array as the first capture group and the entry point id as the second. Defaults to a matcher that captures the lazy chunk loading found in the module factory - * @returns A promise that resolves when the chunks were loaded - */ -export async function extractAndLoadChunks(code: string[], matcher: RegExp = DefaultExtractAndLoadChunksRegex) { - 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 chunk loading 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 [, rawChunkIds, entryPointId] = match; - if (Number.isNaN(entryPointId)) { - const err = new Error("extractAndLoadChunks: Matcher didn't return a capturing group with the chunk ids array, or the entry point id returned as the second group wasn't a number"); - logger.warn(err, "Code:", code, "Matcher:", matcher); - - // Strict behaviour in DevBuilds to fail early and make sure the issue is found - if (IS_DEV && !devToolsOpen) - throw err; - - return; - } - - if (rawChunkIds) { - const chunkIds = Array.from(rawChunkIds.matchAll(/\("(.+?)"\)/g)).map((m: any) => m[1]); - await Promise.all(chunkIds.map(id => wreq.e(id))); - } - - wreq(entryPointId); -} - -/** - * 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 lazy chunk loading must include - * @param matcher A RegExp that returns the chunk ids array as the first capture group and the entry point id as the second. Defaults to a matcher that captures the lazy chunk loading found in the module factory - * @returns A function that returns a promise that resolves when the chunks were loaded, on first call - */ -export function extractAndLoadChunksLazy(code: string[], matcher = DefaultExtractAndLoadChunksRegex) { - if (IS_DEV) lazyWebpackSearchHistory.push(["extractAndLoadChunks", [code, matcher]]); - - return () => extractAndLoadChunks(code, matcher); -} - -/** - * 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, { 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)) - filter = filters.byProps(...filter); - else if (typeof filter !== "function") - throw new Error("filter must be a string, string[] or function, got " + typeof filter); - - if (cache != null) { - const [existing, id] = find(filter, { isIndirect: true, isWaitFor: true }); - if (existing) return void callback(existing, id); - } - - subscriptions.set(filter, callback); -} - -/** - * Search modules by keyword. This searches the factory methods, - * meaning you can search all sorts of things, displayName, methodName, strings somewhere in the code, etc - * @param filters One or more strings or regexes - * @returns Mapping of found modules - */ -export function search(...filters: Array) { - const results = {} as Record; - const factories = wreq.m; - outer: - for (const id in factories) { - const factory = factories[id].original ?? factories[id]; - const str: string = factory.toString(); - for (const filter of filters) { - if (typeof filter === "string" && !str.includes(filter)) continue outer; - if (filter instanceof RegExp && !filter.test(str)) continue outer; - } - results[id] = factory; - } - - return results; -} - -/** - * Extract a specific module by id into its own Source File. This has no effect on - * the code, it is only useful to be able to look at a specific module without having - * to view a massive file. extract then returns the extracted module so you can jump to it. - * As mentioned above, note that this extracted module is not actually used, - * so putting breakpoints or similar will have no effect. - * @param id The id of the module to extract - */ -export function extract(id: string | number) { - const mod = wreq.m[id] as Function; - if (!mod) return null; - - const code = ` -// [EXTRACTED] WebpackModule${id} -// WARNING: This module was extracted to be more easily readable. -// This module is NOT ACTUALLY USED! This means putting breakpoints will have NO EFFECT!! - -0,${mod.toString()} -//# sourceURL=ExtractedWebpackModule${id} -`; - const extracted = (0, eval)(code); - return extracted as Function; -} diff --git a/src/webpack/webpack.tsx b/src/webpack/webpack.tsx new file mode 100644 index 000000000..73f7c5c73 --- /dev/null +++ b/src/webpack/webpack.tsx @@ -0,0 +1,609 @@ +/* + * 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 { proxyLazy } from "@utils/lazy"; +import { LazyComponent } from "@utils/lazyReact"; +import { Logger } from "@utils/Logger"; +import { canonicalizeMatch } from "@utils/patches"; +import { proxyInner, proxyInnerValue } from "@utils/proxyInner"; +import type { WebpackInstance } from "discord-types/other"; + +import { traceFunction } from "../debug/Tracer"; + +const logger = new Logger("Webpack"); + +export let _resolveReady: () => void; +/** + * Fired once a gateway connection to Discord has been established. + * This indicates that the core webpack modules have been initialised + */ +export const onceReady = new Promise(r => _resolveReady = r); + +export let wreq: WebpackInstance; +export let cache: WebpackInstance["c"]; + +export type FilterFn = (mod: any) => boolean; + +export const filters = { + byProps: (...props: string[]): FilterFn => { + const filter = props.length === 1 + ? m => m?.[props[0]] !== void 0 + : m => props.every(p => m?.[p] !== void 0); + + // @ts-ignore + filter.$$vencordProps = ["byProps", ...props]; + return filter; + }, + + byCode: (...code: string[]): FilterFn => { + const filter = m => { + if (typeof m !== "function") return false; + const s = Function.prototype.toString.call(m); + for (const c of code) { + if (!s.includes(c)) return false; + } + return true; + }; + + filter.$$vencordProps = ["byCode", ...code]; + return filter; + }, + + byStoreName: (name: string): FilterFn => { + const filter = m => m?.constructor?.displayName === name; + + filter.$$vencordProps = ["byStoreName", name]; + return filter; + }, + + componentByCode: (...code: string[]): FilterFn => { + const filter = filters.byCode(...code); + const wrapper = m => { + if (filter(m)) return true; + if (!m?.$$typeof) return false; + if (m?.type && m.type.render) return filter(m.type.render); // memo + forwardRef + if (m?.type) return filter(m.type); // memos + if (m?.render) return filter(m.render); // forwardRefs + return false; + }; + + wrapper.$$vencordProps = ["componentByCode", ...code]; + return wrapper; + } +}; + +export type ModCallbackFn = (mod: any) => void; +export type ModCallbackFnWithId = (mod: any, id: string) => void; + +export const waitForSubscriptions = new Map(); +export const moduleListeners = new Set(); +export const factoryListeners = new Set<(factory: (module: any, exports: any, require: WebpackInstance) => void) => void>(); +export const beforeInitListeners = new Set<(wreq: WebpackInstance) => void>(); + +export function _initWebpack(webpackRequire: WebpackInstance) { + wreq = webpackRequire; + cache = webpackRequire.c; +} + +let devToolsOpen = false; +if (IS_DEV && IS_DISCORD_DESKTOP) { + // At this point in time, DiscordNative has not been exposed yet, so setImmediate is needed + setTimeout(() => { + DiscordNative/* just to make sure */?.window.setDevtoolsCallbacks(() => devToolsOpen = true, () => devToolsOpen = false); + }, 0); +} + +export const webpackSearchHistory = [] as Array<["waitFor" | "find" | "findComponent" | "findExportedComponent" | "findComponentByCode" | "findByProps" | "findByCode" | "findStore" | "extractAndLoadChunks" | "webpackDependantLazy" | "webpackDependantLazyComponent", any[]]>; + +function printFilter(filter: FilterFn) { + if ("$$vencordProps" in filter) { + const props = filter.$$vencordProps as string[]; + return `${props[0]}(${props.slice(1).map(arg => `"${arg}"`).join(", ")})`; + } + + return filter.toString(); +} + +/** + * Wait for the first module that matches the provided filter to be required, + * then call the callback with the module as the first argument. + * + * If the module is already required, the callback will be called immediately. + * @param filter A function that takes a module and returns a boolean + * @param callback A function that takes the found module as its first argument + */ +export function waitFor(filter: FilterFn, callback: ModCallbackFn, { isIndirect = false }: { isIndirect?: boolean; } = {}) { + if (typeof filter !== "function") + throw new Error("Invalid filter. Expected a function got " + typeof filter); + if (typeof callback !== "function") + throw new Error("Invalid callback. Expected a function got " + typeof callback); + + if (IS_DEV && !isIndirect) webpackSearchHistory.push(["waitFor", [filter]]); + + if (cache != null) { + const existing = cacheFind(filter); + if (existing) return callback(existing); + } + + waitForSubscriptions.set(filter, callback); +} + +/** + * Find the first module that matches the filter. + * + * The way this works internally is: + * Wait for the first module that matches the provided filter to be required, + * then call the callback with the module as the first argument. + * + * If the module is already required, the callback will be called immediately. + * + * The callback must return a value that will be used as the proxy inner value. + * + * If no callback is specified, the default callback will assign the proxy inner value to all the module + * @param filter A function that takes a module and returns a boolean + * @param callback A function that takes the found module as its first argument and returns to use as the proxy inner value + * @returns A proxy that has the callback return value as its true value, or the callback return value if the callback was called when the function was called + */ +export function find(filter: FilterFn, callback: (mod: any) => any = m => m, { isIndirect = false }: { isIndirect?: boolean; } = {}) { + if (typeof filter !== "function") + throw new Error("Invalid filter. Expected a function got " + typeof filter); + if (typeof callback !== "function") + throw new Error("Invalid callback. Expected a function got " + typeof callback); + + if (IS_DEV && !isIndirect) webpackSearchHistory.push(["find", [filter]]); + + const [proxy, setInnerValue] = proxyInner(new Error(`Webpack find matched no module. Filter: ${printFilter(filter)}`)); + waitFor(filter, mod => setInnerValue(callback(mod)), { isIndirect: true }); + + if (proxy[proxyInnerValue] != null) return proxy[proxyInnerValue] as T; + + return proxy; +} + +/** + * Find the first component that matches the filter. + * @param filter A function that takes a module and returns a boolean + * @param parse A function that takes the found component as its first argument and returns a component. Useful if you want to wrap the found component in something. Defaults to the original component + * @returns The component if found, or a noop component + */ +export function findComponent(filter: FilterFn, parse: (component: any) => React.ComponentType = m => m, { isIndirect = false }: { isIndirect?: boolean; } = {}) { + if (typeof filter !== "function") + throw new Error("Invalid filter. Expected a function got " + typeof filter); + if (typeof parse !== "function") + throw new Error("Invalid component parse. Expected a function got " + typeof parse); + + if (IS_DEV && !isIndirect) webpackSearchHistory.push(["findComponent", [filter]]); + + let noMatchLogged = false; + const NoopComponent = (() => { + if (!noMatchLogged) { + noMatchLogged = true; + logger.error(`Webpack find matched no module. Filter: ${printFilter(filter)}`); + } + + return null; + }) as React.ComponentType; + + let InnerComponent = NoopComponent; + + const WrapperComponent = (props: T) => { + return ; + }; + + WrapperComponent.$$vencordGetter = () => InnerComponent; + + waitFor(filter, (v: any) => { + const parsedComponent = parse(v); + InnerComponent = parsedComponent; + Object.assign(InnerComponent, parsedComponent); + }, { isIndirect: true }); + + if (InnerComponent !== NoopComponent) return InnerComponent; + + return WrapperComponent; +} + +/** + * Find the first component that is exported by the first prop name. + * + * @example findExportedComponent("FriendRow") + * @example findExportedComponent("FriendRow", "Friend", FriendRow => React.memo(FriendRow)) + * + * @param props A list of prop names to search the exports for + * @param parse A function that takes the found component as its first argument and returns a component. Useful if you want to wrap the found component in something. Defaults to the original component + * @returns The component if found, or a noop component + */ +export function findExportedComponent(...props: string[] | [...string[], (component: any) => React.ComponentType]) { + const parse = (typeof props.at(-1) === "function" ? props.pop() : m => m) as (component: any) => React.ComponentType; + const newProps = props as string[]; + + const filter = filters.byProps(...newProps); + + if (IS_DEV) webpackSearchHistory.push(["findExportedComponent", props]); + + let noMatchLogged = false; + const NoopComponent = (() => { + if (!noMatchLogged) { + noMatchLogged = true; + logger.error(`Webpack find matched no module. Filter: ${printFilter(filter)}`); + } + + return null; + }) as React.ComponentType; + + let InnerComponent = NoopComponent; + + const WrapperComponent = (props: T) => { + return ; + }; + + WrapperComponent.$$vencordGetter = () => InnerComponent; + + waitFor(filter, (v: any) => { + const parsedComponent = parse(v[newProps[0]]); + InnerComponent = parsedComponent; + Object.assign(InnerComponent, parsedComponent); + }, { isIndirect: true }); + + if (InnerComponent !== NoopComponent) return InnerComponent; + + return WrapperComponent as React.ComponentType; +} + +/** + * Find the first component that includes all the given code. + * + * @example findComponentByCode(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR") + * @example findComponentByCode(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)", ColorPicker => React.memo(ColorPicker)) + * + * @param code A list of code to search each export for + * @param parse A function that takes the found component as its first argument and returns a component. Useful if you want to wrap the found component in something. Defaults to the original component + * @returns The component if found, or a noop component + */ +export function findComponentByCode(...code: string[] | [...string[], (component: any) => React.ComponentType]) { + const parse = (typeof code.at(-1) === "function" ? code.pop() : m => m) as (component: any) => React.ComponentType; + const newCode = code as string[]; + + if (IS_DEV) webpackSearchHistory.push(["findComponentByCode", code]); + + return findComponent(filters.componentByCode(...newCode), parse, { isIndirect: true }); +} + +/** + * Find the first module or default export that includes all the given props + * + * @param props A list of props to search the exports for + */ +export function findByProps(...props: string[]) { + if (IS_DEV) webpackSearchHistory.push(["findByProps", props]); + + return find(filters.byProps(...props), m => m, { isIndirect: true }); +} + +/** + * Find the first export that includes all the given code + * + * @param code A list of code to search each export for + */ +export function findByCode(...code: string[]) { + if (IS_DEV) webpackSearchHistory.push(["findByCode", code]); + + return find(filters.byCode(...code), m => m, { isIndirect: true }); +} + +/** + * Find a store by its name + * + * @param name The store name + */ +export function findStore(name: string) { + if (IS_DEV) webpackSearchHistory.push(["findStore", [name]]); + + return find(filters.byStoreName(name), m => m, { isIndirect: true }); +} + +/** + * Find the first already required module that matches the filter. + * @param filter A function that takes a module and returns a boolean + * @returns The found module or null + */ +export const cacheFind = traceFunction("find", function find(filter: FilterFn) { + if (typeof filter !== "function") + throw new Error("Invalid filter. Expected a function got " + typeof filter); + + for (const key in cache) { + const mod = cache[key]; + if (!mod?.exports || mod.exports === window) continue; + + if (filter(mod.exports)) { + return mod.exports; + } + + if (mod.exports.default && filter(mod.exports.default)) { + return mod.exports.default; + } + } + + return null; +}); + + +export function cacheFindAll(filter: FilterFn) { + if (typeof filter !== "function") + throw new Error("Invalid filter. Expected a function got " + typeof filter); + + const ret = [] as any[]; + for (const key in cache) { + const mod = cache[key]; + if (!mod?.exports) continue; + + if (filter(mod.exports)) { + ret.push(mod.exports); + } + + if (mod.exports.default && filter(mod.exports.default)) { + ret.push(mod.exports.default); + } + } + + return ret; +} + +/** + * Same as {@link cacheFind} but in bulk + * @param filterFns Array of filters. Please note that this array will be modified in place, so if you still + * need it afterwards, pass a copy. + * @returns Array of results in the same order as the passed filters + */ +export const cacheFindBulk = traceFunction("findBulk", function findBulk(...filterFns: FilterFn[]) { + if (!Array.isArray(filterFns)) + throw new Error("Invalid filters. Expected function[] got " + typeof filterFns); + + const { length } = filterFns; + + if (length === 0) + throw new Error("Expected at least two filters."); + + if (length === 1) { + if (IS_DEV) { + throw new Error("bulk called with only one filter. Use find"); + } + + return cacheFind(filterFns[0]); + } + + let found = 0; + const results = Array(length); + + outer: + for (const key in cache) { + const mod = cache[key]; + if (!mod?.exports) continue; + + for (let j = 0; j < length; j++) { + const filter = filterFns[j]; + + if (filter(mod.exports)) { + results[j] = mod.exports; + filterFns.splice(j--, 1); + if (++found === length) break outer; + break; + } + + if (mod.exports.default && filter(mod.exports.default)) { + results[j] = mod.exports.default; + filterFns.splice(j--, 1); + if (++found === length) break outer; + break; + } + } + } + + if (found !== length) { + const err = new Error(`Got ${length} filters, but only found ${found} modules!`); + + if (!IS_DEV) { + logger.warn(err); + return results; + } + + if (!devToolsOpen) { + throw err; // Strict behaviour in DevBuilds to fail early and make sure the issue is found + } + } + + return results; +}); + +/** + * 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[]) { + outer: + for (const id in wreq.m) { + 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(s):\n" + code.join("\n")); + if (!IS_DEV) { + logger.warn(err); + return null; + } + + if (!devToolsOpen) { + throw err; // Strict behaviour in DevBuilds to fail early and make sure the issue is found + } + + return null; +}); + +/** + * 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]; +} + +/** + * This is just a wrapper around {@link proxyLazy} to make our reporter test for your webpack finds. + * + * Wraps the result of factory in a Proxy you can consume as if it wasn't lazy. + * On first property access, the factory is evaluated + * @param factory Factory returning the result + * @param attempts How many times to try to evaluate the factory before giving up + * @returns Result of factory function + */ +export function webpackDependantLazy(factory: () => any, attempts?: number) { + if (IS_DEV) webpackSearchHistory.push(["webpackDependantLazy", [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 webpackDependantLazyComponent(factory: () => any, attempts?: number) { + if (IS_DEV) webpackSearchHistory.push(["webpackDependantLazyComponent", [factory]]); + + return LazyComponent(factory, attempts); +} + +const DefaultExtractAndLoadChunksRegex = /(?:Promise\.all\((\[\i\.\i\(".+?"\).+?\])\)|Promise\.resolve\(\)).then\(\i\.bind\(\i,"(.+?)"\)\)/; + +/** + * Extract and load chunks using their entry point + * @param code An array of all the code the module factory containing the lazy chunk loading must include + * @param matcher A RegExp that returns the chunk ids array as the first capture group and the entry point id as the second. Defaults to a matcher that captures the lazy chunk loading found in the module factory + * @returns A promise that resolves when the chunks were loaded + */ +export async function extractAndLoadChunks(code: string[], matcher: RegExp = DefaultExtractAndLoadChunksRegex) { + 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 chunk loading 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 [, rawChunkIds, entryPointId] = match; + if (Number.isNaN(entryPointId)) { + const err = new Error("extractAndLoadChunks: Matcher didn't return a capturing group with the chunk ids array, or the entry point id returned as the second group wasn't a number"); + logger.warn(err, "Code:", code, "Matcher:", matcher); + + // Strict behaviour in DevBuilds to fail early and make sure the issue is found + if (IS_DEV && !devToolsOpen) + throw err; + + return; + } + + if (rawChunkIds) { + const chunkIds = Array.from(rawChunkIds.matchAll(/\("(.+?)"\)/g)).map((m: any) => m[1]); + await Promise.all(chunkIds.map(id => wreq.e(id))); + } + + wreq(entryPointId); +} + +/** + * 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 lazy chunk loading must include + * @param matcher A RegExp that returns the chunk ids array as the first capture group and the entry point id as the second. Defaults to a matcher that captures the lazy chunk loading found in the module factory + * @returns A function that returns a promise that resolves when the chunks were loaded, on first call + */ +export function extractAndLoadChunksLazy(code: string[], matcher = DefaultExtractAndLoadChunksRegex) { + if (IS_DEV) webpackSearchHistory.push(["extractAndLoadChunks", [code, matcher]]); + + return () => extractAndLoadChunks(code, matcher); +} + +/** + * Search modules by keyword. This searches the factory methods, + * meaning you can search all sorts of things, displayName, methodName, strings somewhere in the code, etc + * @param filters One or more strings or regexes + * @returns Mapping of found modules + */ +export function search(...filters: Array) { + const results = {} as Record; + const factories = wreq.m; + outer: + for (const id in factories) { + const factory = factories[id]; + const str: string = factory.toString(); + for (const filter of filters) { + if (typeof filter === "string" && !str.includes(filter)) continue outer; + if (filter instanceof RegExp && !filter.test(str)) continue outer; + } + results[id] = factory; + } + + return results; +} + +/** + * Extract a specific module by id into its own Source File. This has no effect on + * the code, it is only useful to be able to look at a specific module without having + * to view a massive file. extract then returns the extracted module so you can jump to it. + * As mentioned above, note that this extracted module is not actually used, + * so putting breakpoints or similar will have no effect. + * @param id The id of the module to extract + */ +export function extract(id: string | number) { + const mod = wreq.m[id] as Function; + if (!mod) return null; + + const code = ` +// [EXTRACTED] WebpackModule${id} +// WARNING: This module was extracted to be more easily readable. +// This module is NOT ACTUALLY USED! This means putting breakpoints will have NO EFFECT!! + +0,${mod.toString()} +//# sourceURL=ExtractedWebpackModule${id} +`; + const extracted = (0, eval)(code); + return extracted as Function; +}