This commit is contained in:
Nuckyz 2024-09-18 18:12:42 +00:00 committed by GitHub
commit eee1e08a97
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
155 changed files with 3478 additions and 2407 deletions

View file

@ -90,7 +90,13 @@ export default tseslint.config(
"no-invalid-regexp": "error", "no-invalid-regexp": "error",
"no-constant-condition": ["error", { "checkLoops": false }], "no-constant-condition": ["error", { "checkLoops": false }],
"no-duplicate-imports": "error", "no-duplicate-imports": "error",
"dot-notation": "error", "@typescript-eslint/dot-notation": [
"error",
{
"allowPrivateClassPropertyAccess": true,
"allowProtectedClassPropertyAccess": true
}
],
"no-useless-escape": [ "no-useless-escape": [
"error", "error",
{ {

View file

@ -312,7 +312,7 @@ export const commonOpts = {
logLevel: "info", logLevel: "info",
bundle: true, bundle: true,
watch, watch,
minify: !watch, minify: !watch && !IS_REPORTER,
sourcemap: watch ? "inline" : "", sourcemap: watch ? "inline" : "",
legalComments: "linked", legalComments: "linked",
banner, banner,

View file

@ -26,7 +26,7 @@
import { readFileSync } from "fs"; import { readFileSync } from "fs";
import pup, { JSHandle } from "puppeteer-core"; import pup, { JSHandle } from "puppeteer-core";
for (const variable of ["DISCORD_TOKEN", "CHROMIUM_BIN"]) { for (const variable of ["CHROMIUM_BIN"]) {
if (!process.env[variable]) { if (!process.env[variable]) {
console.error(`Missing environment variable ${variable}`); console.error(`Missing environment variable ${variable}`);
process.exit(1); process.exit(1);
@ -214,7 +214,7 @@ page.on("console", async e => {
switch (tag) { switch (tag) {
case "WebpackInterceptor:": case "WebpackInterceptor:":
const patchFailMatch = message.match(/Patch by (.+?) (had no effect|errored|found no module) \(Module id is (.+?)\): (.+)/)!; const patchFailMatch = message.match(/Patch by (.+?) (had no effect|errored|found no module|took [\d.]+?ms) \(Module id is (.+?)\): (.+)/)!;
if (!patchFailMatch) break; if (!patchFailMatch) break;
console.error(await getText()); console.error(await getText());
@ -225,7 +225,7 @@ page.on("console", async e => {
plugin, plugin,
type, type,
id, id,
match: regex.replace(/\[A-Za-z_\$\]\[\\w\$\]\*/g, "\\i"), match: regex,
error: await maybeGetError(e.args()[3]) error: await maybeGetError(e.args()[3])
}); });
@ -291,7 +291,7 @@ page.on("error", e => console.error("[Error]", e.message));
page.on("pageerror", e => { page.on("pageerror", e => {
if (e.message.includes("Sentry successfully disabled")) return; if (e.message.includes("Sentry successfully disabled")) return;
if (!e.message.startsWith("Object") && !e.message.includes("Cannot find module")) { if (!e.message.startsWith("Object") && !e.message.includes("Cannot find module") && !/^.{1,2}$/.test(e.message)) {
console.error("[Page Error]", e.message); console.error("[Page Error]", e.message);
report.otherErrors.push(e.message); report.otherErrors.push(e.message);
} else { } else {
@ -299,20 +299,9 @@ page.on("pageerror", e => {
} }
}); });
async function reporterRuntime(token: string) {
Vencord.Webpack.waitFor(
"loginToken",
m => {
console.log("[PUP_DEBUG]", "Logging in with token...");
m.loginToken(token);
}
);
}
await page.evaluateOnNewDocument(` await page.evaluateOnNewDocument(`
if (location.host.endsWith("discord.com")) { if (location.host.endsWith("discord.com")) {
${readFileSync("./dist/browser.js", "utf-8")}; ${readFileSync("./dist/browser.js", "utf-8")};
(${reporterRuntime.toString()})(${JSON.stringify(process.env.DISCORD_TOKEN)});
} }
`); `);

View file

@ -23,10 +23,10 @@ export * as Util from "./utils";
export * as QuickCss from "./utils/quickCss"; export * as QuickCss from "./utils/quickCss";
export * as Updater from "./utils/updater"; export * as Updater from "./utils/updater";
export * as Webpack from "./webpack"; export * as Webpack from "./webpack";
export * as WebpackPatcher from "./webpack/patchWebpack";
export { PlainSettings, Settings }; export { PlainSettings, Settings };
import "./utils/quickCss"; import "./utils/quickCss";
import "./webpack/patchWebpack";
import { openUpdaterModal } from "@components/VencordSettings/UpdaterTab"; import { openUpdaterModal } from "@components/VencordSettings/UpdaterTab";
import { StartAt } from "@utils/types"; import { StartAt } from "@utils/types";
@ -39,7 +39,7 @@ import { localStorage } from "./utils/localStorage";
import { relaunch } from "./utils/native"; import { relaunch } from "./utils/native";
import { getCloudSettings, putCloudSettings } from "./utils/settingsSync"; import { getCloudSettings, putCloudSettings } from "./utils/settingsSync";
import { checkForUpdates, update, UpdateLogger } from "./utils/updater"; import { checkForUpdates, update, UpdateLogger } from "./utils/updater";
import { onceReady } from "./webpack"; import { onceDiscordLoaded } from "./webpack";
import { SettingsRouter } from "./webpack/common"; import { SettingsRouter } from "./webpack/common";
if (IS_REPORTER) { if (IS_REPORTER) {
@ -86,7 +86,7 @@ async function syncSettings() {
} }
async function init() { async function init() {
await onceReady; await onceDiscordLoaded;
startAllPlugins(StartAt.WebpackReady); startAllPlugins(StartAt.WebpackReady);
syncSettings(); syncSettings();
@ -125,7 +125,7 @@ async function init() {
const pendingPatches = patches.filter(p => !p.all && p.predicate?.() !== false); const pendingPatches = patches.filter(p => !p.all && p.predicate?.() !== false);
if (pendingPatches.length) if (pendingPatches.length)
PMLogger.warn( PMLogger.warn(
"Webpack has finished initialising, but some patches haven't been applied yet.", "Webpack has finished initializing, but some patches haven't been applied yet.",
"This might be expected since some Modules are lazy loaded, but please verify", "This might be expected since some Modules are lazy loaded, but please verify",
"that all plugins are working as intended.", "that all plugins are working as intended.",
"You are seeing this warning because this is a Development build of Vencord.", "You are seeing this warning because this is a Development build of Vencord.",

View file

@ -17,7 +17,6 @@
*/ */
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { ComponentType, HTMLProps } from "react";
import Plugins from "~plugins"; import Plugins from "~plugins";
@ -30,7 +29,7 @@ export interface ProfileBadge {
/** The tooltip to show on hover. Required for image badges */ /** The tooltip to show on hover. Required for image badges */
description?: string; description?: string;
/** Custom component for the badge (tooltip not included) */ /** Custom component for the badge (tooltip not included) */
component?: ComponentType<ProfileBadge & BadgeUserArgs>; component?: React.ComponentType<ProfileBadge & BadgeUserArgs>;
/** The custom image to use */ /** The custom image to use */
image?: string; image?: string;
link?: string; link?: string;
@ -39,7 +38,7 @@ export interface ProfileBadge {
/** Should the user display this badge? */ /** Should the user display this badge? */
shouldShow?(userInfo: BadgeUserArgs): boolean; shouldShow?(userInfo: BadgeUserArgs): boolean;
/** Optional props (e.g. style) for the badge, ignored for component badges */ /** Optional props (e.g. style) for the badge, ignored for component badges */
props?: HTMLProps<HTMLImageElement>; props?: React.ComponentPropsWithoutRef<"img">;
/** Insert at start or end? */ /** Insert at start or end? */
position?: BadgePosition; position?: BadgePosition;
/** The badge name to display, Discord uses this. Required for component badges */ /** The badge name to display, Discord uses this. Required for component badges */

View file

@ -8,13 +8,12 @@ import "./ChatButton.css";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import { waitFor } from "@webpack"; import { findByProps } from "@webpack";
import { Button, ButtonLooks, ButtonWrapperClasses, Tooltip } from "@webpack/common"; import { Button, ButtonLooks, ButtonWrapperClasses, Tooltip } from "@webpack/common";
import { Channel } from "discord-types/general"; import { Channel } from "discord-types/general";
import { HTMLProps, MouseEventHandler, ReactNode } from "react"; import { HTMLProps, MouseEventHandler, ReactNode } from "react";
let ChannelTextAreaClasses: Record<"button" | "buttonContainer", string>; const ChannelTextAreaClasses = findByProps<Record<"button" | "buttonContainer", string>>("buttonContainer", "channelTextArea");
waitFor(["buttonContainer", "channelTextArea"], m => ChannelTextAreaClasses = m);
export interface ChatBarProps { export interface ChatBarProps {
channel: Channel; channel: Channel;

View file

@ -17,14 +17,14 @@
*/ */
import { mergeDefaults } from "@utils/mergeDefaults"; import { mergeDefaults } from "@utils/mergeDefaults";
import { findByCodeLazy } from "@webpack"; import { findByCode } from "@webpack";
import { MessageActions, SnowflakeUtils } from "@webpack/common"; import { MessageActions, SnowflakeUtils } from "@webpack/common";
import { Message } from "discord-types/general"; import { Message } from "discord-types/general";
import type { PartialDeep } from "type-fest"; import type { PartialDeep } from "type-fest";
import { Argument } from "./types"; import { Argument } from "./types";
const createBotMessage = findByCodeLazy('username:"Clyde"'); const createBotMessage = findByCode('username:"Clyde"');
export function generateId() { export function generateId() {
return `-${SnowflakeUtils.fromTimestamp(Date.now())}`; return `-${SnowflakeUtils.fromTimestamp(Date.now())}`;

View file

@ -19,18 +19,17 @@
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import { Channel, Message } from "discord-types/general"; import { Channel, Message } from "discord-types/general";
import type { ComponentType, MouseEventHandler } from "react";
const logger = new Logger("MessagePopover"); const logger = new Logger("MessagePopover");
export interface ButtonItem { export interface ButtonItem {
key?: string, key?: string,
label: string, label: string,
icon: ComponentType<any>, icon: React.ComponentType<AnyRecord>,
message: Message, message: Message,
channel: Channel, channel: Channel,
onClick?: MouseEventHandler<HTMLButtonElement>, onClick?: React.MouseEventHandler<HTMLButtonElement>,
onContextMenu?: MouseEventHandler<HTMLButtonElement>; onContextMenu?: React.MouseEventHandler<HTMLButtonElement>;
} }
export type getButtonItem = (message: Message) => ButtonItem | null; export type getButtonItem = (message: Message) => ButtonItem | null;

View file

@ -16,23 +16,22 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { waitFor } from "@webpack"; import { find } from "@webpack";
let NoticesModule: any; const Notices = find(m => m.show && m.dismiss && !m.suppressAll);
waitFor(m => m.show && m.dismiss && !m.suppressAll, m => NoticesModule = m);
export const noticesQueue = [] as any[]; export const noticesQueue = [] as any[];
export let currentNotice: any = null; export let currentNotice: any = null;
export function popNotice() { export function popNotice() {
NoticesModule.dismiss(); Notices.dismiss();
} }
export function nextNotice() { export function nextNotice() {
currentNotice = noticesQueue.shift(); currentNotice = noticesQueue.shift();
if (currentNotice) { if (currentNotice) {
NoticesModule.show(...currentNotice, "VencordNotice"); Notices.show(...currentNotice, "VencordNotice");
} }
} }

View file

@ -32,9 +32,10 @@ export interface Settings {
autoUpdate: boolean; autoUpdate: boolean;
autoUpdateNotification: boolean, autoUpdateNotification: boolean,
useQuickCss: boolean; useQuickCss: boolean;
enableReactDevtools: boolean;
themeLinks: string[]; themeLinks: string[];
eagerPatches: boolean;
enabledThemes: string[]; enabledThemes: string[];
enableReactDevtools: boolean;
frameless: boolean; frameless: boolean;
transparent: boolean; transparent: boolean;
winCtrlQ: boolean; winCtrlQ: boolean;
@ -81,6 +82,7 @@ const DefaultSettings: Settings = {
autoUpdateNotification: true, autoUpdateNotification: true,
useQuickCss: true, useQuickCss: true,
themeLinks: [], themeLinks: [],
eagerPatches: IS_REPORTER,
enabledThemes: [], enabledThemes: [],
enableReactDevtools: false, enableReactDevtools: false,
frameless: false, frameless: false,
@ -116,7 +118,6 @@ const saveSettingsOnFrequentAction = debounce(async () => {
} }
}, 60_000); }, 60_000);
export const SettingsStore = new SettingsStoreClass(settings, { export const SettingsStore = new SettingsStoreClass(settings, {
readOnly: true, readOnly: true,
getDefaultValue({ getDefaultValue({

View file

@ -89,8 +89,8 @@ export const isStyleEnabled = (name: string) => requireStyle(name).dom?.isConnec
* // -- plugin.ts -- * // -- plugin.ts --
* import pluginStyle from "./plugin.css?managed"; * import pluginStyle from "./plugin.css?managed";
* import { setStyleVars } from "@api/Styles"; * import { setStyleVars } from "@api/Styles";
* import { findByPropsLazy } from "@webpack"; * import { findByProps } from "@webpack";
* const classNames = findByPropsLazy("thin", "scrollerBase"); // { thin: "thin-31rlnD scrollerBase-_bVAAt", ... } * const classNames = findByProps("thin", "scrollerBase"); // { thin: "thin-31rlnD scrollerBase-_bVAAt", ... }
* *
* // Inside some plugin method like "start()" * // Inside some plugin method like "start()"
* setStyleClassNames(pluginStyle, classNames); * setStyleClassNames(pluginStyle, classNames);

View file

@ -17,8 +17,7 @@
*/ */
import { proxyLazy } from "@utils/lazy"; import { proxyLazy } from "@utils/lazy";
import { Logger } from "@utils/Logger"; import { findByFactoryCode } from "@webpack";
import { findModuleId, proxyLazyWebpack, wreq } from "@webpack";
interface UserSettingDefinition<T> { interface UserSettingDefinition<T> {
/** /**
@ -43,12 +42,7 @@ interface UserSettingDefinition<T> {
userSettingsAPIName: string; userSettingsAPIName: string;
} }
export const UserSettings: Record<PropertyKey, UserSettingDefinition<any>> | undefined = proxyLazyWebpack(() => { export const UserSettings = findByFactoryCode<Record<PropertyKey, UserSettingDefinition<any>>>('"textAndImages","renderSpoilers"');
const modId = findModuleId('"textAndImages","renderSpoilers"');
if (modId == null) return new Logger("UserSettingsAPI ").error("Didn't find settings module.");
return wreq(modId as any);
});
/** /**
* Get the setting with the given setting group and name. * Get the setting with the given setting group and name.
@ -56,7 +50,7 @@ export const UserSettings: Record<PropertyKey, UserSettingDefinition<any>> | und
* @param group The setting group * @param group The setting group
* @param name The name of the setting * @param name The name of the setting
*/ */
export function getUserSetting<T = any>(group: string, name: string): UserSettingDefinition<T> | undefined { export function getUserSetting<T = any>(group: string, name: string): UserSettingDefinition<T> {
if (!Vencord.Plugins.isPluginEnabled("UserSettingsAPI")) throw new Error("Cannot use UserSettingsAPI without setting as dependency."); if (!Vencord.Plugins.isPluginEnabled("UserSettingsAPI")) throw new Error("Cannot use UserSettingsAPI without setting as dependency.");
for (const key in UserSettings) { for (const key in UserSettings) {
@ -66,10 +60,12 @@ export function getUserSetting<T = any>(group: string, name: string): UserSettin
return userSetting; return userSetting;
} }
} }
throw new Error(`UserSettingsAPI: Setting ${group}.${name} not found.`);
} }
/** /**
* {@link getUserSettingDefinition}, lazy. * Lazy version of {@link getUserSetting}
* *
* Get the setting with the given setting group and name. * Get the setting with the given setting group and name.
* *

View file

@ -4,10 +4,10 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
*/ */
import { findByPropsLazy } from "@webpack"; import { findByProps } from "@webpack";
import { Parser } from "@webpack/common"; import { Parser } from "@webpack/common";
const CodeContainerClasses = findByPropsLazy("markup", "codeContainer"); const CodeContainerClasses = findByProps("markup", "codeContainer");
/** /**
* Renders code in a Discord codeblock * Renders code in a Discord codeblock

View file

@ -18,7 +18,7 @@
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import { Margins } from "@utils/margins"; import { Margins } from "@utils/margins";
import { LazyComponent } from "@utils/react"; import { LazyComponent, LazyComponentType } from "@utils/react";
import { React } from "@webpack/common"; import { React } from "@webpack/common";
import { ErrorCard } from "./ErrorCard"; import { ErrorCard } from "./ErrorCard";
@ -104,8 +104,8 @@ const ErrorBoundary = LazyComponent(() => {
} }
}; };
}) as }) as
React.ComponentType<React.PropsWithChildren<Props>> & { LazyComponentType<React.PropsWithChildren<Props>> & {
wrap<T extends object = any>(Component: React.ComponentType<T>, errorBoundaryProps?: Omit<Props<T>, "wrappedProps">): React.FunctionComponent<T>; wrap<T extends AnyRecord>(Component: React.ComponentType<T>, errorBoundaryProps?: Omit<Props<T>, "wrappedProps">): React.FunctionComponent<T>;
}; };
ErrorBoundary.wrap = (Component, errorBoundaryProps) => props => ( ErrorBoundary.wrap = (Component, errorBoundaryProps) => props => (

View file

@ -24,13 +24,12 @@ import { classNameFactory } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Flex } from "@components/Flex"; import { Flex } from "@components/Flex";
import { gitRemote } from "@shared/vencordUserAgent"; import { gitRemote } from "@shared/vencordUserAgent";
import { proxyLazy } from "@utils/lazy";
import { Margins } from "@utils/margins"; import { Margins } from "@utils/margins";
import { classes, isObjectEmpty } from "@utils/misc"; import { classes, isObjectEmpty } from "@utils/misc";
import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
import { OptionType, Plugin } from "@utils/types"; import { OptionType, Plugin } from "@utils/types";
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; import { find, findByProps, findComponentByCode } from "@webpack";
import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common"; import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserUtils } from "@webpack/common";
import { User } from "discord-types/general"; import { User } from "discord-types/general";
import { Constructor } from "type-fest"; import { Constructor } from "type-fest";
@ -50,9 +49,9 @@ import { GithubButton, WebsiteButton } from "./LinkIconButton";
const cl = classNameFactory("vc-plugin-modal-"); const cl = classNameFactory("vc-plugin-modal-");
const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers"); const UserSummaryItem = findComponentByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers");
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar"); const AvatarStyles = findByProps("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
const UserRecord: Constructor<Partial<User>> = proxyLazy(() => UserStore.getCurrentUser().constructor) as any; const UserRecord = find<Constructor<Partial<User>>>(m => m?.prototype?.getAvatarURL && m?.prototype?.hasHadPremium);
interface PluginModalProps extends ModalProps { interface PluginModalProps extends ModalProps {
plugin: Plugin; plugin: Plugin;

View file

@ -33,19 +33,19 @@ import { Margins } from "@utils/margins";
import { classes, isObjectEmpty } from "@utils/misc"; import { classes, isObjectEmpty } from "@utils/misc";
import { useAwaiter } from "@utils/react"; import { useAwaiter } from "@utils/react";
import { Plugin } from "@utils/types"; import { Plugin } from "@utils/types";
import { findByPropsLazy } from "@webpack"; import { findByProps } from "@webpack";
import { Alerts, Button, Card, Forms, lodash, Parser, React, Select, Text, TextInput, Toasts, Tooltip, useMemo } from "@webpack/common"; import { Alerts, Button, Card, Forms, lodash, Parser, React, Select, Text, TextInput, Toasts, Tooltip, useMemo } from "@webpack/common";
import Plugins, { ExcludedPlugins } from "~plugins"; import Plugins, { ExcludedPlugins } from "~plugins";
// Avoid circular dependency // Avoid circular dependency
const { startDependenciesRecursive, startPlugin, stopPlugin } = proxyLazy(() => require("../../plugins")); const PluginManager = proxyLazy(() => require("../../plugins")) as typeof import("../../plugins");
const cl = classNameFactory("vc-plugins-"); const cl = classNameFactory("vc-plugins-");
const logger = new Logger("PluginSettings", "#a6d189"); const logger = new Logger("PluginSettings", "#a6d189");
const InputStyles = findByPropsLazy("inputWrapper", "inputDefault", "error"); const InputStyles = findByProps("inputWrapper", "inputDefault", "error");
const ButtonClasses = findByPropsLazy("button", "disabled", "enabled"); const ButtonClasses = findByProps("button", "disabled", "enabled");
function showErrorToast(message: string) { function showErrorToast(message: string) {
@ -100,7 +100,7 @@ export function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, on
// If we're enabling a plugin, make sure all deps are enabled recursively. // If we're enabling a plugin, make sure all deps are enabled recursively.
if (!wasEnabled) { if (!wasEnabled) {
const { restartNeeded, failures } = startDependenciesRecursive(plugin); const { restartNeeded, failures } = PluginManager.startDependenciesRecursive(plugin);
if (failures.length) { if (failures.length) {
logger.error(`Failed to start dependencies for ${plugin.name}: ${failures.join(", ")}`); logger.error(`Failed to start dependencies for ${plugin.name}: ${failures.join(", ")}`);
showNotice("Failed to start dependencies: " + failures.join(", "), "Close", () => null); showNotice("Failed to start dependencies: " + failures.join(", "), "Close", () => null);
@ -126,7 +126,7 @@ export function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, on
return; return;
} }
const result = wasEnabled ? stopPlugin(plugin) : startPlugin(plugin); const result = wasEnabled ? PluginManager.stopPlugin(plugin) : PluginManager.startPlugin(plugin);
if (!result) { if (!result) {
settings.enabled = false; settings.enabled = false;

View file

@ -19,7 +19,7 @@
import "./Switch.css"; import "./Switch.css";
import { classes } from "@utils/misc"; import { classes } from "@utils/misc";
import { findByPropsLazy } from "@webpack"; import { findByProps } from "@webpack";
interface SwitchProps { interface SwitchProps {
checked: boolean; checked: boolean;
@ -29,7 +29,7 @@ interface SwitchProps {
const SWITCH_ON = "var(--green-360)"; const SWITCH_ON = "var(--green-360)";
const SWITCH_OFF = "var(--primary-400)"; const SWITCH_OFF = "var(--primary-400)";
const SwitchClasses = findByPropsLazy("slider", "input", "container"); const SwitchClasses = findByProps("slider", "input", "container");
export function Switch({ checked, onChange, disabled }: SwitchProps) { export function Switch({ checked, onChange, disabled }: SwitchProps) {
return ( return (

View file

@ -22,7 +22,7 @@ import { Margins } from "@utils/margins";
import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches"; import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches";
import { makeCodeblock } from "@utils/text"; import { makeCodeblock } from "@utils/text";
import { Patch, ReplaceFn } from "@utils/types"; import { Patch, ReplaceFn } from "@utils/types";
import { search } from "@webpack"; import { searchFactories } from "@webpack";
import { Button, Clipboard, Forms, Parser, React, Switch, TextArea, TextInput } from "@webpack/common"; import { Button, Clipboard, Forms, Parser, React, Switch, TextArea, TextInput } from "@webpack/common";
import { SettingsTab, wrapTab } from "./shared"; import { SettingsTab, wrapTab } from "./shared";
@ -33,7 +33,7 @@ if (IS_DEV) {
} }
const findCandidates = debounce(function ({ find, setModule, setError }) { const findCandidates = debounce(function ({ find, setModule, setError }) {
const candidates = search(find); const candidates = searchFactories(find);
const keys = Object.keys(candidates); const keys = Object.keys(candidates);
const len = keys.length; const len = keys.length;
if (len === 0) if (len === 0)
@ -56,7 +56,7 @@ function ReplacementComponent({ module, match, replacement, setReplacementError
const [compileResult, setCompileResult] = React.useState<[boolean, string]>(); const [compileResult, setCompileResult] = React.useState<[boolean, string]>();
const [patchedCode, matchResult, diff] = React.useMemo(() => { const [patchedCode, matchResult, diff] = React.useMemo(() => {
const src: string = fact.toString().replaceAll("\n", ""); const src = String(fact).replaceAll("\n", "");
try { try {
new RegExp(match); new RegExp(match);

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Settings, useSettings } from "@api/Settings"; import { useSettings } from "@api/Settings";
import { classNameFactory } from "@api/Styles"; import { classNameFactory } from "@api/Styles";
import { Flex } from "@components/Flex"; import { Flex } from "@components/Flex";
import { DeleteIcon, FolderIcon, PaintbrushIcon, PencilIcon, PlusIcon, RestartIcon } from "@components/Icons"; import { DeleteIcon, FolderIcon, PaintbrushIcon, PencilIcon, PlusIcon, RestartIcon } from "@components/Icons";
@ -27,9 +27,9 @@ import { openInviteModal } from "@utils/discord";
import { Margins } from "@utils/margins"; import { Margins } from "@utils/margins";
import { showItemInFolder } from "@utils/native"; import { showItemInFolder } from "@utils/native";
import { useAwaiter } from "@utils/react"; import { useAwaiter } from "@utils/react";
import { findLazy } from "@webpack"; import { findComponentByFields } from "@webpack";
import { Card, Forms, React, showToast, TabBar, TextArea, useEffect, useRef, useState } from "@webpack/common"; import { Card, Forms, React, showToast, TabBar, TextArea, useEffect, useRef, useState } from "@webpack/common";
import type { ComponentType, Ref, SyntheticEvent } from "react"; import type { Ref, SyntheticEvent } from "react";
import Plugins from "~plugins"; import Plugins from "~plugins";
@ -37,14 +37,14 @@ import { AddonCard } from "./AddonCard";
import { QuickAction, QuickActionCard } from "./quickActions"; import { QuickAction, QuickActionCard } from "./quickActions";
import { SettingsTab, wrapTab } from "./shared"; import { SettingsTab, wrapTab } from "./shared";
type FileInput = ComponentType<{ type FileInputProps = {
ref: Ref<HTMLInputElement>; ref: Ref<HTMLInputElement>;
onChange: (e: SyntheticEvent<HTMLInputElement>) => void; onChange: (e: SyntheticEvent<HTMLInputElement>) => void;
multiple?: boolean; multiple?: boolean;
filters?: { name?: string; extensions: string[]; }[]; filters?: { name?: string; extensions: string[]; }[];
}>; };
const FileInput: FileInput = findLazy(m => m.prototype?.activateUploadDialogue && m.prototype.setRef); const FileInput = findComponentByFields<FileInputProps>("activateUploadDialogue", "setRef");
const cl = classNameFactory("vc-settings-theme-"); const cl = classNameFactory("vc-settings-theme-");
@ -257,7 +257,7 @@ function ThemesTab() {
Icon={PaintbrushIcon} Icon={PaintbrushIcon}
/> />
{Settings.plugins.ClientTheme.enabled && ( {Vencord.Plugins.isPluginEnabled("ClientTheme") && (
<QuickAction <QuickAction
text="Edit ClientTheme" text="Edit ClientTheme"
action={() => openPluginModal(Plugins.ClientTheme)} action={() => openPluginModal(Plugins.ClientTheme)}

View file

@ -8,13 +8,12 @@ import "./quickActions.css";
import { classNameFactory } from "@api/Styles"; import { classNameFactory } from "@api/Styles";
import { Card } from "@webpack/common"; import { Card } from "@webpack/common";
import type { ComponentType, PropsWithChildren, ReactNode } from "react";
const cl = classNameFactory("vc-settings-quickActions-"); const cl = classNameFactory("vc-settings-quickActions-");
export interface QuickActionProps { export interface QuickActionProps {
Icon: ComponentType<{ className?: string; }>; Icon: React.ComponentType<{ className?: string; }>;
text: ReactNode; text: React.ReactNode;
action?: () => void; action?: () => void;
disabled?: boolean; disabled?: boolean;
} }
@ -30,7 +29,7 @@ export function QuickAction(props: QuickActionProps) {
); );
} }
export function QuickActionCard(props: PropsWithChildren) { export function QuickActionCard(props: React.PropsWithChildren) {
return ( return (
<Card className={cl("card")}> <Card className={cl("card")}>
{props.children} {props.children}

View file

@ -24,9 +24,8 @@ import { handleComponentFailed } from "@components/handleComponentFailed";
import { Margins } from "@utils/margins"; import { Margins } from "@utils/margins";
import { onlyOnce } from "@utils/onlyOnce"; import { onlyOnce } from "@utils/onlyOnce";
import { Forms, Text } from "@webpack/common"; import { Forms, Text } from "@webpack/common";
import type { ComponentType, PropsWithChildren } from "react";
export function SettingsTab({ title, children }: PropsWithChildren<{ title: string; }>) { export function SettingsTab({ title, children }: React.PropsWithChildren<{ title: string; }>) {
return ( return (
<Forms.FormSection> <Forms.FormSection>
<Text <Text
@ -44,7 +43,7 @@ export function SettingsTab({ title, children }: PropsWithChildren<{ title: stri
export const handleSettingsTabError = onlyOnce(handleComponentFailed); export const handleSettingsTabError = onlyOnce(handleComponentFailed);
export function wrapTab(component: ComponentType<any>, tab: string) { export function wrapTab<P extends AnyRecord>(component: React.ComponentType<P>, tab: string) {
return ErrorBoundary.wrap(component, { return ErrorBoundary.wrap(component, {
message: `Failed to render the ${tab} tab. If this issue persists, try using the installer to reinstall!`, message: `Failed to render the ${tab} tab. If this issue persists, try using the installer to reinstall!`,
onError: handleSettingsTabError, onError: handleSettingsTabError,

View file

@ -23,35 +23,61 @@ if (IS_DEV || IS_REPORTER) {
var logger = new Logger("Tracer", "#FFD166"); var logger = new Logger("Tracer", "#FFD166");
} }
const noop = function () { }; export const beginTrace = !(IS_DEV || IS_REPORTER) ? () => { } :
export const beginTrace = !(IS_DEV || IS_REPORTER) ? noop :
function beginTrace(name: string, ...args: any[]) { function beginTrace(name: string, ...args: any[]) {
if (name in traces) if (name in traces) {
throw new Error(`Trace ${name} already exists!`); throw new Error(`Trace ${name} already exists!`);
}
traces[name] = [performance.now(), args]; traces[name] = [performance.now(), args];
}; };
export const finishTrace = !(IS_DEV || IS_REPORTER) ? noop : function finishTrace(name: string) { export const finishTrace = !(IS_DEV || IS_REPORTER) ? () => 0 :
const end = performance.now(); function finishTrace(name: string) {
const end = performance.now();
const [start, args] = traces[name]; const [start, args] = traces[name];
delete traces[name]; delete traces[name];
logger.debug(`${name} took ${end - start}ms`, args); const totalTime = end - start;
}; logger.debug(`${name} took ${totalTime}ms`, args);
return totalTime;
};
type Func = (...args: any[]) => any; type Func = (...args: any[]) => any;
type TraceNameMapper<F extends Func> = (...args: Parameters<F>) => string; type TraceNameMapper<F extends Func> = (...args: Parameters<F>) => string;
const noopTracer = function noopTracerWithResults<F extends Func>(name: string, f: F, mapper?: TraceNameMapper<F>) {
<F extends Func>(name: string, f: F, mapper?: TraceNameMapper<F>) => f; return function (this: unknown, ...args: Parameters<F>): [ReturnType<F>, number] {
return [f.apply(this, args), 0];
};
}
function noopTracer<F extends Func>(name: string, f: F, mapper?: TraceNameMapper<F>) {
return f;
}
export const traceFunctionWithResults = !(IS_DEV || IS_REPORTER)
? noopTracerWithResults
: function traceFunctionWithResults<F extends Func>(name: string, f: F, mapper?: TraceNameMapper<F>): (this: unknown, ...args: Parameters<F>) => [ReturnType<F>, number] {
return function (this: unknown, ...args: Parameters<F>) {
const traceName = mapper?.(...args) ?? name;
beginTrace(traceName, ...arguments);
try {
return [f.apply(this, args), finishTrace(traceName)];
} catch (e) {
finishTrace(traceName);
throw e;
}
};
};
export const traceFunction = !(IS_DEV || IS_REPORTER) export const traceFunction = !(IS_DEV || IS_REPORTER)
? noopTracer ? noopTracer
: function traceFunction<F extends Func>(name: string, f: F, mapper?: TraceNameMapper<F>): F { : function traceFunction<F extends Func>(name: string, f: F, mapper?: TraceNameMapper<F>): F {
return function (this: any, ...args: Parameters<F>) { return function (this: unknown, ...args: Parameters<F>) {
const traceName = mapper?.(...args) ?? name; const traceName = mapper?.(...args) ?? name;
beginTrace(traceName, ...arguments); beginTrace(traceName, ...arguments);

View file

@ -8,10 +8,11 @@ import { Logger } from "@utils/Logger";
import { canonicalizeMatch } from "@utils/patches"; import { canonicalizeMatch } from "@utils/patches";
import * as Webpack from "@webpack"; import * as Webpack from "@webpack";
import { wreq } from "@webpack"; import { wreq } from "@webpack";
import { AnyModuleFactory, ModuleFactory } from "webpack";
const LazyChunkLoaderLogger = new Logger("LazyChunkLoader");
export async function loadLazyChunks() { export async function loadLazyChunks() {
const LazyChunkLoaderLogger = new Logger("LazyChunkLoader");
try { try {
LazyChunkLoaderLogger.log("Loading all chunks..."); LazyChunkLoaderLogger.log("Loading all chunks...");
@ -25,16 +26,12 @@ export async function loadLazyChunks() {
// True if resolved, false otherwise // True if resolved, false otherwise
const chunksSearchPromises = [] as Array<() => boolean>; const chunksSearchPromises = [] as Array<() => boolean>;
const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?)\.then\(\i\.bind\(\i,"?([^)]+?)"?\)\)/g); const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?)\.then\(\i(?:\.\i)?\.bind\(\i,"?([^)]+?)"?(?:,[^)]+?)?\)\)/g);
async function searchAndLoadLazyChunks(factoryCode: string) { async function searchAndLoadLazyChunks(factoryCode: string) {
const lazyChunks = factoryCode.matchAll(LazyChunkRegex); const lazyChunks = factoryCode.matchAll(LazyChunkRegex);
const validChunkGroups = new Set<[chunkIds: number[], entryPoint: number]>(); const validChunkGroups = new Set<[chunkIds: number[], entryPoint: number]>();
// Workaround for a chunk that depends on the ChannelMessage component but may be be force loaded before
// the chunk containing the component
const shouldForceDefer = factoryCode.includes(".Messages.GUILD_FEED_UNFEATURE_BUTTON_TEXT");
await Promise.all(Array.from(lazyChunks).map(async ([, rawChunkIds, entryPoint]) => { await Promise.all(Array.from(lazyChunks).map(async ([, rawChunkIds, entryPoint]) => {
const chunkIds = rawChunkIds ? Array.from(rawChunkIds.matchAll(Webpack.ChunkIdsRegex)).map(m => Number(m[1])) : []; const chunkIds = rawChunkIds ? Array.from(rawChunkIds.matchAll(Webpack.ChunkIdsRegex)).map(m => Number(m[1])) : [];
@ -69,19 +66,14 @@ export async function loadLazyChunks() {
await Promise.all( await Promise.all(
Array.from(validChunkGroups) Array.from(validChunkGroups)
.map(([chunkIds]) => .map(([chunkIds]) =>
Promise.all(chunkIds.map(id => wreq.e(id as any).catch(() => { }))) Promise.all(chunkIds.map(id => wreq.e(id)))
) )
); );
// Requires the entry points for all valid chunk groups // Requires the entry points for all valid chunk groups
for (const [, entryPoint] of validChunkGroups) { for (const [, entryPoint] of validChunkGroups) {
try { try {
if (shouldForceDefer) { if (wreq.m[entryPoint]) wreq(entryPoint);
deferredRequires.add(entryPoint);
continue;
}
if (wreq.m[entryPoint]) wreq(entryPoint as any);
} catch (err) { } catch (err) {
console.error(err); console.error(err);
} }
@ -109,32 +101,33 @@ export async function loadLazyChunks() {
}, 0); }, 0);
} }
Webpack.factoryListeners.add(factory => { function factoryListener(factory: AnyModuleFactory | ModuleFactory) {
let isResolved = false; let isResolved = false;
searchAndLoadLazyChunks(factory.toString()).then(() => isResolved = true); searchAndLoadLazyChunks(String(factory))
.then(() => isResolved = true)
chunksSearchPromises.push(() => isResolved); .catch(() => isResolved = true);
});
for (const factoryId in wreq.m) {
let isResolved = false;
searchAndLoadLazyChunks(wreq.m[factoryId].toString()).then(() => isResolved = true);
chunksSearchPromises.push(() => isResolved); chunksSearchPromises.push(() => isResolved);
} }
Webpack.factoryListeners.add(factoryListener);
for (const factoryId in wreq.m) {
factoryListener(wreq.m[factoryId]);
}
await chunksSearchingDone; await chunksSearchingDone;
Webpack.factoryListeners.delete(factoryListener);
// Require deferred entry points // Require deferred entry points
for (const deferredRequire of deferredRequires) { for (const deferredRequire of deferredRequires) {
wreq!(deferredRequire as any); wreq(deferredRequire);
} }
// All chunks Discord has mapped to asset files, even if they are not used anymore // All chunks Discord has mapped to asset files, even if they are not used anymore
const allChunks = [] as number[]; const allChunks = [] as number[];
// Matches "id" or id: // Matches "id" or id:
for (const currentMatch of wreq!.u.toString().matchAll(/(?:"([\deE]+?)"(?![,}]))|(?:([\deE]+?):)/g)) { for (const currentMatch of String(wreq.u).matchAll(/(?:"([\deE]+?)"(?![,}]))|(?:([\deE]+?):)/g)) {
const id = currentMatch[1] ?? currentMatch[2]; const id = currentMatch[1] ?? currentMatch[2];
if (id == null) continue; if (id == null) continue;
@ -143,7 +136,8 @@ export async function loadLazyChunks() {
if (allChunks.length === 0) throw new Error("Failed to get all chunks"); if (allChunks.length === 0) throw new Error("Failed to get all chunks");
// Chunks that are not loaded (not used) by Discord code anymore // Chunks which our regex could not catch to load
// It will always contain WebWorker assets, and also currently contains some language packs which are loaded differently
const chunksLeft = allChunks.filter(id => { const chunksLeft = allChunks.filter(id => {
return !(validChunks.has(id) || invalidChunks.has(id)); return !(validChunks.has(id) || invalidChunks.has(id));
}); });
@ -153,12 +147,9 @@ export async function loadLazyChunks() {
.then(r => r.text()) .then(r => r.text())
.then(t => t.includes("importScripts(")); .then(t => t.includes("importScripts("));
// Loads and requires a chunk // Loads the chunk. Currently this only happens with the language packs which are loaded differently
if (!isWorkerAsset) { if (!isWorkerAsset) {
await wreq.e(id as any); await wreq.e(id);
// 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);
} }
})); }));

View file

@ -4,22 +4,39 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
*/ */
import { SYM_LAZY_COMPONENT_INNER } from "@utils/lazyReact";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import { SYM_PROXY_INNER_GET, SYM_PROXY_INNER_VALUE } from "@utils/proxyInner";
import * as Webpack from "@webpack"; import * as Webpack from "@webpack";
import { patches } from "plugins"; import { addPatch, patches } from "plugins";
import { loadLazyChunks } from "./loadLazyChunks"; import { loadLazyChunks } from "./loadLazyChunks";
const ReporterLogger = new Logger("Reporter");
async function runReporter() { async function runReporter() {
const ReporterLogger = new Logger("Reporter");
try { try {
ReporterLogger.log("Starting test..."); ReporterLogger.log("Starting test...");
let loadLazyChunksResolve: (value: void | PromiseLike<void>) => void; let loadLazyChunksResolve: (value: void) => void;
const loadLazyChunksDone = new Promise<void>(r => loadLazyChunksResolve = r); const loadLazyChunksDone = new Promise<void>(r => loadLazyChunksResolve = r);
Webpack.beforeInitListeners.add(() => loadLazyChunks().then((loadLazyChunksResolve))); // The main patch for starting the reporter chunk loading
addPatch({
find: '"Could not find app-mount"',
replacement: {
match: /(?<="use strict";)/,
replace: "Vencord.Webpack._initReporter();"
}
}, "Vencord Reporter");
// @ts-ignore
Vencord.Webpack._initReporter = function () {
// initReporter is called in the patched entry point of Discord
// setImmediate to only start searching for lazy chunks after Discord initialized the app
setTimeout(() => loadLazyChunks().then(loadLazyChunksResolve), 0);
};
await loadLazyChunksDone; await loadLazyChunksDone;
for (const patch of patches) { for (const patch of patches) {
@ -28,52 +45,158 @@ async function runReporter() {
} }
} }
for (const [searchType, args] of Webpack.lazyWebpackSearchHistory) { for (const [plugin, moduleId, match, totalTime] of Vencord.WebpackPatcher.patchTimings) {
let method = searchType; if (totalTime > 3) {
new Logger("WebpackInterceptor").warn(`Patch by ${plugin} took ${totalTime}ms (Module id is ${String(moduleId)}): ${match}`);
if (searchType === "findComponent") method = "find";
if (searchType === "findExportedComponent") method = "findByProps";
if (searchType === "waitFor" || searchType === "waitForComponent") {
if (typeof args[0] === "string") method = "findByProps";
else method = "find";
} }
if (searchType === "waitForStore") method = "findStore"; }
let result: any; await Promise.all(Webpack.webpackSearchHistory.map(async ([searchType, args]) => {
args = [...args];
let result = null as any;
try { try {
if (method === "proxyLazyWebpack" || method === "LazyComponentWebpack") { switch (searchType) {
const [factory] = args; case "webpackDependantLazy":
result = factory(); case "webpackDependantLazyComponent": {
} else if (method === "extractAndLoadChunks") { const [factory] = args;
const [code, matcher] = args; result = factory();
break;
}
case "extractAndLoadChunks": {
const extractAndLoadChunks = args.shift();
result = await Webpack.extractAndLoadChunks(code, matcher); result = await extractAndLoadChunks();
if (result === false) result = null; if (result === false) {
} else if (method === "mapMangledModule") { result = null;
const [code, mapper] = args; }
result = Webpack.mapMangledModule(code, mapper); break;
if (Object.keys(result).length !== Object.keys(mapper).length) throw new Error("Webpack Find Fail"); }
} else { default: {
// @ts-ignore const findResult = args.shift();
result = Webpack[method](...args);
if (findResult != null) {
if (findResult.$$vencordCallbackCalled != null && findResult.$$vencordCallbackCalled()) {
result = findResult;
break;
}
if (findResult[SYM_PROXY_INNER_GET] != null) {
result = findResult[SYM_PROXY_INNER_VALUE];
break;
}
if (findResult[SYM_LAZY_COMPONENT_INNER] != null) {
result = findResult[SYM_LAZY_COMPONENT_INNER]();
break;
}
if (searchType === "mapMangledModule") {
result = findResult;
for (const innerMap in result) {
if (
(result[innerMap][SYM_PROXY_INNER_GET] != null && result[innerMap][SYM_PROXY_INNER_VALUE] == null) ||
(result[innerMap][SYM_LAZY_COMPONENT_INNER] != null && result[innerMap][SYM_LAZY_COMPONENT_INNER]() == null)
) {
throw new Error("Webpack Find Fail");
}
}
break;
}
// This can happen if a `find` was immediately found
result = findResult;
}
break;
}
} }
if (result == null || (result.$$vencordInternal != null && result.$$vencordInternal() == null)) throw new Error("Webpack Find Fail"); if (result == null) {
throw new Error("Webpack Find Fail");
}
} catch (e) { } catch (e) {
let logMessage = searchType; 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})`; let filterName = "";
let parsedArgs = args;
if (args[0].$$vencordProps != null) {
if (["find", "findComponent", "waitFor"].includes(searchType)) {
filterName = args[0].$$vencordProps[0];
parsedArgs = args[0].$$vencordProps.slice(1);
} else {
parsedArgs = args[0].$$vencordProps;
}
}
function stringifyFilter(code: Webpack.CodeFilterWithSingle) {
if (Array.isArray(code)) {
return `[${code.map(arg => arg instanceof RegExp ? String(arg) : JSON.stringify(arg)).join(", ")}]`;
}
return code instanceof RegExp ? String(code) : JSON.stringify(code);
}
// if parsedArgs is the same as args, it means vencordProps of the filter was not available (like in normal filter functions),
// so log the filter function instead
if (
parsedArgs === args &&
["waitFor", "find", "findComponent", "webpackDependantLazy", "webpackDependantLazyComponent"].includes(searchType)
) {
let filter = String(parsedArgs[0]);
if (filter.length > 150) {
filter = filter.slice(0, 147) + "...";
}
logMessage += `(${filter})`;
} else if (searchType === "extractAndLoadChunks") {
const [code, matcher] = parsedArgs;
let regexStr: string;
if (matcher === Webpack.DefaultExtractAndLoadChunksRegex) {
regexStr = "DefaultExtractAndLoadChunksRegex";
} else {
regexStr = String(matcher);
}
logMessage += `(${stringifyFilter(code)}, ${regexStr})`;
} else if (searchType === "mapMangledModule") {
const [code, mappers] = parsedArgs;
const parsedFailedMappers = Object.entries<any>(mappers)
.filter(([key]) =>
result == null ||
(result[key]?.[SYM_PROXY_INNER_GET] != null && result[key][SYM_PROXY_INNER_VALUE] == null) ||
(result[key]?.[SYM_LAZY_COMPONENT_INNER] != null && result[key][SYM_LAZY_COMPONENT_INNER]() == null)
)
.map(([key, filter]) => {
let parsedFilter: string;
if (filter.$$vencordProps != null) {
const filterName = filter.$$vencordProps[0];
parsedFilter = `${filterName}(${filter.$$vencordProps.slice(1).map((arg: any) => arg instanceof RegExp ? String(arg) : JSON.stringify(arg)).join(", ")})`;
} else {
parsedFilter = String(filter);
if (parsedFilter.length > 150) {
parsedFilter = parsedFilter.slice(0, 147) + "...";
}
}
return [key, parsedFilter];
});
logMessage += `(${stringifyFilter(code)}, {\n${parsedFailedMappers.map(([key, parsedFilter]) => `\t${key}: ${parsedFilter}`).join(",\n")}\n})`;
} else {
logMessage += `(${filterName.length ? `${filterName}(` : ""}${parsedArgs.map(stringifyFilter).join(", ")})${filterName.length ? ")" : ""}`;
} }
else logMessage += `(${args.map(arg => `"${arg}"`).join(", ")})`;
ReporterLogger.log("Webpack Find Fail:", logMessage); ReporterLogger.log("Webpack Find Fail:", logMessage);
} }
} }));
ReporterLogger.log("Finished test"); ReporterLogger.log("Finished test");
} catch (e) { } catch (e) {
@ -81,4 +204,5 @@ async function runReporter() {
} }
} }
runReporter(); // Run after the Vencord object has been created
setTimeout(runReporter, 0);

11
src/globals.d.ts vendored
View file

@ -19,6 +19,10 @@
import { LoDashStatic } from "lodash"; import { LoDashStatic } from "lodash";
declare global { declare global {
type AnyRecord = Record<PropertyKey, any>;
type AnyComponentType<P extends AnyRecord = AnyRecord> = React.ComponentType<P & AnyRecord> & AnyRecord;
type AnyComponentTypeWithChildren<P extends AnyRecord = AnyRecord> = AnyComponentType<React.PropsWithChildren<P>>;
/** /**
* This exists only at build time, so references to it in patches should insert it * This exists only at build time, so references to it in patches should insert it
* via String interpolation OR use different replacement code based on this * via String interpolation OR use different replacement code based on this
@ -64,13 +68,8 @@ declare global {
export var Vesktop: any; export var Vesktop: any;
export var VesktopNative: any; export var VesktopNative: any;
interface Window { interface Window extends AnyRecord {
webpackChunkdiscord_app: {
push(chunk: any): any;
pop(): any;
};
_: LoDashStatic; _: LoDashStatic;
[k: string]: any;
} }
} }

View file

@ -20,6 +20,7 @@ import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import definePlugin, { OptionType, StartAt } from "@utils/types"; import definePlugin, { OptionType, StartAt } from "@utils/types";
import { WebpackRequire } from "webpack";
const settings = definePluginSettings({ const settings = definePluginSettings({
disableAnalytics: { disableAnalytics: {
@ -81,9 +82,9 @@ export default definePlugin({
Object.defineProperty(Function.prototype, "g", { Object.defineProperty(Function.prototype, "g", {
configurable: true, configurable: true,
set(v: any) { set(this: WebpackRequire, globalObj: WebpackRequire["g"]) {
Object.defineProperty(this, "g", { Object.defineProperty(this, "g", {
value: v, value: globalObj,
configurable: true, configurable: true,
enumerable: true, enumerable: true,
writable: true writable: true
@ -92,11 +93,11 @@ export default definePlugin({
// Ensure this is most likely the Sentry WebpackInstance. // Ensure this is most likely the Sentry WebpackInstance.
// Function.g is a very generic property and is not uncommon for another WebpackInstance (or even a React component: <g></g>) to include it // Function.g is a very generic property and is not uncommon for another WebpackInstance (or even a React component: <g></g>) to include it
const { stack } = new Error(); const { stack } = new Error();
if (!(stack?.includes("discord.com") || stack?.includes("discordapp.com")) || !String(this).includes("exports:{}") || this.c != null) { if (this.c != null || !stack?.includes("http") || !String(this).includes("exports:{}")) {
return; return;
} }
const assetPath = stack?.match(/\/assets\/.+?\.js/)?.[0]; const assetPath = stack.match(/http.+?(?=:\d+?:\d+?$)/m)?.[0];
if (!assetPath) { if (!assetPath) {
return; return;
} }
@ -106,7 +107,8 @@ export default definePlugin({
srcRequest.send(); srcRequest.send();
// Final condition to see if this is the Sentry WebpackInstance // Final condition to see if this is the Sentry WebpackInstance
if (!srcRequest.responseText.includes("window.DiscordSentry=")) { // This is matching window.DiscordSentry=, but without `window` to avoid issues on some proxies
if (!srcRequest.responseText.includes(".DiscordSentry=")) {
return; return;
} }

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Settings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import BackupAndRestoreTab from "@components/VencordSettings/BackupAndRestoreTab"; import BackupAndRestoreTab from "@components/VencordSettings/BackupAndRestoreTab";
import CloudTab from "@components/VencordSettings/CloudTab"; import CloudTab from "@components/VencordSettings/CloudTab";
import PatchHelperTab from "@components/VencordSettings/PatchHelperTab"; import PatchHelperTab from "@components/VencordSettings/PatchHelperTab";
@ -33,11 +33,27 @@ import gitHash from "~git-hash";
type SectionType = "HEADER" | "DIVIDER" | "CUSTOM"; type SectionType = "HEADER" | "DIVIDER" | "CUSTOM";
type SectionTypes = Record<SectionType, SectionType>; type SectionTypes = Record<SectionType, SectionType>;
const settings = definePluginSettings({
settingsLocation: {
type: OptionType.SELECT,
description: "Where to put the Vencord settings section",
options: [
{ label: "At the very top", value: "top" },
{ label: "Above the Nitro section", value: "aboveNitro", default: true },
{ label: "Below the Nitro section", value: "belowNitro" },
{ label: "Above Activity Settings", value: "aboveActivity" },
{ label: "Below Activity Settings", value: "belowActivity" },
{ label: "At the very bottom", value: "bottom" },
]
}
});
export default definePlugin({ export default definePlugin({
name: "Settings", name: "Settings",
description: "Adds Settings UI and debug info", description: "Adds Settings UI and debug info",
authors: [Devs.Ven, Devs.Megu], authors: [Devs.Ven, Devs.Megu],
required: true, required: true,
settings,
patches: [ patches: [
{ {
@ -136,12 +152,12 @@ export default definePlugin({
].filter(Boolean); ].filter(Boolean);
}, },
isRightSpot({ header, settings }: { header?: string; settings?: string[]; }) { isRightSpot({ header, settingsChilds }: { header?: string; settingsChilds?: string[]; }) {
const firstChild = settings?.[0]; const firstChild = settingsChilds?.[0];
// lowest two elements... sanity backup // lowest two elements... sanity backup
if (firstChild === "LOGOUT" || firstChild === "SOCIAL_LINKS") return true; if (firstChild === "LOGOUT" || firstChild === "SOCIAL_LINKS") return true;
const { settingsLocation } = Settings.plugins.Settings; const { settingsLocation } = settings.store;
if (settingsLocation === "bottom") return firstChild === "LOGOUT"; if (settingsLocation === "bottom") return firstChild === "LOGOUT";
if (settingsLocation === "belowActivity") return firstChild === "CHANGELOG"; if (settingsLocation === "belowActivity") return firstChild === "CHANGELOG";
@ -181,21 +197,6 @@ export default definePlugin({
}; };
}, },
options: {
settingsLocation: {
type: OptionType.SELECT,
description: "Where to put the Vencord settings section",
options: [
{ label: "At the very top", value: "top" },
{ label: "Above the Nitro section", value: "aboveNitro", default: true },
{ label: "Below the Nitro section", value: "belowNitro" },
{ label: "Above Activity Settings", value: "aboveActivity" },
{ label: "Below Activity Settings", value: "belowActivity" },
{ label: "At the very bottom", value: "bottom" },
]
},
},
get electronVersion() { get electronVersion() {
return VencordNative.native.getVersions().electron || window.armcord?.electron || null; return VencordNative.native.getVersions().electron || window.armcord?.electron || null;
}, },

View file

@ -59,7 +59,7 @@ const TrustedRolesIds = [
const AsyncFunction = async function () { }.constructor; const AsyncFunction = async function () { }.constructor;
const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame")!; const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame");
async function forceUpdate() { async function forceUpdate() {
const outdated = await checkForUpdates(); const outdated = await checkForUpdates();

View file

@ -9,7 +9,7 @@ import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { getCurrentChannel } from "@utils/discord"; import { getCurrentChannel } from "@utils/discord";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; import { findByProps, findComponentByCode } from "@webpack";
import { ContextMenuApi, Menu, useEffect, useRef } from "@webpack/common"; import { ContextMenuApi, Menu, useEffect, useRef } from "@webpack/common";
import { User } from "discord-types/general"; import { User } from "discord-types/general";
@ -19,11 +19,11 @@ interface UserProfileProps {
originalPopout: () => React.ReactNode; originalPopout: () => React.ReactNode;
} }
const UserProfile = findComponentByCodeLazy("UserProfilePopoutWrapper: user cannot be undefined"); const UserProfile = findComponentByCode("UserProfilePopoutWrapper: user cannot be undefined");
const styles = findByPropsLazy("accountProfilePopoutWrapper"); const styles = findByProps("accountProfilePopoutWrapper");
let openAlternatePopout = false; let openAlternatePopout = false;
let accountPanelRef: React.MutableRefObject<Record<PropertyKey, any> | null> = { current: null }; let accountPanelRef: React.MutableRefObject<AnyRecord | null> = { current: null };
const AccountPanelContextMenu = ErrorBoundary.wrap(() => { const AccountPanelContextMenu = ErrorBoundary.wrap(() => {
const { prioritizeServerProfile } = settings.use(["prioritizeServerProfile"]); const { prioritizeServerProfile } = settings.use(["prioritizeServerProfile"]);

View file

@ -21,12 +21,12 @@ import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByCodeLazy, findByPropsLazy } from "@webpack"; import { findByProps, findComponentByCode } from "@webpack";
type AnonUpload = Upload & { anonymise?: boolean; }; type AnonUpload = Upload & { anonymise?: boolean; };
const ActionBarIcon = findByCodeLazy(".actionBarIcon)"); const ActionBarIcon = findComponentByCode(".actionBarIcon)");
const UploadDraft = findByPropsLazy("popFirstFile", "update"); const UploadDraft = findByProps("popFirstFile", "update");
const enum Methods { const enum Methods {
Random, Random,

View file

@ -20,10 +20,10 @@ import { popNotice, showNotice } from "@api/Notices";
import { Link } from "@components/Link"; import { Link } from "@components/Link";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { ReporterTestable } from "@utils/types"; import definePlugin, { ReporterTestable } from "@utils/types";
import { findByCodeLazy } from "@webpack"; import { findByCode } from "@webpack";
import { ApplicationAssetUtils, FluxDispatcher, Forms, Toasts } from "@webpack/common"; import { ApplicationAssetUtils, FluxDispatcher, Forms, Toasts } from "@webpack/common";
const fetchApplicationsRPC = findByCodeLazy("APPLICATION_RPC(", "Client ID"); const fetchApplicationsRPC = findByCode("APPLICATION_RPC(", "Client ID");
async function lookupAsset(applicationId: string, key: string): Promise<string> { async function lookupAsset(applicationId: string, key: string): Promise<string> {
return (await ApplicationAssetUtils.fetchAssetIds(applicationId, [key]))[0]; return (await ApplicationAssetUtils.fetchAssetIds(applicationId, [key]))[0];

View file

@ -17,15 +17,15 @@
*/ */
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack"; import { findByProps, findComponentByCode, findStore } from "@webpack";
import { useStateFromStores } from "@webpack/common"; import { useStateFromStores } from "@webpack/common";
import type { CSSProperties } from "react"; import type { CSSProperties } from "react";
import { ExpandedGuildFolderStore, settings } from "."; import { ExpandedGuildFolderStore, settings } from ".";
const ChannelRTCStore = findStoreLazy("ChannelRTCStore"); const ChannelRTCStore = findStore("ChannelRTCStore");
const Animations = findByPropsLazy("a", "animated", "useTransition"); const Animations = findByProps("a", "animated", "useTransition");
const GuildsBar = findComponentByCodeLazy('("guildsnav")'); const GuildsBar = findComponentByCode('("guildsnav")');
export default ErrorBoundary.wrap(guildsBarProps => { export default ErrorBoundary.wrap(guildsBarProps => {
const expandedFolders = useStateFromStores([ExpandedGuildFolderStore], () => ExpandedGuildFolderStore.getExpandedFolders()); const expandedFolders = useStateFromStores([ExpandedGuildFolderStore], () => ExpandedGuildFolderStore.getExpandedFolders());

View file

@ -19,7 +19,7 @@
import { definePluginSettings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy, findLazy, findStoreLazy } from "@webpack"; import { find, findByProps, findStore } from "@webpack";
import { FluxDispatcher, i18n, useMemo } from "@webpack/common"; import { FluxDispatcher, i18n, useMemo } from "@webpack/common";
import FolderSideBar from "./FolderSideBar"; import FolderSideBar from "./FolderSideBar";
@ -30,10 +30,10 @@ enum FolderIconDisplay {
MoreThanOneFolderExpanded MoreThanOneFolderExpanded
} }
export const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore"); export const ExpandedGuildFolderStore = findStore("ExpandedGuildFolderStore");
const SortedGuildStore = findStoreLazy("SortedGuildStore"); const SortedGuildStore = findStore("SortedGuildStore");
const GuildsTree = findLazy(m => m.prototype?.moveNextTo); const GuildsTree = find(m => m.prototype?.moveNextTo);
const FolderUtils = findByPropsLazy("move", "toggleGuildFolderExpand"); const FolderUtils = findByProps("move", "toggleGuildFolderExpand");
let lastGuildId = null as string | null; let lastGuildId = null as string | null;
let dispatchingFoldersClose = false; let dispatchingFoldersClose = false;

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { definePluginSettings, Settings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { canonicalizeMatch } from "@utils/patches"; import { canonicalizeMatch } from "@utils/patches";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
@ -31,7 +31,7 @@ const settings = definePluginSettings({
noSpellCheck: { noSpellCheck: {
type: OptionType.BOOLEAN, type: OptionType.BOOLEAN,
description: "Disable spellcheck in notes", description: "Disable spellcheck in notes",
disabled: () => Settings.plugins.BetterNotesBox.hide, disabled: () => settings.store.hide,
default: false default: false
} }
}); });

View file

@ -10,12 +10,12 @@ import { ImageIcon } from "@components/Icons";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { getCurrentGuild, openImageModal } from "@utils/discord"; import { getCurrentGuild, openImageModal } from "@utils/discord";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack"; import { findByProps } from "@webpack";
import { Clipboard, GuildStore, Menu, PermissionStore } from "@webpack/common"; import { Clipboard, GuildStore, Menu, PermissionStore } from "@webpack/common";
const GuildSettingsActions = findByPropsLazy("open", "selectRole", "updateGuild"); const GuildSettingsActions = findByProps("open", "selectRole", "updateGuild");
const DeveloperMode = getUserSettingLazy("appearance", "developerMode")!; const DeveloperMode = getUserSettingLazy("appearance", "developerMode");
function PencilIcon() { function PencilIcon() {
return ( return (

View file

@ -16,16 +16,32 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Settings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { Clipboard, Toasts } from "@webpack/common"; import { Clipboard, Toasts } from "@webpack/common";
const settings = definePluginSettings({
bothStyles: {
type: OptionType.BOOLEAN,
description: "Show both role dot and coloured names",
restartNeeded: true,
default: false,
},
copyRoleColorInProfilePopout: {
type: OptionType.BOOLEAN,
description: "Allow click on role dot in profile popout to copy role color",
restartNeeded: true,
default: false
}
});
export default definePlugin({ export default definePlugin({
name: "BetterRoleDot", name: "BetterRoleDot",
authors: [Devs.Ven, Devs.AutumnVN], authors: [Devs.Ven, Devs.AutumnVN],
description: description:
"Copy role colour on RoleDot (accessibility setting) click. Also allows using both RoleDot and coloured names simultaneously", "Copy role colour on RoleDot (accessibility setting) click. Also allows using both RoleDot and coloured names simultaneously",
settings,
patches: [ patches: [
{ {
@ -39,7 +55,7 @@ export default definePlugin({
find: '"dot"===', find: '"dot"===',
all: true, all: true,
noWarn: true, noWarn: true,
predicate: () => Settings.plugins.BetterRoleDot.bothStyles, predicate: () => settings.store.bothStyles,
replacement: { replacement: {
match: /"(?:username|dot)"===\i(?!\.\i)/g, match: /"(?:username|dot)"===\i(?!\.\i)/g,
replace: "true", replace: "true",
@ -49,7 +65,7 @@ export default definePlugin({
{ {
find: ".ADD_ROLE_A11Y_LABEL", find: ".ADD_ROLE_A11Y_LABEL",
all: true, all: true,
predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles, predicate: () => settings.store.copyRoleColorInProfilePopout && !settings.store.bothStyles,
noWarn: true, noWarn: true,
replacement: { replacement: {
match: /"dot"===\i/, match: /"dot"===\i/,
@ -59,7 +75,7 @@ export default definePlugin({
{ {
find: ".roleVerifiedIcon", find: ".roleVerifiedIcon",
all: true, all: true,
predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles, predicate: () => settings.store.copyRoleColorInProfilePopout && !settings.store.bothStyles,
noWarn: true, noWarn: true,
replacement: { replacement: {
match: /"dot"===\i/, match: /"dot"===\i/,
@ -68,21 +84,6 @@ export default definePlugin({
} }
], ],
options: {
bothStyles: {
type: OptionType.BOOLEAN,
description: "Show both role dot and coloured names",
restartNeeded: true,
default: false,
},
copyRoleColorInProfilePopout: {
type: OptionType.BOOLEAN,
description: "Allow click on role dot in profile popout to copy role color",
restartNeeded: true,
default: false
}
},
copyToClipBoard(color: string) { copyToClipBoard(color: string) {
Clipboard.copy(color); Clipboard.copy(color);
Toasts.show({ Toasts.show({

View file

@ -21,20 +21,20 @@ import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy, findExportedComponentLazy, findStoreLazy } from "@webpack"; import { findByProps, findExportedComponent, findStore } from "@webpack";
import { Constants, React, RestAPI, Tooltip } from "@webpack/common"; import { Constants, React, RestAPI, Tooltip } from "@webpack/common";
import { RenameButton } from "./components/RenameButton"; import { RenameButton } from "./components/RenameButton";
import { Session, SessionInfo } from "./types"; import { Session, SessionInfo } from "./types";
import { fetchNamesFromDataStore, getDefaultName, GetOsColor, GetPlatformIcon, savedSessionsCache, saveSessionsToDataStore } from "./utils"; import { fetchNamesFromDataStore, getDefaultName, GetOsColor, GetPlatformIcon, savedSessionsCache, saveSessionsToDataStore } from "./utils";
const AuthSessionsStore = findStoreLazy("AuthSessionsStore"); const AuthSessionsStore = findStore("AuthSessionsStore");
const UserSettingsModal = findByPropsLazy("saveAccountChanges", "open"); const UserSettingsModal = findByProps("saveAccountChanges", "open");
const TimestampClasses = findByPropsLazy("timestampTooltip", "blockquoteContainer"); const TimestampClasses = findByProps("timestampTooltip", "blockquoteContainer");
const SessionIconClasses = findByPropsLazy("sessionIcon"); const SessionIconClasses = findByProps("sessionIcon");
const BlobMask = findExportedComponentLazy("BlobMask"); const BlobMask = findExportedComponent("BlobMask");
const settings = definePluginSettings({ const settings = definePluginSettings({
backgroundCheck: { backgroundCheck: {

View file

@ -8,8 +8,10 @@ import { definePluginSettings } from "@api/Settings";
import { classNameFactory } from "@api/Styles"; import { classNameFactory } from "@api/Styles";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import { SYM_PROXY_INNER_VALUE } from "@utils/proxyInner";
import { NoopComponent } from "@utils/react";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { waitFor } from "@webpack"; import { findByProps } from "@webpack";
import { ComponentDispatch, FocusLock, i18n, Menu, useEffect, useRef } from "@webpack/common"; import { ComponentDispatch, FocusLock, i18n, Menu, useEffect, useRef } from "@webpack/common";
import type { HTMLAttributes, ReactElement } from "react"; import type { HTMLAttributes, ReactElement } from "react";
@ -18,8 +20,7 @@ import PluginsSubmenu from "./PluginsSubmenu";
type SettingsEntry = { section: string, label: string; }; type SettingsEntry = { section: string, label: string; };
const cl = classNameFactory(""); const cl = classNameFactory("");
let Classes: Record<string, string>; const Classes = findByProps("animating", "baseLayer", "bg", "layer", "layers");
waitFor(["animating", "baseLayer", "bg", "layer", "layers"], m => Classes = m);
const settings = definePluginSettings({ const settings = definePluginSettings({
disableFade: { disableFade: {
@ -142,7 +143,7 @@ export default definePlugin({
// try catch will only catch errors in the Layer function (hence why it's called as a plain function rather than a component), but // try catch will only catch errors in the Layer function (hence why it's called as a plain function rather than a component), but
// not in children // not in children
Layer(props: LayerProps) { Layer(props: LayerProps) {
if (!FocusLock || !ComponentDispatch || !Classes) { if (FocusLock === NoopComponent || ComponentDispatch[SYM_PROXY_INNER_VALUE] == null || Classes[SYM_PROXY_INNER_VALUE] == null) {
new Logger("BetterSettings").error("Failed to find some components"); new Logger("BetterSettings").error("Failed to find some components");
return props.children; return props.children;
} }

View file

@ -16,9 +16,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { findStoreLazy } from "@webpack"; import { findStore } from "@webpack";
import * as t from "./types/stores"; import * as t from "./types/stores";
export const ApplicationStreamPreviewStore: t.ApplicationStreamPreviewStore = findStoreLazy("ApplicationStreamPreviewStore"); export const ApplicationStreamPreviewStore: t.ApplicationStreamPreviewStore = findStore("ApplicationStreamPreviewStore");
export const ApplicationStreamingStore: t.ApplicationStreamingStore = findStoreLazy("ApplicationStreamingStore"); export const ApplicationStreamingStore: t.ApplicationStreamingStore = findStore("ApplicationStreamingStore");

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Settings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
@ -26,7 +26,7 @@ function setCss() {
style.textContent = ` style.textContent = `
.vc-nsfw-img [class^=imageWrapper] img, .vc-nsfw-img [class^=imageWrapper] img,
.vc-nsfw-img [class^=wrapperPaused] video { .vc-nsfw-img [class^=wrapperPaused] video {
filter: blur(${Settings.plugins.BlurNSFW.blurAmount}px); filter: blur(${settings.store.blurAmount}px);
transition: filter 0.2s; transition: filter 0.2s;
} }
.vc-nsfw-img [class^=imageWrapper]:hover img, .vc-nsfw-img [class^=imageWrapper]:hover img,
@ -36,10 +36,20 @@ function setCss() {
`; `;
} }
const settings = definePluginSettings({
blurAmount: {
type: OptionType.NUMBER,
description: "Blur Amount",
default: 10,
onChange: setCss
}
});
export default definePlugin({ export default definePlugin({
name: "BlurNSFW", name: "BlurNSFW",
description: "Blur attachments in NSFW channels until hovered", description: "Blur attachments in NSFW channels until hovered",
authors: [Devs.Ven], authors: [Devs.Ven],
settings,
patches: [ patches: [
{ {
@ -51,15 +61,6 @@ export default definePlugin({
} }
], ],
options: {
blurAmount: {
type: OptionType.NUMBER,
description: "Blur Amount",
default: 10,
onChange: setCss
}
},
start() { start() {
style = document.createElement("style"); style = document.createElement("style");
style.id = "VcBlurNsfw"; style.id = "VcBlurNsfw";

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Settings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { useTimer } from "@utils/react"; import { useTimer } from "@utils/react";
@ -25,7 +25,7 @@ import { React } from "@webpack/common";
function formatDuration(ms: number) { function formatDuration(ms: number) {
// here be dragons (moment fucking sucks) // here be dragons (moment fucking sucks)
const human = Settings.plugins.CallTimer.format === "human"; const human = settings.store.format === "human";
const format = (n: number) => human ? n : n.toString().padStart(2, "0"); const format = (n: number) => human ? n : n.toString().padStart(2, "0");
const unit = (s: string) => human ? s : ""; const unit = (s: string) => human ? s : "";
@ -46,32 +46,33 @@ function formatDuration(ms: number) {
return res; return res;
} }
const settings = definePluginSettings({
format: {
type: OptionType.SELECT,
description: "The timer format. This can be any valid moment.js format",
options: [
{
label: "30d 23:00:42",
value: "stopwatch",
default: true
},
{
label: "30d 23h 00m 42s",
value: "human"
}
]
}
});
export default definePlugin({ export default definePlugin({
name: "CallTimer", name: "CallTimer",
description: "Adds a timer to vcs", description: "Adds a timer to vcs",
authors: [Devs.Ven], authors: [Devs.Ven],
settings,
startTime: 0, startTime: 0,
interval: void 0 as NodeJS.Timeout | undefined, interval: void 0 as NodeJS.Timeout | undefined,
options: {
format: {
type: OptionType.SELECT,
description: "The timer format. This can be any valid moment.js format",
options: [
{
label: "30d 23:00:42",
value: "stopwatch",
default: true
},
{
label: "30d 23h 00m 42s",
value: "human"
}
]
}
},
patches: [{ patches: [{
find: "renderConnectionStatus(){", find: "renderConnectionStatus(){",
replacement: { replacement: {

View file

@ -11,10 +11,10 @@ import { Devs } from "@utils/constants";
import { Margins } from "@utils/margins"; import { Margins } from "@utils/margins";
import { classes } from "@utils/misc"; import { classes } from "@utils/misc";
import definePlugin, { OptionType, StartAt } from "@utils/types"; import definePlugin, { OptionType, StartAt } from "@utils/types";
import { findByCodeLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack"; import { findByCode, findComponentByCode, findStore } from "@webpack";
import { Button, Forms, ThemeStore, useStateFromStores } from "@webpack/common"; import { Button, Forms, ThemeStore, useStateFromStores } from "@webpack/common";
const ColorPicker = findComponentByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); const ColorPicker = findComponentByCode(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)");
const colorPresets = [ const colorPresets = [
"#1E1514", "#172019", "#13171B", "#1C1C28", "#402D2D", "#1E1514", "#172019", "#13171B", "#1C1C28", "#402D2D",
@ -30,20 +30,20 @@ function onPickColor(color: number) {
updateColorVars(hexColor); updateColorVars(hexColor);
} }
const saveClientTheme = findByCodeLazy('type:"UNSYNCED_USER_SETTINGS_UPDATE', '"system"==='); const saveClientTheme = findByCode('type:"UNSYNCED_USER_SETTINGS_UPDATE', '"system"===');
function setTheme(theme: string) { function setTheme(theme: string) {
saveClientTheme({ theme }); saveClientTheme({ theme });
} }
const NitroThemeStore = findStoreLazy("ClientThemesBackgroundStore"); const ClientThemesBackgroundStore = findStore("ClientThemesBackgroundStore");
function ThemeSettings() { function ThemeSettings() {
const theme = useStateFromStores([ThemeStore], () => ThemeStore.theme); const theme = useStateFromStores([ThemeStore], () => ThemeStore.theme);
const isLightTheme = theme === "light"; const isLightTheme = theme === "light";
const oppositeTheme = isLightTheme ? "dark" : "light"; const oppositeTheme = isLightTheme ? "dark" : "light";
const nitroTheme = useStateFromStores([NitroThemeStore], () => NitroThemeStore.gradientPreset); const nitroTheme = useStateFromStores([ClientThemesBackgroundStore], () => ClientThemesBackgroundStore.gradientPreset);
const nitroThemeEnabled = nitroTheme !== undefined; const nitroThemeEnabled = nitroTheme !== undefined;
const selectedLuminance = relativeLuminance(settings.store.color); const selectedLuminance = relativeLuminance(settings.store.color);

View file

@ -19,40 +19,43 @@
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { getCurrentChannel, getCurrentGuild } from "@utils/discord"; import { getCurrentChannel, getCurrentGuild } from "@utils/discord";
import { SYM_LAZY_CACHED, SYM_LAZY_GET } from "@utils/lazy"; import { SYM_LAZY_CACHED, SYM_LAZY_GET } from "@utils/lazy";
import { SYM_LAZY_COMPONENT_INNER } from "@utils/lazyReact";
import { relaunch } from "@utils/native"; import { relaunch } from "@utils/native";
import { canonicalizeMatch, canonicalizeReplace, canonicalizeReplacement } from "@utils/patches"; import { canonicalizeMatch, canonicalizeReplace, canonicalizeReplacement } from "@utils/patches";
import { SYM_PROXY_INNER_GET, SYM_PROXY_INNER_VALUE } from "@utils/proxyInner";
import definePlugin, { PluginNative, StartAt } from "@utils/types"; import definePlugin, { PluginNative, StartAt } from "@utils/types";
import * as Webpack from "@webpack"; import * as Webpack from "@webpack";
import { extract, filters, findAll, findModuleId, search } from "@webpack"; import { cacheFindAll, cacheFindModuleId, extract, filters, searchFactories } from "@webpack";
import * as Common from "@webpack/common"; import * as Common from "@webpack/common";
import { loadLazyChunks } from "debug/loadLazyChunks"; import { loadLazyChunks } from "debug/loadLazyChunks";
import type { ComponentType } from "react";
const DESKTOP_ONLY = (f: string) => () => { const DESKTOP_ONLY = (f: string) => () => {
throw new Error(`'${f}' is Discord Desktop only.`); throw new Error(`'${f}' is Discord Desktop only.`);
}; };
const define: typeof Object.defineProperty =
(obj, prop, desc) => {
if (Object.hasOwn(desc, "value"))
desc.writable = true;
return Object.defineProperty(obj, prop, { type Define = typeof Object.defineProperty;
configurable: true, const define: Define = (target, p, attributes) => {
enumerable: true, if (Object.hasOwn(attributes, "value")) {
...desc attributes.writable = true;
}); }
};
return Object.defineProperty(target, p, {
configurable: true,
enumerable: true,
...attributes
});
};
function makeShortcuts() { function makeShortcuts() {
function newFindWrapper(filterFactory: (...props: any[]) => Webpack.FilterFn) { function newFindWrapper(filterFactory: (...props: any[]) => Webpack.FilterFn, shouldReturnFactory = false) {
const cache = new Map<string, unknown>(); const cache = new Map<string, unknown>();
return function (...filterProps: unknown[]) { return function (...filterProps: unknown[]) {
const cacheKey = String(filterProps); const cacheKey = String(filterProps);
if (cache.has(cacheKey)) return cache.get(cacheKey); if (cache.has(cacheKey)) return cache.get(cacheKey);
const matches = findAll(filterFactory(...filterProps)); const matches = cacheFindAll(filterFactory(...filterProps), shouldReturnFactory);
const result = (() => { const result = (() => {
switch (matches.length) { switch (matches.length) {
@ -60,51 +63,72 @@ function makeShortcuts() {
case 1: return matches[0]; case 1: return matches[0];
default: default:
const uniqueMatches = [...new Set(matches)]; const uniqueMatches = [...new Set(matches)];
if (uniqueMatches.length > 1) if (uniqueMatches.length > 1) {
console.warn(`Warning: This filter matches ${matches.length} modules. Make it more specific!\n`, uniqueMatches); console.warn(`Warning: This filter matches ${matches.length} modules. Make it more specific!\n`, uniqueMatches);
}
return matches[0]; return uniqueMatches[0];
} }
})(); })();
if (result && cacheKey) cache.set(cacheKey, result); if (result && cacheKey) cache.set(cacheKey, result);
return result; return result;
}; };
} }
let fakeRenderWin: WeakRef<Window> | undefined; let fakeRenderWin: WeakRef<Window> | undefined;
const find = newFindWrapper(f => f); const find = newFindWrapper(f => f);
const findByProps = newFindWrapper(filters.byProps); const findByProps = newFindWrapper(filters.byProps);
return { return {
...Object.fromEntries(Object.keys(Common).map(key => [key, { getter: () => Common[key] }])),
wp: Webpack, wp: Webpack,
wpc: { getter: () => Webpack.cache }, wpc: { getter: () => Webpack.cache },
wreq: { getter: () => Webpack.wreq }, wreq: { getter: () => Webpack.wreq },
wpsearch: search,
wpex: extract, WebpackInstances: { getter: () => Vencord.WebpackPatcher.allWebpackInstances },
wpexs: (code: string) => extract(findModuleId(code)!),
loadLazyChunks: IS_DEV ? loadLazyChunks : () => { throw new Error("loadLazyChunks is dev only."); }, loadLazyChunks: IS_DEV ? loadLazyChunks : () => { throw new Error("loadLazyChunks is dev only."); },
wpsearch: searchFactories,
wpex: extract,
wpexs: (...code: Webpack.CodeFilter) => extract(cacheFindModuleId(...code)!),
filters,
find, find,
findAll: findAll, findAll: cacheFindAll,
findByProps, findComponent: find,
findAllByProps: (...props: string[]) => findAll(filters.byProps(...props)), findAllComponents: cacheFindAll,
findByCode: newFindWrapper(filters.byCode), findExportedComponent: (...props: Webpack.PropsFilter) => findByProps(...props)[props[0]],
findAllByCode: (code: string) => findAll(filters.byCode(code)),
findComponentByCode: newFindWrapper(filters.componentByCode), findComponentByCode: newFindWrapper(filters.componentByCode),
findAllComponentsByCode: (...code: string[]) => findAll(filters.componentByCode(...code)), findAllComponentsByCode: (...code: Webpack.PropsFilter) => cacheFindAll(filters.componentByCode(...code)),
findExportedComponent: (...props: string[]) => findByProps(...props)[props[0]], findComponentByFields: newFindWrapper(filters.componentByFields),
findAllComponentsByFields: (...fields: Webpack.PropsFilter) => cacheFindAll(filters.componentByFields(...fields)),
findByProps,
findAllByProps: (...props: Webpack.PropsFilter) => cacheFindAll(filters.byProps(...props)),
findProp: (...props: Webpack.PropsFilter) => findByProps(...props)[props[0]],
findByCode: newFindWrapper(filters.byCode),
findAllByCode: (code: Webpack.CodeFilter) => cacheFindAll(filters.byCode(...code)),
findStore: newFindWrapper(filters.byStoreName), findStore: newFindWrapper(filters.byStoreName),
PluginsApi: { getter: () => Vencord.Plugins }, findByFactoryCode: newFindWrapper(filters.byFactoryCode),
findAllByFactoryCode: (...code: Webpack.CodeFilter) => cacheFindAll(filters.byFactoryCode(...code)),
findModuleFactory: newFindWrapper(filters.byFactoryCode, true),
findAllModuleFactories: (...code: Webpack.CodeFilter) => cacheFindAll(filters.byFactoryCode(...code), true),
plugins: { getter: () => Vencord.Plugins.plugins }, plugins: { getter: () => Vencord.Plugins.plugins },
PluginsApi: { getter: () => Vencord.Plugins },
Settings: { getter: () => Vencord.Settings }, Settings: { getter: () => Vencord.Settings },
Api: { getter: () => Vencord.Api }, Api: { getter: () => Vencord.Api },
Util: { getter: () => Vencord.Util }, Util: { getter: () => Vencord.Util },
reload: () => location.reload(), reload: () => location.reload(),
restart: IS_WEB ? DESKTOP_ONLY("restart") : relaunch, restart: IS_WEB ? DESKTOP_ONLY("restart") : relaunch,
canonicalizeMatch, canonicalizeMatch,
canonicalizeReplace, canonicalizeReplace,
canonicalizeReplacement, canonicalizeReplacement,
fakeRender: (component: ComponentType, props: any) => {
preEnable: (plugin: string) => (Vencord.Settings.plugins[plugin] ??= { enabled: true }).enabled = true,
fakeRender: (component: React.ComponentType<AnyRecord>, props: any) => {
const prevWin = fakeRenderWin?.deref(); const prevWin = fakeRenderWin?.deref();
const win = prevWin?.closed === false const win = prevWin?.closed === false
? prevWin ? prevWin
@ -133,8 +157,6 @@ function makeShortcuts() {
Common.ReactDOM.render(Common.React.createElement(component, props), doc.body.appendChild(document.createElement("div"))); Common.ReactDOM.render(Common.React.createElement(component, props), doc.body.appendChild(document.createElement("div")));
}, },
preEnable: (plugin: string) => (Vencord.Settings.plugins[plugin] ??= { enabled: true }).enabled = true,
channel: { getter: () => getCurrentChannel(), preload: false }, channel: { getter: () => getCurrentChannel(), preload: false },
channelId: { getter: () => Common.SelectedChannelStore.getChannelId(), preload: false }, channelId: { getter: () => Common.SelectedChannelStore.getChannelId(), preload: false },
guild: { getter: () => getCurrentGuild(), preload: false }, guild: { getter: () => getCurrentGuild(), preload: false },
@ -143,6 +165,7 @@ function makeShortcuts() {
meId: { getter: () => Common.UserStore.getCurrentUser().id, 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 },
...Object.fromEntries(Object.keys(Common).map(key => [key, { getter: () => Common[key] }])),
Stores: { Stores: {
getter: () => Object.fromEntries( getter: () => Object.fromEntries(
Common.Flux.Store.getAll() Common.Flux.Store.getAll()
@ -157,11 +180,39 @@ function loadAndCacheShortcut(key: string, val: any, forceLoad: boolean) {
const currentVal = val.getter(); const currentVal = val.getter();
if (!currentVal || val.preload === false) return currentVal; if (!currentVal || val.preload === false) return currentVal;
const value = currentVal[SYM_LAZY_GET] function unwrapProxy(value: any) {
? forceLoad ? currentVal[SYM_LAZY_GET]() : currentVal[SYM_LAZY_CACHED] if (value[SYM_LAZY_GET]) {
: currentVal; return forceLoad ? value[SYM_LAZY_GET]() : value[SYM_LAZY_CACHED];
} else if (value[SYM_PROXY_INNER_GET]) {
return forceLoad ? value[SYM_PROXY_INNER_GET]() : value[SYM_PROXY_INNER_VALUE];
} else if (value[SYM_LAZY_COMPONENT_INNER]) {
return value[SYM_LAZY_COMPONENT_INNER]() != null ? value[SYM_LAZY_COMPONENT_INNER]() : value;
}
if (value) define(window.shortcutList, key, { value }); return value;
}
const value = unwrapProxy(currentVal);
if (value != null && typeof value === "object") {
const descriptors = Object.getOwnPropertyDescriptors(value);
for (const propKey in descriptors) {
if (value[propKey] == null) continue;
const descriptor = descriptors[propKey];
if (descriptor.writable === true || descriptor.set != null) {
const currentValue = value[propKey];
const newValue = unwrapProxy(currentValue);
if (newValue != null && currentValue !== newValue) {
value[propKey] = newValue;
}
}
}
}
if (value != null) {
define(window.shortcutList, key, { value });
}
return value; return value;
} }
@ -176,8 +227,10 @@ export default definePlugin({
const shortcuts = makeShortcuts(); const shortcuts = makeShortcuts();
window.shortcutList = {}; window.shortcutList = {};
for (const [key, val] of Object.entries(shortcuts)) { for (const key in shortcuts) {
if ("getter" in val) { const val = shortcuts[key];
if (Object.hasOwn(val, "getter")) {
define(window.shortcutList, key, { define(window.shortcutList, key, {
get: () => loadAndCacheShortcut(key, val, true) get: () => loadAndCacheShortcut(key, val, true)
}); });
@ -191,8 +244,8 @@ export default definePlugin({
} }
} }
// unproxy loaded modules // Unproxy loaded modules
Webpack.onceReady.then(() => { Webpack.onceDiscordLoaded.then(() => {
setTimeout(() => this.eagerLoad(false), 1000); setTimeout(() => this.eagerLoad(false), 1000);
if (!IS_WEB) { if (!IS_WEB) {
@ -203,13 +256,13 @@ export default definePlugin({
}, },
async eagerLoad(forceLoad: boolean) { async eagerLoad(forceLoad: boolean) {
await Webpack.onceReady; await Webpack.onceDiscordLoaded;
const shortcuts = makeShortcuts(); const shortcuts = makeShortcuts();
for (const key in shortcuts) {
const val = shortcuts[key];
for (const [key, val] of Object.entries(shortcuts)) { if (!Object.hasOwn(val, "getter") || val.preload === false) continue;
if (!Object.hasOwn(val, "getter") || (val as any).preload === false) continue;
try { try {
loadAndCacheShortcut(key, val, forceLoad); loadAndCacheShortcut(key, val, forceLoad);
} catch { } // swallow not found errors in DEV } catch { } // swallow not found errors in DEV

View file

@ -8,10 +8,10 @@ import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { copyWithToast } from "@utils/misc"; import { copyWithToast } from "@utils/misc";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack"; import { findProp } from "@webpack";
import { Menu } from "@webpack/common"; import { Menu } from "@webpack/common";
const { convertNameToSurrogate } = findByPropsLazy("convertNameToSurrogate"); const convertNameToSurrogate = findProp("convertNameToSurrogate");
interface Emoji { interface Emoji {
type: string; type: string;
@ -55,7 +55,7 @@ export default definePlugin({
settings, settings,
contextMenus: { contextMenus: {
"expression-picker"(children, { target }: { target: Target }) { "expression-picker"(children, { target }: { target: Target; }) {
if (target.dataset.type !== "emoji") return; if (target.dataset.type !== "emoji") return;
children.push( children.push(

View file

@ -23,22 +23,12 @@ import { Logger } from "@utils/Logger";
import { closeAllModals } from "@utils/modal"; import { closeAllModals } from "@utils/modal";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { maybePromptToUpdate } from "@utils/updater"; import { maybePromptToUpdate } from "@utils/updater";
import { filters, findBulk, proxyLazyWebpack } from "@webpack"; import { findByProps } from "@webpack";
import { DraftType, ExpressionPickerStore, FluxDispatcher, NavigationRouter, SelectedChannelStore } from "@webpack/common"; import { DraftType, ExpressionPickerStore, FluxDispatcher, NavigationRouter, SelectedChannelStore } from "@webpack/common";
const CrashHandlerLogger = new Logger("CrashHandler"); const CrashHandlerLogger = new Logger("CrashHandler");
const ModalStack = findByProps("pushLazy", "popAll");
const { ModalStack, DraftManager } = proxyLazyWebpack(() => { const DraftManager = findByProps("clearDraft", "saveDraft");
const [ModalStack, DraftManager] = findBulk(
filters.byProps("pushLazy", "popAll"),
filters.byProps("clearDraft", "saveDraft"),
);
return {
ModalStack,
DraftManager
};
});
const settings = definePluginSettings({ const settings = definePluginSettings({
attemptToPreventCrashes: { attemptToPreventCrashes: {

View file

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { definePluginSettings, Settings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { getUserSettingLazy } from "@api/UserSettings"; import { getUserSettingLazy } from "@api/UserSettings";
import { ErrorCard } from "@components/ErrorCard"; import { ErrorCard } from "@components/ErrorCard";
import { Link } from "@components/Link"; import { Link } from "@components/Link";
@ -26,13 +26,13 @@ import { Margins } from "@utils/margins";
import { classes } from "@utils/misc"; import { classes } from "@utils/misc";
import { useAwaiter } from "@utils/react"; import { useAwaiter } from "@utils/react";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByCodeLazy, findComponentByCodeLazy } from "@webpack"; import { findByCode, findComponentByCode } from "@webpack";
import { ApplicationAssetUtils, Button, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, 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 useProfileThemeStyle = findByCode("profileThemeStyle:", "--profile-gradient-primary-color");
const ActivityComponent = findComponentByCodeLazy("onOpenGameProfile"); const ActivityComponent = findComponentByCode("onOpenGameProfile");
const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame")!; const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame");
async function getApplicationAsset(key: string): Promise<string> { async function getApplicationAsset(key: string): Promise<string> {
if (/https?:\/\/(cdn|media)\.discordapp\.(com|net)\/attachments\//.test(key)) return "mp:" + key.replace(/https?:\/\/(cdn|media)\.discordapp\.(com|net)\//, ""); if (/https?:\/\/(cdn|media)\.discordapp\.(com|net)\/attachments\//.test(key)) return "mp:" + key.replace(/https?:\/\/(cdn|media)\.discordapp\.(com|net)\//, "");
@ -260,7 +260,7 @@ const settings = definePluginSettings({
function onChange() { function onChange() {
setRpc(true); setRpc(true);
if (Settings.plugins.CustomRPC.enabled) setRpc(); if (Vencord.Plugins.isPluginEnabled("CustomRPC")) setRpc();
} }
function isStreamLinkDisabled() { function isStreamLinkDisabled() {

View file

@ -5,7 +5,7 @@
*/ */
import { Flex } from "@components/Flex"; import { Flex } from "@components/Flex";
import { findByCodeLazy } from "@webpack"; import { findComponentByCode } from "@webpack";
import { Button, useEffect } from "@webpack/common"; import { Button, useEffect } from "@webpack/common";
import { useAuthorizationStore } from "../../lib/stores/AuthorizationStore"; import { useAuthorizationStore } from "../../lib/stores/AuthorizationStore";
@ -13,7 +13,7 @@ import { useCurrentUserDecorationsStore } from "../../lib/stores/CurrentUserDeco
import { cl } from "../"; import { cl } from "../";
import { openChangeDecorationModal } from "../modals/ChangeDecorationModal"; import { openChangeDecorationModal } from "../modals/ChangeDecorationModal";
const CustomizationSection = findByCodeLazy(".customizationSectionBackground"); const CustomizationSection = findComponentByCode(".customizationSectionBackground");
export interface DecorSectionProps { export interface DecorSectionProps {
hideTitle?: boolean; hideTitle?: boolean;

View file

@ -5,13 +5,13 @@
*/ */
import { classes } from "@utils/misc"; import { classes } from "@utils/misc";
import { findByPropsLazy } from "@webpack"; import { findByProps } from "@webpack";
import { React } from "@webpack/common"; import { React } from "@webpack/common";
import { cl } from "../"; import { cl } from "../";
import Grid, { GridProps } from "./Grid"; import Grid, { GridProps } from "./Grid";
const ScrollerClasses = findByPropsLazy("managedReactiveScroller"); const ScrollerClasses = findByProps("managedReactiveScroller");
type Section<SectionT, ItemT> = SectionT & { type Section<SectionT, ItemT> = SectionT & {
items: Array<ItemT>; items: Array<ItemT>;

View file

@ -4,30 +4,27 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
*/ */
import { findComponentByCode, LazyComponentWebpack } from "@webpack"; import { NoopComponent } from "@utils/react";
import { findComponentByCode } from "@webpack";
import { React } from "@webpack/common"; import { React } from "@webpack/common";
import type { ComponentType, HTMLProps, PropsWithChildren } from "react";
import { AvatarDecoration } from "../.."; import { AvatarDecoration } from "../..";
type DecorationGridItemComponent = ComponentType<PropsWithChildren<HTMLProps<HTMLDivElement>> & { type DecorationGridItemComponent = AnyComponentTypeWithChildren<React.ComponentPropsWithoutRef<"div"> & {
onSelect: () => void, onSelect: () => void,
isSelected: boolean, isSelected: boolean,
}>; }>;
export let DecorationGridItem: DecorationGridItemComponent; export let DecorationGridItem: DecorationGridItemComponent = NoopComponent;
export const setDecorationGridItem = v => DecorationGridItem = v; export const setDecorationGridItem = v => DecorationGridItem = v;
export const AvatarDecorationModalPreview = LazyComponentWebpack(() => { export const AvatarDecorationModalPreview = findComponentByCode(".shopPreviewBanner", component => React.memo(component));
const component = findComponentByCode(".shopPreviewBanner");
return React.memo(component);
});
type DecorationGridDecorationComponent = React.ComponentType<HTMLProps<HTMLDivElement> & { type DecorationGridDecorationComponent = AnyComponentType<React.ComponentPropsWithoutRef<"div"> & {
avatarDecoration: AvatarDecoration; avatarDecoration: AvatarDecoration;
onSelect: () => void, onSelect: () => void,
isSelected: boolean, isSelected: boolean,
}>; }>;
export let DecorationGridDecoration: DecorationGridDecorationComponent; export let DecorationGridDecoration: DecorationGridDecorationComponent = NoopComponent;
export const setDecorationGridDecoration = v => DecorationGridDecoration = v; export const setDecorationGridDecoration = v => DecorationGridDecoration = v;

View file

@ -5,10 +5,10 @@
*/ */
import { classNameFactory } from "@api/Styles"; import { classNameFactory } from "@api/Styles";
import { extractAndLoadChunksLazy, findByPropsLazy } from "@webpack"; import { extractAndLoadChunksLazy, findByProps } from "@webpack";
export const cl = classNameFactory("vc-decor-"); export const cl = classNameFactory("vc-decor-");
export const DecorationModalStyles = findByPropsLazy("modalFooterShopButton"); export const DecorationModalStyles = findByProps("modalFooterShopButton");
export const requireAvatarDecorationModal = extractAndLoadChunksLazy([".COLLECTIBLES_SHOP_FULLSCREEN&&"]); export const requireAvatarDecorationModal = extractAndLoadChunksLazy(".COLLECTIBLES_SHOP_FULLSCREEN&&");
export const requireCreateStickerModal = extractAndLoadChunksLazy(["stickerInspected]:"]); export const requireCreateStickerModal = extractAndLoadChunksLazy("stickerInspected]:");

View file

@ -10,7 +10,7 @@ import { openInviteModal } from "@utils/discord";
import { Margins } from "@utils/margins"; import { Margins } from "@utils/margins";
import { classes, copyWithToast } from "@utils/misc"; import { classes, copyWithToast } from "@utils/misc";
import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
import { findComponentByCodeLazy } from "@webpack"; import { findComponentByCode } from "@webpack";
import { Alerts, Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, Parser, Text, Tooltip, useEffect, UserStore, UserUtils, useState } from "@webpack/common"; import { Alerts, Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, Parser, Text, Tooltip, useEffect, UserStore, UserUtils, useState } from "@webpack/common";
import { User } from "discord-types/general"; import { User } from "discord-types/general";
@ -29,7 +29,7 @@ import SectionedGridList from "../components/SectionedGridList";
import { openCreateDecorationModal } from "./CreateDecorationModal"; import { openCreateDecorationModal } from "./CreateDecorationModal";
import { openGuidelinesModal } from "./GuidelinesModal"; import { openGuidelinesModal } from "./GuidelinesModal";
const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers"); const UserSummaryItem = findComponentByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers");
function usePresets() { function usePresets() {
const [presets, setPresets] = useState<Preset[]>([]); const [presets, setPresets] = useState<Preset[]>([]);

View file

@ -9,7 +9,7 @@ import { Link } from "@components/Link";
import { openInviteModal } from "@utils/discord"; import { openInviteModal } from "@utils/discord";
import { Margins } from "@utils/margins"; import { Margins } from "@utils/margins";
import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
import { filters, findComponentByCodeLazy, mapMangledModuleLazy } from "@webpack"; import { filters, findComponentByCode, mapMangledModule } from "@webpack";
import { Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, Text, TextInput, useEffect, useMemo, UserStore, useState } from "@webpack/common"; import { Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, Text, TextInput, useEffect, useMemo, UserStore, useState } from "@webpack/common";
import { GUILD_ID, INVITE_KEY, RAW_SKU_ID } from "../../lib/constants"; import { GUILD_ID, INVITE_KEY, RAW_SKU_ID } from "../../lib/constants";
@ -17,11 +17,11 @@ import { useCurrentUserDecorationsStore } from "../../lib/stores/CurrentUserDeco
import { cl, DecorationModalStyles, requireAvatarDecorationModal, requireCreateStickerModal } from "../"; import { cl, DecorationModalStyles, requireAvatarDecorationModal, requireCreateStickerModal } from "../";
import { AvatarDecorationModalPreview } from "../components"; import { AvatarDecorationModalPreview } from "../components";
const FileUpload = findComponentByCodeLazy("fileUploadInput,"); const FileUpload = findComponentByCode("fileUploadInput,");
const { HelpMessage, HelpMessageTypes } = mapMangledModuleLazy('POSITIVE=3]="POSITIVE', { const { HelpMessage, HelpMessageTypes } = mapMangledModule('POSITIVE=3]="POSITIVE', {
HelpMessage: filters.componentByCode(".iconDiv,", "messageType"),
HelpMessageTypes: filters.byProps("POSITIVE", "WARNING"), HelpMessageTypes: filters.byProps("POSITIVE", "WARNING"),
HelpMessage: filters.byCode(".iconDiv")
}); });
function useObjectURL(object: Blob | MediaSource | null) { function useObjectURL(object: Blob | MediaSource | null) {

View file

@ -22,7 +22,7 @@ import { Devs } from "@utils/constants";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches"; import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches";
import definePlugin, { OptionType, ReporterTestable } from "@utils/types"; import definePlugin, { OptionType, ReporterTestable } from "@utils/types";
import { filters, findAll, search } from "@webpack"; import { cacheFindAll, filters, searchFactories } from "@webpack";
const PORT = 8485; const PORT = 8485;
const NAV_ID = "dev-companion-reconnect"; const NAV_ID = "dev-companion-reconnect";
@ -154,13 +154,13 @@ function initWs(isManual = false) {
case "testPatch": { case "testPatch": {
const { find, replacement } = data as PatchData; const { find, replacement } = data as PatchData;
const candidates = search(find); const candidates = searchFactories(find);
const keys = Object.keys(candidates); const keys = Object.keys(candidates);
if (keys.length !== 1) if (keys.length !== 1)
return reply("Expected exactly one 'find' matches, found " + keys.length); return reply("Expected exactly one 'find' matches, found " + keys.length);
const mod = candidates[keys[0]]; const mod = candidates[keys[0]];
let src = String(mod.original ?? mod).replaceAll("\n", ""); let src = String(mod).replaceAll("\n", "");
if (src.startsWith("function(")) { if (src.startsWith("function(")) {
src = "0," + src; src = "0," + src;
@ -201,22 +201,22 @@ function initWs(isManual = false) {
let results: any[]; let results: any[];
switch (type.replace("find", "").replace("Lazy", "")) { switch (type.replace("find", "").replace("Lazy", "")) {
case "": case "":
results = findAll(parsedArgs[0]); results = cacheFindAll(parsedArgs[0]);
break; break;
case "ByProps": case "ByProps":
results = findAll(filters.byProps(...parsedArgs)); results = cacheFindAll(filters.byProps(...parsedArgs));
break; break;
case "Store": case "Store":
results = findAll(filters.byStoreName(parsedArgs[0])); results = cacheFindAll(filters.byStoreName(parsedArgs[0]));
break; break;
case "ByCode": case "ByCode":
results = findAll(filters.byCode(...parsedArgs)); results = cacheFindAll(filters.byCode(...parsedArgs));
break; break;
case "ModuleId": case "ModuleId":
results = Object.keys(search(parsedArgs[0])); results = Object.keys(searchFactories(parsedArgs[0]));
break; break;
case "ComponentByCode": case "ComponentByCode":
results = findAll(filters.componentByCode(...parsedArgs)); results = cacheFindAll(filters.componentByCode(...parsedArgs));
break; break;
default: default:
return reply("Unknown Find Type " + type); return reply("Unknown Find Type " + type);

View file

@ -23,12 +23,12 @@ import { Logger } from "@utils/Logger";
import { Margins } from "@utils/margins"; import { Margins } from "@utils/margins";
import { ModalContent, ModalHeader, ModalRoot, openModalLazy } from "@utils/modal"; import { ModalContent, ModalHeader, ModalRoot, openModalLazy } from "@utils/modal";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { findByCodeLazy, findStoreLazy } from "@webpack"; import { findByCode, findStore } from "@webpack";
import { Constants, EmojiStore, FluxDispatcher, Forms, GuildStore, Menu, PermissionsBits, PermissionStore, React, RestAPI, Toasts, Tooltip, UserStore } from "@webpack/common"; import { Constants, EmojiStore, FluxDispatcher, Forms, GuildStore, Menu, PermissionsBits, PermissionStore, React, RestAPI, Toasts, Tooltip, UserStore } from "@webpack/common";
import { Promisable } from "type-fest"; import { Promisable } from "type-fest";
const StickersStore = findStoreLazy("StickersStore"); const StickersStore = findStore("StickersStore");
const uploadEmoji = findByCodeLazy(".GUILD_EMOJIS(", "EMOJI_UPLOAD_START"); const uploadEmoji = findByCode(".GUILD_EMOJIS(", "EMOJI_UPLOAD_START");
interface Sticker { interface Sticker {
t: "Sticker"; t: "Sticker";

View file

@ -23,13 +23,13 @@ import { ErrorCard } from "@components/ErrorCard";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { Margins } from "@utils/margins"; import { Margins } from "@utils/margins";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy, findLazy } from "@webpack"; import { find, findByProps } from "@webpack";
import { Forms, React } from "@webpack/common"; import { Forms, React } from "@webpack/common";
import hideBugReport from "./hideBugReport.css?managed"; import hideBugReport from "./hideBugReport.css?managed";
const KbdStyles = findByPropsLazy("key", "combo"); const KbdStyles = findByProps("key", "combo");
const BugReporterExperiment = findLazy(m => m?.definition?.id === "2024-09_bug_reporter"); const BugReporterExperiment = find(m => m?.definition?.id === "2024-09_bug_reporter");
const settings = definePluginSettings({ const settings = definePluginSettings({
toolbarDevMenu: { toolbarDevMenu: {

View file

@ -23,36 +23,36 @@ import { ApngBlendOp, ApngDisposeOp, importApngJs } from "@utils/dependencies";
import { getCurrentGuild } from "@utils/discord"; import { getCurrentGuild } from "@utils/discord";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByCodeLazy, findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack"; import { findByCode, findByProps, findStore, webpackDependantLazy } from "@webpack";
import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, Forms, GuildMemberStore, IconUtils, lodash, Parser, PermissionsBits, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common"; import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, Forms, GuildMemberStore, IconUtils, lodash, Parser, PermissionsBits, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common";
import type { Emoji } from "@webpack/types"; import type { Emoji } from "@webpack/types";
import type { Message } from "discord-types/general"; import type { Message } from "discord-types/general";
import { applyPalette, GIFEncoder, quantize } from "gifenc"; import { applyPalette, GIFEncoder, quantize } from "gifenc";
import type { ReactElement, ReactNode } from "react"; import type { ReactElement, ReactNode } from "react";
const StickerStore = findStoreLazy("StickersStore") as { const StickersStore = findStore("StickersStore") as {
getPremiumPacks(): StickerPack[]; getPremiumPacks(): StickerPack[];
getAllGuildStickers(): Map<string, Sticker[]>; getAllGuildStickers(): Map<string, Sticker[]>;
getStickerById(id: string): Sticker | undefined; getStickerById(id: string): Sticker | undefined;
}; };
const UserSettingsProtoStore = findStoreLazy("UserSettingsProtoStore"); const UserSettingsProtoStore = findStore("UserSettingsProtoStore");
const BINARY_READ_OPTIONS = findByPropsLazy("readerFactory"); const BINARY_READ_OPTIONS = findByProps("readerFactory");
function searchProtoClassField(localName: string, protoClass: any) { function searchProtoClassField(localName: string, protoClass: any) {
const field = protoClass?.fields?.find((field: any) => field.localName === localName); const field = protoClass?.fields?.find((field: any) => field.localName === localName);
if (!field) return; if (!field) return;
const fieldGetter = Object.values(field).find(value => typeof value === "function") as any; const fieldGetter = Object.values<any>(field).find(value => typeof value === "function");
return fieldGetter?.(); return fieldGetter?.();
} }
const PreloadedUserSettingsActionCreators = proxyLazyWebpack(() => UserSettingsActionCreators.PreloadedUserSettingsActionCreators); const PreloadedUserSettingsActionCreators = webpackDependantLazy(() => UserSettingsActionCreators.PreloadedUserSettingsActionCreators);
const AppearanceSettingsActionCreators = proxyLazyWebpack(() => searchProtoClassField("appearance", PreloadedUserSettingsActionCreators.ProtoClass)); const AppearanceSettingsActionCreators = webpackDependantLazy(() => searchProtoClassField("appearance", PreloadedUserSettingsActionCreators.ProtoClass));
const ClientThemeSettingsActionsCreators = proxyLazyWebpack(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators)); const ClientThemeSettingsActionsCreators = webpackDependantLazy(() => searchProtoClassField("clientThemeSettings", AppearanceSettingsActionCreators));
const isUnusableRoleSubscriptionEmoji = findByCodeLazy(".getUserIsAdmin("); const isUnusableRoleSubscriptionEmoji = findByCode(".getUserIsAdmin(");
const enum EmojiIntentions { const enum EmojiIntentions {
REACTION, REACTION,
@ -566,8 +566,8 @@ export default definePlugin({
const gifMatch = child.props.href.match(fakeNitroGifStickerRegex); const gifMatch = child.props.href.match(fakeNitroGifStickerRegex);
if (gifMatch) { if (gifMatch) {
// There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickerStore contains the id of the fake sticker // There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickersStore contains the id of the fake sticker
if (StickerStore.getStickerById(gifMatch[1])) return null; if (StickersStore.getStickerById(gifMatch[1])) return null;
} }
} }
@ -655,7 +655,7 @@ export default definePlugin({
url = new URL(item); url = new URL(item);
} catch { } } catch { }
const stickerName = StickerStore.getStickerById(imgMatch[1])?.name ?? url?.searchParams.get("name") ?? "FakeNitroSticker"; const stickerName = StickersStore.getStickerById(imgMatch[1])?.name ?? url?.searchParams.get("name") ?? "FakeNitroSticker";
stickers.push({ stickers.push({
format_type: 1, format_type: 1,
id: imgMatch[1], id: imgMatch[1],
@ -668,9 +668,9 @@ export default definePlugin({
const gifMatch = item.match(fakeNitroGifStickerRegex); const gifMatch = item.match(fakeNitroGifStickerRegex);
if (gifMatch) { if (gifMatch) {
if (!StickerStore.getStickerById(gifMatch[1])) continue; if (!StickersStore.getStickerById(gifMatch[1])) continue;
const stickerName = StickerStore.getStickerById(gifMatch[1])?.name ?? "FakeNitroSticker"; const stickerName = StickersStore.getStickerById(gifMatch[1])?.name ?? "FakeNitroSticker";
stickers.push({ stickers.push({
format_type: 2, format_type: 2,
id: gifMatch[1], id: gifMatch[1],
@ -703,8 +703,8 @@ export default definePlugin({
const gifMatch = embed.url!.match(fakeNitroGifStickerRegex); const gifMatch = embed.url!.match(fakeNitroGifStickerRegex);
if (gifMatch) { if (gifMatch) {
// There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickerStore contains the id of the fake sticker // There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickersStore contains the id of the fake sticker
if (StickerStore.getStickerById(gifMatch[1])) return true; if (StickersStore.getStickerById(gifMatch[1])) return true;
} }
} }
@ -721,8 +721,8 @@ export default definePlugin({
const match = attachment.url.match(fakeNitroGifStickerRegex); const match = attachment.url.match(fakeNitroGifStickerRegex);
if (match) { if (match) {
// There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickerStore contains the id of the fake sticker // There is no way to differentiate a regular gif attachment from a fake nitro animated sticker, so we check if the StickersStore contains the id of the fake sticker
if (StickerStore.getStickerById(match[1])) return false; if (StickersStore.getStickerById(match[1])) return false;
} }
return true; return true;
@ -878,7 +878,7 @@ export default definePlugin({
if (!s.enableStickerBypass) if (!s.enableStickerBypass)
break stickerBypass; break stickerBypass;
const sticker = StickerStore.getStickerById(extra.stickers?.[0]!); const sticker = StickersStore.getStickerById(extra.stickers?.[0]!);
if (!sticker) if (!sticker)
break stickerBypass; break stickerBypass;

View file

@ -26,7 +26,7 @@ import { Margins } from "@utils/margins";
import { classes, copyWithToast } from "@utils/misc"; import { classes, copyWithToast } from "@utils/misc";
import { useAwaiter } from "@utils/react"; import { useAwaiter } from "@utils/react";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { extractAndLoadChunksLazy, findComponentByCodeLazy } from "@webpack"; import { extractAndLoadChunksLazy, findComponentByCode } from "@webpack";
import { Button, Flex, Forms, React, Text, UserProfileStore, UserStore, useState } from "@webpack/common"; import { Button, Flex, Forms, React, Text, UserProfileStore, UserStore, useState } from "@webpack/common";
import { User } from "discord-types/general"; import { User } from "discord-types/general";
import virtualMerge from "virtual-merge"; import virtualMerge from "virtual-merge";
@ -108,10 +108,10 @@ interface ProfileModalProps {
isTryItOutFlow: boolean; isTryItOutFlow: boolean;
} }
const ColorPicker = findComponentByCodeLazy<ColorPickerProps>(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); const ColorPicker = findComponentByCode<ColorPickerProps>(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)");
const ProfileModal = findComponentByCodeLazy<ProfileModalProps>("isTryItOutFlow:", "pendingThemeColors:", "pendingAvatarDecoration:", "EDIT_PROFILE_BANNER"); const ProfileModal = findComponentByCode<ProfileModalProps>("isTryItOutFlow:", "pendingThemeColors:", "pendingAvatarDecoration:", "EDIT_PROFILE_BANNER");
const requireColorPicker = extractAndLoadChunksLazy(["USER_SETTINGS_PROFILE_COLOR_DEFAULT_BUTTON.format"], /createPromise:\(\)=>\i\.\i(\("?.+?"?\)).then\(\i\.bind\(\i,"?(.+?)"?\)\)/); const requireColorPicker = extractAndLoadChunksLazy("USER_SETTINGS_PROFILE_COLOR_DEFAULT_BUTTON.format");
export default definePlugin({ export default definePlugin({
name: "FakeProfileThemes", name: "FakeProfileThemes",

View file

@ -20,7 +20,7 @@ import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack"; import { findByProps } from "@webpack";
import { useCallback, useEffect, useRef, useState } from "@webpack/common"; import { useCallback, useEffect, useRef, useState } from "@webpack/common";
interface SearchBarComponentProps { interface SearchBarComponentProps {
@ -35,7 +35,7 @@ interface SearchBarComponentProps {
} }
type TSearchBarComponent = type TSearchBarComponent =
React.FC<SearchBarComponentProps> & { Sizes: Record<"SMALL" | "MEDIUM" | "LARGE", string>; }; React.ComponentType<SearchBarComponentProps> & { Sizes: Record<"SMALL" | "MEDIUM" | "LARGE", string>; };
interface Gif { interface Gif {
format: number; format: number;
@ -60,7 +60,7 @@ interface Instance {
} }
const containerClasses: { searchBar: string; } = findByPropsLazy("searchBar", "searchBarFullRow"); const containerClasses: { searchBar: string; } = findByProps("searchBar", "searchBarFullRow");
export const settings = definePluginSettings({ export const settings = definePluginSettings({
searchOption: { searchOption: {

View file

@ -19,9 +19,9 @@
import { ApplicationCommandInputType, sendBotMessage } from "@api/Commands"; import { ApplicationCommandInputType, sendBotMessage } from "@api/Commands";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { findByPropsLazy } from "@webpack"; import { findByProps } from "@webpack";
const FriendInvites = findByPropsLazy("createFriendInvite"); const FriendInvites = findByProps("createFriendInvite");
export default definePlugin({ export default definePlugin({
name: "FriendInvites", name: "FriendInvites",

View file

@ -8,14 +8,14 @@ import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { getCurrentChannel } from "@utils/discord"; import { getCurrentChannel } from "@utils/discord";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy } from "@webpack"; import { findByCode, findByProps, findComponentByCode } from "@webpack";
import { RelationshipStore, Text } from "@webpack/common"; import { RelationshipStore, Text } from "@webpack/common";
const containerWrapper = findByPropsLazy("memberSinceWrapper"); const containerWrapper = findByProps("memberSinceWrapper");
const container = findByPropsLazy("memberSince"); const container = findByProps("memberSince");
const getCreatedAtDate = findByCodeLazy('month:"short",day:"numeric"'); const getCreatedAtDate = findByCode('month:"short",day:"numeric"');
const locale = findByPropsLazy("getLocale"); const locale = findByProps("getLocale");
const Section = findComponentByCodeLazy('"auto":"smooth"', ".section"); const Section = findComponentByCode('"auto":"smooth"', ".section");
export default definePlugin({ export default definePlugin({
name: "FriendsSince", name: "FriendsSince",

View file

@ -22,13 +22,13 @@ import { getUserSettingLazy } from "@api/UserSettings";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findComponentByCodeLazy } from "@webpack"; import { findComponentByCode } from "@webpack";
import style from "./style.css?managed"; import style from "./style.css?managed";
const Button = findComponentByCodeLazy("Button.Sizes.NONE,disabled:"); const Button = findComponentByCode("Button.Sizes.NONE,disabled:");
const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame")!; const ShowCurrentGame = getUserSettingLazy<boolean>("status", "showCurrentGame");
function makeIcon(showCurrentGame?: boolean) { function makeIcon(showCurrentGame?: boolean) {
const { oldIcon } = settings.use(["oldIcon"]); const { oldIcon } = settings.use(["oldIcon"]);

View file

@ -19,7 +19,7 @@
import { definePluginSettings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findLazy } from "@webpack"; import { find } from "@webpack";
import { ContextMenuApi, FluxDispatcher, Menu, MessageActions } from "@webpack/common"; import { ContextMenuApi, FluxDispatcher, Menu, MessageActions } from "@webpack/common";
import { Channel, Message } from "discord-types/general"; import { Channel, Message } from "discord-types/general";
@ -49,7 +49,7 @@ const settings = definePluginSettings({
unholyMultiGreetEnabled?: boolean; unholyMultiGreetEnabled?: boolean;
}>(); }>();
const WELCOME_STICKERS = findLazy(m => Array.isArray(m) && m[0]?.name === "Wave"); const WELCOME_STICKERS = find(m => Array.isArray(m) && m[0]?.name === "Wave");
function greet(channel: Channel, message: Message, stickers: string[]) { function greet(channel: Channel, message: Message, stickers: string[]) {
const options = MessageActions.getSendMessageOptionsForReply({ const options = MessageActions.getSendMessageOptionsForReply({

View file

@ -12,7 +12,7 @@ import { Flex } from "@components/Flex";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { Margins } from "@utils/margins"; import { Margins } from "@utils/margins";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findStoreLazy } from "@webpack"; import { findStore } from "@webpack";
import { Button, Forms, showToast, TextInput, Toasts, Tooltip, useEffect, useState } from "webpack/common"; import { Button, Forms, showToast, TextInput, Toasts, Tooltip, useEffect, useState } from "webpack/common";
const enum ActivitiesTypes { const enum ActivitiesTypes {
@ -31,9 +31,9 @@ const enum FilterMode {
Blacklist Blacklist
} }
const RunningGameStore = findStoreLazy("RunningGameStore"); const RunningGameStore = findStore("RunningGameStore");
const ShowCurrentGame = getUserSettingLazy("status", "showCurrentGame")!; const ShowCurrentGame = getUserSettingLazy("status", "showCurrentGame");
function ToggleIcon(activity: IgnoredActivity, tooltipText: string, path: string, fill: string) { function ToggleIcon(activity: IgnoredActivity, tooltipText: string, path: string, fill: string) {
return ( return (

View file

@ -26,7 +26,7 @@ import definePlugin, { OptionType } from "@utils/types";
import { Menu, ReactDOM } from "@webpack/common"; import { Menu, ReactDOM } from "@webpack/common";
import type { Root } from "react-dom/client"; import type { Root } from "react-dom/client";
import { Magnifier, MagnifierProps } from "./components/Magnifier"; import { Magnifier } from "./components/Magnifier";
import { ELEMENT_ID } from "./constants"; import { ELEMENT_ID } from "./constants";
import styles from "./styles.css?managed"; import styles from "./styles.css?managed";
@ -201,7 +201,7 @@ export default definePlugin({
}, },
// to stop from rendering twice /shrug // to stop from rendering twice /shrug
currentMagnifierElement: null as React.FunctionComponentElement<MagnifierProps & JSX.IntrinsicAttributes> | null, currentMagnifierElement: null as React.ReactNode,
element: null as HTMLDivElement | null, element: null as HTMLDivElement | null,
Magnifier, Magnifier,

View file

@ -19,16 +19,26 @@
import { definePluginSettings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findStoreLazy } from "@webpack"; import { findStore } from "@webpack";
import { ChannelStore, Constants, FluxDispatcher, GuildStore, RelationshipStore, SnowflakeUtils, UserStore } from "@webpack/common"; import { ChannelStore, Constants, FluxDispatcher, GuildStore, RelationshipStore, SnowflakeUtils, UserStore } from "@webpack/common";
import { Settings } from "Vencord";
const UserAffinitiesStore = findStoreLazy("UserAffinitiesStore"); const UserAffinitiesStore = findStore("UserAffinitiesStore");
const settings = definePluginSettings({
sortByAffinity: {
type: OptionType.BOOLEAN,
default: true,
description: "Whether to sort implicit relationships by their affinity to you.",
restartNeeded: true
}
});
export default definePlugin({ export default definePlugin({
name: "ImplicitRelationships", name: "ImplicitRelationships",
description: "Shows your implicit relationships in the Friends tab.", description: "Shows your implicit relationships in the Friends tab.",
authors: [Devs.Dolfies], authors: [Devs.Dolfies],
settings,
patches: [ patches: [
// Counts header // Counts header
{ {
@ -75,7 +85,7 @@ export default definePlugin({
{ {
find: "getRelationshipCounts(){", find: "getRelationshipCounts(){",
replacement: { replacement: {
predicate: () => Settings.plugins.ImplicitRelationships.sortByAffinity, predicate: () => settings.store.sortByAffinity,
match: /\}\)\.sortBy\((.+?)\)\.value\(\)/, match: /\}\)\.sortBy\((.+?)\)\.value\(\)/,
replace: "}).sortBy(row => $self.wrapSort(($1), row)).value()" replace: "}).sortBy(row => $self.wrapSort(($1), row)).value()"
} }
@ -104,16 +114,6 @@ export default definePlugin({
}, },
} }
], ],
settings: definePluginSettings(
{
sortByAffinity: {
type: OptionType.BOOLEAN,
default: true,
description: "Whether to sort implicit relationships by their affinity to you.",
restartNeeded: true
},
}
),
wrapSort(comparator: Function, row: any) { wrapSort(comparator: Function, row: any) {
return row.type === 5 return row.type === 5

View file

@ -20,7 +20,7 @@ import { registerCommand, unregisterCommand } from "@api/Commands";
import { addContextMenuPatch, removeContextMenuPatch } from "@api/ContextMenu"; import { addContextMenuPatch, removeContextMenuPatch } from "@api/ContextMenu";
import { Settings } from "@api/Settings"; import { Settings } from "@api/Settings";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import { canonicalizeFind } from "@utils/patches"; import { canonicalizeFind, canonicalizeReplacement } from "@utils/patches";
import { Patch, Plugin, ReporterTestable, StartAt } from "@utils/types"; import { Patch, Plugin, ReporterTestable, StartAt } from "@utils/types";
import { FluxDispatcher } from "@webpack/common"; import { FluxDispatcher } from "@webpack/common";
import { FluxEvents } from "@webpack/types"; import { FluxEvents } from "@webpack/types";
@ -66,10 +66,12 @@ export function addPatch(newPatch: Omit<Patch, "plugin">, pluginName: string) {
patch.replacement = [patch.replacement]; patch.replacement = [patch.replacement];
} }
if (IS_REPORTER) { for (const replacement of patch.replacement) {
patch.replacement.forEach(r => { canonicalizeReplacement(replacement, pluginName);
delete r.predicate;
}); if (IS_REPORTER) {
delete replacement.predicate;
}
} }
patch.replacement = patch.replacement.filter(({ predicate }) => !predicate || predicate()); patch.replacement = patch.replacement.filter(({ predicate }) => !predicate || predicate());

View file

@ -21,7 +21,7 @@ import { Link } from "@components/Link";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack"; import { findStore } from "@webpack";
import { ApplicationAssetUtils, FluxDispatcher, Forms } from "@webpack/common"; import { ApplicationAssetUtils, FluxDispatcher, Forms } from "@webpack/common";
interface ActivityAssets { interface ActivityAssets {
@ -86,7 +86,7 @@ const placeholderId = "2a96cbd8b46e442fc41c2b86b821562f";
const logger = new Logger("LastFMRichPresence"); const logger = new Logger("LastFMRichPresence");
const presenceStore = findByPropsLazy("getLocalPresence"); const SelfPresenceStore = findStore("SelfPresenceStore");
async function getApplicationAsset(key: string): Promise<string> { async function getApplicationAsset(key: string): Promise<string> {
return (await ApplicationAssetUtils.fetchAssetIds(applicationId, [key]))[0]; return (await ApplicationAssetUtils.fetchAssetIds(applicationId, [key]))[0];
@ -275,7 +275,7 @@ export default definePlugin({
async getActivity(): Promise<Activity | null> { async getActivity(): Promise<Activity | null> {
if (settings.store.hideWithSpotify) { if (settings.store.hideWithSpotify) {
for (const activity of presenceStore.getActivities()) { for (const activity of SelfPresenceStore.getActivities()) {
if (activity.type === ActivityType.LISTENING && activity.application_id !== applicationId) { if (activity.type === ActivityType.LISTENING && activity.application_id !== applicationId) {
// there is already music status because of Spotify or richerCider (probably more) // there is already music status because of Spotify or richerCider (probably more)
return null; return null;

View file

@ -23,20 +23,19 @@ import { classNameFactory } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findStoreLazy } from "@webpack"; import { findStore } from "@webpack";
import { FluxStore } from "@webpack/types"; import { FluxStore } from "@webpack/types";
import { MemberCount } from "./MemberCount"; import { MemberCount } from "./MemberCount";
export const GuildMemberCountStore = findStoreLazy("GuildMemberCountStore") as FluxStore & { getMemberCount(guildId?: string): number | null; }; export const GuildMemberCountStore = findStore("GuildMemberCountStore") as FluxStore & { getMemberCount(guildId?: string): number | null; };
export const ChannelMemberStore = findStoreLazy("ChannelMemberStore") as FluxStore & { export const ChannelMemberStore = findStore("ChannelMemberStore") as FluxStore & {
getProps(guildId?: string, channelId?: string): { groups: { count: number; id: string; }[]; }; getProps(guildId?: string, channelId?: string): { groups: { count: number; id: string; }[]; };
}; };
export const ThreadMemberListStore = findStoreLazy("ThreadMemberListStore") as FluxStore & { export const ThreadMemberListStore = findStore("ThreadMemberListStore") as FluxStore & {
getMemberListSections(channelId?: string): { [sectionId: string]: { sectionId: string; userIds: string[]; }; }; getMemberListSections(channelId?: string): { [sectionId: string]: { sectionId: string; userIds: string[]; }; };
}; };
const settings = definePluginSettings({ const settings = definePluginSettings({
toolTip: { toolTip: {
type: OptionType.BOOLEAN, type: OptionType.BOOLEAN,

View file

@ -20,11 +20,11 @@ import { addClickListener, removeClickListener } from "@api/MessageEvents";
import { definePluginSettings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack"; import { findByProps, findStore } from "@webpack";
import { FluxDispatcher, PermissionsBits, PermissionStore, UserStore } from "@webpack/common"; import { FluxDispatcher, PermissionsBits, PermissionStore, UserStore } from "@webpack/common";
const MessageActions = findByPropsLazy("deleteMessage", "startEditMessage"); const MessageActions = findByProps("deleteMessage", "startEditMessage");
const EditStore = findByPropsLazy("isEditing", "isEditingAny"); const EditMessageStore = findStore("EditMessageStore");
let isDeletePressed = false; let isDeletePressed = false;
const keydown = (e: KeyboardEvent) => e.key === "Backspace" && (isDeletePressed = true); const keydown = (e: KeyboardEvent) => e.key === "Backspace" && (isDeletePressed = true);
@ -74,7 +74,7 @@ export default definePlugin({
if (msg.deleted === true) return; if (msg.deleted === true) return;
if (isMe) { if (isMe) {
if (!settings.store.enableDoubleClickToEdit || EditStore.isEditing(channel.id, msg.id)) return; if (!settings.store.enableDoubleClickToEdit || EditMessageStore.isEditing(channel.id, msg.id)) return;
MessageActions.startEditMessage(channel.id, msg.id, msg.content); MessageActions.startEditMessage(channel.id, msg.id, msg.content);
event.preventDefault(); event.preventDefault();

View file

@ -9,7 +9,7 @@ import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { isNonNullish } from "@utils/guards"; import { isNonNullish } from "@utils/guards";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findExportedComponentLazy } from "@webpack"; import { findExportedComponent } from "@webpack";
import { SnowflakeUtils, Tooltip } from "@webpack/common"; import { SnowflakeUtils, Tooltip } from "@webpack/common";
import { Message } from "discord-types/general"; import { Message } from "discord-types/general";
@ -26,7 +26,7 @@ interface Diff {
} }
const DISCORD_KT_DELAY = 1471228928; const DISCORD_KT_DELAY = 1471228928;
const HiddenVisually = findExportedComponentLazy("HiddenVisually"); const HiddenVisually = findExportedComponent("HiddenVisually");
export default definePlugin({ export default definePlugin({
name: "MessageLatency", name: "MessageLatency",

View file

@ -25,7 +25,7 @@ import { Devs } from "@utils/constants.js";
import { classes } from "@utils/misc"; import { classes } from "@utils/misc";
import { Queue } from "@utils/Queue"; import { Queue } from "@utils/Queue";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; import { findByProps, findComponentByCode } from "@webpack";
import { import {
Button, Button,
ChannelStore, ChannelStore,
@ -47,14 +47,14 @@ const messageCache = new Map<string, {
fetched: boolean; fetched: boolean;
}>(); }>();
const Embed = findComponentByCodeLazy(".inlineMediaEmbed"); const Embed = findComponentByCode(".inlineMediaEmbed");
const AutoModEmbed = findComponentByCodeLazy(".withFooter]:", "childrenMessageContent:"); const AutoModEmbed = findComponentByCode(".withFooter]:", "childrenMessageContent:");
const ChannelMessage = findComponentByCodeLazy("childrenExecutedCommand:", ".hideAccessories"); const ChannelMessage = findComponentByCode("childrenExecutedCommand:", ".hideAccessories");
const SearchResultClasses = findByPropsLazy("message", "searchResult"); const SearchResultClasses = findByProps("message", "searchResult");
const EmbedClasses = findByPropsLazy("embedAuthorIcon", "embedAuthor", "embedAuthor"); const EmbedClasses = findByProps("embedAuthorIcon", "embedAuthor", "embedAuthor");
const MessageDisplayCompact = getUserSettingLazy("textAndImages", "messageDisplayCompact")!; const MessageDisplayCompact = getUserSettingLazy("textAndImages", "messageDisplayCompact");
const messageLinkRegex = /(?<!<)https?:\/\/(?:\w+\.)?discord(?:app)?\.com\/channels\/(?:\d{17,20}|@me)\/(\d{17,20})\/(\d{17,20})/g; const messageLinkRegex = /(?<!<)https?:\/\/(?:\w+\.)?discord(?:app)?\.com\/channels\/(?:\d{17,20}|@me)\/(\d{17,20})\/(\d{17,20})/g;
const tenorRegex = /^https:\/\/(?:www\.)?tenor\.com\//; const tenorRegex = /^https:\/\/(?:www\.)?tenor\.com\//;
@ -215,10 +215,9 @@ function computeWidthAndHeight(width: number, height: number) {
function withEmbeddedBy(message: Message, embeddedBy: string[]) { function withEmbeddedBy(message: Message, embeddedBy: string[]) {
return new Proxy(message, { return new Proxy(message, {
get(_, prop) { get(target, prop, receiver) {
if (prop === "vencordEmbeddedBy") return embeddedBy; if (prop === "vencordEmbeddedBy") return embeddedBy;
// @ts-ignore ts so bad return Reflect.get(target, prop, receiver);
return Reflect.get(...arguments);
} }
}); });
} }

View file

@ -9,13 +9,13 @@ import ErrorBoundary from "@components/ErrorBoundary";
import { Margins } from "@utils/margins"; import { Margins } from "@utils/margins";
import { classes } from "@utils/misc"; import { classes } from "@utils/misc";
import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
import { findByPropsLazy } from "@webpack"; import { findByProps } from "@webpack";
import { TabBar, Text, Timestamp, TooltipContainer, useState } from "@webpack/common"; import { TabBar, Text, Timestamp, TooltipContainer, useState } from "@webpack/common";
import { parseEditContent } from "."; import { parseEditContent } from ".";
const CodeContainerClasses = findByPropsLazy("markup", "codeContainer"); const CodeContainerClasses = findByProps("markup", "codeContainer");
const MiscClasses = findByPropsLazy("messageContent", "markupRtl"); const MiscClasses = findByProps("messageContent", "markupRtl");
const cl = classNameFactory("vc-ml-modal-"); const cl = classNameFactory("vc-ml-modal-");

View file

@ -20,7 +20,7 @@ import "./messageLogger.css";
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
import { updateMessage } from "@api/MessageUpdater"; import { updateMessage } from "@api/MessageUpdater";
import { Settings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { disableStyle, enableStyle } from "@api/Styles"; import { disableStyle, enableStyle } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
@ -28,7 +28,7 @@ import { proxyLazy } from "@utils/lazy";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import { classes } from "@utils/misc"; import { classes } from "@utils/misc";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByCodeLazy, findByPropsLazy } from "@webpack"; import { findByCode, findByProps } from "@webpack";
import { ChannelStore, FluxDispatcher, i18n, Menu, MessageStore, Parser, SelectedChannelStore, Timestamp, UserStore, useStateFromStores } from "@webpack/common"; import { ChannelStore, FluxDispatcher, i18n, Menu, MessageStore, Parser, SelectedChannelStore, Timestamp, UserStore, useStateFromStores } from "@webpack/common";
import { Message } from "discord-types/general"; import { Message } from "discord-types/general";
@ -42,11 +42,11 @@ interface MLMessage extends Message {
firstEditTimestamp?: Date; firstEditTimestamp?: Date;
} }
const styles = findByPropsLazy("edited", "communicationDisabled", "isSystemMessage"); const styles = findByProps("edited", "communicationDisabled", "isSystemMessage");
const getMessage = findByCodeLazy('replace(/^\\n+|\\n+$/g,"")'); const getMessage = findByCode('replace(/^\\n+|\\n+$/g,"")');
function addDeleteStyle() { function addDeleteStyle() {
if (Settings.plugins.MessageLogger.deleteStyle === "text") { if (settings.store.deleteStyle === "text") {
enableStyle(textStyle); enableStyle(textStyle);
disableStyle(overlayStyle); disableStyle(overlayStyle);
} else { } else {
@ -142,11 +142,70 @@ export function parseEditContent(content: string, message: Message) {
}); });
} }
const settings = definePluginSettings({
deleteStyle: {
type: OptionType.SELECT,
description: "The style of deleted messages",
default: "text",
options: [
{ label: "Red text", value: "text", default: true },
{ label: "Red overlay", value: "overlay" }
],
onChange: () => addDeleteStyle()
},
logDeletes: {
type: OptionType.BOOLEAN,
description: "Whether to log deleted messages",
default: true,
},
collapseDeleted: {
type: OptionType.BOOLEAN,
description: "Whether to collapse deleted messages, similar to blocked messages",
default: false
},
logEdits: {
type: OptionType.BOOLEAN,
description: "Whether to log edited messages",
default: true,
},
inlineEdits: {
type: OptionType.BOOLEAN,
description: "Whether to display edit history as part of message content",
default: true
},
ignoreBots: {
type: OptionType.BOOLEAN,
description: "Whether to ignore messages by bots",
default: false
},
ignoreSelf: {
type: OptionType.BOOLEAN,
description: "Whether to ignore messages by yourself",
default: false
},
ignoreUsers: {
type: OptionType.STRING,
description: "Comma-separated list of user IDs to ignore",
default: ""
},
ignoreChannels: {
type: OptionType.STRING,
description: "Comma-separated list of channel IDs to ignore",
default: ""
},
ignoreGuilds: {
type: OptionType.STRING,
description: "Comma-separated list of guild IDs to ignore",
default: ""
},
});
export default definePlugin({ export default definePlugin({
name: "MessageLogger", name: "MessageLogger",
description: "Temporarily logs deleted and edited messages.", description: "Temporarily logs deleted and edited messages.",
authors: [Devs.rushii, Devs.Ven, Devs.AutumnVN, Devs.Nickyux, Devs.Kyuuhachi], authors: [Devs.rushii, Devs.Ven, Devs.AutumnVN, Devs.Nickyux, Devs.Kyuuhachi],
dependencies: ["MessageUpdaterAPI"], dependencies: ["MessageUpdaterAPI"],
settings,
contextMenus: { contextMenus: {
"message": patchMessageContextMenu, "message": patchMessageContextMenu,
@ -168,7 +227,7 @@ export default definePlugin({
(oldMsg, newMsg) => oldMsg?.editHistory === newMsg?.editHistory (oldMsg, newMsg) => oldMsg?.editHistory === newMsg?.editHistory
); );
return Settings.plugins.MessageLogger.inlineEdits && ( return settings.store.inlineEdits && (
<> <>
{message.editHistory?.map(edit => ( {message.editHistory?.map(edit => (
<div className="messagelogger-edited"> <div className="messagelogger-edited">
@ -193,64 +252,6 @@ export default definePlugin({
}; };
}, },
options: {
deleteStyle: {
type: OptionType.SELECT,
description: "The style of deleted messages",
default: "text",
options: [
{ label: "Red text", value: "text", default: true },
{ label: "Red overlay", value: "overlay" }
],
onChange: () => addDeleteStyle()
},
logDeletes: {
type: OptionType.BOOLEAN,
description: "Whether to log deleted messages",
default: true,
},
collapseDeleted: {
type: OptionType.BOOLEAN,
description: "Whether to collapse deleted messages, similar to blocked messages",
default: false
},
logEdits: {
type: OptionType.BOOLEAN,
description: "Whether to log edited messages",
default: true,
},
inlineEdits: {
type: OptionType.BOOLEAN,
description: "Whether to display edit history as part of message content",
default: true
},
ignoreBots: {
type: OptionType.BOOLEAN,
description: "Whether to ignore messages by bots",
default: false
},
ignoreSelf: {
type: OptionType.BOOLEAN,
description: "Whether to ignore messages by yourself",
default: false
},
ignoreUsers: {
type: OptionType.STRING,
description: "Comma-separated list of user IDs to ignore",
default: ""
},
ignoreChannels: {
type: OptionType.STRING,
description: "Comma-separated list of channel IDs to ignore",
default: ""
},
ignoreGuilds: {
type: OptionType.STRING,
description: "Comma-separated list of guild IDs to ignore",
default: ""
},
},
handleDelete(cache: any, data: { ids: string[], id: string; mlDeleted?: boolean; }, isBulk: boolean) { handleDelete(cache: any, data: { ids: string[], id: string; mlDeleted?: boolean; }, isBulk: boolean) {
try { try {
if (cache == null || (!isBulk && !cache.has(data.id))) return cache; if (cache == null || (!isBulk && !cache.has(data.id))) return cache;
@ -285,7 +286,7 @@ export default definePlugin({
}, },
shouldIgnore(message: any, isEdit = false) { shouldIgnore(message: any, isEdit = false) {
const { ignoreBots, ignoreSelf, ignoreUsers, ignoreChannels, ignoreGuilds, logEdits, logDeletes } = Settings.plugins.MessageLogger; const { ignoreBots, ignoreSelf, ignoreUsers, ignoreChannels, ignoreGuilds, logEdits, logDeletes } = settings.store;
const myId = UserStore.getCurrentUser().id; const myId = UserStore.getCurrentUser().id;
return ignoreBots && message.author?.bot || return ignoreBots && message.author?.bot ||
@ -493,7 +494,7 @@ export default definePlugin({
match: /if\((\i)\.blocked\)return \i\.\i\.MESSAGE_GROUP_BLOCKED;/, match: /if\((\i)\.blocked\)return \i\.\i\.MESSAGE_GROUP_BLOCKED;/,
replace: '$&else if($1.deleted) return"MESSAGE_GROUP_DELETED";', replace: '$&else if($1.deleted) return"MESSAGE_GROUP_DELETED";',
}, },
predicate: () => Settings.plugins.MessageLogger.collapseDeleted predicate: () => settings.store.collapseDeleted
}, },
{ {
// Message group rendering // Message group rendering
@ -508,7 +509,7 @@ export default definePlugin({
replace: '$&$1.type==="MESSAGE_GROUP_DELETED"?$self.Messages.DELETED_MESSAGE_COUNT:', replace: '$&$1.type==="MESSAGE_GROUP_DELETED"?$self.Messages.DELETED_MESSAGE_COUNT:',
}, },
], ],
predicate: () => Settings.plugins.MessageLogger.collapseDeleted predicate: () => settings.store.collapseDeleted
} }
] ]
}); });

View file

@ -18,7 +18,7 @@
import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, registerCommand, sendBotMessage, unregisterCommand } from "@api/Commands"; import { ApplicationCommandInputType, ApplicationCommandOptionType, findOption, registerCommand, sendBotMessage, unregisterCommand } from "@api/Commands";
import * as DataStore from "@api/DataStore"; import * as DataStore from "@api/DataStore";
import { Settings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
@ -60,7 +60,7 @@ function createTagCommand(tag: Tag) {
return { content: `/${tag.name}` }; return { content: `/${tag.name}` };
} }
if (Settings.plugins.MessageTags.clyde) sendBotMessage(ctx.channel.id, { if (settings.store.clyde) sendBotMessage(ctx.channel.id, {
content: `${EMOTE} The tag **${tag.name}** has been sent!` content: `${EMOTE} The tag **${tag.name}** has been sent!`
}); });
return { content: tag.message.replaceAll("\\n", "\n") }; return { content: tag.message.replaceAll("\\n", "\n") };
@ -69,19 +69,20 @@ function createTagCommand(tag: Tag) {
}, "CustomTags"); }, "CustomTags");
} }
const settings = definePluginSettings({
clyde: {
name: "Clyde message on send",
description: "If enabled, clyde will send you an ephemeral message when a tag was used.",
type: OptionType.BOOLEAN,
default: true
}
});
export default definePlugin({ export default definePlugin({
name: "MessageTags", name: "MessageTags",
description: "Allows you to save messages and to use them with a simple command.", description: "Allows you to save messages and to use them with a simple command.",
authors: [Devs.Luna], authors: [Devs.Luna],
options: { settings,
clyde: {
name: "Clyde message on send",
description: "If enabled, clyde will send you an ephemeral message when a tag was used.",
type: OptionType.BOOLEAN,
default: true
}
},
async start() { async start() {
for (const tag of await getTags()) createTagCommand(tag); for (const tag of await getTags()) createTagCommand(tag);

View file

@ -19,11 +19,12 @@
import { definePluginSettings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { Flex } from "@components/Flex"; import { Flex } from "@components/Flex";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { LazyComponentType } from "@utils/lazyReact";
import { Margins } from "@utils/margins"; import { Margins } from "@utils/margins";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByCodeLazy, findLazy } from "@webpack"; import { findByCode, findComponentByCode } from "@webpack";
import { Card, ChannelStore, Forms, GuildStore, PermissionsBits, Switch, TextInput, Tooltip } from "@webpack/common"; import { Card, ChannelStore, Forms, GuildStore, PermissionsBits, Switch, TextInput, Tooltip } from "@webpack/common";
import type { Permissions, RC } from "@webpack/types"; import type { Permissions } from "@webpack/types";
import type { Channel, Guild, Message, User } from "discord-types/general"; import type { Channel, Guild, Message, User } from "discord-types/general";
interface Tag { interface Tag {
@ -59,9 +60,9 @@ const computePermissions: (options: {
overwrites?: Channel["permissionOverwrites"] | null; overwrites?: Channel["permissionOverwrites"] | null;
checkElevated?: boolean /* = true */; checkElevated?: boolean /* = true */;
excludeGuildPermissions?: boolean /* = false */; excludeGuildPermissions?: boolean /* = false */;
}) => bigint = findByCodeLazy(".getCurrentUser()", ".computeLurkerPermissionsAllowList()"); }) => bigint = findByCode(".getCurrentUser()", ".computeLurkerPermissionsAllowList()");
const Tag = findLazy(m => m.Types?.[0] === "BOT") as RC<{ type?: number, className?: string, useRemSizes?: boolean; }> & { Types: Record<string, number>; }; const Tag = findComponentByCode(".DISCORD_SYSTEM_MESSAGE_BOT_TAG_TOOLTIP_OFFICIAL,") as LazyComponentType<{ type?: number, className?: string, useRemSizes?: boolean; }> & { Types: Record<string, number>; };
const isWebhook = (message: Message, user: User) => !!message?.webhookId && user.isNonUserBot(); const isWebhook = (message: Message, user: User) => !!message?.webhookId && user.isNonUserBot();

View file

@ -20,16 +20,16 @@ import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { isNonNullish } from "@utils/guards"; import { isNonNullish } from "@utils/guards";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack"; import { findByProps, findComponentByCode } from "@webpack";
import { Avatar, ChannelStore, Clickable, IconUtils, RelationshipStore, ScrollerThin, useMemo, UserStore } from "@webpack/common"; import { Avatar, ChannelStore, Clickable, IconUtils, RelationshipStore, ScrollerThin, useMemo, UserStore } from "@webpack/common";
import { Channel, User } from "discord-types/general"; import { Channel, User } from "discord-types/general";
const SelectedChannelActionCreators = findByPropsLazy("selectPrivateChannel"); const SelectedChannelActionCreators = findByProps("selectPrivateChannel");
const UserUtils = findByPropsLazy("getGlobalName"); const UserUtils = findByProps("getGlobalName");
const ProfileListClasses = findByPropsLazy("emptyIconFriends", "emptyIconGuilds"); const ProfileListClasses = findByProps("emptyIconFriends", "emptyIconGuilds");
const ExpandableList = findComponentByCodeLazy(".mutualFriendItem]"); const ExpandableList = findComponentByCode(".mutualFriendItem]");
const GuildLabelClasses = findByPropsLazy("guildNick", "guildAvatarWithoutIcon"); const GuildLabelClasses = findByProps("guildNick", "guildAvatarWithoutIcon");
function getGroupDMName(channel: Channel) { function getGroupDMName(channel: Channel) {
return channel.name || return channel.name ||

View file

@ -24,18 +24,18 @@ import { definePluginSettings, migratePluginSettings } from "@api/Settings";
import { CogWheel } from "@components/Icons"; import { CogWheel } from "@components/Icons";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByCodeLazy, findByPropsLazy, mapMangledModuleLazy } from "@webpack"; import { findByCode, findProp, mapMangledModule } from "@webpack";
import { Menu } from "@webpack/common"; import { Menu } from "@webpack/common";
import { Guild } from "discord-types/general"; import { Guild } from "discord-types/general";
const { updateGuildNotificationSettings } = findByPropsLazy("updateGuildNotificationSettings"); const updateGuildNotificationSettings = findProp("updateGuildNotificationSettings");
const { toggleShowAllChannels } = mapMangledModuleLazy(".onboardExistingMember(", { const { toggleShowAllChannels } = mapMangledModule(".onboardExistingMember(", {
toggleShowAllChannels: m => { toggleShowAllChannels: m => {
const s = String(m); const s = String(m);
return s.length < 100 && !s.includes("onboardExistingMember") && !s.includes("getOptedInChannels"); return s.length < 100 && !s.includes("onboardExistingMember") && !s.includes("getOptedInChannels");
} }
}); });
const isOptInEnabledForGuild = findByCodeLazy(".COMMUNITY)||", ".isOptInEnabled("); const isOptInEnabledForGuild = findByCode(".COMMUNITY)||", ".isOptInEnabled(");
const settings = definePluginSettings({ const settings = definePluginSettings({
guild: { guild: {

View file

@ -16,19 +16,28 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Settings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack"; import { RelationshipStore } from "@webpack/common";
import { Message } from "discord-types/general"; import { Message } from "discord-types/general";
const RelationshipStore = findByPropsLazy("getRelationships", "isBlocked"); const settings = definePluginSettings({
ignoreBlockedMessages: {
description: "Completely ignores (recent) incoming messages from blocked users (locally).",
type: OptionType.BOOLEAN,
default: false,
restartNeeded: true,
},
});
export default definePlugin({ export default definePlugin({
name: "NoBlockedMessages", name: "NoBlockedMessages",
description: "Hides all blocked messages from chat completely.", description: "Hides all blocked messages from chat completely.",
authors: [Devs.rushii, Devs.Samu], authors: [Devs.rushii, Devs.Samu],
settings,
patches: [ patches: [
{ {
find: "Messages.BLOCKED_MESSAGES_HIDE", find: "Messages.BLOCKED_MESSAGES_HIDE",
@ -44,7 +53,7 @@ export default definePlugin({
'"displayName","ReadStateStore")' '"displayName","ReadStateStore")'
].map(find => ({ ].map(find => ({
find, find,
predicate: () => Settings.plugins.NoBlockedMessages.ignoreBlockedMessages === true, predicate: () => settings.store.ignoreBlockedMessages === true,
replacement: [ replacement: [
{ {
match: /(?<=MESSAGE_CREATE:function\((\i)\){)/, match: /(?<=MESSAGE_CREATE:function\((\i)\){)/,

View file

@ -19,9 +19,9 @@
import { definePluginSettings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack"; import { findStore } from "@webpack";
const MessageRequestStore = findByPropsLazy("getMessageRequestsCount"); const MessageRequestStore = findStore("MessageRequestStore");
const settings = definePluginSettings({ const settings = definePluginSettings({
hideFriendRequestsCount: { hideFriendRequestsCount: {

View file

@ -21,7 +21,7 @@ import { Flex } from "@components/Flex";
import { InfoIcon, OwnerCrownIcon } from "@components/Icons"; import { InfoIcon, OwnerCrownIcon } from "@components/Icons";
import { getUniqueUsername } from "@utils/discord"; import { getUniqueUsername } from "@utils/discord";
import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal"; import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
import { findByCodeLazy } from "@webpack"; import { findByCode } from "@webpack";
import { Clipboard, ContextMenuApi, FluxDispatcher, GuildMemberStore, GuildStore, i18n, Menu, PermissionsBits, ScrollerThin, Text, Tooltip, useEffect, UserStore, useState, useStateFromStores } from "@webpack/common"; import { Clipboard, ContextMenuApi, FluxDispatcher, GuildMemberStore, GuildStore, i18n, Menu, PermissionsBits, ScrollerThin, Text, Tooltip, useEffect, UserStore, useState, useStateFromStores } from "@webpack/common";
import { UnicodeEmoji } from "@webpack/types"; import { UnicodeEmoji } from "@webpack/types";
import type { Guild, Role, User } from "discord-types/general"; import type { Guild, Role, User } from "discord-types/general";
@ -45,7 +45,7 @@ export interface RoleOrUserPermission {
} }
type GetRoleIconData = (role: Role, size: number) => { customIconSrc?: string; unicodeEmoji?: UnicodeEmoji; }; type GetRoleIconData = (role: Role, size: number) => { customIconSrc?: string; unicodeEmoji?: UnicodeEmoji; };
const getRoleIconData: GetRoleIconData = findByCodeLazy("convertSurrogateToName", "customIconSrc", "unicodeEmoji"); const getRoleIconData: GetRoleIconData = findByCode("convertSurrogateToName", "customIconSrc", "unicodeEmoji");
function getRoleIconSrc(role: Role) { function getRoleIconSrc(role: Role) {
const icon = getRoleIconData(role, 20); const icon = getRoleIconData(role, 20);

View file

@ -19,7 +19,7 @@
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { ExpandableHeader } from "@components/ExpandableHeader"; import { ExpandableHeader } from "@components/ExpandableHeader";
import { classes } from "@utils/misc"; import { classes } from "@utils/misc";
import { filters, findBulk, proxyLazyWebpack } from "@webpack"; import { findByProps } from "@webpack";
import { i18n, PermissionsBits, Text, Tooltip, useMemo, UserStore } from "@webpack/common"; import { i18n, PermissionsBits, Text, Tooltip, useMemo, UserStore } from "@webpack/common";
import type { Guild, GuildMember } from "discord-types/general"; import type { Guild, GuildMember } from "discord-types/general";
@ -36,15 +36,9 @@ interface UserPermission {
type UserPermissions = Array<UserPermission>; type UserPermissions = Array<UserPermission>;
const { RoleRootClasses, RoleClasses, RoleBorderClasses } = proxyLazyWebpack(() => { const RoleRootClasses = findByProps("root", "expandButton", "collapseButton");
const [RoleRootClasses, RoleClasses, RoleBorderClasses] = findBulk( const RoleClasses = findByProps("role", "roleCircle", "roleName");
filters.byProps("root", "expandButton", "collapseButton"), const RoleBorderClasses = findByProps("roleCircle", "dot", "dotBorderColor");
filters.byProps("role", "roleCircle", "roleName"),
filters.byProps("roleCircle", "dot", "dotBorderColor")
) as Record<string, string>[];
return { RoleRootClasses, RoleClasses, RoleBorderClasses };
});
interface FakeRoleProps extends React.HTMLAttributes<HTMLDivElement> { interface FakeRoleProps extends React.HTMLAttributes<HTMLDivElement> {
text: string; text: string;

View file

@ -25,7 +25,7 @@ import { SafetyIcon } from "@components/Icons";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { classes } from "@utils/misc"; import { classes } from "@utils/misc";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack"; import { findByProps } from "@webpack";
import { Button, ChannelStore, Dialog, GuildMemberStore, GuildStore, match, Menu, PermissionsBits, Popout, TooltipContainer, UserStore } from "@webpack/common"; import { Button, ChannelStore, Dialog, GuildMemberStore, GuildStore, match, Menu, PermissionsBits, Popout, TooltipContainer, UserStore } from "@webpack/common";
import type { Guild, GuildMember } from "discord-types/general"; import type { Guild, GuildMember } from "discord-types/general";
@ -33,8 +33,8 @@ import openRolesAndUsersPermissionsModal, { PermissionType, RoleOrUserPermission
import UserPermissions from "./components/UserPermissions"; import UserPermissions from "./components/UserPermissions";
import { getSortedRoles, sortPermissionOverwrites } from "./utils"; import { getSortedRoles, sortPermissionOverwrites } from "./utils";
const PopoutClasses = findByPropsLazy("container", "scroller", "list"); const PopoutClasses = findByProps("container", "scroller", "list");
const RoleButtonClasses = findByPropsLazy("button", "buttonInner", "icon", "banner"); const RoleButtonClasses = findByProps("button", "buttonInner", "icon", "banner");
export const enum PermissionsSortOrder { export const enum PermissionsSortOrder {
HighestRole, HighestRole,

View file

@ -20,7 +20,7 @@ import { ApplicationCommandInputType, ApplicationCommandOptionType, Argument, Co
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { makeLazy } from "@utils/lazy"; import { makeLazy } from "@utils/lazy";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { findByPropsLazy } from "@webpack"; import { findStore } from "@webpack";
import { DraftType, UploadHandler, UploadManager, UserUtils } from "@webpack/common"; import { DraftType, UploadHandler, UploadManager, UserUtils } from "@webpack/common";
import { applyPalette, GIFEncoder, quantize } from "gifenc"; import { applyPalette, GIFEncoder, quantize } from "gifenc";
@ -35,7 +35,7 @@ const getFrames = makeLazy(() => Promise.all(
)) ))
); );
const UploadStore = findByPropsLazy("getUploads"); const UploadAttachmentStore = findStore("UploadAttachmentStore");
function loadImage(source: File | string) { function loadImage(source: File | string) {
const isFile = source instanceof File; const isFile = source instanceof File;
@ -58,7 +58,7 @@ async function resolveImage(options: Argument[], ctx: CommandContext, noServerPf
for (const opt of options) { for (const opt of options) {
switch (opt.name) { switch (opt.name) {
case "image": case "image":
const upload = UploadStore.getUpload(ctx.channel.id, opt.name, DraftType.SlashCommand); const upload = UploadAttachmentStore.getUpload(ctx.channel.id, opt.name, DraftType.SlashCommand);
if (upload) { if (upload) {
if (!upload.isImage) { if (!upload.isImage) {
UploadManager.clearAll(ctx.channel.id, DraftType.SlashCommand); UploadManager.clearAll(ctx.channel.id, DraftType.SlashCommand);

View file

@ -6,7 +6,7 @@
import { classNameFactory } from "@api/Styles"; import { classNameFactory } from "@api/Styles";
import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModalLazy } from "@utils/modal"; import { ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, openModalLazy } from "@utils/modal";
import { extractAndLoadChunksLazy, findComponentByCodeLazy, findExportedComponentLazy } from "@webpack"; import { extractAndLoadChunksLazy, findComponentByCode, findExportedComponent } from "@webpack";
import { Button, Forms, Text, TextInput, Toasts, useEffect, useState } from "@webpack/common"; import { Button, Forms, Text, TextInput, Toasts, useEffect, useState } from "@webpack/common";
import { DEFAULT_COLOR, SWATCHES } from "../constants"; import { DEFAULT_COLOR, SWATCHES } from "../constants";
@ -30,10 +30,10 @@ interface ColorPickerWithSwatchesProps {
renderCustomButton?: () => React.ReactNode; renderCustomButton?: () => React.ReactNode;
} }
const ColorPicker = findComponentByCodeLazy<ColorPickerProps>(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); const ColorPicker = findComponentByCode<ColorPickerProps>(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)");
const ColorPickerWithSwatches = findExportedComponentLazy<ColorPickerWithSwatchesProps>("ColorPicker", "CustomColorPicker"); const ColorPickerWithSwatches = findExportedComponent<ColorPickerWithSwatchesProps>("ColorPicker", "CustomColorPicker");
export const requireSettingsMenu = extractAndLoadChunksLazy(['name:"UserSettings"'], /createPromise:.{0,20}Promise\.all\((\[\i\.\i\("?.+?"?\).+?\])\).then\(\i\.bind\(\i,"?(.+?)"?\)\).{0,50}"UserSettings"/); export const requireSettingsMenu = extractAndLoadChunksLazy('name:"UserSettings"', /createPromise:.{0,20}Promise\.all\((\[\i\.\i\("?.+?"?\).+?\])\).then\(\i\.bind\(\i,"?(.+?)"?\)\).{0,50}"UserSettings"/);
const cl = classNameFactory("vc-pindms-modal-"); const cl = classNameFactory("vc-pindms-modal-");

View file

@ -11,7 +11,7 @@ import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { classes } from "@utils/misc"; import { classes } from "@utils/misc";
import definePlugin, { OptionType, StartAt } from "@utils/types"; import definePlugin, { OptionType, StartAt } from "@utils/types";
import { findByPropsLazy, findStoreLazy } from "@webpack"; import { findByProps, findStore } from "@webpack";
import { ContextMenuApi, FluxDispatcher, Menu, React } from "@webpack/common"; import { ContextMenuApi, FluxDispatcher, Menu, React } from "@webpack/common";
import { Channel } from "discord-types/general"; import { Channel } from "discord-types/general";
@ -27,9 +27,9 @@ interface ChannelComponentProps {
} }
const headerClasses = findByPropsLazy("privateChannelsHeaderContainer"); const headerClasses = findByProps("privateChannelsHeaderContainer");
export const PrivateChannelSortStore = findStoreLazy("PrivateChannelSortStore") as { getPrivateChannelIds: () => string[]; }; export const PrivateChannelSortStore = findStore("PrivateChannelSortStore") as { getPrivateChannelIds: () => string[]; };
export let instance: any; export let instance: any;
export const forceUpdate = () => instance?.props?._forceUpdate?.(); export const forceUpdate = () => instance?.props?._forceUpdate?.();

View file

@ -21,11 +21,11 @@ import "./style.css";
import { addBadge, BadgePosition, BadgeUserArgs, ProfileBadge, removeBadge } from "@api/Badges"; import { addBadge, BadgePosition, BadgeUserArgs, ProfileBadge, removeBadge } from "@api/Badges";
import { addDecorator, removeDecorator } from "@api/MemberListDecorators"; import { addDecorator, removeDecorator } from "@api/MemberListDecorators";
import { addDecoration, removeDecoration } from "@api/MessageDecorations"; import { addDecoration, removeDecoration } from "@api/MessageDecorations";
import { Settings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy, findStoreLazy } from "@webpack"; import { findByProps, findStore } from "@webpack";
import { PresenceStore, Tooltip, UserStore } from "@webpack/common"; import { PresenceStore, Tooltip, UserStore } from "@webpack/common";
import { User } from "discord-types/general"; import { User } from "discord-types/general";
@ -40,7 +40,7 @@ export interface Session {
}; };
} }
const SessionsStore = findStoreLazy("SessionsStore") as { const SessionsStore = findStore("SessionsStore") as {
getSessions(): Record<string, Session>; getSessions(): Record<string, Session>;
}; };
@ -70,7 +70,7 @@ const Icons = {
}; };
type Platform = keyof typeof Icons; type Platform = keyof typeof Icons;
const StatusUtils = findByPropsLazy("useStatusFillColor", "StatusTypes"); const StatusUtils = findByProps("useStatusFillColor", "StatusTypes");
const PlatformIcon = ({ platform, status, small }: { platform: Platform, status: string; small: boolean; }) => { const PlatformIcon = ({ platform, status, small }: { platform: Platform, status: string; small: boolean; }) => {
const tooltip = platform === "embedded" const tooltip = platform === "embedded"
@ -195,29 +195,36 @@ const indicatorLocations = {
} }
}; };
const settings = definePluginSettings({
...Object.fromEntries(
Object.entries(indicatorLocations).map(([key, value]) => {
return [key, {
type: OptionType.BOOLEAN,
description: `Show indicators ${value.description.toLowerCase()}`,
// onChange doesn't give any way to know which setting was changed, so restart required
restartNeeded: true,
default: true
}];
})
) as Record<"list" | "badges" | "messages", { type: OptionType.BOOLEAN; description: string; restartNeeded: boolean; default: boolean; }>,
colorMobileIndicator: {
type: OptionType.BOOLEAN,
description: "Whether to make the mobile indicator match the color of the user status.",
default: true,
restartNeeded: true
}
});
export default definePlugin({ export default definePlugin({
name: "PlatformIndicators", name: "PlatformIndicators",
description: "Adds platform indicators (Desktop, Mobile, Web...) to users", description: "Adds platform indicators (Desktop, Mobile, Web...) to users",
authors: [Devs.kemo, Devs.TheSun, Devs.Nuckyz, Devs.Ven], authors: [Devs.kemo, Devs.TheSun, Devs.Nuckyz, Devs.Ven],
dependencies: ["MessageDecorationsAPI", "MemberListDecoratorsAPI"], dependencies: ["MessageDecorationsAPI", "MemberListDecoratorsAPI"],
settings,
start() { start() {
const settings = Settings.plugins.PlatformIndicators;
const { displayMode } = settings;
// transfer settings from the old ones, which had a select menu instead of booleans
if (displayMode) {
if (displayMode !== "both") settings[displayMode] = true;
else {
settings.list = true;
settings.badges = true;
}
settings.messages = true;
delete settings.displayMode;
}
Object.entries(indicatorLocations).forEach(([key, value]) => { Object.entries(indicatorLocations).forEach(([key, value]) => {
if (settings[key]) value.onEnable(); if (settings.store[key]) value.onEnable();
}); });
}, },
@ -230,7 +237,7 @@ export default definePlugin({
patches: [ patches: [
{ {
find: ".Masks.STATUS_ONLINE_MOBILE", find: ".Masks.STATUS_ONLINE_MOBILE",
predicate: () => Settings.plugins.PlatformIndicators.colorMobileIndicator, predicate: () => settings.store.colorMobileIndicator,
replacement: [ replacement: [
{ {
// Return the STATUS_ONLINE_MOBILE mask if the user is on mobile, no matter the status // Return the STATUS_ONLINE_MOBILE mask if the user is on mobile, no matter the status
@ -246,7 +253,7 @@ export default definePlugin({
}, },
{ {
find: ".AVATAR_STATUS_MOBILE_16;", find: ".AVATAR_STATUS_MOBILE_16;",
predicate: () => Settings.plugins.PlatformIndicators.colorMobileIndicator, predicate: () => settings.store.colorMobileIndicator,
replacement: [ replacement: [
{ {
// Return the AVATAR_STATUS_MOBILE size mask if the user is on mobile, no matter the status // Return the AVATAR_STATUS_MOBILE size mask if the user is on mobile, no matter the status
@ -267,32 +274,12 @@ export default definePlugin({
}, },
{ {
find: "}isMobileOnline(", find: "}isMobileOnline(",
predicate: () => Settings.plugins.PlatformIndicators.colorMobileIndicator, predicate: () => settings.store.colorMobileIndicator,
replacement: { replacement: {
// Make isMobileOnline return true no matter what is the user status // Make isMobileOnline return true no matter what is the user status
match: /(?<=\i\[\i\.\i\.MOBILE\])===\i\.\i\.ONLINE/, match: /(?<=\i\[\i\.\i\.MOBILE\])===\i\.\i\.ONLINE/,
replace: "!= null" replace: "!= null"
} }
} }
], ]
options: {
...Object.fromEntries(
Object.entries(indicatorLocations).map(([key, value]) => {
return [key, {
type: OptionType.BOOLEAN,
description: `Show indicators ${value.description.toLowerCase()}`,
// onChange doesn't give any way to know which setting was changed, so restart required
restartNeeded: true,
default: true
}];
})
),
colorMobileIndicator: {
type: OptionType.BOOLEAN,
description: "Whether to make the mobile indicator match the color of the user status.",
default: true,
restartNeeded: true
}
}
}); });

View file

@ -20,15 +20,14 @@ import { addChatBarButton, ChatBarButton, removeChatBarButton } from "@api/ChatB
import { generateId, sendBotMessage } from "@api/Commands"; import { generateId, sendBotMessage } from "@api/Commands";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { StartAt } from "@utils/types"; import definePlugin, { StartAt } from "@utils/types";
import { findByPropsLazy } from "@webpack"; import { findStore } from "@webpack";
import { DraftStore, DraftType, SelectedChannelStore, UserStore, useStateFromStores } from "@webpack/common"; import { DraftStore, DraftType, SelectedChannelStore, UserStore, useStateFromStores } from "@webpack/common";
import { MessageAttachment } from "discord-types/general"; import { MessageAttachment } from "discord-types/general";
const UploadStore = findByPropsLazy("getUploads"); const UploadAttachmentStore = findStore("UploadAttachmentStore");
const getDraft = (channelId: string) => DraftStore.getDraft(channelId, DraftType.ChannelMessage); const getDraft = (channelId: string) => DraftStore.getDraft(channelId, DraftType.ChannelMessage);
const getImageBox = (url: string): Promise<{ width: number, height: number; } | null> => const getImageBox = (url: string): Promise<{ width: number, height: number; } | null> =>
new Promise(res => { new Promise(res => {
const img = new Image(); const img = new Image();
@ -44,7 +43,7 @@ const getImageBox = (url: string): Promise<{ width: number, height: number; } |
const getAttachments = async (channelId: string) => const getAttachments = async (channelId: string) =>
await Promise.all( await Promise.all(
UploadStore.getUploads(channelId, DraftType.ChannelMessage) UploadAttachmentStore.getUploads(channelId, DraftType.ChannelMessage)
.map(async (upload: any) => { .map(async (upload: any) => {
const { isImage, filename, spoiler, item: { file } } = upload; const { isImage, filename, spoiler, item: { file } } = upload;
const url = URL.createObjectURL(file); const url = URL.createObjectURL(file);
@ -79,7 +78,7 @@ const PreviewButton: ChatBarButton = ({ isMainChat, isEmpty, type: { attachments
if (!isMainChat) return null; if (!isMainChat) return null;
const hasAttachments = attachments && UploadStore.getUploads(channelId, DraftType.ChannelMessage).length > 0; const hasAttachments = attachments && UploadAttachmentStore.getUploads(channelId, DraftType.ChannelMessage).length > 0;
const hasContent = !isEmpty && draft?.length > 0; const hasContent = !isEmpty && draft?.length > 0;
if (!hasContent && !hasAttachments) return null; if (!hasContent && !hasAttachments) return null;

View file

@ -18,14 +18,14 @@
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { classes } from "@utils/misc"; import { classes } from "@utils/misc";
import { findByPropsLazy } from "@webpack"; import { findByProps } from "@webpack";
import { UserStore } from "@webpack/common"; import { UserStore } from "@webpack/common";
import { Message } from "discord-types/general"; import { Message } from "discord-types/general";
import { useFormattedPronouns } from "../pronoundbUtils"; import { useFormattedPronouns } from "../pronoundbUtils";
import { settings } from "../settings"; import { settings } from "../settings";
const styles: Record<string, string> = findByPropsLazy("timestampInline"); const styles: Record<string, string> = findByProps("timestampInline");
const AUTO_MODERATION_ACTION = 24; const AUTO_MODERATION_ACTION = 24;

View file

@ -16,18 +16,17 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Settings } from "@api/Settings";
import { debounce } from "@shared/debounce"; import { debounce } from "@shared/debounce";
import { VENCORD_USER_AGENT } from "@shared/vencordUserAgent"; import { VENCORD_USER_AGENT } from "@shared/vencordUserAgent";
import { getCurrentChannel } from "@utils/discord"; import { getCurrentChannel } from "@utils/discord";
import { useAwaiter } from "@utils/react"; import { useAwaiter } from "@utils/react";
import { findStoreLazy } from "@webpack"; import { findStore } from "@webpack";
import { UserProfileStore, UserStore } from "@webpack/common"; import { UserProfileStore, UserStore } from "@webpack/common";
import { settings } from "./settings"; import { settings } from "./settings";
import { CachePronouns, PronounCode, PronounMapping, PronounsResponse } from "./types"; import { CachePronouns, PronounCode, PronounMapping, PronounsResponse } from "./types";
const UserSettingsAccountStore = findStoreLazy("UserSettingsAccountStore"); const UserSettingsAccountStore = findStore("UserSettingsAccountStore");
type PronounsWithSource = [pronouns: string | null, source: string, hasPendingPronouns: boolean]; type PronounsWithSource = [pronouns: string | null, source: string, hasPendingPronouns: boolean];
const EmptyPronouns: PronounsWithSource = [null, "", false]; const EmptyPronouns: PronounsWithSource = [null, "", false];
@ -156,7 +155,7 @@ export function extractPronouns(pronounSet?: { [locale: string]: PronounCode[];
if (!pronounSet || !pronounSet.en) return PronounMapping.unspecified; if (!pronounSet || !pronounSet.en) return PronounMapping.unspecified;
// PronounDB returns an empty set instead of {sets: {en: ["unspecified"]}}. // PronounDB returns an empty set instead of {sets: {en: ["unspecified"]}}.
const pronouns = pronounSet.en; const pronouns = pronounSet.en;
const { pronounsFormat } = Settings.plugins.PronounDB as { pronounsFormat: PronounsFormat, enabled: boolean; }; const { pronounsFormat } = settings.store;
if (pronouns.length === 1) { if (pronouns.length === 1) {
// For capitalized pronouns or special codes (any, ask, avoid), we always return the normal (capitalized) string // For capitalized pronouns or special codes (any, ask, avoid), we always return the normal (capitalized) string

View file

@ -19,12 +19,11 @@
import { definePluginSettings, Settings } from "@api/Settings"; import { definePluginSettings, Settings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack"; import { findByProps } from "@webpack";
import { ChannelStore, FluxDispatcher as Dispatcher, MessageStore, PermissionsBits, PermissionStore, SelectedChannelStore, UserStore } from "@webpack/common"; import { ChannelStore, FluxDispatcher as Dispatcher, MessageStore, PermissionsBits, PermissionStore, RelationshipStore, SelectedChannelStore, UserStore } from "@webpack/common";
import { Message } from "discord-types/general"; import { Message } from "discord-types/general";
const Kangaroo = findByPropsLazy("jumpToMessage"); const Kangaroo = findByProps("jumpToMessage");
const RelationshipStore = findByPropsLazy("getRelationships", "isBlocked");
const isMac = navigator.platform.includes("Mac"); // bruh const isMac = navigator.platform.includes("Mac"); // bruh
let replyIdx = -1; let replyIdx = -1;

View file

@ -22,8 +22,8 @@ import { addServerListElement, removeServerListElement, ServerListRenderPosition
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { findStoreLazy } from "@webpack"; import { findStore } from "@webpack";
import { Button, FluxDispatcher, GuildChannelStore, GuildStore, React, ReadStateStore } from "@webpack/common"; import { Button, FluxDispatcher, GenericStore, GuildChannelStore, GuildStore, React, ReadStateStore } from "@webpack/common";
import { Channel } from "discord-types/general"; import { Channel } from "discord-types/general";
interface ThreadJoined { interface ThreadJoined {
@ -34,11 +34,11 @@ interface ThreadJoined {
type ThreadsJoined = Record<string, ThreadJoined>; type ThreadsJoined = Record<string, ThreadJoined>;
type ThreadsJoinedByParent = Record<string, ThreadsJoined>; type ThreadsJoinedByParent = Record<string, ThreadsJoined>;
interface ActiveJoinedThreadsStore { interface ActiveJoinedThreadsStore extends GenericStore {
getActiveJoinedThreadsForGuild(guildId: string): ThreadsJoinedByParent; getActiveJoinedThreadsForGuild(guildId: string): ThreadsJoinedByParent;
} }
const ActiveJoinedThreadsStore: ActiveJoinedThreadsStore = findStoreLazy("ActiveJoinedThreadsStore"); const ActiveJoinedThreadsStore = findStore<ActiveJoinedThreadsStore>("ActiveJoinedThreadsStore");
function onClick() { function onClick() {
const channels: Array<any> = []; const channels: Array<any> = [];

View file

@ -19,14 +19,14 @@
import { DataStore, Notices } from "@api/index"; import { DataStore, Notices } from "@api/index";
import { showNotification } from "@api/Notifications"; import { showNotification } from "@api/Notifications";
import { getUniqueUsername, openUserProfile } from "@utils/discord"; import { getUniqueUsername, openUserProfile } from "@utils/discord";
import { findStoreLazy } from "@webpack"; import { findStore } from "@webpack";
import { ChannelStore, GuildMemberStore, GuildStore, RelationshipStore, UserStore, UserUtils } from "@webpack/common"; import { ChannelStore, GuildMemberStore, GuildStore, RelationshipStore, UserStore, UserUtils } from "@webpack/common";
import { FluxStore } from "@webpack/types"; import { FluxStore } from "@webpack/types";
import settings from "./settings"; import settings from "./settings";
import { ChannelType, RelationshipType, SimpleGroupChannel, SimpleGuild } from "./types"; import { ChannelType, RelationshipType, SimpleGroupChannel, SimpleGuild } from "./types";
export const GuildAvailabilityStore = findStoreLazy("GuildAvailabilityStore") as FluxStore & { export const GuildAvailabilityStore = findStore("GuildAvailabilityStore") as FluxStore & {
totalGuilds: number; totalGuilds: number;
totalUnavailableGuilds: number; totalUnavailableGuilds: number;
unavailableGuilds: string[]; unavailableGuilds: string[];

View file

@ -9,17 +9,17 @@ import "./style.css";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { filters, findByPropsLazy, mapMangledModuleLazy } from "@webpack"; import { filters, findByProps, mapMangledModule } from "@webpack";
import { Timestamp } from "@webpack/common"; import { Timestamp } from "@webpack/common";
import type { Message } from "discord-types/general"; import type { Message } from "discord-types/general";
import type { HTMLAttributes } from "react"; import type { HTMLAttributes } from "react";
const { calendarFormat, dateFormat, isSameDay } = mapMangledModuleLazy("millisecondsInUnit:", { const { calendarFormat, dateFormat, isSameDay } = mapMangledModule("millisecondsInUnit:", {
calendarFormat: filters.byCode("sameElse"), calendarFormat: filters.byCode("sameElse"),
dateFormat: filters.byCode('":'), dateFormat: filters.byCode(':").concat'),
isSameDay: filters.byCode("Math.abs(+"), isSameDay: filters.byCode("Math.abs(+"),
}); });
const MessageClasses = findByPropsLazy("separator", "latin24CompactTimeStamp"); const MessageClasses = findByProps("separator", "latin24CompactTimeStamp");
function Sep(props: HTMLAttributes<HTMLElement>) { function Sep(props: HTMLAttributes<HTMLElement>) {
return <i className={MessageClasses.separator} aria-hidden={true} {...props} />; return <i className={MessageClasses.separator} aria-hidden={true} {...props} />;

View file

@ -18,10 +18,10 @@
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { findByPropsLazy } from "@webpack"; import { findByProps } from "@webpack";
const SpoilerClasses = findByPropsLazy("spoilerContent"); const SpoilerClasses = findByProps("spoilerContent");
const MessagesClasses = findByPropsLazy("messagesWrapper"); const MessagesClasses = findByProps("messagesWrapper");
export default definePlugin({ export default definePlugin({
name: "RevealAllSpoilers", name: "RevealAllSpoilers",

View file

@ -7,15 +7,12 @@
import { DataStore } from "@api/index"; import { DataStore } from "@api/index";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import { openModal } from "@utils/modal"; import { openModal } from "@utils/modal";
import { findByPropsLazy } from "@webpack"; import { OAuth2AuthorizeModal, showToast, Toasts, UserStore } from "@webpack/common";
import { showToast, Toasts, UserStore } from "@webpack/common";
import { ReviewDBAuth } from "./entities"; import { ReviewDBAuth } from "./entities";
const DATA_STORE_KEY = "rdb-auth"; const DATA_STORE_KEY = "rdb-auth";
const { OAuth2AuthorizeModal } = findByPropsLazy("OAuth2AuthorizeModal");
export let Auth: ReviewDBAuth = {}; export let Auth: ReviewDBAuth = {};
export async function initAuth() { export async function initAuth() {

View file

@ -18,10 +18,10 @@
import { DeleteIcon } from "@components/Icons"; import { DeleteIcon } from "@components/Icons";
import { classes } from "@utils/misc"; import { classes } from "@utils/misc";
import { findByPropsLazy } from "@webpack"; import { findByProps } from "@webpack";
import { Tooltip } from "@webpack/common"; import { Tooltip } from "@webpack/common";
const iconClasses = findByPropsLazy("button", "wrapper", "disabled", "separator"); const iconClasses = findByProps("button", "wrapper", "disabled", "separator");
export function DeleteButton({ onClick }: { onClick(): void; }) { export function DeleteButton({ onClick }: { onClick(): void; }) {
return ( return (

View file

@ -18,8 +18,7 @@
import { openUserProfile } from "@utils/discord"; import { openUserProfile } from "@utils/discord";
import { classes } from "@utils/misc"; import { classes } from "@utils/misc";
import { LazyComponent } from "@utils/react"; import { findByProps } from "@webpack";
import { filters, findBulk } from "@webpack";
import { Alerts, Parser, Timestamp, useState } from "@webpack/common"; import { Alerts, Parser, Timestamp, useState } from "@webpack/common";
import { Auth, getToken } from "../auth"; import { Auth, getToken } from "../auth";
@ -31,161 +30,150 @@ import { openBlockModal } from "./BlockedUserModal";
import { BlockButton, DeleteButton, ReportButton } from "./MessageButton"; import { BlockButton, DeleteButton, ReportButton } from "./MessageButton";
import ReviewBadge from "./ReviewBadge"; import ReviewBadge from "./ReviewBadge";
export default LazyComponent(() => { const messageClasses = findByProps("cozyMessage");
// this is terrible, blame mantika const containerClasses = findByProps("container", "isHeader");
const p = filters.byProps; const avatarClasses = findByProps("avatar", "zalgo");
const [ const buttonClasses = findByProps("button", "wrapper", "selected");
{ cozyMessage, buttons, message, buttonsInner, groupStart }, const botTagClasses = findByProps("botTagRegular");
{ container, isHeader },
{ avatar, clickable, username, wrapper, cozy },
buttonClasses,
botTag
] = findBulk(
p("cozyMessage"),
p("container", "isHeader"),
p("avatar", "zalgo"),
p("button", "wrapper", "selected"),
p("botTagRegular")
);
const dateFormat = new Intl.DateTimeFormat(); const dateFormat = new Intl.DateTimeFormat();
return function ReviewComponent({ review, refetch, profileId }: { review: Review; refetch(): void; profileId: string; }) { export default function ReviewComponent({ review, refetch, profileId }: { review: Review; refetch(): void; profileId: string; }) {
const [showAll, setShowAll] = useState(false); const [showAll, setShowAll] = useState(false);
function openModal() { function openModal() {
openUserProfile(review.sender.discordID); openUserProfile(review.sender.discordID);
} }
function delReview() { function delReview() {
Alerts.show({ Alerts.show({
title: "Are you sure?", title: "Are you sure?",
body: "Do you really want to delete this review?", body: "Do you really want to delete this review?",
confirmText: "Delete", confirmText: "Delete",
cancelText: "Nevermind", cancelText: "Nevermind",
onConfirm: async () => { onConfirm: async () => {
if (!(await getToken())) { if (!(await getToken())) {
return showToast("You must be logged in to delete reviews."); return showToast("You must be logged in to delete reviews.");
} else { } else {
deleteReview(review.id).then(res => { deleteReview(review.id).then(res => {
if (res) { if (res) {
refetch(); refetch();
} }
}); });
}
} }
}); }
} });
}
function reportRev() { function reportRev() {
Alerts.show({ Alerts.show({
title: "Are you sure?", title: "Are you sure?",
body: "Do you really you want to report this review?", body: "Do you really you want to report this review?",
confirmText: "Report", confirmText: "Report",
cancelText: "Nevermind", cancelText: "Nevermind",
// confirmColor: "red", this just adds a class name and breaks the submit button guh // confirmColor: "red", this just adds a class name and breaks the submit button guh
onConfirm: async () => { onConfirm: async () => {
if (!(await getToken())) { if (!(await getToken())) {
return showToast("You must be logged in to report reviews."); return showToast("You must be logged in to report reviews.");
} else { } else {
reportReview(review.id); reportReview(review.id);
}
} }
}); }
} });
}
const isAuthorBlocked = Auth?.user?.blockedUsers?.includes(review.sender.discordID) ?? false; const isAuthorBlocked = Auth?.user?.blockedUsers?.includes(review.sender.discordID) ?? false;
function blockReviewSender() { function blockReviewSender() {
if (isAuthorBlocked) if (isAuthorBlocked)
return unblockUser(review.sender.discordID); return unblockUser(review.sender.discordID);
Alerts.show({ Alerts.show({
title: "Are you sure?", title: "Are you sure?",
body: "Do you really you want to block this user? They will be unable to leave further reviews on your profile. You can unblock users in the plugin settings.", body: "Do you really you want to block this user? They will be unable to leave further reviews on your profile. You can unblock users in the plugin settings.",
confirmText: "Block", confirmText: "Block",
cancelText: "Nevermind", cancelText: "Nevermind",
// confirmColor: "red", this just adds a class name and breaks the submit button guh // confirmColor: "red", this just adds a class name and breaks the submit button guh
onConfirm: async () => { onConfirm: async () => {
if (!(await getToken())) { if (!(await getToken())) {
return showToast("You must be logged in to block users."); return showToast("You must be logged in to block users.");
} else { } else {
blockUser(review.sender.discordID); blockUser(review.sender.discordID);
}
} }
}); }
} });
}
return ( return (
<div className={classes(cl("review"), cozyMessage, wrapper, message, groupStart, cozy)} style={ <div className={classes(cl("review"), messageClasses.cozyMessage, avatarClasses.wrapper, messageClasses.message, messageClasses.groupStart, avatarClasses.cozy)} style={
{ {
marginLeft: "0px", marginLeft: "0px",
paddingLeft: "52px", // wth is this paddingLeft: "52px", // wth is this
// nobody knows anymore // nobody knows anymore
} }
}> }>
<img <img
className={classes(avatar, clickable)} className={classes(avatarClasses.avatar, avatarClasses.clickable)}
onClick={openModal} onClick={openModal}
src={review.sender.profilePhoto || "/assets/1f0bfc0865d324c2587920a7d80c609b.png?size=128"} src={review.sender.profilePhoto || "/assets/1f0bfc0865d324c2587920a7d80c609b.png?size=128"}
style={{ left: "0px", zIndex: 0 }} style={{ left: "0px", zIndex: 0 }}
/> />
<div style={{ display: "inline-flex", justifyContent: "center", alignItems: "center" }}> <div style={{ display: "inline-flex", justifyContent: "center", alignItems: "center" }}>
<span
className={classes(avatarClasses.clickable, avatarClasses.username)}
style={{ color: "var(--channels-default)", fontSize: "14px" }}
onClick={() => openModal()}
>
{review.sender.username}
</span>
{review.type === ReviewType.System && (
<span <span
className={classes(clickable, username)} className={classes(botTagClasses.botTagVerified, botTagClasses.botTagRegular, botTagClasses.px, botTagClasses.rem)}
style={{ color: "var(--channels-default)", fontSize: "14px" }} style={{ marginLeft: "4px" }}>
onClick={() => openModal()} <span className={botTagClasses.botText}>
> System
{review.sender.username}
</span>
{review.type === ReviewType.System && (
<span
className={classes(botTag.botTagVerified, botTag.botTagRegular, botTag.px, botTag.rem)}
style={{ marginLeft: "4px" }}>
<span className={botTag.botText}>
System
</span>
</span> </span>
)} </span>
</div>
{isAuthorBlocked && (
<ReviewBadge
name="You have blocked this user"
description="You have blocked this user"
icon="/assets/aaee57e0090991557b66.svg"
type={0}
onClick={() => openBlockModal()}
/>
)}
{review.sender.badges.map(badge => <ReviewBadge {...badge} />)}
{
!settings.store.hideTimestamps && review.type !== ReviewType.System && (
<Timestamp timestamp={new Date(review.timestamp * 1000)} >
{dateFormat.format(review.timestamp * 1000)}
</Timestamp>)
}
<div className={cl("review-comment")}>
{(review.comment.length > 200 && !showAll)
? [Parser.parseGuildEventDescription(review.comment.substring(0, 200)), "...", <br />, (<a onClick={() => setShowAll(true)}>Read more</a>)]
: Parser.parseGuildEventDescription(review.comment)}
</div>
{review.id !== 0 && (
<div className={classes(container, isHeader, buttons)} style={{
padding: "0px",
}}>
<div className={classes(buttonClasses.wrapper, buttonsInner)} >
{canReportReview(review) && <ReportButton onClick={reportRev} />}
{canBlockReviewAuthor(profileId, review) && <BlockButton isBlocked={isAuthorBlocked} onClick={blockReviewSender} />}
{canDeleteReview(profileId, review) && <DeleteButton onClick={delReview} />}
</div>
</div>
)} )}
</div> </div>
); {isAuthorBlocked && (
}; <ReviewBadge
}); name="You have blocked this user"
description="You have blocked this user"
icon="/assets/aaee57e0090991557b66.svg"
type={0}
onClick={() => openBlockModal()}
/>
)}
{review.sender.badges.map(badge => <ReviewBadge {...badge} />)}
{
!settings.store.hideTimestamps && review.type !== ReviewType.System && (
<Timestamp timestamp={new Date(review.timestamp * 1000)} >
{dateFormat.format(review.timestamp * 1000)}
</Timestamp>)
}
<div className={cl("review-comment")}>
{(review.comment.length > 200 && !showAll)
? [Parser.parseGuildEventDescription(review.comment.substring(0, 200)), "...", <br />, (<a onClick={() => setShowAll(true)}>Read more</a>)]
: Parser.parseGuildEventDescription(review.comment)}
</div>
{review.id !== 0 && (
<div className={classes(containerClasses.container, containerClasses.isHeader, messageClasses.buttons)} style={{
padding: "0px",
}}>
<div className={classes(buttonClasses.wrapper, messageClasses.buttonsInner)} >
{canReportReview(review) && <ReportButton onClick={reportRev} />}
{canBlockReviewAuthor(profileId, review) && <BlockButton isBlocked={isAuthorBlocked} onClick={blockReviewSender} />}
{canDeleteReview(profileId, review) && <DeleteButton onClick={delReview} />}
</div>
</div>
)}
</div>
);
}

View file

@ -17,7 +17,7 @@
*/ */
import { useAwaiter, useForceUpdater } from "@utils/react"; import { useAwaiter, useForceUpdater } from "@utils/react";
import { findByCodeLazy, findByPropsLazy, findComponentByCodeLazy } from "@webpack"; import { findByCode, findByProps, findComponentByCode } from "@webpack";
import { Forms, React, RelationshipStore, useRef, UserStore } from "@webpack/common"; import { Forms, React, RelationshipStore, useRef, UserStore } from "@webpack/common";
import { Auth, authorize } from "../auth"; import { Auth, authorize } from "../auth";
@ -27,11 +27,11 @@ import { settings } from "../settings";
import { cl, showToast } from "../utils"; import { cl, showToast } from "../utils";
import ReviewComponent from "./ReviewComponent"; import ReviewComponent from "./ReviewComponent";
const Transforms = findByPropsLazy("insertNodes", "textToText"); const Transforms = findByProps("insertNodes", "textToText");
const Editor = findByPropsLazy("start", "end", "toSlateRange"); const Editor = findByProps("start", "end", "toSlateRange");
const ChatInputTypes = findByPropsLazy("FORM"); const ChatInputTypes = findByProps("FORM");
const InputComponent = findComponentByCodeLazy("disableThemedBackground", "CHANNEL_TEXT_AREA"); const InputComponent = findComponentByCode("disableThemedBackground", "CHANNEL_TEXT_AREA");
const createChannelRecordFromServer = findByCodeLazy(".GUILD_TEXT])", "fromServer)"); const createChannelRecordFromServer = findByCode(".GUILD_TEXT])", "fromServer)");
interface UserProps { interface UserProps {
discordId: string; discordId: string;

View file

@ -24,7 +24,7 @@ import { NotesIcon, OpenExternalIcon } from "@components/Icons";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { classes } from "@utils/misc"; import { classes } from "@utils/misc";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { findByPropsLazy } from "@webpack"; import { findByProps } from "@webpack";
import { Alerts, Button, Menu, Parser, TooltipContainer } from "@webpack/common"; import { Alerts, Button, Menu, Parser, TooltipContainer } from "@webpack/common";
import { Guild, User } from "discord-types/general"; import { Guild, User } from "discord-types/general";
@ -35,7 +35,7 @@ import { getCurrentUserInfo, readNotification } from "./reviewDbApi";
import { settings } from "./settings"; import { settings } from "./settings";
import { showToast } from "./utils"; import { showToast } from "./utils";
const RoleButtonClasses = findByPropsLazy("button", "buttonInner", "icon", "banner"); const RoleButtonClasses = findByProps("button", "buttonInner", "icon", "banner");
const guildPopoutPatch: NavContextMenuPatchCallback = (children, { guild }: { guild: Guild, onClose(): void; }) => { const guildPopoutPatch: NavContextMenuPatchCallback = (children, { guild }: { guild: Guild, onClose(): void; }) => {
if (!guild) return; if (!guild) return;

View file

@ -20,12 +20,11 @@ import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/Co
import { ReplyIcon } from "@components/Icons"; import { ReplyIcon } from "@components/Icons";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { findByCodeLazy } from "@webpack"; import { findByCode } from "@webpack";
import { ChannelStore, i18n, Menu, PermissionsBits, PermissionStore, SelectedChannelStore } from "@webpack/common"; import { ChannelStore, i18n, Menu, PermissionsBits, PermissionStore, SelectedChannelStore } from "@webpack/common";
import { Message } from "discord-types/general"; import { Message } from "discord-types/general";
const replyToMessage = findByCode(".TEXTAREA_FOCUS)", "showMentionToggle:");
const replyToMessage = findByCodeLazy(".TEXTAREA_FOCUS)", "showMentionToggle:");
const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { message }: { message: Message; }) => { const messageContextMenuPatch: NavContextMenuPatchCallback = (children, { message }: { message: Message; }) => {
// make sure the message is in the selected channel // make sure the message is in the selected channel

Some files were not shown because too many files have changed in this diff Show more