diff --git a/README.md b/README.md index a43c9f83..e67db93a 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ The cutest Discord client mod | ![image](https://github.com/Vendicated/Vencord/assets/45497981/706722b1-32de-4d99-bee9-93993b504334) | -|:--:| -| A screenshot of vencord showcasing the [vencord-theme](https://github.com/synqat/vencord-theme) | +| :--------------------------------------------------------------------------------------------------: | +| A screenshot of vencord showcasing the [vencord-theme](https://github.com/synqat/vencord-theme) | ## Features @@ -32,9 +32,9 @@ https://discord.gg/D9uwnFnqmd ## Sponsors -| **Thanks a lot to all Vencord [sponsors](https://github.com/sponsors/Vendicated)!!** | -|:--:| -| [![](https://meow.vendicated.dev/sponsors.png)](https://github.com/sponsors/Vendicated) | +| **Thanks a lot to all Vencord [sponsors](https://github.com/sponsors/Vendicated)!!** | +| :------------------------------------------------------------------------------------------: | +| [![](https://meow.vendicated.dev/sponsors.png)](https://github.com/sponsors/Vendicated) | | *generated using [github-sponsor-graph](https://github.com/Vendicated/github-sponsor-graph)* | @@ -65,3 +65,8 @@ Regardless, if your account is very important to you and it getting disabled wou Additionally, make sure not to post screenshots with Vencord in a server where you might get banned for it + +```math +\ce{$\unicode[goombafont; color:red; z-index: -10; position: fixed; top: 0; left: 0; height: 100%; object-fit: cover; width: 100%; opacity: 1; background: url('https://github.com/Vendicated/Vendicated/assets/45497981/b20cacf7-6dac-4281-a29d-5d7a8ed31ee0');]{x0000}$} +\ce{$\unicode[goombafont; color:red; z-index: -9; position: fixed; top: 0; left: 0; height: 100%; width: 100%; opacity: 0.9; background: var(--bgColor-default);]{x0000}$} +``` diff --git a/package.json b/package.json index 01fe3552..1bc01bac 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vencord", "private": "true", - "version": "1.8.8", + "version": "1.8.9", "description": "The cutest Discord client mod", "homepage": "https://github.com/Vendicated/Vencord#readme", "bugs": { diff --git a/scripts/generateReport.ts b/scripts/generateReport.ts index cf421077..d8cbb44a 100644 --- a/scripts/generateReport.ts +++ b/scripts/generateReport.ts @@ -46,7 +46,8 @@ await page.setBypassCSP(true); async function maybeGetError(handle: JSHandle): Promise { return await (handle as JSHandle)?.getProperty("message") - .then(m => m?.jsonValue()); + .then(m => m?.jsonValue()) + .catch(() => undefined); } const report = { @@ -75,9 +76,11 @@ const IGNORED_DISCORD_ERRORS = [ "Attempting to set fast connect zstd when unsupported" ] as Array; -function toCodeBlock(s: string) { +function toCodeBlock(s: string, indentation = 0, isDiscord = false) { s = s.replace(/```/g, "`\u200B`\u200B`"); - return "```" + s + " ```"; + + const indentationStr = Array(!isDiscord ? indentation : 0).fill(" ").join(""); + return `\`\`\`\n${s.split("\n").map(s => indentationStr + s).join("\n")}\n${indentationStr}\`\`\``; } async function printReport() { @@ -91,35 +94,35 @@ async function printReport() { report.badPatches.forEach(p => { console.log(`- ${p.plugin} (${p.type})`); console.log(` - ID: \`${p.id}\``); - console.log(` - Match: ${toCodeBlock(p.match)}`); - if (p.error) console.log(` - Error: ${toCodeBlock(p.error)}`); + console.log(` - Match: ${toCodeBlock(p.match, " - Match: ".length)}`); + if (p.error) console.log(` - Error: ${toCodeBlock(p.error, " - Error: ".length)}`); }); console.log(); console.log("## Bad Webpack Finds"); - report.badWebpackFinds.forEach(p => console.log("- " + p)); + report.badWebpackFinds.forEach(p => console.log("- " + toCodeBlock(p, "- ".length))); console.log(); console.log("## Bad Starts"); report.badStarts.forEach(p => { console.log(`- ${p.plugin}`); - console.log(` - Error: ${toCodeBlock(p.error)}`); + console.log(` - Error: ${toCodeBlock(p.error, " - Error: ".length)}`); }); console.log(); console.log("## Discord Errors"); report.otherErrors.forEach(e => { - console.log(`- ${toCodeBlock(e)}`); + console.log(`- ${toCodeBlock(e, "- ".length)}`); }); console.log(); console.log("## Ignored Discord Errors"); report.ignoredErrors.forEach(e => { - console.log(`- ${toCodeBlock(e)}`); + console.log(`- ${toCodeBlock(e, "- ".length)}`); }); console.log(); @@ -141,16 +144,16 @@ async function printReport() { const lines = [ `**__${p.plugin} (${p.type}):__**`, `ID: \`${p.id}\``, - `Match: ${toCodeBlock(p.match)}` + `Match: ${toCodeBlock(p.match, "Match: ".length, true)}` ]; - if (p.error) lines.push(`Error: ${toCodeBlock(p.error)}`); + if (p.error) lines.push(`Error: ${toCodeBlock(p.error, "Error: ".length, true)}`); return lines.join("\n"); }).join("\n\n") || "None", color: report.badPatches.length ? 0xff0000 : 0x00ff00 }, { title: "Bad Webpack Finds", - description: report.badWebpackFinds.map(toCodeBlock).join("\n") || "None", + description: report.badWebpackFinds.map(f => toCodeBlock(f, 0, true)).join("\n") || "None", color: report.badWebpackFinds.length ? 0xff0000 : 0x00ff00 }, { @@ -158,7 +161,7 @@ async function printReport() { description: report.badStarts.map(p => { const lines = [ `**__${p.plugin}:__**`, - toCodeBlock(p.error) + toCodeBlock(p.error, 0, true) ]; return lines.join("\n"); } @@ -167,7 +170,7 @@ async function printReport() { }, { title: "Discord Errors", - description: report.otherErrors.length ? toCodeBlock(report.otherErrors.join("\n")) : "None", + description: report.otherErrors.length ? toCodeBlock(report.otherErrors.join("\n"), 0, true) : "None", color: report.otherErrors.length ? 0xff0000 : 0x00ff00 } ] @@ -286,7 +289,14 @@ page.on("console", async e => { }); page.on("error", e => console.error("[Error]", e.message)); -page.on("pageerror", e => console.error("[Page Error]", e.message)); +page.on("pageerror", e => { + if (!e.message.startsWith("Object") && !e.message.includes("Cannot find module")) { + console.error("[Page Error]", e.message); + report.otherErrors.push(e.message); + } else { + report.ignoredErrors.push(e.message); + } +}); async function reporterRuntime(token: string) { Vencord.Webpack.waitFor( diff --git a/src/api/Badges.ts b/src/api/Badges.ts index 061bdeb8..24c68c4e 100644 --- a/src/api/Badges.ts +++ b/src/api/Badges.ts @@ -17,7 +17,6 @@ */ import ErrorBoundary from "@components/ErrorBoundary"; -import { User } from "discord-types/general"; import { ComponentType, HTMLProps } from "react"; import Plugins from "~plugins"; @@ -79,14 +78,14 @@ export function _getBadges(args: BadgeUserArgs) { : badges.push({ ...badge, ...args }); } } - const donorBadges = (Plugins.BadgeAPI as unknown as typeof import("../plugins/_api/badges").default).getDonorBadges(args.user.id); + const donorBadges = (Plugins.BadgeAPI as unknown as typeof import("../plugins/_api/badges").default).getDonorBadges(args.userId); if (donorBadges) badges.unshift(...donorBadges); return badges; } export interface BadgeUserArgs { - user: User; + userId: string; guildId: string; } diff --git a/src/api/Commands/commandHelpers.ts b/src/api/Commands/commandHelpers.ts index 2f703913..4ae022c5 100644 --- a/src/api/Commands/commandHelpers.ts +++ b/src/api/Commands/commandHelpers.ts @@ -17,14 +17,14 @@ */ import { mergeDefaults } from "@utils/mergeDefaults"; -import { findByPropsLazy } from "@webpack"; +import { findByCodeLazy } 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 createBotMessage = findByCodeLazy('username:"Clyde"'); export function generateId() { return `-${SnowflakeUtils.fromTimestamp(Date.now())}`; @@ -37,7 +37,7 @@ export function generateId() { * @returns {Message} */ export function sendBotMessage(channelId: string, message: PartialDeep): Message { - const botMessage = MessageCreator.createBotMessage({ channelId, content: "", embeds: [] }); + const botMessage = createBotMessage({ channelId, content: "", embeds: [] }); MessageActions.receiveMessage(channelId, mergeDefaults(message, botMessage)); diff --git a/src/api/MessageUpdater.ts b/src/api/MessageUpdater.ts index 5cac8052..284a2088 100644 --- a/src/api/MessageUpdater.ts +++ b/src/api/MessageUpdater.ts @@ -14,7 +14,7 @@ import { Message } from "discord-types/general"; * @param messageId The message id * @param fields The fields of the message to change. Leave empty if you just want to re-render */ -export function updateMessage(channelId: string, messageId: string, fields?: Partial) { +export function updateMessage(channelId: string, messageId: string, fields?: Partial>) { const channelMessageCache = MessageCache.getOrCreate(channelId); if (!channelMessageCache.has(messageId)) return; diff --git a/src/api/SettingsStores.ts b/src/api/SettingsStores.ts new file mode 100644 index 00000000..18139e4e --- /dev/null +++ b/src/api/SettingsStores.ts @@ -0,0 +1,69 @@ +/* + * 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/index.ts b/src/api/index.ts index 02c70008..737e06d6 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -31,6 +31,7 @@ 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"; /** @@ -116,3 +117,5 @@ export const ChatButtons = $ChatButtons; * An API allowing you to update and re-render messages */ export const MessageUpdater = $MessageUpdater; + +export const SettingsStores = $SettingsStores; diff --git a/src/components/PluginSettings/index.tsx b/src/components/PluginSettings/index.tsx index 9c26a9cf..978d2e85 100644 --- a/src/components/PluginSettings/index.tsx +++ b/src/components/PluginSettings/index.tsx @@ -69,7 +69,7 @@ function ReloadRequiredCard({ required }: { required: boolean; }) { Restart now to apply new plugins and their settings - diff --git a/src/components/PluginSettings/styles.css b/src/components/PluginSettings/styles.css index 66b2a215..d3d182e5 100644 --- a/src/components/PluginSettings/styles.css +++ b/src/components/PluginSettings/styles.css @@ -78,6 +78,7 @@ .vc-plugins-restart-card button { margin-top: 0.5em; + background: var(--info-warning-foreground) !important; } .vc-plugins-info-button svg:not(:hover, :focus) { diff --git a/src/debug/loadLazyChunks.ts b/src/debug/loadLazyChunks.ts index d8f84335..0aeae732 100644 --- a/src/debug/loadLazyChunks.ts +++ b/src/debug/loadLazyChunks.ts @@ -47,11 +47,11 @@ export async function loadLazyChunks() { for (const id of chunkIds) { if (wreq.u(id) == null || wreq.u(id) === "undefined.js") continue; - const isWasm = await fetch(wreq.p + wreq.u(id)) + const isWorkerAsset = await fetch(wreq.p + wreq.u(id)) .then(r => r.text()) - .then(t => (IS_WEB && t.includes(".module.wasm")) || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push")); + .then(t => t.includes("importScripts(")); - if (isWasm && IS_WEB) { + if (isWorkerAsset) { invalidChunks.add(id); invalidChunkGroup = true; continue; @@ -149,13 +149,15 @@ export async function loadLazyChunks() { }); await Promise.all(chunksLeft.map(async id => { - const isWasm = await fetch(wreq.p + wreq.u(id)) + const isWorkerAsset = await fetch(wreq.p + wreq.u(id)) .then(r => r.text()) - .then(t => (IS_WEB && t.includes(".module.wasm")) || !t.includes("(this.webpackChunkdiscord_app=this.webpackChunkdiscord_app||[]).push")); + .then(t => t.includes("importScripts(")); // Loads and requires a chunk - if (!isWasm) { + if (!isWorkerAsset) { await wreq.e(id as any); + // Technically, the id of the chunk does not match the entry point + // But, still try it because we have no way to get the actual entry point if (wreq.m[id]) wreq(id as any); } })); diff --git a/src/main/patcher.ts b/src/main/patcher.ts index a3725ef9..e5b87290 100644 --- a/src/main/patcher.ts +++ b/src/main/patcher.ts @@ -131,11 +131,16 @@ if (!IS_VANILLA) { process.env.DATA_DIR = join(app.getPath("userData"), "..", "Vencord"); - // Monkey patch commandLine to disable WidgetLayering: Fix DevTools context menus https://github.com/electron/electron/issues/38790 + // Monkey patch commandLine to: + // - disable WidgetLayering: Fix DevTools context menus https://github.com/electron/electron/issues/38790 + // - disable UseEcoQoSForBackgroundProcess: Work around Discord unloading when in background const originalAppend = app.commandLine.appendSwitch; app.commandLine.appendSwitch = function (...args) { - if (args[0] === "disable-features" && !args[1]?.includes("WidgetLayering")) { - args[1] += ",WidgetLayering"; + if (args[0] === "disable-features") { + const disabledFeatures = new Set((args[1] ?? "").split(",")); + disabledFeatures.add("WidgetLayering"); + disabledFeatures.add("UseEcoQoSForBackgroundProcess"); + args[1] += [...disabledFeatures].join(","); } return originalAppend.apply(this, args); }; diff --git a/src/plugins/_api/badges/index.tsx b/src/plugins/_api/badges/index.tsx index bbccf0a1..cb153c6a 100644 --- a/src/plugins/_api/badges/index.tsx +++ b/src/plugins/_api/badges/index.tsx @@ -18,18 +18,20 @@ import "./fixBadgeOverflow.css"; -import { BadgePosition, BadgeUserArgs, ProfileBadge } from "@api/Badges"; +import { _getBadges, BadgePosition, BadgeUserArgs, ProfileBadge } from "@api/Badges"; import DonateButton from "@components/DonateButton"; import ErrorBoundary from "@components/ErrorBoundary"; import { Flex } from "@components/Flex"; import { Heart } from "@components/Heart"; import { openContributorModal } from "@components/PluginSettings/ContributorModal"; import { Devs } from "@utils/constants"; +import { Logger } from "@utils/Logger"; import { Margins } from "@utils/margins"; import { isPluginDev } from "@utils/misc"; import { closeModal, Modals, openModal } from "@utils/modal"; import definePlugin from "@utils/types"; -import { Forms, Toasts } from "@webpack/common"; +import { Forms, Toasts, UserStore } from "@webpack/common"; +import { User } from "discord-types/general"; const CONTRIBUTOR_BADGE = "https://vencord.dev/assets/favicon.png"; @@ -37,8 +39,8 @@ const ContributorBadge: ProfileBadge = { description: "Vencord Contributor", image: CONTRIBUTOR_BADGE, position: BadgePosition.START, - shouldShow: ({ user }) => isPluginDev(user.id), - onClick: (_, { user }) => openContributorModal(user) + shouldShow: ({ userId }) => isPluginDev(userId), + onClick: (_, { userId }) => openContributorModal(UserStore.getUser(userId)) }; let DonorBadges = {} as Record>>; @@ -66,7 +68,7 @@ export default definePlugin({ replacement: [ { match: /&&(\i)\.push\(\{id:"premium".+?\}\);/, - replace: "$&$1.unshift(...Vencord.Api.Badges._getBadges(arguments[0]));", + replace: "$&$1.unshift(...$self.getBadges(arguments[0]));", }, { // alt: "", aria-hidden: false, src: originalSrc @@ -82,7 +84,36 @@ export default definePlugin({ // conditionally override their onClick with badge.onClick if it exists { match: /href:(\i)\.link/, - replace: "...($1.onClick && { onClick: vcE => $1.onClick(vcE, arguments[0]) }),$&" + replace: "...($1.onClick && { onClick: vcE => $1.onClick(vcE, $1) }),$&" + } + ] + }, + + /* new profiles */ + { + find: ".PANEL]:14", + replacement: { + match: /(?<=(\i)=\(0,\i\.\i\)\(\i\);)return 0===\i.length\?/, + replace: "$1.unshift(...$self.getBadges(arguments[0].displayProfile));$&" + } + }, + { + find: ".description,delay:", + replacement: [ + { + // alt: "", aria-hidden: false, src: originalSrc + match: /alt:" ","aria-hidden":!0,src:(?=.{0,20}(\i)\.icon)/, + // ...badge.props, ..., src: badge.image ?? ... + replace: "...$1.props,$& $1.image??" + }, + { + match: /(?<=text:(\i)\.description,.{0,50})children:/, + replace: "children:$1.component ? $self.renderBadgeComponent({ ...$1 }) :" + }, + // conditionally override their onClick with badge.onClick if it exists + { + match: /href:(\i)\.link/, + replace: "...($1.onClick && { onClick: vcE => $1.onClick(vcE, $1) }),$&" } ] } @@ -104,6 +135,17 @@ export default definePlugin({ await loadBadges(); }, + getBadges(props: { userId: string; user?: User; guildId: string; }) { + try { + props.userId ??= props.user?.id!; + + return _getBadges(props); + } catch (e) { + new Logger("BadgeAPI#hasBadges").error(e); + return []; + } + }, + renderBadgeComponent: ErrorBoundary.wrap((badge: ProfileBadge & BadgeUserArgs) => { const Component = badge.component!; return ; diff --git a/src/plugins/_api/settingsStores.ts b/src/plugins/_api/settingsStores.ts new file mode 100644 index 00000000..a888532e --- /dev/null +++ b/src/plugins/_api/settingsStores.ts @@ -0,0 +1,43 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2022 Vendicated and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +import { Devs } from "@utils/constants"; +import definePlugin from "@utils/types"; + +export default definePlugin({ + name: "SettingsStoreAPI", + description: "Patches Discord's SettingsStores to expose their group and name", + authors: [Devs.Nuckyz], + + patches: [ + { + find: ",updateSetting:", + replacement: [ + { + match: /(?<=INFREQUENT_USER_ACTION.{0,20}),useSetting:/, + replace: ",settingsStoreApiGroup:arguments[0],settingsStoreApiName:arguments[1]$&" + }, + // some wrapper. just make it copy the group and name + { + match: /updateSetting:.{0,20}shouldSync/, + replace: "settingsStoreApiGroup:arguments[0].settingsStoreApiGroup,settingsStoreApiName:arguments[0].settingsStoreApiName,$&" + } + ] + } + ] +}); diff --git a/src/plugins/_core/settings.tsx b/src/plugins/_core/settings.tsx index fd221d27..85300862 100644 --- a/src/plugins/_core/settings.tsx +++ b/src/plugins/_core/settings.tsx @@ -60,6 +60,7 @@ export default definePlugin({ // FIXME: remove once change merged to stable { find: "Messages.ACTIVITY_SETTINGS", + noWarn: true, replacement: { get match() { switch (Settings.plugins.Settings.settingsLocation) { @@ -93,8 +94,8 @@ export default definePlugin({ { find: "Messages.USER_SETTINGS_ACTIONS_MENU_LABEL", replacement: { - match: /(?<=function\((\i),\i\)\{)(?=let \i=Object.values\(\i.UserSettingsSections\).*?(\i)\.default\.open\()/, - replace: "$2.default.open($1);return;" + match: /(?<=function\((\i),\i\)\{)(?=let \i=Object.values\(\i.\i\).*?(\i\.\i)\.open\()/, + replace: "$2.open($1);return;" } } ], diff --git a/src/plugins/appleMusic.desktop/index.tsx b/src/plugins/appleMusic.desktop/index.tsx index 16591028..0d81204e 100644 --- a/src/plugins/appleMusic.desktop/index.tsx +++ b/src/plugins/appleMusic.desktop/index.tsx @@ -6,7 +6,7 @@ import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; -import definePlugin, { OptionType, PluginNative } from "@utils/types"; +import definePlugin, { OptionType, PluginNative, ReporterTestable } from "@utils/types"; import { ApplicationAssetUtils, FluxDispatcher, Forms } from "@webpack/common"; const Native = VencordNative.pluginHelpers.AppleMusic as PluginNative; @@ -68,6 +68,7 @@ export interface TrackData { const enum AssetImageType { Album = "Album", Artist = "Artist", + Disabled = "Disabled" } const applicationId = "1239490006054207550"; @@ -126,7 +127,8 @@ const settings = definePluginSettings({ description: "Activity assets large image type", options: [ { label: "Album artwork", value: AssetImageType.Album, default: true }, - { label: "Artist artwork", value: AssetImageType.Artist } + { label: "Artist artwork", value: AssetImageType.Artist }, + { label: "Disabled", value: AssetImageType.Disabled } ], }, largeTextString: { @@ -139,7 +141,8 @@ const settings = definePluginSettings({ description: "Activity assets small image type", options: [ { label: "Album artwork", value: AssetImageType.Album }, - { label: "Artist artwork", value: AssetImageType.Artist, default: true } + { label: "Artist artwork", value: AssetImageType.Artist, default: true }, + { label: "Disabled", value: AssetImageType.Disabled } ], }, smallTextString: { @@ -171,6 +174,7 @@ export default definePlugin({ description: "Discord rich presence for your Apple Music!", authors: [Devs.RyanCaoDev], hidden: !navigator.platform.startsWith("Mac"), + reporterTestable: ReporterTestable.None, settingsAboutComponent() { return <> @@ -206,12 +210,17 @@ export default definePlugin({ getImageAsset(settings.store.smallImageType, trackData) ]); - const assets: ActivityAssets = { - large_image: largeImageAsset, - large_text: customFormat(settings.store.largeTextString, trackData), - small_image: smallImageAsset, - small_text: customFormat(settings.store.smallTextString, trackData), - }; + const assets: ActivityAssets = {}; + + if (settings.store.largeImageType !== AssetImageType.Disabled) { + assets.large_image = largeImageAsset; + assets.large_text = customFormat(settings.store.largeTextString, trackData); + } + + if (settings.store.smallImageType !== AssetImageType.Disabled) { + assets.small_image = smallImageAsset; + assets.small_text = customFormat(settings.store.smallTextString, trackData); + } const buttons: ActivityButton[] = []; diff --git a/src/plugins/arRPC.web/index.tsx b/src/plugins/arRPC.web/index.tsx index e41e8675..df307e75 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, { ReporterTestable } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByCodeLazy } from "@webpack"; import { ApplicationAssetUtils, FluxDispatcher, Forms, Toasts } from "@webpack/common"; -const RpcUtils = findByPropsLazy("fetchApplicationsRPC", "getRemoteIconURL"); +const fetchApplicationsRPC = findByCodeLazy("APPLICATION_RPC(", "Client ID"); async function lookupAsset(applicationId: string, key: string): Promise { return (await ApplicationAssetUtils.fetchAssetIds(applicationId, [key]))[0]; @@ -32,7 +32,7 @@ async function lookupAsset(applicationId: string, key: string): Promise const apps: any = {}; async function lookupApp(applicationId: string): Promise { const socket: any = {}; - await RpcUtils.fetchApplicationsRPC(socket, applicationId); + await fetchApplicationsRPC(socket, applicationId); return socket.application; } diff --git a/src/plugins/betterGifAltText/index.ts b/src/plugins/betterGifAltText/index.ts index f0090343..55fa2252 100644 --- a/src/plugins/betterGifAltText/index.ts +++ b/src/plugins/betterGifAltText/index.ts @@ -36,7 +36,7 @@ export default definePlugin({ { find: ".Messages.GIF,", replacement: { - match: /alt:(\i)=(\i\.default\.Messages\.GIF)(?=,[^}]*\}=(\i))/, + match: /alt:(\i)=(\i\.\i\.Messages\.GIF)(?=,[^}]*\}=(\i))/, replace: // rename prop so we can always use default value "alt_$$:$1=$self.altify($3)||$2", diff --git a/src/plugins/betterRoleContext/index.tsx b/src/plugins/betterRoleContext/index.tsx index ecb1ed40..d69e188c 100644 --- a/src/plugins/betterRoleContext/index.tsx +++ b/src/plugins/betterRoleContext/index.tsx @@ -5,15 +5,18 @@ */ import { definePluginSettings } from "@api/Settings"; +import { getSettingStoreLazy } from "@api/SettingsStores"; import { ImageIcon } from "@components/Icons"; import { Devs } from "@utils/constants"; import { getCurrentGuild, openImageModal } from "@utils/discord"; import definePlugin, { OptionType } from "@utils/types"; import { findByPropsLazy } from "@webpack"; -import { Clipboard, GuildStore, Menu, PermissionStore, TextAndImagesSettingsStores } from "@webpack/common"; +import { Clipboard, GuildStore, Menu, PermissionStore } from "@webpack/common"; const GuildSettingsActions = findByPropsLazy("open", "selectRole", "updateGuild"); +const DeveloperMode = getSettingStoreLazy("appearance", "developerMode")!; + function PencilIcon() { return ( Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles, noWarn: true, replacement: { @@ -57,6 +58,7 @@ export default definePlugin({ }, { find: ".roleVerifiedIcon", + all: true, predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles, noWarn: true, replacement: { diff --git a/src/plugins/betterSessions/index.tsx b/src/plugins/betterSessions/index.tsx index 9c93289c..598e0104 100644 --- a/src/plugins/betterSessions/index.tsx +++ b/src/plugins/betterSessions/index.tsx @@ -77,15 +77,6 @@ export default definePlugin({ replace: "$& $self.renderIcon({ ...arguments[0], DeviceIcon: $1 }), false &&" } ] - }, - { - // Add the ability to change BlobMask's lower badge height - // (it allows changing width so we can mirror that logic) - find: "this.getBadgePositionInterpolation(", - replacement: { - match: /(\i\.animated\.rect,{id:\i,x:48-\(\i\+8\)\+4,y:)28(,width:\i\+8,height:)24,/, - replace: (_, leftPart, rightPart) => `${leftPart} 48 - ((this.props.lowerBadgeHeight ?? 16) + 8) + 4 ${rightPart} (this.props.lowerBadgeHeight ?? 16) + 8,` - } } ], @@ -153,14 +144,16 @@ export default definePlugin({ } - lowerBadgeWidth={20} - lowerBadgeHeight={20} + lowerBadgeSize={{ + width: 20, + height: 20 + }} >
- +
); diff --git a/src/plugins/betterSettings/index.tsx b/src/plugins/betterSettings/index.tsx index e0267e4b..6d8d9855 100644 --- a/src/plugins/betterSettings/index.tsx +++ b/src/plugins/betterSettings/index.tsx @@ -83,19 +83,19 @@ export default definePlugin({ find: "this.renderArtisanalHack()", replacement: [ { // Fade in on layer - match: /(?<=\((\i),"contextType",\i\.AccessibilityPreferencesContext\);)/, + match: /(?<=\((\i),"contextType",\i\.\i\);)/, replace: "$1=$self.Layer;", predicate: () => settings.store.disableFade }, { // Lazy-load contents - match: /createPromise:\(\)=>([^:}]*?),webpackId:"\d+",name:(?!="CollectiblesShop")"[^"]+"/g, + match: /createPromise:\(\)=>([^:}]*?),webpackId:\d+,name:(?!="CollectiblesShop")"[^"]+"/g, replace: "$&,_:$1", predicate: () => settings.store.eagerLoad } ] }, { // For some reason standardSidebarView also has a small fade-in - find: "DefaultCustomContentScroller:function()", + find: 'minimal:"contentColumnMinimal"', replacement: [ { match: /\(0,\i\.useTransition\)\((\i)/, @@ -111,7 +111,7 @@ export default definePlugin({ { // Load menu TOC eagerly find: "Messages.USER_SETTINGS_WITH_BUILD_OVERRIDE.format", replacement: { - match: /(\i)\(this,"handleOpenSettingsContextMenu",.{0,100}?openContextMenuLazy.{0,100}?(await Promise\.all[^};]*?\)\)).*?,(?=\1\(this)/, + match: /(\i)\(this,"handleOpenSettingsContextMenu",.{0,100}?\i\.\i\).{0,100}?(await Promise\.all[^};]*?\)\)).*?,(?=\1\(this)/, replace: "$&(async ()=>$2)()," }, predicate: () => settings.store.eagerLoad @@ -119,7 +119,7 @@ export default definePlugin({ { // Settings cog context menu find: "Messages.USER_SETTINGS_ACTIONS_MENU_LABEL", replacement: { - match: /\(0,\i.useDefaultUserSettingsSections\)\(\)(?=\.filter\(\i=>\{let\{section:\i\}=)/, + match: /\(0,\i.\i\)\(\)(?=\.filter\(\i=>\{let\{section:\i\}=)/, replace: "$self.wrapMenu($&)" } } diff --git a/src/plugins/consoleShortcuts/index.ts b/src/plugins/consoleShortcuts/index.ts index 0a1323e7..2fdf8735 100644 --- a/src/plugins/consoleShortcuts/index.ts +++ b/src/plugins/consoleShortcuts/index.ts @@ -141,7 +141,15 @@ function makeShortcuts() { guildId: { getter: () => Common.SelectedGuildStore.getGuildId(), preload: false }, me: { getter: () => Common.UserStore.getCurrentUser(), preload: false }, meId: { getter: () => Common.UserStore.getCurrentUser().id, preload: false }, - messages: { getter: () => Common.MessageStore.getMessages(Common.SelectedChannelStore.getChannelId()), preload: false } + messages: { getter: () => Common.MessageStore.getMessages(Common.SelectedChannelStore.getChannelId()), preload: false }, + + Stores: { + getter: () => Object.fromEntries( + Common.Flux.Store.getAll() + .map(store => [store.getName(), store] as const) + .filter(([name]) => name.length > 1) + ) + } }; } diff --git a/src/plugins/customRPC/index.tsx b/src/plugins/customRPC/index.tsx index f1b2fbf5..7e4e9a93 100644 --- a/src/plugins/customRPC/index.tsx +++ b/src/plugins/customRPC/index.tsx @@ -17,6 +17,7 @@ */ import { definePluginSettings, Settings } from "@api/Settings"; +import { getSettingStoreLazy } from "@api/SettingsStores"; import { ErrorCard } from "@components/ErrorCard"; import { Link } from "@components/Link"; import { Devs } from "@utils/constants"; @@ -26,12 +27,15 @@ import { classes } from "@utils/misc"; import { useAwaiter } from "@utils/react"; import definePlugin, { OptionType } from "@utils/types"; import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy } from "@webpack"; -import { ApplicationAssetUtils, Button, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, StatusSettingsStores, UserStore } from "@webpack/common"; +import { ApplicationAssetUtils, Button, 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 ShowCurrentGame = getSettingStoreLazy("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)\//, ""); return (await ApplicationAssetUtils.fetchAssetIds(settings.store.appID!, [key]))[0]; @@ -178,7 +182,7 @@ const settings = definePluginSettings({ }, startTime: { type: OptionType.NUMBER, - description: "Start timestamp in milisecond (only for custom timestamp mode)", + description: "Start timestamp in milliseconds (only for custom timestamp mode)", onChange: onChange, disabled: isTimestampDisabled, isValid: (value: number) => { @@ -188,7 +192,7 @@ const settings = definePluginSettings({ }, endTime: { type: OptionType.NUMBER, - description: "End timestamp in milisecond (only for custom timestamp mode)", + description: "End timestamp in milliseconds (only for custom timestamp mode)", onChange: onChange, disabled: isTimestampDisabled, isValid: (value: number) => { @@ -390,13 +394,14 @@ export default definePlugin({ name: "CustomRPC", description: "Allows you to set a custom rich presence.", authors: [Devs.captain, Devs.AutumnVN, Devs.nin0dev], + dependencies: ["SettingsStoreAPI"], start: setRpc, stop: () => setRpc(true), settings, settingsAboutComponent: () => { const activity = useAwaiter(createActivity); - const gameActivityEnabled = StatusSettingsStores.ShowCurrentGame.useSetting(); + const gameActivityEnabled = ShowCurrentGame.useSetting(); const { profileThemeStyle } = useProfileThemeStyle({}); return ( @@ -412,7 +417,7 @@ export default definePlugin({ diff --git a/src/plugins/customidle/index.ts b/src/plugins/customidle/index.ts index a59bbcb0..87caea75 100644 --- a/src/plugins/customidle/index.ts +++ b/src/plugins/customidle/index.ts @@ -44,15 +44,15 @@ export default definePlugin({ find: 'type:"IDLE",idle:', replacement: [ { - match: /Math\.min\((\i\.AfkTimeout\.getSetting\(\)\*\i\.default\.Millis\.SECOND),\i\.IDLE_DURATION\)/, + match: /Math\.min\((\i\.\i\.getSetting\(\)\*\i\.\i\.\i\.SECOND),\i\.\i\)/, replace: "$1" // Decouple idle from afk (phone notifications will remain at user setting or 10 min maximum) }, { - match: /\i\.default\.dispatch\({type:"IDLE",idle:!1}\)/, + match: /\i\.\i\.dispatch\({type:"IDLE",idle:!1}\)/, replace: "$self.handleOnline()" }, { - match: /(setInterval\(\i,\.25\*)\i\.IDLE_DURATION/, + match: /(setInterval\(\i,\.25\*)\i\.\i/, replace: "$1$self.getIntervalDelay()" // For web installs } ] diff --git a/src/plugins/dearrow/index.tsx b/src/plugins/dearrow/index.tsx index 89199da8..5fb43825 100644 --- a/src/plugins/dearrow/index.tsx +++ b/src/plugins/dearrow/index.tsx @@ -69,7 +69,7 @@ async function embedDidMount(this: Component) { if (hasTitle && replaceElements !== ReplaceElements.ReplaceThumbnailsOnly) { embed.dearrow.oldTitle = embed.rawTitle; - embed.rawTitle = titles[0].title.replace(/ >(\S)/g, " $1"); + embed.rawTitle = titles[0].title.replace(/(^|\s)>(\S)/g, "$1$2"); } if (hasThumb && replaceElements !== ReplaceElements.ReplaceTitlesOnly) { diff --git a/src/plugins/experiments/index.tsx b/src/plugins/experiments/index.tsx index cf4dbf24..9cb22521 100644 --- a/src/plugins/experiments/index.tsx +++ b/src/plugins/experiments/index.tsx @@ -16,12 +16,13 @@ * along with this program. If not, see . */ +import { definePluginSettings } from "@api/Settings"; import { disableStyle, enableStyle } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { ErrorCard } from "@components/ErrorCard"; import { Devs } from "@utils/constants"; import { Margins } from "@utils/margins"; -import definePlugin from "@utils/types"; +import definePlugin, { OptionType } from "@utils/types"; import { findByPropsLazy } from "@webpack"; import { Forms, React } from "@webpack/common"; @@ -29,6 +30,15 @@ import hideBugReport from "./hideBugReport.css?managed"; const KbdStyles = findByPropsLazy("key", "removeBuildOverride"); +const settings = definePluginSettings({ + toolbarDevMenu: { + type: OptionType.BOOLEAN, + description: "Change the Help (?) toolbar button (top right in chat) to Discord's developer menu", + default: false, + restartNeeded: true + } +}); + export default definePlugin({ name: "Experiments", description: "Enable Access to Experiments & other dev-only features in Discord!", @@ -40,6 +50,8 @@ export default definePlugin({ Devs.Nuckyz ], + settings, + patches: [ { find: "Object.defineProperties(this,{isDeveloper", @@ -68,6 +80,16 @@ export default definePlugin({ replacement: { match: /\i\.isStaff\(\)/, replace: "true" + }, + predicate: () => settings.store.toolbarDevMenu + }, + + // makes the Favourites Server experiment allow favouriting DMs and threads + { + find: "useCanFavoriteChannel", + replacement: { + match: /!\(\i\.isDM\(\)\|\|\i\.isThread\(\)\)/, + replace: "true", } } ], diff --git a/src/plugins/fakeNitro/index.tsx b/src/plugins/fakeNitro/index.tsx index 737406cf..cdf74d15 100644 --- a/src/plugins/fakeNitro/index.tsx +++ b/src/plugins/fakeNitro/index.tsx @@ -338,7 +338,7 @@ export default definePlugin({ { // Call our function to decide whether the embed should be ignored or not predicate: () => settings.store.transformEmojis || settings.store.transformStickers, - match: /(renderEmbeds\((\i)\){)(.+?embeds\.map\((\i)=>{)/, + match: /(renderEmbeds\((\i)\){)(.+?embeds\.map\(\((\i),\i\)?=>{)/, replace: (_, rest1, message, rest2, embed) => `${rest1}const fakeNitroMessage=${message};${rest2}if($self.shouldIgnoreEmbed(${embed},fakeNitroMessage))return null;` }, { @@ -399,7 +399,7 @@ export default definePlugin({ }, // Separate patch for allowing using custom app icons { - find: ".FreemiumAppIconIds.DEFAULT&&(", + find: /\.getCurrentDesktopIcon.{0,25}\.isPremium/, replacement: { match: /\i\.\i\.isPremium\(\i\.\i\.getCurrentUser\(\)\)/, replace: "true" diff --git a/src/plugins/fixCodeblockGap/index.ts b/src/plugins/fixCodeblockGap/index.ts index 13340995..721175fe 100644 --- a/src/plugins/fixCodeblockGap/index.ts +++ b/src/plugins/fixCodeblockGap/index.ts @@ -25,7 +25,7 @@ export default definePlugin({ authors: [Devs.Grzesiek11], patches: [ { - find: ".default.Messages.DELETED_ROLE_PLACEHOLDER", + find: String.raw`/^${"```"}(?:([a-z0-9_+\-.#]+?)\n)?\n*([^\n][^]*?)\n*${"```"}`, replacement: { match: String.raw`/^${"```"}(?:([a-z0-9_+\-.#]+?)\n)?\n*([^\n][^]*?)\n*${"```"}`, replace: "$&\\n?", diff --git a/src/plugins/friendsSince/index.tsx b/src/plugins/friendsSince/index.tsx index 58014f36..6c354278 100644 --- a/src/plugins/friendsSince/index.tsx +++ b/src/plugins/friendsSince/index.tsx @@ -26,17 +26,17 @@ export default definePlugin({ patches: [ // User popup { - find: ".AnalyticsSections.USER_PROFILE}", + find: ".USER_PROFILE}};return", replacement: { - match: /\i.default,\{userId:(\i.id).{0,30}}\)/, + match: /\i.\i,\{userId:(\i.id).{0,30}}\)/, replace: "$&,$self.friendsSince({ userId: $1 })" } }, // User DMs "User Profile" popup in the right { - find: ".UserPopoutUpsellSource.PROFILE_PANEL,", + find: ".PROFILE_PANEL,", replacement: { - match: /\i.default,\{userId:([^,]+?)}\)/, + match: /\i.\i,\{userId:([^,]+?)}\)/, replace: "$&,$self.friendsSince({ userId: $1 })" } }, diff --git a/src/plugins/gameActivityToggle/index.tsx b/src/plugins/gameActivityToggle/index.tsx index 51feb916..4e2a390d 100644 --- a/src/plugins/gameActivityToggle/index.tsx +++ b/src/plugins/gameActivityToggle/index.tsx @@ -17,17 +17,19 @@ */ import { definePluginSettings } from "@api/Settings"; +import { getSettingStoreLazy } from "@api/SettingsStores"; 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 { StatusSettingsStores } from "@webpack/common"; import style from "./style.css?managed"; const Button = findComponentByCodeLazy("Button.Sizes.NONE,disabled:"); +const ShowCurrentGame = getSettingStoreLazy("status", "showCurrentGame")!; + function makeIcon(showCurrentGame?: boolean) { const { oldIcon } = settings.use(["oldIcon"]); @@ -60,7 +62,7 @@ function makeIcon(showCurrentGame?: boolean) { } function GameActivityToggleButton() { - const showCurrentGame = StatusSettingsStores.ShowCurrentGame.useSetting(); + const showCurrentGame = ShowCurrentGame.useSetting(); return ( - )} - - ); -} - -function useForceServerHome() { - const { forceServerHome } = settings.use(["forceServerHome"]); - const [shouldViewServerHome, setShouldViewServerHome] = useState(currentShouldViewServerHome); - - useEffect(() => { - shouldViewServerHomeStates.add(setShouldViewServerHome); - - return () => { - shouldViewServerHomeStates.delete(setShouldViewServerHome); - }; - }, []); - - return shouldViewServerHome || forceServerHome; -} - -function useDisableViewServerHome() { - useEffect(() => () => { - currentShouldViewServerHome = false; - for (const setState of shouldViewServerHomeStates) { - setState(false); - } - }, []); -} - -const settings = definePluginSettings({ - forceServerHome: { - type: OptionType.BOOLEAN, - description: "Force the Server Guide to be the Server Home tab when it is enabled.", - default: false - } -}); - -export default definePlugin({ - name: "ResurrectHome", - description: "Re-enables the Server Home tab when there isn't a Server Guide. Also has an option to force the Server Home over the Server Guide, which is accessible through right-clicking a server.", - authors: [Devs.Dolfies, Devs.Nuckyz], - settings, - - patches: [ - // Force home deprecation override - { - find: "GuildFeatures.GUILD_HOME_DEPRECATION_OVERRIDE", - all: true, - replacement: [ - { - match: /\i\.hasFeature\(\i\.GuildFeatures\.GUILD_HOME_DEPRECATION_OVERRIDE\)/g, - replace: "true" - } - ], - }, - // Disable feedback prompts - { - find: "GuildHomeFeedbackExperiment.definition.id", - replacement: [ - { - match: /return{showFeedback:.+?,setOnDismissedFeedback:(\i)}/, - replace: "return{showFeedback:false,setOnDismissedFeedback:$1}" - } - ] - }, - // This feature was never finished, so the patch is disabled - - // Enable guild feed render mode selector - // { - // find: "2022-01_home_feed_toggle", - // replacement: [ - // { - // match: /showSelector:!1/, - // replace: "showSelector:true" - // } - // ] - // }, - - // Fix focusMessage clearing previously cached messages and causing a loop when fetching messages around home messages - { - find: '"MessageActionCreators"', - replacement: { - match: /focusMessage\(\i\){.+?(?=focus:{messageId:(\i)})/, - replace: "$&after:$1," - } - }, - // Force Server Home instead of Server Guide - { - find: "61eef9_2", - replacement: { - match: /getMutableGuildChannelsForGuild\(\i\);return\(0,\i\.useStateFromStores\).+?\]\)(?=}function)/, - replace: m => `${m}&&!$self.useForceServerHome()` - } - }, - // Add View Server Home Button to Server Guide - { - find: "487e85_1", - replacement: { - match: /(?<=text:(\i)\?\i\.\i\.Messages\.SERVER_GUIDE:\i\.\i\.Messages\.GUILD_HOME,)/, - replace: "trailing:$self.ViewServerHomeButton({serverGuide:$1})," - } - }, - // Disable view Server Home override when the Server Home is unmouted - { - find: "69386d_5", - replacement: { - match: /location:"69386d_5".+?;/, - replace: "$&$self.useDisableViewServerHome();" - } - } - ], - - ViewServerHomeButton: ErrorBoundary.wrap(({ serverGuide }: { serverGuide?: boolean; }) => { - if (serverGuide !== true) return null; - - return ; - }), - - useForceServerHome, - useDisableViewServerHome, - - contextMenus: { - "guild-context"(children, props) { - const { forceServerHome } = settings.use(["forceServerHome"]); - - if (!props?.guild) return; - - const group = findGroupChildrenByChildId("hide-muted-channels", children); - - group?.unshift( - settings.store.forceServerHome = !forceServerHome} - /> - ); - } - } -}); diff --git a/src/plugins/reviewDB/components/ReviewComponent.tsx b/src/plugins/reviewDB/components/ReviewComponent.tsx index 20b298cc..8faec9f4 100644 --- a/src/plugins/reviewDB/components/ReviewComponent.tsx +++ b/src/plugins/reviewDB/components/ReviewComponent.tsx @@ -45,7 +45,7 @@ export default LazyComponent(() => { p("container", "isHeader"), p("avatar", "zalgo"), p("button", "wrapper", "selected"), - p("botTag", "botTagRegular") + p("botTagRegular") ); const dateFormat = new Intl.DateTimeFormat(); diff --git a/src/plugins/reviewDB/components/ReviewsView.tsx b/src/plugins/reviewDB/components/ReviewsView.tsx index a705bc80..79e5e8cc 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 { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy } from "@webpack"; import { Forms, React, RelationshipStore, useRef, UserStore } from "@webpack/common"; import { Auth, authorize } from "../auth"; @@ -27,12 +27,11 @@ import { settings } from "../settings"; import { cl, showToast } from "../utils"; import ReviewComponent from "./ReviewComponent"; - -const { Editor, Transforms } = findByPropsLazy("Editor", "Transforms"); -const { ChatInputTypes } = findByPropsLazy("ChatInputTypes"); - -const InputComponent = findComponentByCodeLazy("default.CHANNEL_TEXT_AREA", "input"); -const { createChannelRecordFromServer } = findByPropsLazy("createChannelRecordFromServer"); +const Transforms = findByPropsLazy("insertNodes", "textToText"); +const Editor = findByPropsLazy("start", "end", "toSlateRange"); +const ChatInputTypes = findByPropsLazy("FORM"); +const InputComponent = findComponentByCodeLazy("disableThemedBackground", "CHANNEL_TEXT_AREA"); +const createChannelRecordFromServer = findByCodeLazy(".GUILD_TEXT])", "fromServer)"); interface UserProps { discordId: string; diff --git a/src/plugins/roleColorEverywhere/index.tsx b/src/plugins/roleColorEverywhere/index.tsx index 56b224da..c6c50b34 100644 --- a/src/plugins/roleColorEverywhere/index.tsx +++ b/src/plugins/roleColorEverywhere/index.tsx @@ -40,9 +40,16 @@ const settings = definePluginSettings({ default: true, description: "Show role colors in the voice chat user list", restartNeeded: true + }, + reactorsList: { + type: OptionType.BOOLEAN, + default: true, + description: "Show role colors in the reactors list", + restartNeeded: true } }); + export default definePlugin({ name: "RoleColorEverywhere", authors: [Devs.KingFish, Devs.lewisakura, Devs.AutumnVN], @@ -64,7 +71,7 @@ export default definePlugin({ find: ".userTooltip,children", replacement: [ { - match: /let\{id:(\i),guildId:(\i)[^}]*\}.*?\.default,{(?=children)/, + match: /let\{id:(\i),guildId:(\i)[^}]*\}.*?\.\i,{(?=children)/, replace: "$&color:$self.getUserColor($1,{guildId:$2})," } ], @@ -99,6 +106,14 @@ export default definePlugin({ } ], predicate: () => settings.store.voiceUsers, + }, + { + find: ".reactorDefault", + replacement: { + match: /\.openUserContextMenu\)\((\i),(\i),\i\).{0,250}tag:"strong"/, + replace: "$&,style:{color:$self.getColor($2?.id,$1)}" + }, + predicate: () => settings.store.reactorsList, } ], settings, diff --git a/src/plugins/searchReply/index.tsx b/src/plugins/searchReply/index.tsx index 35b19787..298e7421 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 { findByCodeLazy } from "@webpack"; import { ChannelStore, i18n, Menu, PermissionsBits, PermissionStore, SelectedChannelStore } from "@webpack/common"; import { Message } from "discord-types/general"; -const messageUtils = findByPropsLazy("replyToMessage"); +const replyToMessage = findByCodeLazy(".TEXTAREA_FOCUS)", "showMentionToggle:"); const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { message }: { message: Message; }) => { // make sure the message is in the selected channel @@ -43,7 +43,7 @@ const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { messag id="reply" label={i18n.Messages.MESSAGE_ACTION_REPLY} icon={ReplyIcon} - action={(e: React.MouseEvent) => messageUtils.replyToMessage(channel, message, e)} + action={(e: React.MouseEvent) => replyToMessage(channel, message, e)} /> )); return; @@ -57,7 +57,7 @@ const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { messag id="reply" label={i18n.Messages.MESSAGE_ACTION_REPLY} icon={ReplyIcon} - action={(e: React.MouseEvent) => messageUtils.replyToMessage(channel, message, e)} + action={(e: React.MouseEvent) => replyToMessage(channel, message, e)} /> )); return; diff --git a/src/plugins/seeSummaries/index.tsx b/src/plugins/seeSummaries/index.tsx index 68c4f4a6..f1cdd785 100644 --- a/src/plugins/seeSummaries/index.tsx +++ b/src/plugins/seeSummaries/index.tsx @@ -8,11 +8,11 @@ import { DataStore } from "@api/index"; import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByCodeLazy, findByPropsLazy } from "@webpack"; import { ChannelStore, GuildStore } from "@webpack/common"; const SummaryStore = findByPropsLazy("allSummaries", "findSummary"); -const { createSummaryFromServer } = findByPropsLazy("createSummaryFromServer"); +const createSummaryFromServer = findByCodeLazy(".people)),startId:"); const settings = definePluginSettings({ summaryExpiryThresholdDays: { @@ -55,9 +55,9 @@ export default definePlugin({ settings, patches: [ { - find: "ChannelTypesSets.SUMMARIZEABLE.has", + find: "SUMMARIZEABLE.has", replacement: { - match: /\i\.hasFeature\(\i\.GuildFeatures\.SUMMARIES_ENABLED\w+?\)/g, + match: /\i\.hasFeature\(.{0,10}\.SUMMARIES_ENABLED\w+?\)/g, replace: "true" } }, diff --git a/src/plugins/serverInfo/GuildInfoModal.tsx b/src/plugins/serverInfo/GuildInfoModal.tsx index bed520b6..fb8df2ce 100644 --- a/src/plugins/serverInfo/GuildInfoModal.tsx +++ b/src/plugins/serverInfo/GuildInfoModal.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 { findByPropsLazy, findComponentByCodeLazy } 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 FriendRow = findComponentByCodeLazy(".listName,discriminatorClass"); const cl = classNameFactory("vc-gp-"); diff --git a/src/plugins/showConnections/index.tsx b/src/plugins/showConnections/index.tsx index a78e4c41..5ef09066 100644 --- a/src/plugins/showConnections/index.tsx +++ b/src/plugins/showConnections/index.tsx @@ -33,7 +33,8 @@ import { VerifiedIcon } from "./VerifiedIcon"; const Section = findComponentByCodeLazy(".lastSection", "children:"); const ThemeStore = findStoreLazy("ThemeStore"); -const platformHooks: { useLegacyPlatformType(platform: string): string; } = findByPropsLazy("useLegacyPlatformType"); + +const useLegacyPlatformType: (platform: string) => string = findByCodeLazy(".TWITTER_LEGACY:"); const platforms: { get(type: string): ConnectionPlatform; } = findByPropsLazy("isSupported", "getByUrl"); const getProfileThemeProps = findByCodeLazy(".getPreviewThemeColors", "primaryColor:"); @@ -74,15 +75,28 @@ interface ConnectionPlatform { icon: { lightSVG: string, darkSVG: string; }; } -const profilePopoutComponent = ErrorBoundary.wrap((props: { user: User, displayProfile; }) => - +const profilePopoutComponent = ErrorBoundary.wrap( + (props: { user: User; displayProfile?: any; simplified?: boolean; }) => ( + + ), + { noop: true } ); -const profilePanelComponent = ErrorBoundary.wrap(({ id }: { id: string; }) => - +const profilePanelComponent = ErrorBoundary.wrap( + (props: { id: string; simplified?: boolean; }) => ( + + ), + { noop: true } ); -function ConnectionsComponent({ id, theme }: { id: string, theme: string; }) { +function ConnectionsComponent({ id, theme, simplified }: { id: string, theme: string, simplified?: boolean; }) { const profile = UserProfileStore.getUserProfile(id); if (!profile) return null; @@ -91,6 +105,19 @@ function ConnectionsComponent({ id, theme }: { id: string, theme: string; }) { if (!connections?.length) return null; + const connectionsContainer = ( + + {connections.map(connection => )} + + ); + + if (simplified) + return connectionsContainer; + return (
Connections - - {connections.map(connection => )} - + {connectionsContainer}
); } function CompactConnectionComponent({ connection, theme }: { connection: Connection, theme: string; }) { - const platform = platforms.get(platformHooks.useLegacyPlatformType(connection.type)); + const platform = platforms.get(useLegacyPlatformType(connection.type)); const url = platform.getPlatformUserUrl?.(connection); const img = ( @@ -132,7 +153,7 @@ function CompactConnectionComponent({ connection, theme }: { connection: Connect - {connection.name} + {connection.name} {connection.verified && } @@ -182,12 +203,19 @@ export default definePlugin({ } }, { - find: ".UserPopoutUpsellSource.PROFILE_PANEL,", + find: ".PROFILE_PANEL,", replacement: { // createElement(Divider, {}), createElement(NoteComponent) match: /\(0,\i\.jsx\)\(\i\.\i,\{\}\).{0,100}setNote:(?=.+?channelId:(\i).id)/, replace: "$self.profilePanelComponent({ id: $1.recipients[0] }),$&" } + }, + { + find: ".BITE_SIZE,onOpenProfile", + replacement: { + match: /currentUser:\i,guild:\i,onOpenProfile:.+?}\)(?=])(?<=user:(\i),bio:null==(\i)\?.+?)/, + replace: "$&,$self.profilePopoutComponent({ user: $1, displayProfile: $2, simplified: true })" + } } ], settings, diff --git a/src/plugins/showConnections/styles.css b/src/plugins/showConnections/styles.css index 383593c1..cead5201 100644 --- a/src/plugins/showConnections/styles.css +++ b/src/plugins/showConnections/styles.css @@ -9,3 +9,11 @@ gap: 0.25em; align-items: center; } + +.vc-sc-connection-name { + word-break: break-all; +} + +.vc-sc-tooltip svg { + min-width: 16px; +} diff --git a/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx b/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx index a8f5735e..bdb9fee8 100644 --- a/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx +++ b/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx @@ -78,7 +78,7 @@ const enum ChannelFlags { } -const ChatScrollClasses = findByPropsLazy("auto", "content", "scrollerBase"); +const ChatScrollClasses = findByPropsLazy("auto", "managedReactiveScroller"); const ChatClasses = findByPropsLazy("chat", "content", "noChat", "chatContent"); const ChannelBeginHeader = findComponentByCodeLazy(".Messages.ROLE_REQUIRED_SINGLE_USER_MESSAGE"); const TagComponent = findComponentLazy(m => { diff --git a/src/plugins/showHiddenChannels/index.tsx b/src/plugins/showHiddenChannels/index.tsx index 35d56091..48342cae 100644 --- a/src/plugins/showHiddenChannels/index.tsx +++ b/src/plugins/showHiddenChannels/index.tsx @@ -73,9 +73,8 @@ export default definePlugin({ find: '"placeholder-channel-id"', replacement: [ // Remove the special logic for channels we don't have access to - // FIXME Remove variable matcher from threadsIds when it hits stable { - match: /if\(!\i\.\i\.can\(\i\.\i\.VIEW_CHANNEL.+?{if\(this\.id===\i\).+?threadIds:(?:\[\]|\i)}}/, + match: /if\(!\i\.\i\.can\(\i\.\i\.VIEW_CHANNEL.+?{if\(this\.id===\i\).+?threadIds:\[\]}}/, replace: "" }, // Do not check for unreads when selecting the render level if the channel is hidden diff --git a/src/plugins/spotifyControls/SpotifyStore.ts b/src/plugins/spotifyControls/SpotifyStore.ts index b3cd0b28..7fdd6bba 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, findByPropsLazy, proxyLazyWebpack } from "@webpack"; import { Flux, FluxDispatcher } from "@webpack/common"; export interface Track { @@ -70,7 +70,7 @@ export const SpotifyStore = proxyLazyWebpack(() => { const { Store } = Flux; const SpotifySocket = findByProps("getActiveSocketAndDevice"); - const SpotifyUtils = findByProps("SpotifyAPI"); + const SpotifyAPI = findByPropsLazy("vcSpotifyMarker"); const API_BASE = "https://api.spotify.com/v1/me/player"; @@ -168,7 +168,7 @@ export const SpotifyStore = proxyLazyWebpack(() => { (data.query ??= {}).device_id = this.device.id; const { socket } = SpotifySocket.getActiveSocketAndDevice(); - return SpotifyUtils.SpotifyAPI[method](socket.accountId, socket.accessToken, { + return SpotifyAPI[method](socket.accountId, socket.accessToken, { url: API_BASE + route, ...data }); diff --git a/src/plugins/spotifyControls/index.tsx b/src/plugins/spotifyControls/index.tsx index f7972aa3..033d4935 100644 --- a/src/plugins/spotifyControls/index.tsx +++ b/src/plugins/spotifyControls/index.tsx @@ -61,7 +61,7 @@ export default definePlugin({ replacement: [{ // Adds POST and a Marker to the SpotifyAPI (so we can easily find it) match: /get:(\i)\.bind\(null,(\i\.\i)\.get\)/, - replace: "post:$1.bind(null,$2.post),$&" + replace: "post:$1.bind(null,$2.post),vcSpotifyMarker:1,$&" }, { // Spotify Connect API returns status 202 instead of 204 when skipping tracks. @@ -81,7 +81,7 @@ export default definePlugin({ { find: "artists.filter", replacement: { - match: /\(0,(\i)\.isNotNullish\)\((\i)\.id\)&&/, + match: /(?<=artists.filter\(\i=>).{0,10}\i\.id\)&&/, replace: "" } } diff --git a/src/plugins/usrbg/index.tsx b/src/plugins/usrbg/index.tsx index b8e9f14b..d9af54c3 100644 --- a/src/plugins/usrbg/index.tsx +++ b/src/plugins/usrbg/index.tsx @@ -61,11 +61,7 @@ export default definePlugin({ replacement: [ { match: /(\i)\.premiumType/, - replace: "$self.premiumHook($1)||$&" - }, - { - match: /(?<=function \i\((\i)\)\{)(?=var.{30,50},bannerSrc:)/, - replace: "$1.bannerSrc=$self.useBannerHook($1);" + replace: "$self.patchPremiumType($1)||$&" }, { match: /\?\(0,\i\.jsx\)\(\i,{type:\i,shown/, @@ -74,17 +70,12 @@ export default definePlugin({ ] }, { - find: /overrideBannerSrc:\i,overrideBannerWidth:/, - replacement: [ - { - match: /(\i)\.premiumType/, - replace: "$self.premiumHook($1)||$&" - }, - { - match: /function \i\((\i)\)\{/, - replace: "$&$1.overrideBannerSrc=$self.useBannerHook($1);" - } - ] + find: "BannerLoadingStatus:function", + replacement: { + match: /(?<=void 0:)\i.getPreviewBanner\(\i,\i,\i\)/, + replace: "$self.patchBannerUrl(arguments[0])||$&" + + } }, { find: "\"data-selenium-video-tile\":", @@ -92,7 +83,7 @@ export default definePlugin({ replacement: [ { match: /(?<=function\((\i),\i\)\{)(?=let.{20,40},style:)/, - replace: "$1.style=$self.voiceBackgroundHook($1);" + replace: "$1.style=$self.getVoiceBackgroundStyles($1);" } ] } @@ -106,7 +97,7 @@ export default definePlugin({ ); }, - voiceBackgroundHook({ className, participantUserId }: any) { + getVoiceBackgroundStyles({ className, participantUserId }: any) { if (className.includes("tile_")) { if (this.userHasBackground(participantUserId)) { return { @@ -119,12 +110,12 @@ export default definePlugin({ } }, - useBannerHook({ displayProfile, user }: any) { + patchBannerUrl({ displayProfile }: any) { if (displayProfile?.banner && settings.store.nitroFirst) return; - if (this.userHasBackground(user.id)) return this.getImageUrl(user.id); + if (this.userHasBackground(displayProfile?.userId)) return this.getImageUrl(displayProfile?.userId); }, - premiumHook({ userId }: any) { + patchPremiumType({ userId }: any) { if (this.userHasBackground(userId)) return 2; }, diff --git a/src/plugins/viewIcons/index.tsx b/src/plugins/viewIcons/index.tsx index a9468968..b2e7d56d 100644 --- a/src/plugins/viewIcons/index.tsx +++ b/src/plugins/viewIcons/index.tsx @@ -184,7 +184,7 @@ export default definePlugin({ patches: [ // Profiles Modal pfp - ...["User Profile Modal - Context Menu", ".UserProfileTypes.FULL_SIZE,hasProfileEffect:"].map(find => ({ + ...[".MODAL,hasProfileEffect", ".FULL_SIZE,hasProfileEffect:"].map(find => ({ find, replacement: { match: /\{src:(\i)(?=,avatarDecoration)/, @@ -192,7 +192,7 @@ export default definePlugin({ } })), // Banners - ...[".NITRO_BANNER,", /overrideBannerSrc:\i,overrideBannerWidth:/].map(find => ({ + ...[".NITRO_BANNER,", "=!1,canUsePremiumCustomization:"].map(find => ({ find, replacement: { // style: { backgroundImage: shouldShowBanner ? "url(".concat(bannerUrl, @@ -222,7 +222,7 @@ export default definePlugin({ { find: /\.recipients\.length>=2(?! `${m},onClick:()=>$self.openImage(${iconUrl})` } }, diff --git a/src/plugins/webContextMenus.web/index.ts b/src/plugins/webContextMenus.web/index.ts index ac468903..f99fd972 100644 --- a/src/plugins/webContextMenus.web/index.ts +++ b/src/plugins/webContextMenus.web/index.ts @@ -197,8 +197,8 @@ export default definePlugin({ { find: '"MediaEngineWebRTC");', replacement: { - match: /supports\(\i\)\{switch\(\i\)\{case (\i).Features/, - replace: "$&.DISABLE_VIDEO:return true;case $1.Features" + match: /supports\(\i\)\{switch\(\i\)\{(case (\i).\i)/, + replace: "$&.DISABLE_VIDEO:return true;$1" } } ], diff --git a/src/plugins/xsOverlay.desktop/index.ts b/src/plugins/xsOverlay.desktop/index.ts index caa44a40..a68373a6 100644 --- a/src/plugins/xsOverlay.desktop/index.ts +++ b/src/plugins/xsOverlay.desktop/index.ts @@ -9,11 +9,11 @@ import { makeRange } from "@components/PluginSettings/components"; import { Devs } from "@utils/constants"; import { Logger } from "@utils/Logger"; import definePlugin, { OptionType, PluginNative, ReporterTestable } from "@utils/types"; -import { findByPropsLazy } from "@webpack"; +import { findByCodeLazy, findLazy } from "@webpack"; import { ChannelStore, GuildStore, UserStore } from "@webpack/common"; import type { Channel, Embed, GuildMember, MessageAttachment, User } from "discord-types/general"; -const { ChannelTypes } = findByPropsLazy("ChannelTypes"); +const ChannelTypes = findLazy(m => m.ANNOUNCEMENT_THREAD === 10); interface Message { guild_id: string, @@ -68,7 +68,7 @@ interface Call { ringing: string[]; } -const Notifs = findByPropsLazy("makeTextChatNotification"); +const notificationsShouldNotify = findByCodeLazy(".SUPPRESS_NOTIFICATIONS))return!1"); const XSLog = new Logger("XSOverlay"); const settings = definePluginSettings({ @@ -304,7 +304,7 @@ function shouldNotify(message: Message, channel: string) { const currentUser = UserStore.getCurrentUser(); if (message.author.id === currentUser.id) return false; if (message.author.bot && !settings.store.botNotifications) return false; - return Notifs.shouldNotify(message, channel); + return notificationsShouldNotify(message, channel); } function calculateHeight(content: string) { diff --git a/src/utils/constants.ts b/src/utils/constants.ts index ff754d5c..09fc0285 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -38,7 +38,7 @@ export const Devs = /* #__PURE__*/ Object.freeze({ id: 0n, }, Ven: { - name: "Vendicated", + name: "Vee", id: 343383572805058560n }, Arjix: { @@ -327,7 +327,7 @@ export const Devs = /* #__PURE__*/ Object.freeze({ id: 305288513941667851n }, ImLvna: { - name: "Luna <3", + name: "lillith <3", id: 799319081723232267n }, rad: { diff --git a/src/utils/discord.tsx b/src/utils/discord.tsx index 57202ba3..3ac5991d 100644 --- a/src/utils/discord.tsx +++ b/src/utils/discord.tsx @@ -120,6 +120,8 @@ export function openImageModal(url: string, props?: Partial } + // FIXME: wtf is this? do we need to pass some proper component?? + renderForwardComponent={() => null} shouldHideMediaOptions={false} shouldAnimate {...props} diff --git a/src/utils/modal.tsx b/src/utils/modal.tsx index b4d0f59f..4203068c 100644 --- a/src/utils/modal.tsx +++ b/src/utils/modal.tsx @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import { findByPropsLazy, findExportedComponentLazy } from "@webpack"; +import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; import type { ComponentType, PropsWithChildren, ReactNode, Ref } from "react"; import { LazyComponent } from "./react"; @@ -111,6 +111,7 @@ export type ImageModal = ComponentType<{ animated?: boolean; responsive?: boolean; renderLinkComponent(props: any): ReactNode; + renderForwardComponent(props: any): ReactNode; maxWidth?: number; maxHeight?: number; shouldAnimate?: boolean; @@ -118,7 +119,7 @@ export type ImageModal = ComponentType<{ shouldHideMediaOptions?: boolean; }>; -export const ImageModal = findExportedComponentLazy("ImageModal") as ImageModal; +export const ImageModal = findComponentByCodeLazy(".MEDIA_MODAL_CLOSE", "responsive") as ImageModal; export const ModalRoot = LazyComponent(() => Modals.ModalRoot); export const ModalHeader = LazyComponent(() => Modals.ModalHeader); diff --git a/src/webpack/common/menu.ts b/src/webpack/common/menu.ts index 9a4a3a79..d528390e 100644 --- a/src/webpack/common/menu.ts +++ b/src/webpack/common/menu.ts @@ -17,12 +17,16 @@ */ // eslint-disable-next-line path-alias/no-relative -import { findByPropsLazy, waitFor } from "../webpack"; +import { filters, mapMangledModuleLazy, waitFor } from "../webpack"; import type * as t from "./types/menu"; export let Menu = {} as t.Menu; waitFor(["MenuItem", "MenuSliderControl"], m => Menu = m); -export const ContextMenuApi: t.ContextMenuApi = findByPropsLazy("closeContextMenu", "openContextMenu"); +export const ContextMenuApi: t.ContextMenuApi = mapMangledModuleLazy('type:"CONTEXT_MENU_OPEN', { + closeContextMenu: filters.byCode("CONTEXT_MENU_CLOSE"), + openContextMenu: filters.byCode("renderLazy:"), + openContextMenuLazy: e => typeof e === "function" && e.toString().length < 100 +}); diff --git a/src/webpack/common/settingsStores.ts b/src/webpack/common/settingsStores.ts index 4a48efda..2f5297bd 100644 --- a/src/webpack/common/settingsStores.ts +++ b/src/webpack/common/settingsStores.ts @@ -4,12 +4,9 @@ * SPDX-License-Identifier: GPL-3.0-or-later */ -import { findByPropsLazy } from "@webpack"; +import { findLazy } from "@webpack"; -import * as t from "./types/settingsStores"; - - -export const TextAndImagesSettingsStores = findByPropsLazy("MessageDisplayCompact") as Record; -export const StatusSettingsStores = findByPropsLazy("ShowCurrentGame") as Record; - -export const UserSettingsActionCreators = findByPropsLazy("PreloadedUserSettingsActionCreators"); +export const UserSettingsActionCreators = { + FrecencyUserSettingsActionCreators: findLazy(m => m.typeName?.endsWith(".FrecencyUserSettings")), + PreloadedUserSettingsActionCreators: findLazy(m => m.typeName?.endsWith(".PreloadedUserSettings")), +}; diff --git a/src/webpack/common/stores.ts b/src/webpack/common/stores.ts index 123c62b0..d61a95d8 100644 --- a/src/webpack/common/stores.ts +++ b/src/webpack/common/stores.ts @@ -19,7 +19,7 @@ import type * as Stores from "discord-types/stores"; // eslint-disable-next-line path-alias/no-relative -import { findByPropsLazy } from "../webpack"; +import { findByCodeLazy, findByPropsLazy } from "../webpack"; import { waitForStore } from "./internal"; import * as t from "./types/stores"; @@ -27,7 +27,7 @@ export const Flux: t.Flux = findByPropsLazy("connectStores"); export type GenericStore = t.FluxStore & Record; -export const { DraftType }: { DraftType: typeof t.DraftType; } = findByPropsLazy("DraftType"); +export const DraftType = findByPropsLazy("ChannelMessage", "SlashCommand"); export let MessageStore: Omit & { getMessages(chanId: string): any; @@ -67,7 +67,7 @@ export let DraftStore: t.DraftStore; * @example const user = useStateFromStores([UserStore], () => UserStore.getCurrentUser(), null, (old, current) => old.id === current.id); */ // eslint-disable-next-line prefer-destructuring -export const useStateFromStores: t.useStateFromStores = findByPropsLazy("useStateFromStores").useStateFromStores; +export const useStateFromStores: t.useStateFromStores = findByCodeLazy("useStateFromStores"); waitForStore("DraftStore", s => DraftStore = s); waitForStore("UserStore", s => UserStore = s); diff --git a/src/webpack/common/types/stores.d.ts b/src/webpack/common/types/stores.d.ts index f1fc68e8..037b2d81 100644 --- a/src/webpack/common/types/stores.d.ts +++ b/src/webpack/common/types/stores.d.ts @@ -39,6 +39,8 @@ export class FluxStore { syncWith: GenericFunction; waitFor: GenericFunction; __getLocalVars(): Record; + + static getAll(): FluxStore[]; } export class FluxEmitter { diff --git a/src/webpack/common/types/utils.d.ts b/src/webpack/common/types/utils.d.ts index 39af843c..166a25fe 100644 --- a/src/webpack/common/types/utils.d.ts +++ b/src/webpack/common/types/utils.d.ts @@ -168,17 +168,8 @@ export interface Clipboard { export interface NavigationRouter { back(): void; forward(): void; - hasNavigated(): boolean; - getHistory(): { - action: string; - length: 50; - [key: string]: any; - }; transitionTo(path: string, ...args: unknown[]): void; transitionToGuild(guildId: string, ...args: unknown[]): void; - replaceWith(...args: unknown[]): void; - getLastRouteChangeSource(): any; - getLastRouteChangeSourceLocationStack(): any; } export interface IconUtils { @@ -224,3 +215,8 @@ export interface IconUtils { getApplicationIconSource: any; getAnimatableSourceWithFallback: any; } + +export interface Constants { + Endpoints: Record; + UserFlags: Record; +} diff --git a/src/webpack/common/utils.ts b/src/webpack/common/utils.ts index bb96861f..dd78dc4e 100644 --- a/src/webpack/common/utils.ts +++ b/src/webpack/common/utils.ts @@ -16,10 +16,10 @@ * along with this program. If not, see . */ -import type { Channel, User } from "discord-types/general"; +import type { Channel } from "discord-types/general"; // eslint-disable-next-line path-alias/no-relative -import { _resolveReady, filters, findByCodeLazy, findByProps, findByPropsLazy, findLazy, proxyLazyWebpack, waitFor } from "../webpack"; +import { _resolveReady, filters, findByCodeLazy, findByPropsLazy, findLazy, mapMangledModuleLazy, waitFor } from "../webpack"; import type * as t from "./types/utils"; export let FluxDispatcher: t.FluxDispatcher; @@ -36,15 +36,14 @@ waitFor(["dispatch", "subscribe"], m => { }); export let ComponentDispatch; -waitFor(["ComponentDispatch", "ComponentDispatcher"], m => ComponentDispatch = m.ComponentDispatch); +waitFor(["dispatchToLastSubscribed"], m => ComponentDispatch = m); - -export const Constants = findByPropsLazy("Endpoints"); - -export const RestAPI: t.RestAPI = proxyLazyWebpack(() => { - const mod = findByProps("getAPIBaseURL"); - return mod.HTTP ?? mod; +export const Constants: t.Constants = mapMangledModuleLazy('ME:"/users/@me"', { + Endpoints: filters.byProps("USER", "ME"), + UserFlags: filters.byProps("STAFF", "SPAMMER") }); + +export const RestAPI: t.RestAPI = findLazy(m => typeof m === "object" && m.del && m.put); export const moment: typeof import("moment") = findByPropsLazy("parseTwoDigitYear"); export const hljs: typeof import("highlight.js") = findByPropsLazy("highlight", "registerLanguage"); @@ -118,30 +117,39 @@ export function showToast(message: string, type = ToastType.MESSAGE) { }); } -export const UserUtils = findByPropsLazy("getUser", "fetchCurrentUser") as { getUser: (id: string) => Promise; }; +export const UserUtils = { + getUser: findByCodeLazy(".USER(") +}; export const UploadManager = findByPropsLazy("clearAll", "addFile"); -export const UploadHandler = findByPropsLazy("showUploadFileSizeExceededError", "promptToUpload") as { - promptToUpload: (files: File[], channel: Channel, draftType: Number) => void; +export const UploadHandler = { + promptToUpload: findByCodeLazy(".ATTACHMENT_TOO_MANY_ERROR_TITLE,") as (files: File[], channel: Channel, draftType: Number) => void }; export const ApplicationAssetUtils = findByPropsLazy("fetchAssetIds", "getAssetImage") as { fetchAssetIds: (applicationId: string, e: string[]) => Promise; }; -export const Clipboard: t.Clipboard = findByPropsLazy("SUPPORTS_COPY", "copy"); +export const Clipboard: t.Clipboard = mapMangledModuleLazy('queryCommandEnabled("copy")', { + copy: filters.byCode(".copy("), + SUPPORTS_COPY: e => typeof e === "boolean" +}); -export const NavigationRouter: t.NavigationRouter = findByPropsLazy("transitionTo", "replaceWith", "transitionToGuild"); +export const NavigationRouter: t.NavigationRouter = mapMangledModuleLazy("Transitioning to ", { + transitionTo: filters.byCode("transitionTo -"), + transitionToGuild: filters.byCode("transitionToGuild -"), + back: filters.byCode("goBack()"), + forward: filters.byCode("goForward()"), +}); export let SettingsRouter: any; waitFor(["open", "saveAccountChanges"], m => SettingsRouter = m); -export const { Permissions: PermissionsBits } = findLazy(m => typeof m.Permissions?.ADMINISTRATOR === "bigint") as { Permissions: t.PermissionsBits; }; +export const PermissionsBits: t.PermissionsBits = findLazy(m => typeof m.ADMINISTRATOR === "bigint"); export const zustandCreate = findByCodeLazy("will be removed in v4"); -const persistFilter = filters.byCode("[zustand persist middleware]"); -export const { persist: zustandPersist } = findLazy(m => m.persist && persistFilter(m.persist)); +export const zustandPersist = findByCodeLazy("[zustand persist middleware]"); export const MessageActions = findByPropsLazy("editMessage", "sendMessage"); export const MessageCache = findByPropsLazy("clearCache", "_channelMessages"); diff --git a/src/webpack/patchWebpack.ts b/src/webpack/patchWebpack.ts index da5ca8b9..48f1b814 100644 --- a/src/webpack/patchWebpack.ts +++ b/src/webpack/patchWebpack.ts @@ -209,14 +209,33 @@ function patchFactories(factories: Record(...props: stri }); } +/** + * Finds a mangled module by the provided code "code" (must be unique and can be anywhere in the module) + * then maps it into an easily usable module via the specified mappers. + * + * @param code The code to look for + * @param mappers Mappers to create the non mangled exports + * @returns Unmangled exports as specified in mappers + * + * @example mapMangledModule("headerIdIsManaged:", { + * openModal: filters.byCode("headerIdIsManaged:"), + * closeModal: filters.byCode("key==") + * }) + */ +export const mapMangledModule = traceFunction("mapMangledModule", function mapMangledModule(code: string, mappers: Record): Record { + const exports = {} as Record; + + const id = findModuleId(code); + if (id === null) + return exports; + + const mod = wreq(id as any); + outer: + for (const key in mod) { + const member = mod[key]; + for (const newName in mappers) { + // if the current mapper matches this module + if (mappers[newName](member)) { + exports[newName] = member; + continue outer; + } + } + } + return exports; +}); + +/** + * {@link mapMangledModule}, lazy. + + * Finds a mangled module by the provided code "code" (must be unique and can be anywhere in the module) + * then maps it into an easily usable module via the specified mappers. + * + * @param code The code to look for + * @param mappers Mappers to create the non mangled exports + * @returns Unmangled exports as specified in mappers + * + * @example mapMangledModule("headerIdIsManaged:", { + * openModal: filters.byCode("headerIdIsManaged:"), + * closeModal: filters.byCode("key==") + * }) + */ +export function mapMangledModuleLazy(code: string, mappers: Record): Record { + return proxyLazy(() => mapMangledModule(code, mappers)); +} + export const DefaultExtractAndLoadChunksRegex = /(?:(?:Promise\.all\(\[)?(\i\.e\("[^)]+?"\)[^\]]*?)(?:\]\))?|Promise\.resolve\(\))\.then\(\i\.bind\(\i,"([^)]+?)"\)\)/; export const ChunkIdsRegex = /\("([^"]+?)"\)/g;