From 76f69125118ebae51e48c64873ab63c143c2349a Mon Sep 17 00:00:00 2001 From: Masterjoona <69722179+Masterjoona@users.noreply.github.com> Date: Wed, 19 Jun 2024 21:04:39 +0300 Subject: [PATCH 1/5] VoiceMessages: properly respect user's microphone choice (#2602) --- src/plugins/voiceMessages/DesktopRecorder.tsx | 2 ++ src/plugins/voiceMessages/WebRecorder.tsx | 2 ++ src/plugins/voiceMessages/utils.ts | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/plugins/voiceMessages/DesktopRecorder.tsx b/src/plugins/voiceMessages/DesktopRecorder.tsx index a69739a41..18581ad16 100644 --- a/src/plugins/voiceMessages/DesktopRecorder.tsx +++ b/src/plugins/voiceMessages/DesktopRecorder.tsx @@ -21,6 +21,7 @@ import { Button, showToast, Toasts, useState } from "@webpack/common"; import type { VoiceRecorder } from "."; import { settings } from "./settings"; +import { MediaEngineStore } from "./utils"; const Native = VencordNative.pluginHelpers.VoiceMessages as PluginNative; @@ -41,6 +42,7 @@ export const VoiceRecorderDesktop: VoiceRecorder = ({ setAudioBlob, onRecordingC { echoCancellation: settings.store.echoCancellation, noiseCancellation: settings.store.noiseSuppression, + deviceId: MediaEngineStore.getInputDeviceId(), }, (success: boolean) => { if (success) diff --git a/src/plugins/voiceMessages/WebRecorder.tsx b/src/plugins/voiceMessages/WebRecorder.tsx index 423a2699a..eb77b1990 100644 --- a/src/plugins/voiceMessages/WebRecorder.tsx +++ b/src/plugins/voiceMessages/WebRecorder.tsx @@ -20,6 +20,7 @@ import { Button, useState } from "@webpack/common"; import type { VoiceRecorder } from "."; import { settings } from "./settings"; +import { MediaEngineStore } from "./utils"; export const VoiceRecorderWeb: VoiceRecorder = ({ setAudioBlob, onRecordingChange }) => { const [recording, setRecording] = useState(false); @@ -40,6 +41,7 @@ export const VoiceRecorderWeb: VoiceRecorder = ({ setAudioBlob, onRecordingChang audio: { echoCancellation: settings.store.echoCancellation, noiseSuppression: settings.store.noiseSuppression, + deviceId: MediaEngineStore.getInputDeviceId() } }).then(stream => { const chunks = [] as Blob[]; diff --git a/src/plugins/voiceMessages/utils.ts b/src/plugins/voiceMessages/utils.ts index dcfd6f26c..ef571bbf3 100644 --- a/src/plugins/voiceMessages/utils.ts +++ b/src/plugins/voiceMessages/utils.ts @@ -17,5 +17,7 @@ */ import { classNameFactory } from "@api/Styles"; +import { findStoreLazy } from "@webpack"; +export const MediaEngineStore = findStoreLazy("MediaEngineStore"); export const cl = classNameFactory("vc-vmsg-"); From 3ce241021f3b837eedfda4da8f85742a84580f17 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Thu, 20 Jun 2024 04:40:07 +0200 Subject: [PATCH 2/5] PluginModals: add plugin website & source code links --- scripts/build/common.mjs | 24 ++++++--- .../PluginSettings/ContributorModal.tsx | 46 +++++------------ .../PluginSettings/LinkIconButton.css | 12 +++++ .../PluginSettings/LinkIconButton.tsx | 45 ++++++++++++++++ src/components/PluginSettings/PluginModal.css | 7 +++ src/components/PluginSettings/PluginModal.tsx | 51 ++++++++++++++++++- .../PluginSettings/contributorModal.css | 10 ---- src/modules.d.ts | 4 ++ src/utils/modal.tsx | 2 +- src/webpack/common/types/utils.d.ts | 35 +++++++++++++ src/webpack/common/utils.ts | 10 +++- 11 files changed, 190 insertions(+), 56 deletions(-) create mode 100644 src/components/PluginSettings/LinkIconButton.css create mode 100644 src/components/PluginSettings/LinkIconButton.tsx create mode 100644 src/components/PluginSettings/PluginModal.css diff --git a/scripts/build/common.mjs b/scripts/build/common.mjs index cdbb26eec..eb7ab905b 100644 --- a/scripts/build/common.mjs +++ b/scripts/build/common.mjs @@ -89,15 +89,20 @@ export const globPlugins = kind => ({ const pluginDirs = ["plugins/_api", "plugins/_core", "plugins", "userplugins"]; let code = ""; let plugins = "\n"; + let meta = "\n"; let i = 0; for (const dir of pluginDirs) { - if (!await exists(`./src/${dir}`)) continue; - const files = await readdir(`./src/${dir}`); - for (const file of files) { - if (file.startsWith("_") || file.startsWith(".")) continue; - if (file === "index.ts") continue; + const userPlugin = dir === "userplugins"; + + if (!await exists(`./src/${dir}`)) continue; + const files = await readdir(`./src/${dir}`, { withFileTypes: true }); + for (const file of files) { + const fileName = file.name; + if (fileName.startsWith("_") || fileName.startsWith(".")) continue; + if (fileName === "index.ts") continue; + + const target = getPluginTarget(fileName); - const target = getPluginTarget(file); if (target && !IS_REPORTER) { if (target === "dev" && !watch) continue; if (target === "web" && kind === "discordDesktop") continue; @@ -106,13 +111,16 @@ export const globPlugins = kind => ({ if (target === "vencordDesktop" && kind !== "vencordDesktop") continue; } + const folderName = `src/${dir}/${fileName}`.replace(/^src\/plugins\//, ""); + const mod = `p${i}`; - code += `import ${mod} from "./${dir}/${file.replace(/\.tsx?$/, "")}";\n`; + code += `import ${mod} from "./${dir}/${fileName.replace(/\.tsx?$/, "")}";\n`; plugins += `[${mod}.name]:${mod},\n`; + meta += `[${mod}.name]:${JSON.stringify({ folderName, userPlugin })},\n`; // TODO: add excluded plugins to display in the UI? i++; } } - code += `export default {${plugins}};`; + code += `export default {${plugins}};export const PluginMeta={${meta}};`; return { contents: code, resolveDir: "./src" diff --git a/src/components/PluginSettings/ContributorModal.tsx b/src/components/PluginSettings/ContributorModal.tsx index 99a8da168..c3c36f1e6 100644 --- a/src/components/PluginSettings/ContributorModal.tsx +++ b/src/components/PluginSettings/ContributorModal.tsx @@ -11,20 +11,16 @@ import { classNameFactory } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { Link } from "@components/Link"; import { DevsById } from "@utils/constants"; -import { fetchUserProfile, getTheme, Theme } from "@utils/discord"; -import { pluralise } from "@utils/misc"; +import { fetchUserProfile } from "@utils/discord"; +import { classes, pluralise } from "@utils/misc"; import { ModalContent, ModalRoot, openModal } from "@utils/modal"; -import { Forms, MaskedLink, showToast, Tooltip, useEffect, useMemo, UserProfileStore, useStateFromStores } from "@webpack/common"; +import { Forms, showToast, useEffect, useMemo, UserProfileStore, useStateFromStores } from "@webpack/common"; import { User } from "discord-types/general"; import Plugins from "~plugins"; import { PluginCard } from "."; - -const WebsiteIconDark = "/assets/e1e96d89e192de1997f73730db26e94f.svg"; -const WebsiteIconLight = "/assets/730f58bcfd5a57a5e22460c445a0c6cf.svg"; -const GithubIconLight = "/assets/3ff98ad75ac94fa883af5ed62d17c459.svg"; -const GithubIconDark = "/assets/6a853b4c87fce386cbfef4a2efbacb09.svg"; +import { GithubButton, WebsiteButton } from "./LinkIconButton"; const cl = classNameFactory("vc-author-modal-"); @@ -40,16 +36,6 @@ export function openContributorModal(user: User) { ); } -function GithubIcon() { - const src = getTheme() === Theme.Light ? GithubIconLight : GithubIconDark; - return GitHub; -} - -function WebsiteIcon() { - const src = getTheme() === Theme.Light ? WebsiteIconLight : WebsiteIconDark; - return Website; -} - function ContributorModal({ user }: { user: User; }) { useSettings(); @@ -86,24 +72,18 @@ function ContributorModal({ user }: { user: User; }) { /> {user.username} -
+
{website && ( - - {props => ( - - - - )} - + )} {githubName && ( - - {props => ( - - - - )} - + )}
diff --git a/src/components/PluginSettings/LinkIconButton.css b/src/components/PluginSettings/LinkIconButton.css new file mode 100644 index 000000000..1055d6c70 --- /dev/null +++ b/src/components/PluginSettings/LinkIconButton.css @@ -0,0 +1,12 @@ +.vc-settings-modal-link-icon { + height: 32px; + width: 32px; + border-radius: 50%; + border: 4px solid var(--background-tertiary); + box-sizing: border-box +} + +.vc-settings-modal-links { + display: flex; + gap: 0.2em; +} diff --git a/src/components/PluginSettings/LinkIconButton.tsx b/src/components/PluginSettings/LinkIconButton.tsx new file mode 100644 index 000000000..ea36dda24 --- /dev/null +++ b/src/components/PluginSettings/LinkIconButton.tsx @@ -0,0 +1,45 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2024 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import "./LinkIconButton.css"; + +import { getTheme, Theme } from "@utils/discord"; +import { MaskedLink, Tooltip } from "@webpack/common"; + +const WebsiteIconDark = "/assets/e1e96d89e192de1997f73730db26e94f.svg"; +const WebsiteIconLight = "/assets/730f58bcfd5a57a5e22460c445a0c6cf.svg"; +const GithubIconLight = "/assets/3ff98ad75ac94fa883af5ed62d17c459.svg"; +const GithubIconDark = "/assets/6a853b4c87fce386cbfef4a2efbacb09.svg"; + +export function GithubIcon() { + const src = getTheme() === Theme.Light ? GithubIconLight : GithubIconDark; + return ; +} + +export function WebsiteIcon() { + const src = getTheme() === Theme.Light ? WebsiteIconLight : WebsiteIconDark; + return ; +} + +interface Props { + text: string; + href: string; +} + +function LinkIcon({ text, href, Icon }: Props & { Icon: React.ComponentType; }) { + return ( + + {props => ( + + + + )} + + ); +} + +export const WebsiteButton = (props: Props) => ; +export const GithubButton = (props: Props) => ; diff --git a/src/components/PluginSettings/PluginModal.css b/src/components/PluginSettings/PluginModal.css new file mode 100644 index 000000000..1f4b9aaad --- /dev/null +++ b/src/components/PluginSettings/PluginModal.css @@ -0,0 +1,7 @@ +.vc-plugin-modal-info { + align-items: center; +} + +.vc-plugin-modal-description { + flex-grow: 1; +} diff --git a/src/components/PluginSettings/PluginModal.tsx b/src/components/PluginSettings/PluginModal.tsx index 34de43c2d..e5da01f36 100644 --- a/src/components/PluginSettings/PluginModal.tsx +++ b/src/components/PluginSettings/PluginModal.tsx @@ -16,10 +16,14 @@ * along with this program. If not, see . */ +import "./PluginModal.css"; + import { generateId } from "@api/Commands"; import { useSettings } from "@api/Settings"; +import { classNameFactory } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { Flex } from "@components/Flex"; +import { gitRemote } from "@shared/vencordUserAgent"; import { proxyLazy } from "@utils/lazy"; import { Margins } from "@utils/margins"; import { classes, isObjectEmpty } from "@utils/misc"; @@ -30,6 +34,8 @@ import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserSto import { User } from "discord-types/general"; import { Constructor } from "type-fest"; +import { PluginMeta } from "~plugins"; + import { ISettingElementProps, SettingBooleanComponent, @@ -40,6 +46,9 @@ import { SettingTextComponent } from "./components"; import { openContributorModal } from "./ContributorModal"; +import { GithubButton, WebsiteButton } from "./LinkIconButton"; + +const cl = classNameFactory("vc-plugin-modal-"); const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers"); const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar"); @@ -180,16 +189,54 @@ export default function PluginModal({ plugin, onRestartNeeded, onClose, transiti ); } + /* + function switchToPopout() { + onClose(); + + const PopoutKey = `DISCORD_VENCORD_PLUGIN_SETTINGS_MODAL_${plugin.name}`; + PopoutActions.open( + PopoutKey, + () => PopoutActions.close(PopoutKey)} + /> + ); + } + */ + + const pluginMeta = PluginMeta[plugin.name]; + return ( {plugin.name} + + {/* + + */} - About {plugin.name} - {plugin.description} + + {plugin.description} + {!pluginMeta.userPlugin && ( +
+ + +
+ )} +
Authors
; export default plugins; + export const PluginMeta: Record; } declare module "~pluginNatives" { diff --git a/src/utils/modal.tsx b/src/utils/modal.tsx index 4203068c9..79f777088 100644 --- a/src/utils/modal.tsx +++ b/src/utils/modal.tsx @@ -38,7 +38,7 @@ const enum ModalTransitionState { export interface ModalProps { transitionState: ModalTransitionState; - onClose(): Promise; + onClose(): void; } export interface ModalOptions { diff --git a/src/webpack/common/types/utils.d.ts b/src/webpack/common/types/utils.d.ts index 1cd2bf69d..7f6249be6 100644 --- a/src/webpack/common/types/utils.d.ts +++ b/src/webpack/common/types/utils.d.ts @@ -18,6 +18,7 @@ import { Guild, GuildMember } from "discord-types/general"; import type { ReactNode } from "react"; +import { LiteralUnion } from "type-fest"; import type { FluxEvents } from "./fluxEvents"; import { i18nMessages } from "./i18nMessages"; @@ -221,3 +222,37 @@ export interface Constants { UserFlags: Record; FriendsSections: Record; } + +export interface ExpressionPickerStore { + closeExpressionPicker(activeViewType?: any): void; + openExpressionPicker(activeView: LiteralUnion<"emoji" | "gif" | "sticker", string>, activeViewType?: any): void; +} + +export interface BrowserWindowFeatures { + toolbar?: boolean; + menubar?: boolean; + location?: boolean; + directories?: boolean; + width?: number; + height?: number; + defaultWidth?: number; + defaultHeight?: number; + left?: number; + top?: number; + defaultAlwaysOnTop?: boolean; + movable?: boolean; + resizable?: boolean; + frame?: boolean; + alwaysOnTop?: boolean; + hasShadow?: boolean; + transparent?: boolean; + skipTaskbar?: boolean; + titleBarStyle?: string | null; + backgroundColor?: string; +} + +export interface PopoutActions { + open(key: string, render: (windowKey: string) => ReactNode, features?: BrowserWindowFeatures); + close(key: string): void; + setAlwaysOnTop(key: string, alwaysOnTop: boolean): void; +} diff --git a/src/webpack/common/utils.ts b/src/webpack/common/utils.ts index a724769c8..a6853c84a 100644 --- a/src/webpack/common/utils.ts +++ b/src/webpack/common/utils.ts @@ -160,9 +160,15 @@ export const InviteActions = findByPropsLazy("resolveInvite"); export const IconUtils: t.IconUtils = findByPropsLazy("getGuildBannerURL", "getUserAvatarURL"); -const openExpressionPickerMatcher = canonicalizeMatch(/setState\({activeView:\i/); +const openExpressionPickerMatcher = canonicalizeMatch(/setState\({activeView:\i,activeViewType:/); // TODO: type -export const ExpressionPickerStore = mapMangledModuleLazy("expression-picker-last-active-view", { +export const ExpressionPickerStore: t.ExpressionPickerStore = mapMangledModuleLazy("expression-picker-last-active-view", { closeExpressionPicker: filters.byCode("setState({activeView:null"), openExpressionPicker: m => typeof m === "function" && openExpressionPickerMatcher.test(m.toString()), }); + +export const PopoutActions: t.PopoutActions = mapMangledModuleLazy('type:"POPOUT_WINDOW_OPEN"', { + open: filters.byCode('type:"POPOUT_WINDOW_OPEN"'), + close: filters.byCode('type:"POPOUT_WINDOW_CLOSE"'), + setAlwaysOnTop: filters.byCode('type:"POPOUT_WINDOW_SET_ALWAYS_ON_TOP"'), +}); From d07042236d5974f9018a55d032a652ad19733313 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 19 Jun 2024 16:16:48 -0300 Subject: [PATCH 3/5] Add wrapSettingsHook back; Fix FakeNitro subscription emoji bypass --- src/plugins/_core/settings.tsx | 33 +++++++++------------------------ src/plugins/fakeNitro/index.tsx | 17 +++++++---------- 2 files changed, 16 insertions(+), 34 deletions(-) diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index 50cd6f04b..3cc020836 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -56,33 +56,18 @@ export default definePlugin({ } ] }, - // Discord Stable - // FIXME: remove once change merged to stable { find: "Messages.ACTIVITY_SETTINGS", - noWarn: true, - replacement: { - get match() { - switch (Settings.plugins.Settings.settingsLocation) { - case "top": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.USER_SETTINGS/; - case "aboveNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.BILLING_SETTINGS/; - case "belowNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.APP_SETTINGS/; - case "belowActivity": return /(?<=\{section:(\i\.\i)\.DIVIDER},)\{section:"changelog"/; - case "bottom": return /\{section:(\i\.\i)\.CUSTOM,\s*element:.+?}/; - case "aboveActivity": - default: - return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.ACTIVITY_SETTINGS/; - } + replacement: [ + { + match: /(?<=section:(.{0,50})\.DIVIDER\}\))([,;])(?=.{0,200}(\i)\.push.{0,100}label:(\i)\.header)/, + replace: (_, sectionTypes, commaOrSemi, elements, element) => `${commaOrSemi} $self.addSettings(${elements}, ${element}, ${sectionTypes}) ${commaOrSemi}` }, - replace: "...$self.makeSettingsCategories($1),$&" - } - }, - { - find: "Messages.ACTIVITY_SETTINGS", - replacement: { - match: /(?<=section:(.{0,50})\.DIVIDER\}\))([,;])(?=.{0,200}(\i)\.push.{0,100}label:(\i)\.header)/, - replace: (_, sectionTypes, commaOrSemi, elements, element) => `${commaOrSemi} $self.addSettings(${elements}, ${element}, ${sectionTypes}) ${commaOrSemi}` - } + { + match: /({(?=.+?function (\i).{0,120}(\i)=\i\.useMemo.{0,30}return \i\.useMemo\(\(\)=>\i\(\3).+?function\(\){return )\2(?=})/, + replace: (_, rest, settingsHook) => `${rest}$self.wrapSettingsHook(${settingsHook})` + } + ] }, { find: "Messages.USER_SETTINGS_ACTIONS_MENU_LABEL", diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx index 26e78ea26..ddcabcbdf 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 { findByCodeLazy, findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack"; import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, Forms, IconUtils, lodash, Parser, PermissionsBits, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common"; import type { Emoji } from "@webpack/types"; import type { Message } from "discord-types/general"; @@ -52,6 +52,7 @@ const PreloadedUserSettingsActionCreators = proxyLazyWebpack(() => UserSettingsA const AppearanceSettingsActionCreators = proxyLazyWebpack(() => searchProtoClassField("appearance", PreloadedUserSettingsActionCreators.ProtoClass)); const ClientThemeSettingsActionsCreators = proxyLazyWebpack(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators)); +const isUnusableRoleSubscriptionEmoji = findByCodeLazy(".getUserIsAdmin("); const enum EmojiIntentions { REACTION, @@ -234,16 +235,14 @@ export default definePlugin({ } ] }, - // FIXME // Allows the usage of subscription-locked emojis - /* { + { find: ".getUserIsAdmin(", replacement: { - match: /(?=.+?\.getUserIsAdmin\((?<=function (\i)\(\i,\i\){.+?))(\i):function\(\){return \1}/, - // Replace the original export with a func that always returns false and alias the original - replace: "$2:()=>()=>false,isUnusableRoleSubscriptionEmojiOriginal:function(){return $1}" + match: /(function \i\(\i,\i)\){(.{0,250}.getUserIsAdmin\(.+?return!1})/, + replace: (_, rest1, rest2) => `${rest1},fakeNitroOriginal){if(!fakeNitroOriginal)return false;${rest2}` } - }, */ + }, // Allow stickers to be sent everywhere { find: "canUseCustomStickersEverywhere:function", @@ -817,9 +816,7 @@ export default definePlugin({ if (e.type === 0) return true; if (e.available === false) return false; - // FIXME - /* const isUnusableRoleSubEmoji = isUnusableRoleSubscriptionEmojiOriginal ?? RoleSubscriptionEmojiUtils.isUnusableRoleSubscriptionEmoji; - if (isUnusableRoleSubEmoji(e, this.guildId)) return false; */ + if (isUnusableRoleSubscriptionEmoji(e, this.guildId, true)) return false; if (this.canUseEmotes) return e.guildId === this.guildId || hasExternalEmojiPerms(channelId); From a01ee40591f7a1e49b903b24a5ab2a6b278b34a2 Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Wed, 19 Jun 2024 23:49:42 -0300 Subject: [PATCH 4/5] Clean-up related additions to mangled exports --- src/api/SettingsStores.ts | 69 --------------- src/api/UserSettingDefinitions.ts | 83 +++++++++++++++++++ src/api/index.ts | 7 +- ...gsStores.ts => userSettingsDefinitions.ts} | 21 +++-- src/plugins/betterRoleContext/index.tsx | 6 +- src/plugins/customRPC/index.tsx | 7 +- src/plugins/gameActivityToggle/index.tsx | 6 +- src/plugins/ignoreActivities/index.tsx | 6 +- src/plugins/messageLinkEmbeds/index.tsx | 6 +- src/utils/discord.tsx | 2 +- src/webpack/common/index.ts | 2 +- src/webpack/common/types/index.d.ts | 1 - src/webpack/common/types/settingsStores.ts | 11 --- src/webpack/common/types/utils.d.ts | 2 +- .../{settingsStores.ts => userSettings.ts} | 0 15 files changed, 120 insertions(+), 109 deletions(-) delete mode 100644 src/api/SettingsStores.ts create mode 100644 src/api/UserSettingDefinitions.ts rename src/plugins/_api/{settingsStores.ts => userSettingsDefinitions.ts} (52%) delete mode 100644 src/webpack/common/types/settingsStores.ts rename src/webpack/common/{settingsStores.ts => userSettings.ts} (100%) diff --git a/src/api/SettingsStores.ts b/src/api/SettingsStores.ts deleted file mode 100644 index 18139e4e6..000000000 --- a/src/api/SettingsStores.ts +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2023 Vendicated and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . -*/ - -import { proxyLazy } from "@utils/lazy"; -import { Logger } from "@utils/Logger"; -import { findModuleId, proxyLazyWebpack, wreq } from "@webpack"; - -import { Settings } from "./Settings"; - -interface Setting { - /** - * Get the setting value - */ - getSetting(): T; - /** - * Update the setting value - * @param value The new value - */ - updateSetting(value: T | ((old: T) => T)): Promise; - /** - * React hook for automatically updating components when the setting is updated - */ - useSetting(): T; - settingsStoreApiGroup: string; - settingsStoreApiName: string; -} - -export const SettingsStores: Array> | undefined = proxyLazyWebpack(() => { - const modId = findModuleId('"textAndImages","renderSpoilers"') as any; - if (modId == null) return new Logger("SettingsStoreAPI").error("Didn't find stores module."); - - const mod = wreq(modId); - if (mod == null) return; - - return Object.values(mod).filter((s: any) => s?.settingsStoreApiGroup) as any; -}); - -/** - * Get the store for a setting - * @param group The setting group - * @param name The name of the setting - */ -export function getSettingStore(group: string, name: string): Setting | undefined { - if (!Settings.plugins.SettingsStoreAPI.enabled) throw new Error("Cannot use SettingsStoreAPI without setting as dependency."); - - return SettingsStores?.find(s => s?.settingsStoreApiGroup === group && s?.settingsStoreApiName === name); -} - -/** - * getSettingStore but lazy - */ -export function getSettingStoreLazy(group: string, name: string) { - return proxyLazy(() => getSettingStore(group, name)); -} diff --git a/src/api/UserSettingDefinitions.ts b/src/api/UserSettingDefinitions.ts new file mode 100644 index 000000000..7a2ed5be9 --- /dev/null +++ b/src/api/UserSettingDefinitions.ts @@ -0,0 +1,83 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2023 Vendicated and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +import { proxyLazy } from "@utils/lazy"; +import { Logger } from "@utils/Logger"; +import { findModuleId, proxyLazyWebpack, wreq } from "@webpack"; + +import { Settings } from "./Settings"; + +interface UserSettingDefinition { + /** + * Get the setting value + */ + getSetting(): T; + /** + * Update the setting value + * @param value The new value + */ + updateSetting(value: T): Promise; + /** + * Update the setting value + * @param value A callback that accepts the old value as the first argument, and returns the new value + */ + updateSetting(value: (old: T) => T): Promise; + /** + * Stateful React hook for this setting value + */ + useSetting(): T; + userSettingDefinitionsAPIGroup: string; + userSettingDefinitionsAPIName: string; +} + +export const UserSettingsDefinitions: Record> | undefined = proxyLazyWebpack(() => { + const modId = findModuleId('"textAndImages","renderSpoilers"'); + if (modId == null) return new Logger("UserSettingDefinitionsAPI").error("Didn't find settings definitions module."); + + return wreq(modId as any); +}); + +/** + * Get the definition for a setting. + * + * @param group The setting group + * @param name The name of the setting + */ +export function getUserSettingDefinition(group: string, name: string): UserSettingDefinition | undefined { + if (!Settings.plugins.UserSettingDefinitionsAPI.enabled) throw new Error("Cannot use UserSettingDefinitionsAPI without setting as dependency."); + + for (const key in UserSettingsDefinitions) { + const userSettingDefinition = UserSettingsDefinitions[key]; + + if (userSettingDefinition.userSettingDefinitionsAPIGroup === group && userSettingDefinition.userSettingDefinitionsAPIName === name) { + return userSettingDefinition; + } + } +} + +/** + * {@link getUserSettingDefinition}, lazy. + * + * Get the definition for a setting. + * + * @param group The setting group + * @param name The name of the setting + */ +export function getUserSettingDefinitionLazy(group: string, name: string) { + return proxyLazy(() => getUserSettingDefinition(group, name)); +} diff --git a/src/api/index.ts b/src/api/index.ts index 737e06d60..5f8233d21 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -31,8 +31,8 @@ import * as $Notices from "./Notices"; import * as $Notifications from "./Notifications"; import * as $ServerList from "./ServerList"; import * as $Settings from "./Settings"; -import * as $SettingsStores from "./SettingsStores"; import * as $Styles from "./Styles"; +import * as $UserSettingDefinitions from "./UserSettingDefinitions"; /** * An API allowing you to listen to Message Clicks or run your own logic @@ -118,4 +118,7 @@ export const ChatButtons = $ChatButtons; */ export const MessageUpdater = $MessageUpdater; -export const SettingsStores = $SettingsStores; +/** + * An API allowing you to get the definition for an user setting + */ +export const UserSettingDefinitions = $UserSettingDefinitions; diff --git a/src/plugins/_api/settingsStores.ts b/src/plugins/_api/userSettingsDefinitions.ts similarity index 52% rename from src/plugins/_api/settingsStores.ts rename to src/plugins/_api/userSettingsDefinitions.ts index a888532ee..5577a1723 100644 --- a/src/plugins/_api/settingsStores.ts +++ b/src/plugins/_api/userSettingsDefinitions.ts @@ -20,23 +20,30 @@ import { Devs } from "@utils/constants"; import definePlugin from "@utils/types"; export default definePlugin({ - name: "SettingsStoreAPI", - description: "Patches Discord's SettingsStores to expose their group and name", + name: "UserSettingDefinitionsAPI", + description: "Patches Discord's UserSettingDefinitions to expose their group and name.", authors: [Devs.Nuckyz], patches: [ { find: ",updateSetting:", replacement: [ + // Main setting definition { - match: /(?<=INFREQUENT_USER_ACTION.{0,20}),useSetting:/, - replace: ",settingsStoreApiGroup:arguments[0],settingsStoreApiName:arguments[1]$&" + match: /(?<=INFREQUENT_USER_ACTION.{0,20},)useSetting:/, + replace: "userSettingDefinitionsAPIGroup:arguments[0],userSettingDefinitionsAPIName:arguments[1],$&" }, - // some wrapper. just make it copy the group and name + // Selective wrapper { - match: /updateSetting:.{0,20}shouldSync/, - replace: "settingsStoreApiGroup:arguments[0].settingsStoreApiGroup,settingsStoreApiName:arguments[0].settingsStoreApiName,$&" + match: /updateSetting:.{0,100}SELECTIVELY_SYNCED_USER_SETTINGS_UPDATE/, + replace: "userSettingDefinitionsAPIGroup:arguments[0].userSettingDefinitionsAPIGroup,userSettingDefinitionsAPIName:arguments[0].userSettingDefinitionsAPIName,$&" + }, + // Override wrapper + { + match: /updateSetting:.{0,60}USER_SETTINGS_OVERRIDE_CLEAR/, + replace: "userSettingDefinitionsAPIGroup:arguments[0].userSettingDefinitionsAPIGroup,userSettingDefinitionsAPIName:arguments[0].userSettingDefinitionsAPIName,$&" } + ] } ] diff --git a/src/plugins/betterRoleContext/index.tsx b/src/plugins/betterRoleContext/index.tsx index d69e188c0..77be0e2b0 100644 --- a/src/plugins/betterRoleContext/index.tsx +++ b/src/plugins/betterRoleContext/index.tsx @@ -5,7 +5,7 @@ */ import { definePluginSettings } from "@api/Settings"; -import { getSettingStoreLazy } from "@api/SettingsStores"; +import { getUserSettingDefinitionLazy } from "@api/UserSettingDefinitions"; import { ImageIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; import { getCurrentGuild, openImageModal } from "@utils/discord"; @@ -15,7 +15,7 @@ import { Clipboard, GuildStore, Menu, PermissionStore } from "@webpack/common"; const GuildSettingsActions = findByPropsLazy("open", "selectRole", "updateGuild"); -const DeveloperMode = getSettingStoreLazy("appearance", "developerMode")!; +const DeveloperMode = getUserSettingDefinitionLazy("appearance", "developerMode")!; function PencilIcon() { return ( @@ -65,7 +65,7 @@ export default definePlugin({ name: "BetterRoleContext", description: "Adds options to copy role color / edit role / view role icon when right clicking roles in the user profile", authors: [Devs.Ven, Devs.goodbee], - dependencies: ["SettingsStoreAPI"], + dependencies: ["UserSettingDefinitionsAPI"], settings, diff --git a/src/plugins/customRPC/index.tsx b/src/plugins/customRPC/index.tsx index 7e4e9a938..ed2de9b4d 100644 --- a/src/plugins/customRPC/index.tsx +++ b/src/plugins/customRPC/index.tsx @@ -17,7 +17,7 @@ */ import { definePluginSettings, Settings } from "@api/Settings"; -import { getSettingStoreLazy } from "@api/SettingsStores"; +import { getUserSettingDefinitionLazy } from "@api/UserSettingDefinitions"; import { ErrorCard } from "@components/ErrorCard"; import { Link } from "@components/Link"; import { Devs } from "@utils/constants"; @@ -33,8 +33,7 @@ const useProfileThemeStyle = findByCodeLazy("profileThemeStyle:", "--profile-gra const ActivityComponent = findComponentByCodeLazy("onOpenGameProfile"); const ActivityClassName = findByPropsLazy("activity", "buttonColor"); -const ShowCurrentGame = getSettingStoreLazy("status", "showCurrentGame")!; - +const ShowCurrentGame = getUserSettingDefinitionLazy("status", "showCurrentGame")!; 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)\//, ""); @@ -394,7 +393,7 @@ export default definePlugin({ name: "CustomRPC", description: "Allows you to set a custom rich presence.", authors: [Devs.captain, Devs.AutumnVN, Devs.nin0dev], - dependencies: ["SettingsStoreAPI"], + dependencies: ["UserSettingDefinitionsAPI"], start: setRpc, stop: () => setRpc(true), settings, diff --git a/src/plugins/gameActivityToggle/index.tsx b/src/plugins/gameActivityToggle/index.tsx index 4e2a390d6..e7353a5d9 100644 --- a/src/plugins/gameActivityToggle/index.tsx +++ b/src/plugins/gameActivityToggle/index.tsx @@ -17,8 +17,8 @@ */ import { definePluginSettings } from "@api/Settings"; -import { getSettingStoreLazy } from "@api/SettingsStores"; import { disableStyle, enableStyle } from "@api/Styles"; +import { getUserSettingDefinitionLazy } from "@api/UserSettingDefinitions"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; @@ -28,7 +28,7 @@ import style from "./style.css?managed"; const Button = findComponentByCodeLazy("Button.Sizes.NONE,disabled:"); -const ShowCurrentGame = getSettingStoreLazy("status", "showCurrentGame")!; +const ShowCurrentGame = getUserSettingDefinitionLazy("status", "showCurrentGame")!; function makeIcon(showCurrentGame?: boolean) { const { oldIcon } = settings.use(["oldIcon"]); @@ -87,7 +87,7 @@ export default definePlugin({ name: "GameActivityToggle", description: "Adds a button next to the mic and deafen button to toggle game activity.", authors: [Devs.Nuckyz, Devs.RuukuLada], - dependencies: ["SettingsStoreAPI"], + dependencies: ["UserSettingDefinitionsAPI"], settings, patches: [ diff --git a/src/plugins/ignoreActivities/index.tsx b/src/plugins/ignoreActivities/index.tsx index 6e34c79f3..431cd3e04 100644 --- a/src/plugins/ignoreActivities/index.tsx +++ b/src/plugins/ignoreActivities/index.tsx @@ -6,7 +6,7 @@ import * as DataStore from "@api/DataStore"; import { definePluginSettings, Settings } from "@api/Settings"; -import { getSettingStoreLazy } from "@api/SettingsStores"; +import { getUserSettingDefinitionLazy } from "@api/UserSettingDefinitions"; import ErrorBoundary from "@components/ErrorBoundary"; import { Flex } from "@components/Flex"; import { Devs } from "@utils/constants"; @@ -28,7 +28,7 @@ interface IgnoredActivity { const RunningGameStore = findStoreLazy("RunningGameStore"); -const ShowCurrentGame = getSettingStoreLazy("status", "showCurrentGame")!; +const ShowCurrentGame = getUserSettingDefinitionLazy("status", "showCurrentGame")!; function ToggleIcon(activity: IgnoredActivity, tooltipText: string, path: string, fill: string) { return ( @@ -208,7 +208,7 @@ export default definePlugin({ name: "IgnoreActivities", authors: [Devs.Nuckyz], description: "Ignore activities from showing up on your status ONLY. You can configure which ones are specifically ignored from the Registered Games and Activities tabs, or use the general settings below.", - dependencies: ["SettingsStoreAPI"], + dependencies: ["UserSettingDefinitionsAPI"], settings, diff --git a/src/plugins/messageLinkEmbeds/index.tsx b/src/plugins/messageLinkEmbeds/index.tsx index 70681fb28..9fefc82a2 100644 --- a/src/plugins/messageLinkEmbeds/index.tsx +++ b/src/plugins/messageLinkEmbeds/index.tsx @@ -19,7 +19,7 @@ import { addAccessory, removeAccessory } from "@api/MessageAccessories"; import { updateMessage } from "@api/MessageUpdater"; import { definePluginSettings } from "@api/Settings"; -import { getSettingStoreLazy } from "@api/SettingsStores"; +import { getUserSettingDefinitionLazy } from "@api/UserSettingDefinitions"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants.js"; import { classes } from "@utils/misc"; @@ -54,7 +54,7 @@ const ChannelMessage = findComponentByCodeLazy("childrenExecutedCommand:", ".hid const SearchResultClasses = findByPropsLazy("message", "searchResult"); const EmbedClasses = findByPropsLazy("embedAuthorIcon", "embedAuthor", "embedAuthor"); -const MessageDisplayCompact = getSettingStoreLazy("textAndImages", "messageDisplayCompact")!; +const MessageDisplayCompact = getUserSettingDefinitionLazy("textAndImages", "messageDisplayCompact")!; const messageLinkRegex = /(? } - // FIXME: wtf is this? do we need to pass some proper component?? + // Don't render forward message button renderForwardComponent={() => null} shouldHideMediaOptions={false} shouldAnimate diff --git a/src/webpack/common/index.ts b/src/webpack/common/index.ts index 5da3cc68b..4193330cb 100644 --- a/src/webpack/common/index.ts +++ b/src/webpack/common/index.ts @@ -20,9 +20,9 @@ export * from "./classes"; export * from "./components"; export * from "./menu"; export * from "./react"; -export * from "./settingsStores"; export * from "./stores"; export * as ComponentTypes from "./types/components.d"; export * as MenuTypes from "./types/menu.d"; export * as UtilTypes from "./types/utils.d"; +export * from "./userSettings"; export * from "./utils"; diff --git a/src/webpack/common/types/index.d.ts b/src/webpack/common/types/index.d.ts index 01c968553..a536cdcf1 100644 --- a/src/webpack/common/types/index.d.ts +++ b/src/webpack/common/types/index.d.ts @@ -21,6 +21,5 @@ export * from "./components"; export * from "./fluxEvents"; export * from "./i18nMessages"; export * from "./menu"; -export * from "./settingsStores"; export * from "./stores"; export * from "./utils"; diff --git a/src/webpack/common/types/settingsStores.ts b/src/webpack/common/types/settingsStores.ts deleted file mode 100644 index 5453ca352..000000000 --- a/src/webpack/common/types/settingsStores.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -export interface SettingsStore { - getSetting(): T; - updateSetting(value: T): void; - useSetting(): T; -} diff --git a/src/webpack/common/types/utils.d.ts b/src/webpack/common/types/utils.d.ts index 7f6249be6..ee3f69944 100644 --- a/src/webpack/common/types/utils.d.ts +++ b/src/webpack/common/types/utils.d.ts @@ -82,7 +82,7 @@ interface RestRequestData { retries?: number; } -export type RestAPI = Record<"delete" | "get" | "patch" | "post" | "put", (data: RestRequestData) => Promise>; +export type RestAPI = Record<"del" | "get" | "patch" | "post" | "put", (data: RestRequestData) => Promise>; export type Permissions = "CREATE_INSTANT_INVITE" | "KICK_MEMBERS" diff --git a/src/webpack/common/settingsStores.ts b/src/webpack/common/userSettings.ts similarity index 100% rename from src/webpack/common/settingsStores.ts rename to src/webpack/common/userSettings.ts From ceaaf9ab8a3bb87494573a8442f31cc4a2396bbf Mon Sep 17 00:00:00 2001 From: Nuckyz <61953774+Nuckyz@users.noreply.github.com> Date: Thu, 20 Jun 2024 01:00:07 -0300 Subject: [PATCH 5/5] Reporter: Test mapMangledModule --- src/debug/runReporter.ts | 15 ++++++++++++--- src/webpack/webpack.ts | 4 +++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/debug/runReporter.ts b/src/debug/runReporter.ts index 6c7a2a03f..ddd5e5f18 100644 --- a/src/debug/runReporter.ts +++ b/src/debug/runReporter.ts @@ -39,9 +39,8 @@ async function runReporter() { } if (searchType === "waitForStore") method = "findStore"; + let result: any; try { - let result: any; - if (method === "proxyLazyWebpack" || method === "LazyComponentWebpack") { const [factory] = args; result = factory(); @@ -50,16 +49,26 @@ async function runReporter() { result = await Webpack.extractAndLoadChunks(code, matcher); if (result === false) result = null; + } else if (method === "mapMangledModule") { + const [code, mapper] = args; + + result = Webpack.mapMangledModule(code, mapper); + if (Object.keys(result).length !== Object.keys(mapper).length) throw new Error("Webpack Find Fail"); } else { // @ts-ignore result = Webpack[method](...args); } - if (result == null || (result.$$vencordInternal != null && result.$$vencordInternal() == null)) throw "a rock at ben shapiro"; + if (result == null || (result.$$vencordInternal != null && result.$$vencordInternal() == null)) throw new Error("Webpack Find Fail"); } 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 if (method === "mapMangledModule") { + const failedMappings = Object.keys(args[1]).filter(key => result?.[key] == null); + + logMessage += `("${args[0]}", {\n${failedMappings.map(mapping => `\t${mapping}: ${args[1][mapping].toString().slice(0, 147)}...`).join(",\n")}\n})`; + } else logMessage += `(${args.map(arg => `"${arg}"`).join(", ")})`; ReporterLogger.log("Webpack Find Fail:", logMessage); diff --git a/src/webpack/webpack.ts b/src/webpack/webpack.ts index b536063e8..f776ab1c3 100644 --- a/src/webpack/webpack.ts +++ b/src/webpack/webpack.ts @@ -279,7 +279,7 @@ export function findModuleFactory(...code: string[]) { return wreq.m[id]; } -export const lazyWebpackSearchHistory = [] as Array<["find" | "findByProps" | "findByCode" | "findStore" | "findComponent" | "findComponentByCode" | "findExportedComponent" | "waitFor" | "waitForComponent" | "waitForStore" | "proxyLazyWebpack" | "LazyComponentWebpack" | "extractAndLoadChunks", any[]]>; +export const lazyWebpackSearchHistory = [] as Array<["find" | "findByProps" | "findByCode" | "findStore" | "findComponent" | "findComponentByCode" | "findExportedComponent" | "waitFor" | "waitForComponent" | "waitForStore" | "proxyLazyWebpack" | "LazyComponentWebpack" | "extractAndLoadChunks" | "mapMangledModule", any[]]>; /** * This is just a wrapper around {@link proxyLazy} to make our reporter test for your webpack finds. @@ -483,6 +483,8 @@ export const mapMangledModule = traceFunction("mapMangledModule", function mapMa * }) */ export function mapMangledModuleLazy(code: string, mappers: Record): Record { + if (IS_REPORTER) lazyWebpackSearchHistory.push(["mapMangledModule", [code, mappers]]); + return proxyLazy(() => mapMangledModule(code, mappers)); }