Compare commits

..

18 commits

Author SHA1 Message Date
Nuckyz
eee1e08a97
Merge 07a9279682 into 8afd79dd50 2024-09-18 18:12:42 +00:00
Nuckyz
07a9279682
incredible
Some checks are pending
Sync to Codeberg / codeberg (push) Waiting to run
test / test (push) Waiting to run
2024-09-18 15:12:36 -03:00
Nuckyz
a0a1aaf0ed
I'm not gonna comment 2024-09-18 15:07:41 -03:00
Nuckyz
356586d68f
I swear 2024-09-18 15:06:57 -03:00
Nuckyz
adf7bd0dcc
clean 2024-09-18 15:06:13 -03:00
Nuckyz
a6971289ad
now it's right 2024-09-18 15:05:23 -03:00
Nuckyz
0953ba89ed
that's wrong actually 2024-09-18 15:01:05 -03:00
Nuckyz
272209069a
lol 2024-09-18 15:00:28 -03:00
Nuckyz
f9ef683191
fix what I broke 2024-09-18 14:51:33 -03:00
Nuckyz
b1f3925361
last one I promise 2024-09-18 14:48:18 -03:00
Nuckyz
5dbe81e459
help 2024-09-18 14:43:27 -03:00
Nuckyz
a493d56487
Add AnyComponentTypeWithChildren 2024-09-18 14:26:50 -03:00
Nuckyz
4722851172
Merge branch 'dev' into immediate-finds 2024-09-18 14:16:35 -03:00
Nuckyz
67175b5b55
fix 2024-09-18 14:02:41 -03:00
Nuckyz
15b7982228
yes 2024-09-18 13:57:33 -03:00
Nuckyz
126f2df811
Lazy components are always functions 2024-09-18 13:44:27 -03:00
Vendicated
8afd79dd50
add Icons to webpack commons
Some checks are pending
Sync to Codeberg / codeberg (push) Waiting to run
test / test (push) Waiting to run
2024-09-18 01:36:52 +02:00
Vendicated
65c5897dc3
remove need to depend on CommandsAPI 2024-09-18 01:26:25 +02:00
36 changed files with 149 additions and 142 deletions

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

@ -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

@ -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

@ -292,10 +292,10 @@ export default function PluginSettings() {
if (!pluginFilter(p)) continue; if (!pluginFilter(p)) continue;
const isRequired = p.required || depMap[p.name]?.some(d => settings.plugins[d].enabled); const isRequired = p.required || p.isDependency || depMap[p.name]?.some(d => settings.plugins[d].enabled);
if (isRequired) { if (isRequired) {
const tooltipText = p.required const tooltipText = p.required || !depMap[p.name]
? "This plugin is required for Vencord to function." ? "This plugin is required for Vencord to function."
: makeDependencyList(depMap[p.name]?.filter(d => settings.plugins[d].enabled)); : makeDependencyList(depMap[p.name]?.filter(d => settings.plugins[d].enabled));

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,

6
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,7 +68,7 @@ declare global {
export var Vesktop: any; export var Vesktop: any;
export var VesktopNative: any; export var VesktopNative: any;
interface Window extends Record<PropertyKey, any> { interface Window extends AnyRecord {
_: LoDashStatic; _: LoDashStatic;
} }
} }

View file

@ -142,7 +142,7 @@ export default definePlugin({
required: true, required: true,
description: "Helps us provide support to you", description: "Helps us provide support to you",
authors: [Devs.Ven], authors: [Devs.Ven],
dependencies: ["CommandsAPI", "UserSettingsAPI", "MessageAccessoriesAPI"], dependencies: ["UserSettingsAPI", "MessageAccessoriesAPI"],
settings, settings,

View file

@ -23,7 +23,7 @@ const UserProfile = findComponentByCode("UserProfilePopoutWrapper: user cannot b
const styles = findByProps("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

@ -28,7 +28,6 @@ import * as Webpack from "@webpack";
import { cacheFindAll, cacheFindModuleId, extract, filters, searchFactories } 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.`);
@ -129,7 +128,7 @@ function makeShortcuts() {
canonicalizeReplacement, canonicalizeReplacement,
preEnable: (plugin: string) => (Vencord.Settings.plugins[plugin] ??= { enabled: true }).enabled = true, preEnable: (plugin: string) => (Vencord.Settings.plugins[plugin] ??= { enabled: true }).enabled = true,
fakeRender: (component: ComponentType, props: any) => { 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

View file

@ -7,11 +7,10 @@
import { NoopComponent } from "@utils/react"; import { NoopComponent } from "@utils/react";
import { findComponentByCode } from "@webpack"; 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,
}>; }>;
@ -19,9 +18,9 @@ type DecorationGridItemComponent = ComponentType<PropsWithChildren<HTMLProps<HTM
export let DecorationGridItem: DecorationGridItemComponent = NoopComponent; export let DecorationGridItem: DecorationGridItemComponent = NoopComponent;
export const setDecorationGridItem = v => DecorationGridItem = v; export const setDecorationGridItem = v => DecorationGridItem = v;
export const AvatarDecorationModalPreview = findComponentByCode<any>(".shopPreviewBanner", component => React.memo(component)); export const AvatarDecorationModalPreview = findComponentByCode(".shopPreviewBanner", component => 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,

View file

@ -27,7 +27,6 @@ export default definePlugin({
name: "FriendInvites", name: "FriendInvites",
description: "Create and manage friend invite links via slash commands (/create friend invite, /view friend invites, /revoke friend invites).", description: "Create and manage friend invite links via slash commands (/create friend invite, /view friend invites, /revoke friend invites).",
authors: [Devs.afn, Devs.Dziurwa], authors: [Devs.afn, Devs.Dziurwa],
dependencies: ["CommandsAPI"],
commands: [ commands: [
{ {
name: "create friend invite", name: "create friend invite",

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

@ -107,6 +107,11 @@ for (const p of pluginsValues) if (isPluginEnabled(p.name)) {
settings[d].enabled = true; settings[d].enabled = true;
dep.isDependency = true; dep.isDependency = true;
}); });
if (p.commands?.length) {
Plugins.CommandsAPI.isDependency = true;
settings.CommandsAPI.enabled = true;
}
} }
for (const p of pluginsValues) { for (const p of pluginsValues) {

View file

@ -84,8 +84,6 @@ export default definePlugin({
authors: [Devs.Luna], authors: [Devs.Luna],
settings, settings,
dependencies: ["CommandsAPI"],
async start() { async start() {
for (const tag of await getTags()) createTagCommand(tag); for (const tag of await getTags()) createTagCommand(tag);
}, },

View file

@ -33,7 +33,6 @@ export default definePlugin({
name: "MoreCommands", name: "MoreCommands",
description: "echo, lenny, mock", description: "echo, lenny, mock",
authors: [Devs.Arjix, Devs.echo, Devs.Samu], authors: [Devs.Arjix, Devs.echo, Devs.Samu],
dependencies: ["CommandsAPI"],
commands: [ commands: [
{ {
name: "echo", name: "echo",

View file

@ -24,7 +24,6 @@ export default definePlugin({
name: "MoreKaomoji", name: "MoreKaomoji",
description: "Adds more Kaomoji to discord. ヽ(´▽`)/", description: "Adds more Kaomoji to discord. ヽ(´▽`)/",
authors: [Devs.JacobTm], authors: [Devs.JacobTm],
dependencies: ["CommandsAPI"],
commands: [ commands: [
{ name: "dissatisfaction", description: " " }, { name: "dissatisfaction", description: " " },
{ name: "smug", description: "ಠ_ಠ" }, { name: "smug", description: "ಠ_ಠ" },

View file

@ -88,7 +88,6 @@ export default definePlugin({
name: "petpet", name: "petpet",
description: "Adds a /petpet slash command to create headpet gifs from any image", description: "Adds a /petpet slash command to create headpet gifs from any image",
authors: [Devs.Ven], authors: [Devs.Ven],
dependencies: ["CommandsAPI"],
commands: [ commands: [
{ {
inputType: ApplicationCommandInputType.BUILT_IN, inputType: ApplicationCommandInputType.BUILT_IN,

View file

@ -13,7 +13,6 @@ import definePlugin, { OptionType } from "@utils/types";
import { findComponentByCode } from "@webpack"; import { findComponentByCode } from "@webpack";
import { ChannelStore, GuildMemberStore, i18n, Text, Tooltip } from "@webpack/common"; import { ChannelStore, GuildMemberStore, i18n, Text, Tooltip } from "@webpack/common";
import { Message } from "discord-types/general"; import { Message } from "discord-types/general";
import { FunctionComponent, ReactNode } from "react";
const CountDown = findComponentByCode(".MAX_AGE_NEVER"); const CountDown = findComponentByCode(".MAX_AGE_NEVER");
@ -75,7 +74,7 @@ export default definePlugin({
} }
], ],
TooltipWrapper: ErrorBoundary.wrap(({ message, children, text }: { message: Message; children: FunctionComponent<any>; text: ReactNode; }) => { TooltipWrapper: ErrorBoundary.wrap(({ message, children, text }: { message: Message; children: React.FunctionComponent<AnyRecord>; text: React.ReactNode; }) => {
if (settings.store.displayStyle === DisplayStyle.Tooltip) return <Tooltip if (settings.store.displayStyle === DisplayStyle.Tooltip) return <Tooltip
children={children} children={children}
text={renderTimeout(message, false)} text={renderTimeout(message, false)}

View file

@ -88,7 +88,7 @@ export default definePlugin({
name: "SilentTyping", name: "SilentTyping",
authors: [Devs.Ven, Devs.Rini, Devs.ImBanana], authors: [Devs.Ven, Devs.Rini, Devs.ImBanana],
description: "Hide that you are typing", description: "Hide that you are typing",
dependencies: ["CommandsAPI", "ChatInputButtonAPI"], dependencies: ["ChatInputButtonAPI"],
settings, settings,
contextMenus: { contextMenus: {
"textarea-context": ChatBarContextCheckbox "textarea-context": ChatBarContextCheckbox

View file

@ -76,7 +76,6 @@ export default definePlugin({
name: "SpotifyShareCommands", name: "SpotifyShareCommands",
description: "Share your current Spotify track, album or artist via slash command (/track, /album, /artist)", description: "Share your current Spotify track, album or artist via slash command (/track, /album, /artist)",
authors: [Devs.katlyn], authors: [Devs.katlyn],
dependencies: ["CommandsAPI"],
commands: [ commands: [
{ {
name: "track", name: "track",

View file

@ -17,7 +17,7 @@ const selectVoiceChannel = findProp("selectVoiceChannel", "selectChannel");
const VoiceStateStore = findStore("VoiceStateStore"); const VoiceStateStore = findStore("VoiceStateStore");
const UserSummaryItem = findComponentByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers"); const UserSummaryItem = findComponentByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers");
interface IconProps extends React.HTMLAttributes<HTMLDivElement> { interface IconProps extends React.ComponentPropsWithoutRef<"div"> {
size?: number; size?: number;
} }

View file

@ -23,7 +23,6 @@ import { sleep } from "@utils/misc";
import { Queue } from "@utils/Queue"; import { Queue } from "@utils/Queue";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { Constants, FluxDispatcher, RestAPI, UserProfileStore, UserStore, useState } from "@webpack/common"; import { Constants, FluxDispatcher, RestAPI, UserProfileStore, UserStore, useState } from "@webpack/common";
import { type ComponentType, type ReactNode } from "react";
// LYING to the type checker here // LYING to the type checker here
const UserFlags = Constants.UserFlags as Record<string, number>; const UserFlags = Constants.UserFlags as Record<string, number>;
@ -60,14 +59,14 @@ interface MentionProps {
channelId?: string; channelId?: string;
content: any; content: any;
}; };
parse: (content: any, props: MentionProps["props"]) => ReactNode; parse: (content: any, props: MentionProps["props"]) => React.ReactNode;
props: { props: {
key: string; key: string;
formatInline: boolean; formatInline: boolean;
noStyleAndInteraction: boolean; noStyleAndInteraction: boolean;
}; };
RoleMention: ComponentType<any>; RoleMention: AnyComponentType;
UserMention: ComponentType<any>; UserMention: AnyComponentType;
} }
async function getUser(id: string) { async function getUser(id: string) {

View file

@ -29,7 +29,6 @@ import definePlugin from "@utils/types";
import { chooseFile } from "@utils/web"; import { chooseFile } from "@utils/web";
import { find, findByProps, findStore } from "@webpack"; import { find, findByProps, findStore } from "@webpack";
import { Button, Card, Constants, FluxDispatcher, Forms, lodash, Menu, MessageActions, PermissionsBits, PermissionStore, RestAPI, SelectedChannelStore, showToast, SnowflakeUtils, Toasts, useEffect, useState } from "@webpack/common"; import { Button, Card, Constants, FluxDispatcher, Forms, lodash, Menu, MessageActions, PermissionsBits, PermissionStore, RestAPI, SelectedChannelStore, showToast, SnowflakeUtils, Toasts, useEffect, useState } from "@webpack/common";
import { ComponentType } from "react";
import { VoiceRecorderDesktop } from "./DesktopRecorder"; import { VoiceRecorderDesktop } from "./DesktopRecorder";
import { settings } from "./settings"; import { settings } from "./settings";
@ -41,7 +40,7 @@ const CloudUpload = find(m => m.prototype?.trackUploadFinished);
const PendingReplyStore = findStore("PendingReplyStore"); const PendingReplyStore = findStore("PendingReplyStore");
const OptionClasses = findByProps("optionName", "optionIcon", "optionLabel"); const OptionClasses = findByProps("optionName", "optionIcon", "optionLabel");
export type VoiceRecorder = ComponentType<{ export type VoiceRecorder = AnyComponentType<{
setAudioBlob(blob: Blob): void; setAudioBlob(blob: Blob): void;
onRecordingChange?(recording: boolean): void; onRecordingChange?(recording: boolean): void;
}>; }>;

View file

@ -17,7 +17,7 @@ export function makeLazy<T>(factory: () => T, attempts = 5, { isIndirect = false
let tries = 0; let tries = 0;
let cache: T; let cache: T;
const getter = () => { const getter: LazyFunction<T> = function () {
if (!cache && attempts > tries) { if (!cache && attempts > tries) {
tries++; tries++;
cache = factory(); cache = factory();
@ -30,7 +30,6 @@ export function makeLazy<T>(factory: () => T, attempts = 5, { isIndirect = false
}; };
getter.$$vencordLazyFailed = () => tries === attempts; getter.$$vencordLazyFailed = () => tries === attempts;
return getter; return getter;
} }
@ -69,17 +68,11 @@ const handler: ProxyHandler<any> = {
* *
* @param factory Factory returning the result * @param factory Factory returning the result
* @param attempts How many times to try to evaluate the factory before giving up * @param attempts How many times to try to evaluate the factory before giving up
* @param errMsg The error message to throw when the factory fails * @param err The error message to throw when the factory fails
* @param primitiveErrMsg The error message to throw when factory result is a primitive * @param primitiveErr The error message to throw when factory result is a primitive
* @returns Result of factory function * @returns Result of factory function
*/ */
export function proxyLazy<T = any>( export function proxyLazy<T = any>(factory: () => T, attempts = 5, err: string | (() => string) = `proxyLazy factory failed:\n${factory}`, primitiveErr = "proxyLazy called on a primitive value.", isChild = false): T {
factory: () => T,
attempts = 5,
errMsg: string | (() => string) = `proxyLazy factory failed:\n${factory}`,
primitiveErrMsg = "proxyLazy called on a primitive value.",
isChild = false
): T {
const get = makeLazy(factory, attempts, { isIndirect: true }); const get = makeLazy(factory, attempts, { isIndirect: true });
let isSameTick = true; let isSameTick = true;
@ -93,7 +86,7 @@ export function proxyLazy<T = any>(
} }
if (!proxyDummy[SYM_LAZY_CACHED]) { if (!proxyDummy[SYM_LAZY_CACHED]) {
throw new Error(typeof errMsg === "string" ? errMsg : errMsg()); throw new Error(typeof err === "string" ? err : err());
} else { } else {
if (typeof proxyDummy[SYM_LAZY_CACHED] === "function") { if (typeof proxyDummy[SYM_LAZY_CACHED] === "function") {
proxy.toString = proxyDummy[SYM_LAZY_CACHED].toString.bind(proxyDummy[SYM_LAZY_CACHED]); proxy.toString = proxyDummy[SYM_LAZY_CACHED].toString.bind(proxyDummy[SYM_LAZY_CACHED]);
@ -129,8 +122,8 @@ export function proxyLazy<T = any>(
return Reflect.get(lazyTarget, p, lazyTarget); return Reflect.get(lazyTarget, p, lazyTarget);
}, },
attempts, attempts,
errMsg, err,
primitiveErrMsg, primitiveErr,
true true
); );
} }
@ -140,7 +133,7 @@ export function proxyLazy<T = any>(
return Reflect.get(lazyTarget, p, lazyTarget); return Reflect.get(lazyTarget, p, lazyTarget);
} }
throw new Error(primitiveErrMsg); throw new Error(primitiveErr);
} }
}); });

View file

@ -6,10 +6,13 @@
import { makeLazy } from "./lazy"; import { makeLazy } from "./lazy";
export type LazyComponentType<T extends object = any> = React.ComponentType<T> & Record<PropertyKey, any>;
export const SYM_LAZY_COMPONENT_INNER = Symbol.for("vencord.lazyComponent.inner"); export const SYM_LAZY_COMPONENT_INNER = Symbol.for("vencord.lazyComponent.inner");
export type LazyComponentType<P extends AnyRecord = AnyRecord> = React.FunctionComponent<P> & AnyRecord & {
[SYM_LAZY_COMPONENT_INNER]: () => AnyComponentType<P> | null;
};
export type AnyLazyComponentType<P extends AnyRecord = AnyRecord> = LazyComponentType<P & AnyRecord>;
/** /**
* A lazy component. The factory method is called on first render. * A lazy component. The factory method is called on first render.
* *
@ -17,15 +20,16 @@ export const SYM_LAZY_COMPONENT_INNER = Symbol.for("vencord.lazyComponent.inner"
* @param attempts How many times to try to get the component before giving up * @param attempts How many times to try to get the component before giving up
* @returns Result of factory function * @returns Result of factory function
*/ */
export function LazyComponent<T extends object = any>(factory: () => LazyComponentType<T>, attempts = 5, errMsg: string | (() => string) = `LazyComponent factory failed:\n${factory}`) { export function LazyComponent<P extends AnyRecord>(factory: () => React.ComponentType<P>, attempts = 5, err: string | (() => string) = `LazyComponent factory failed:\n${factory}`): LazyComponentType<P> {
const get = makeLazy(factory, attempts, { isIndirect: true }); const get = makeLazy(factory, attempts, { isIndirect: true });
let InnerComponent = null as LazyComponentType<T> | null; let InnerComponent = null as AnyComponentType<P> | null;
let lazyFailedLogged = false; let lazyFailedLogged = false;
const LazyComponent = (props: T) => { const LazyComponent: LazyComponentType<P> = function (props) {
if (!get.$$vencordLazyFailed()) { if (!get.$$vencordLazyFailed()) {
const ResultComponent = get(); const ResultComponent = get();
if (ResultComponent != null) { if (ResultComponent != null) {
InnerComponent = ResultComponent; InnerComponent = ResultComponent;
Object.assign(LazyComponent, ResultComponent); Object.assign(LazyComponent, ResultComponent);
@ -37,7 +41,7 @@ export function LazyComponent<T extends object = any>(factory: () => LazyCompone
lazyFailedLogged = true; lazyFailedLogged = true;
} }
console.error(typeof errMsg === "string" ? errMsg : errMsg()); console.error(typeof err === "string" ? err : err());
} }
return InnerComponent && <InnerComponent {...props} />; return InnerComponent && <InnerComponent {...props} />;
@ -45,5 +49,5 @@ export function LazyComponent<T extends object = any>(factory: () => LazyCompone
LazyComponent[SYM_LAZY_COMPONENT_INNER] = () => InnerComponent; LazyComponent[SYM_LAZY_COMPONENT_INNER] = () => InnerComponent;
return LazyComponent as LazyComponentType<T>; return LazyComponent;
} }

View file

@ -17,7 +17,6 @@
*/ */
import { findByProps, findComponentByCode } from "@webpack"; import { findByProps, findComponentByCode } from "@webpack";
import type { ComponentType, PropsWithChildren, ReactNode, Ref } from "react";
import { NoopComponent } from "./react"; import { NoopComponent } from "./react";
@ -47,10 +46,10 @@ export interface ModalOptions {
onCloseCallback?: (() => void); onCloseCallback?: (() => void);
} }
type RenderFunction = (props: ModalProps) => ReactNode; type RenderFunction = (props: ModalProps) => React.ReactNode;
type Modals = { type Modals = {
ModalRoot: ComponentType<PropsWithChildren<{ ModalRoot: AnyComponentTypeWithChildren<{
transitionState: ModalTransitionState; transitionState: ModalTransitionState;
size?: ModalSize; size?: ModalSize;
role?: "alertdialog" | "dialog"; role?: "alertdialog" | "dialog";
@ -59,8 +58,8 @@ type Modals = {
"aria-label"?: string; "aria-label"?: string;
"aria-labelledby"?: string; "aria-labelledby"?: string;
onAnimationEnd?(): string; onAnimationEnd?(): string;
}>>; }>;
ModalHeader: ComponentType<PropsWithChildren<{ ModalHeader: AnyComponentTypeWithChildren<{
/** Flex.Justify.START */ /** Flex.Justify.START */
justify?: string; justify?: string;
/** Flex.Direction.HORIZONTAL */ /** Flex.Direction.HORIZONTAL */
@ -72,14 +71,13 @@ type Modals = {
separator?: boolean; separator?: boolean;
className?: string; className?: string;
}>>; }>;
/** This also accepts Scroller props but good luck with that */ /** This also accepts Scroller props but good luck with that */
ModalContent: ComponentType<PropsWithChildren<{ ModalContent: AnyComponentTypeWithChildren<{
className?: string; className?: string;
scrollerRef?: Ref<HTMLElement>; scrollerRef?: React.Ref<HTMLElement>;
[prop: string]: any; }>;
}>>; ModalFooter: AnyComponentTypeWithChildren<{
ModalFooter: ComponentType<PropsWithChildren<{
/** Flex.Justify.START */ /** Flex.Justify.START */
justify?: string; justify?: string;
/** Flex.Direction.HORIZONTAL_REVERSE */ /** Flex.Direction.HORIZONTAL_REVERSE */
@ -91,8 +89,8 @@ type Modals = {
separator?: boolean; separator?: boolean;
className?: string; className?: string;
}>>; }>;
ModalCloseButton: ComponentType<{ ModalCloseButton: AnyComponentType<{
focusProps?: any; focusProps?: any;
onClick(): void; onClick(): void;
withCircleBackground?: boolean; withCircleBackground?: boolean;
@ -123,8 +121,8 @@ export type ImageModalProps = {
height?: number; height?: number;
animated?: boolean; animated?: boolean;
responsive?: boolean; responsive?: boolean;
renderLinkComponent(props: any): ReactNode; renderLinkComponent(props: any): React.ReactNode;
renderForwardComponent(props: any): ReactNode; renderForwardComponent(props: any): React.ReactNode;
maxWidth?: number; maxWidth?: number;
maxHeight?: number; maxHeight?: number;
shouldAnimate?: boolean; shouldAnimate?: boolean;

View file

@ -42,22 +42,18 @@ const handler: ProxyHandler<any> = {
* IMPORTANT: * IMPORTANT:
* Destructuring at top level is not supported for proxyInner. * Destructuring at top level is not supported for proxyInner.
* *
* @param errMsg The error message to throw when the inner value is not set * @param err The error message to throw when the inner value is not set
* @param primitiveErrMsg The error message to throw when the inner value is a primitive * @param primitiveErr The error message to throw when the inner value is a primitive
* @returns A proxy which will act like the inner value when accessed * @returns A proxy which will act like the inner value when accessed
*/ */
export function proxyInner<T = any>( export function proxyInner<T = any>(err: string | (() => string) = "Proxy inner value is undefined, setInnerValue was never called.", primitiveErr = "proxyInner called on a primitive value. This can happen if you try to destructure a primitive at the same tick as the proxy was created.", isChild = false): [proxy: T, setInnerValue: (innerValue: T) => void] {
errMsg: string | (() => string) = "Proxy inner value is undefined, setInnerValue was never called.",
primitiveErrMsg = "proxyInner called on a primitive value. This can happen if you try to destructure a primitive at the same tick as the proxy was created.",
isChild = false
): [proxy: T, setInnerValue: (innerValue: T) => void] {
let isSameTick = true; let isSameTick = true;
if (!isChild) setTimeout(() => isSameTick = false, 0); if (!isChild) setTimeout(() => isSameTick = false, 0);
const proxyDummy = Object.assign(function () { }, { const proxyDummy = Object.assign(function () { }, {
[SYM_PROXY_INNER_GET]: function () { [SYM_PROXY_INNER_GET]: function () {
if (proxyDummy[SYM_PROXY_INNER_VALUE] == null) { if (proxyDummy[SYM_PROXY_INNER_VALUE] == null) {
throw new Error(typeof errMsg === "string" ? errMsg : errMsg()); throw new Error(typeof err === "string" ? err : err());
} }
return proxyDummy[SYM_PROXY_INNER_VALUE]; return proxyDummy[SYM_PROXY_INNER_VALUE];
@ -85,7 +81,7 @@ export function proxyInner<T = any>(
"\nConsider not destructuring, using findProp or if you really need to destructure, using mapMangledModule instead." "\nConsider not destructuring, using findProp or if you really need to destructure, using mapMangledModule instead."
); );
const [recursiveProxy, recursiveSetInnerValue] = proxyInner(errMsg, primitiveErrMsg, true); const [recursiveProxy, recursiveSetInnerValue] = proxyInner(err, primitiveErr, true);
recursiveSetInnerValues.push((innerValue: T) => { recursiveSetInnerValues.push((innerValue: T) => {
// Set the inner value of the destructured value as the prop value p of the parent // Set the inner value of the destructured value as the prop value p of the parent
@ -100,7 +96,7 @@ export function proxyInner<T = any>(
return Reflect.get(innerTarget, p, innerTarget); return Reflect.get(innerTarget, p, innerTarget);
} }
throw new Error(primitiveErrMsg); throw new Error(primitiveErr);
} }
}); });

View file

@ -72,13 +72,13 @@ export interface PluginDef {
stop?(): void; stop?(): void;
patches?: Omit<Patch, "plugin">[]; patches?: Omit<Patch, "plugin">[];
/** /**
* List of commands. If you specify these, you must add CommandsAPI to dependencies * List of commands that your plugin wants to register
*/ */
commands?: Command[]; commands?: Command[];
/** /**
* A list of other plugins that your plugin depends on. * A list of other plugins that your plugin depends on.
* These will automatically be enabled and loaded before your plugin * These will automatically be enabled and loaded before your plugin
* Common examples are CommandsAPI, MessageEventsAPI... * Generally these will be API plugins
*/ */
dependencies?: string[], dependencies?: string[],
/** /**

View file

@ -5,7 +5,7 @@
*/ */
import { makeLazy, proxyLazy } from "@utils/lazy"; import { makeLazy, proxyLazy } from "@utils/lazy";
import { LazyComponent, LazyComponentType, SYM_LAZY_COMPONENT_INNER } from "@utils/lazyReact"; import { AnyLazyComponentType, LazyComponent, SYM_LAZY_COMPONENT_INNER } from "@utils/lazyReact";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import { canonicalizeMatch } from "@utils/patches"; import { canonicalizeMatch } from "@utils/patches";
import { proxyInner, SYM_PROXY_INNER_GET, SYM_PROXY_INNER_VALUE } from "@utils/proxyInner"; import { proxyInner, SYM_PROXY_INNER_GET, SYM_PROXY_INNER_VALUE } from "@utils/proxyInner";
@ -51,12 +51,13 @@ export type ModCallbackInfo = {
factory: AnyModuleFactory; factory: AnyModuleFactory;
}; };
export type FactoryListernFn = (factory: AnyModuleFactory) => void;
export type ModListenerFn = (module: ModuleExports, info: ModListenerInfo) => void; export type ModListenerFn = (module: ModuleExports, info: ModListenerInfo) => void;
export type ModCallbackFn = ((module: ModuleExports, info: ModCallbackInfo) => void) & { export type ModCallbackFn = ((module: ModuleExports, info: ModCallbackInfo) => void) & {
$$vencordCallbackCalled?: () => boolean; $$vencordCallbackCalled?: () => boolean;
}; };
export const factoryListeners = new Set<(factory: AnyModuleFactory) => void>(); export const factoryListeners = new Set<FactoryListernFn>();
export const moduleListeners = new Set<ModListenerFn>(); export const moduleListeners = new Set<ModListenerFn>();
export const waitForSubscriptions = new Map<FilterFn, ModCallbackFn>(); export const waitForSubscriptions = new Map<FilterFn, ModCallbackFn>();
@ -170,16 +171,14 @@ function printFilter(filter: FilterFn) {
return String(filter); return String(filter);
} }
function wrapWebpackComponent<T extends object = any>( function wrapWebpackComponent<P extends AnyRecord>(err: string | (() => string)): [WrapperComponent: AnyLazyComponentType<P>, setInnerComponent: (rawComponent: any, parsedComponent: React.ComponentType<P>) => void] {
errMsg: string | (() => string) let InnerComponent = null as AnyComponentType<P> | null;
): [WrapperComponent: LazyComponentType<T>, setInnerComponent: (rawComponent: any, parsedComponent: LazyComponentType<T>) => void] {
let InnerComponent = null as LazyComponentType<T> | null;
let findFailedLogged = false; let findFailedLogged = false;
const WrapperComponent = (props: T) => { const WrapperComponent: AnyLazyComponentType<P> = function (props) {
if (InnerComponent === null && !findFailedLogged) { if (InnerComponent === null && !findFailedLogged) {
findFailedLogged = true; findFailedLogged = true;
logger.error(typeof errMsg === "string" ? errMsg : errMsg()); logger.error(typeof err === "string" ? err : err());
} }
return InnerComponent && <InnerComponent {...props} />; return InnerComponent && <InnerComponent {...props} />;
@ -187,7 +186,7 @@ function wrapWebpackComponent<T extends object = any>(
WrapperComponent[SYM_LAZY_COMPONENT_INNER] = () => InnerComponent; WrapperComponent[SYM_LAZY_COMPONENT_INNER] = () => InnerComponent;
function setInnerComponent(RawComponent: any, ParsedComponent: LazyComponentType<T>) { function setInnerComponent(RawComponent: any, ParsedComponent: React.ComponentType<P>) {
InnerComponent = ParsedComponent; InnerComponent = ParsedComponent;
Object.assign(WrapperComponent, RawComponent); Object.assign(WrapperComponent, RawComponent);
} }
@ -282,7 +281,7 @@ export function find<T = any>(filter: FilterFn, parse: (module: ModuleExports) =
* @param parse A function that takes the found component as its first argument and returns a component. Useful if you want to wrap the found component in something. Defaults to the original component * @param parse A function that takes the found component as its first argument and returns a component. Useful if you want to wrap the found component in something. Defaults to the original component
* @returns The component if found, or a noop component * @returns The component if found, or a noop component
*/ */
export function findComponent<T extends object = any>(filter: FilterFn, parse: (component: ModuleExports) => LazyComponentType<T> = m => m, { isIndirect = false }: { isIndirect?: boolean; } = {}) { export function findComponent<P extends AnyRecord>(filter: FilterFn, parse: (component: ModuleExports) => React.ComponentType<P> = m => m, { isIndirect = false }: { isIndirect?: boolean; } = {}) {
if (typeof filter !== "function") { if (typeof filter !== "function") {
throw new Error("Invalid filter. Expected a function got " + typeof filter); throw new Error("Invalid filter. Expected a function got " + typeof filter);
} }
@ -290,14 +289,14 @@ export function findComponent<T extends object = any>(filter: FilterFn, parse: (
throw new Error("Invalid component parse. Expected a function got " + typeof parse); throw new Error("Invalid component parse. Expected a function got " + typeof parse);
} }
const [WrapperComponent, setInnerComponent] = wrapWebpackComponent<T>(`Webpack find matched no module. Filter: ${printFilter(filter)}`); const [WrapperComponent, setInnerComponent] = wrapWebpackComponent<P>(`Webpack find matched no module. Filter: ${printFilter(filter)}`);
waitFor(filter, m => setInnerComponent(m, parse(m)), { isIndirect: true }); waitFor(filter, m => setInnerComponent(m, parse(m)), { isIndirect: true });
if (IS_REPORTER && !isIndirect) { if (IS_REPORTER && !isIndirect) {
webpackSearchHistory.push(["findComponent", [WrapperComponent, filter]]); webpackSearchHistory.push(["findComponent", [WrapperComponent, filter]]);
} }
if (WrapperComponent[SYM_LAZY_COMPONENT_INNER]() != null) return WrapperComponent[SYM_LAZY_COMPONENT_INNER]() as LazyComponentType<T>; if (WrapperComponent[SYM_LAZY_COMPONENT_INNER]() != null) return WrapperComponent[SYM_LAZY_COMPONENT_INNER]() as AnyLazyComponentType<P>;
return WrapperComponent; return WrapperComponent;
} }
@ -312,20 +311,20 @@ export function findComponent<T extends object = any>(filter: FilterFn, parse: (
* @param parse A function that takes the found component as its first argument and returns a component. Useful if you want to wrap the found component in something. Defaults to the original component * @param parse A function that takes the found component as its first argument and returns a component. Useful if you want to wrap the found component in something. Defaults to the original component
* @returns The component if found, or a noop component * @returns The component if found, or a noop component
*/ */
export function findExportedComponent<T extends object = any>(...props: PropsFilter | [...PropsFilter, (component: ModuleExports) => LazyComponentType<T>]) { export function findExportedComponent<P extends AnyRecord>(...props: PropsFilter | [...PropsFilter, (component: ModuleExports) => React.ComponentType<P>]) {
const parse = (typeof props.at(-1) === "function" ? props.pop() : m => m) as (component: ModuleExports) => LazyComponentType<T>; const parse = (typeof props.at(-1) === "function" ? props.pop() : m => m) as (component: ModuleExports) => React.ComponentType<P>;
const newProps = props as PropsFilter; const newProps = props as PropsFilter;
const filter = filters.byProps(...newProps); const filter = filters.byProps(...newProps);
const [WrapperComponent, setInnerComponent] = wrapWebpackComponent<T>(`Webpack find matched no module. Filter: ${printFilter(filter)}`); const [WrapperComponent, setInnerComponent] = wrapWebpackComponent<P>(`Webpack find matched no module. Filter: ${printFilter(filter)}`);
waitFor(filter, m => setInnerComponent(m[newProps[0]], parse(m[newProps[0]])), { isIndirect: true }); waitFor(filter, m => setInnerComponent(m[newProps[0]], parse(m[newProps[0]])), { isIndirect: true });
if (IS_REPORTER) { if (IS_REPORTER) {
webpackSearchHistory.push(["findExportedComponent", [WrapperComponent, ...newProps]]); webpackSearchHistory.push(["findExportedComponent", [WrapperComponent, ...newProps]]);
} }
if (WrapperComponent[SYM_LAZY_COMPONENT_INNER]() != null) return WrapperComponent[SYM_LAZY_COMPONENT_INNER]() as LazyComponentType<T>; if (WrapperComponent[SYM_LAZY_COMPONENT_INNER]() != null) return WrapperComponent[SYM_LAZY_COMPONENT_INNER]() as AnyLazyComponentType<P>;
return WrapperComponent; return WrapperComponent;
} }
@ -340,11 +339,11 @@ export function findExportedComponent<T extends object = any>(...props: PropsFil
* @param parse A function that takes the found component as its first argument and returns a component. Useful if you want to wrap the found component in something. Defaults to the original component * @param parse A function that takes the found component as its first argument and returns a component. Useful if you want to wrap the found component in something. Defaults to the original component
* @returns The component if found, or a noop component * @returns The component if found, or a noop component
*/ */
export function findComponentByCode<T extends object = any>(...code: CodeFilter | [...CodeFilter, (component: ModuleExports) => LazyComponentType<T>]) { export function findComponentByCode<P extends AnyRecord>(...code: CodeFilter | [...CodeFilter, (component: ModuleExports) => React.ComponentType<P>]) {
const parse = (typeof code.at(-1) === "function" ? code.pop() : m => m) as (component: ModuleExports) => LazyComponentType<T>; const parse = (typeof code.at(-1) === "function" ? code.pop() : m => m) as (component: ModuleExports) => React.ComponentType<P>;
const newCode = code as CodeFilter; const newCode = code as CodeFilter;
const ComponentResult = findComponent<T>(filters.componentByCode(...newCode), parse, { isIndirect: true }); const ComponentResult = findComponent<P>(filters.componentByCode(...newCode), parse, { isIndirect: true });
if (IS_REPORTER) { if (IS_REPORTER) {
webpackSearchHistory.push(["findComponentByCode", [ComponentResult, ...newCode]]); webpackSearchHistory.push(["findComponentByCode", [ComponentResult, ...newCode]]);
@ -363,11 +362,11 @@ export function findComponentByCode<T extends object = any>(...code: CodeFilter
* @param parse A function that takes the found component as its first argument and returns a component. Useful if you want to wrap the found component in something. Defaults to the original component * @param parse A function that takes the found component as its first argument and returns a component. Useful if you want to wrap the found component in something. Defaults to the original component
* @returns The component if found, or a noop component * @returns The component if found, or a noop component
*/ */
export function findComponentByFields<T extends object = any>(...fields: PropsFilter | [...PropsFilter, (component: ModuleExports) => LazyComponentType<T>]) { export function findComponentByFields<P extends AnyRecord>(...fields: PropsFilter | [...PropsFilter, (component: ModuleExports) => React.ComponentType<P>]) {
const parse = (typeof fields.at(-1) === "function" ? fields.pop() : m => m) as (component: ModuleExports) => LazyComponentType<T>; const parse = (typeof fields.at(-1) === "function" ? fields.pop() : m => m) as (component: ModuleExports) => React.ComponentType<P>;
const newFields = fields as PropsFilter; const newFields = fields as PropsFilter;
const ComponentResult = findComponent<T>(filters.componentByFields(...newFields), parse, { isIndirect: true }); const ComponentResult = findComponent<P>(filters.componentByFields(...newFields), parse, { isIndirect: true });
if (IS_REPORTER) { if (IS_REPORTER) {
webpackSearchHistory.push(["findComponentByCode", [ComponentResult, ...newFields]]); webpackSearchHistory.push(["findComponentByCode", [ComponentResult, ...newFields]]);
@ -497,14 +496,14 @@ export function mapMangledModule<S extends PropertyKey>(code: CodeFilterWithSing
const mapperFilter = mappers[newName]; const mapperFilter = mappers[newName];
// Wrapper to select whether the parent factory filter or child mapper filter failed when the error is thrown // Wrapper to select whether the parent factory filter or child mapper filter failed when the error is thrown
const errorMsgWrapper = () => `Webpack mapMangledModule ${callbackCalled ? "mapper" : "factory"} filter matched no module. Filter: ${printFilter(callbackCalled ? mapperFilter : factoryFilter)}`; const errorWrapper = () => `Webpack mapMangledModule ${callbackCalled ? "mapper" : "factory"} filter matched no module. Filter: ${printFilter(callbackCalled ? mapperFilter : factoryFilter)}`;
if (mapperFilter.$$vencordIsComponentFilter) { if (mapperFilter.$$vencordIsComponentFilter) {
const [WrapperComponent, setInnerComponent] = wrapWebpackComponent(errorMsgWrapper); const [WrapperComponent, setInnerComponent] = wrapWebpackComponent(errorWrapper);
mapping[newName] = WrapperComponent; mapping[newName] = WrapperComponent;
wrapperComponentSetters[newName] = setInnerComponent; wrapperComponentSetters[newName] = setInnerComponent;
} else { } else {
const [proxy, setInnerValue] = proxyInner(errorMsgWrapper, "Webpack find with proxy called on a primitive value. This may happen if you are trying to destructure a mapMangledModule primitive value on top level."); const [proxy, setInnerValue] = proxyInner(errorWrapper, "Webpack find with proxy called on a primitive value. This may happen if you are trying to destructure a mapMangledModule primitive value on top level.");
mapping[newName] = proxy; mapping[newName] = proxy;
proxyInnerSetters[newName] = setInnerValue; proxyInnerSetters[newName] = setInnerValue;
} }
@ -604,12 +603,12 @@ export function webpackDependantLazy<T = any>(factory: () => T, attempts?: numbe
* @param attempts How many times to try to get the component before giving up * @param attempts How many times to try to get the component before giving up
* @returns Result of factory function * @returns Result of factory function
*/ */
export function webpackDependantLazyComponent<T extends object = any>(factory: () => any, attempts?: number) { export function webpackDependantLazyComponent<P extends AnyRecord>(factory: () => any, attempts?: number) {
if (IS_REPORTER) { if (IS_REPORTER) {
webpackSearchHistory.push(["webpackDependantLazyComponent", [factory]]); webpackSearchHistory.push(["webpackDependantLazyComponent", [factory]]);
} }
return LazyComponent<T>(factory, attempts, `Webpack dependant LazyComponent factory failed:\n${factory}`); return LazyComponent<P>(factory, attempts, `Webpack dependant LazyComponent factory failed:\n${factory}`);
} }
export const DefaultExtractAndLoadChunksRegex = /(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?|Promise\.resolve\(\))\.then\(\i\.bind\(\i,"?([^)]+?)"?\)\)/; export const DefaultExtractAndLoadChunksRegex = /(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?|Promise\.resolve\(\))\.then\(\i\.bind\(\i,"?([^)]+?)"?\)\)/;

View file

@ -44,6 +44,8 @@ export let Avatar: t.Avatar = NoopComponent;
export let FocusLock: t.FocusLock = NoopComponent; export let FocusLock: t.FocusLock = NoopComponent;
export let useToken: t.useToken; export let useToken: t.useToken;
export let Icons = {} as t.Icons;
export const MaskedLink = findComponentByCode<t.MaskedLinkProps>("MASKED_LINK)"); export const MaskedLink = findComponentByCode<t.MaskedLinkProps>("MASKED_LINK)");
export const Timestamp = findComponentByCode<t.TimestampProps>(".Messages.MESSAGE_EDITED_TIMESTAMP_A11Y_LABEL.format"); export const Timestamp = findComponentByCode<t.TimestampProps>(".Messages.MESSAGE_EDITED_TIMESTAMP_A11Y_LABEL.format");
export const Flex = findComponent(filters.byProps("Justify", "Align", "Wrap")) as t.Flex; export const Flex = findComponent(filters.byProps("Justify", "Align", "Wrap")) as t.Flex;
@ -76,5 +78,6 @@ export const Forms = findByProps<t.Forms>("FormItem", "Button", m => {
Heading Heading
} = m); } = m);
Icons = m;
return m; return m;
}); });

View file

@ -18,6 +18,8 @@
import type { ComponentType, CSSProperties, FunctionComponent, HtmlHTMLAttributes, HTMLProps, KeyboardEvent, MouseEvent, PropsWithChildren, PropsWithRef, ReactNode, Ref } from "react"; import type { ComponentType, CSSProperties, FunctionComponent, HtmlHTMLAttributes, HTMLProps, KeyboardEvent, MouseEvent, PropsWithChildren, PropsWithRef, ReactNode, Ref } from "react";
import { IconNames } from "./iconNames";
export type TextVariant = "heading-sm/normal" | "heading-sm/medium" | "heading-sm/semibold" | "heading-sm/bold" | "heading-md/normal" | "heading-md/medium" | "heading-md/semibold" | "heading-md/bold" | "heading-lg/normal" | "heading-lg/medium" | "heading-lg/semibold" | "heading-lg/bold" | "heading-xl/normal" | "heading-xl/medium" | "heading-xl/bold" | "heading-xxl/normal" | "heading-xxl/medium" | "heading-xxl/bold" | "eyebrow" | "heading-deprecated-14/normal" | "heading-deprecated-14/medium" | "heading-deprecated-14/bold" | "text-xxs/normal" | "text-xxs/medium" | "text-xxs/semibold" | "text-xxs/bold" | "text-xs/normal" | "text-xs/medium" | "text-xs/semibold" | "text-xs/bold" | "text-sm/normal" | "text-sm/medium" | "text-sm/semibold" | "text-sm/bold" | "text-md/normal" | "text-md/medium" | "text-md/semibold" | "text-md/bold" | "text-lg/normal" | "text-lg/medium" | "text-lg/semibold" | "text-lg/bold" | "display-sm" | "display-md" | "display-lg" | "code"; export type TextVariant = "heading-sm/normal" | "heading-sm/medium" | "heading-sm/semibold" | "heading-sm/bold" | "heading-md/normal" | "heading-md/medium" | "heading-md/semibold" | "heading-md/bold" | "heading-lg/normal" | "heading-lg/medium" | "heading-lg/semibold" | "heading-lg/bold" | "heading-xl/normal" | "heading-xl/medium" | "heading-xl/bold" | "heading-xxl/normal" | "heading-xxl/medium" | "heading-xxl/bold" | "eyebrow" | "heading-deprecated-14/normal" | "heading-deprecated-14/medium" | "heading-deprecated-14/bold" | "text-xxs/normal" | "text-xxs/medium" | "text-xxs/semibold" | "text-xxs/bold" | "text-xs/normal" | "text-xs/medium" | "text-xs/semibold" | "text-xs/bold" | "text-sm/normal" | "text-sm/medium" | "text-sm/semibold" | "text-sm/bold" | "text-md/normal" | "text-md/medium" | "text-md/semibold" | "text-md/bold" | "text-lg/normal" | "text-lg/medium" | "text-lg/semibold" | "text-lg/bold" | "display-sm" | "display-md" | "display-lg" | "code";
export type FormTextTypes = Record<"DEFAULT" | "INPUT_PLACEHOLDER" | "DESCRIPTION" | "LABEL_BOLD" | "LABEL_SELECTED" | "LABEL_DESCRIPTOR" | "ERROR" | "SUCCESS", string>; export type FormTextTypes = Record<"DEFAULT" | "INPUT_PLACEHOLDER" | "DESCRIPTION" | "LABEL_BOLD" | "LABEL_SELECTED" | "LABEL_DESCRIPTOR" | "ERROR" | "SUCCESS", string>;
export type HeadingTag = `h${1 | 2 | 3 | 4 | 5 | 6}`; export type HeadingTag = `h${1 | 2 | 3 | 4 | 5 | 6}`;
@ -76,7 +78,7 @@ export type Forms = {
}; };
export type Tooltip = ComponentType<{ export type Tooltip = ComponentType<{
text: ReactNode; text: ReactNode | ComponentType;
children: FunctionComponent<{ children: FunctionComponent<{
onClick(): void; onClick(): void;
onMouseEnter(): void; onMouseEnter(): void;
@ -516,3 +518,10 @@ export type Avatar = ComponentType<PropsWithChildren<{
type FocusLock = ComponentType<PropsWithChildren<{ type FocusLock = ComponentType<PropsWithChildren<{
containerRef: RefObject<HTMLElement>; containerRef: RefObject<HTMLElement>;
}>>; }>>;
export type Icon = AnyComponentType<React.ComponentPropsWithoutRef<"svg"> & {
size?: string;
colorClass?: string;
}>;
export type Icons = Record<IconNames, Icon>;

14
src/webpack/common/types/iconNames.d.ts vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -16,12 +16,10 @@
* 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 type { ComponentType, CSSProperties, MouseEvent, PropsWithChildren, ReactNode, UIEvent } from "react"; import type { CSSProperties, MouseEvent, ReactNode, UIEvent } from "react";
type RC<C> = ComponentType<PropsWithChildren<C & Record<PropertyKey, any>>>;
export interface Menu { export interface Menu {
Menu: RC<{ Menu: AnyComponentTypeWithChildren<{
navId: string; navId: string;
onClose(): void; onClose(): void;
className?: string; className?: string;
@ -29,31 +27,31 @@ export interface Menu {
hideScroller?: boolean; hideScroller?: boolean;
onSelect?(): void; onSelect?(): void;
}>; }>;
MenuSeparator: ComponentType; MenuSeparator: AnyComponentType;
MenuGroup: RC<{ MenuGroup: AnyComponentTypeWithChildren<{
label?: string; label?: string;
}>; }>;
MenuItem: RC<{ MenuItem: AnyComponentTypeWithChildren<{
id: string; id: string;
label: ReactNode; label: ReactNode;
action?(e: MouseEvent): void; action?(e: MouseEvent): void;
icon?: ComponentType<any>; icon?: AnyComponentType<any>;
color?: string; color?: string;
render?: ComponentType<any>; render?: AnyComponentType<any>;
onChildrenScroll?: Function; onChildrenScroll?: Function;
childRowHeight?: number; childRowHeight?: number;
listClassName?: string; listClassName?: string;
disabled?: boolean; disabled?: boolean;
}>; }>;
MenuCheckboxItem: RC<{ MenuCheckboxItem: AnyComponentTypeWithChildren<{
id: string; id: string;
label: string; label: string;
checked: boolean; checked: boolean;
action?(e: MouseEvent): void; action?(e: MouseEvent): void;
disabled?: boolean; disabled?: boolean;
}>; }>;
MenuRadioItem: RC<{ MenuRadioItem: AnyComponentTypeWithChildren<{
id: string; id: string;
group: string; group: string;
label: string; label: string;
@ -61,19 +59,19 @@ export interface Menu {
action?(e: MouseEvent): void; action?(e: MouseEvent): void;
disabled?: boolean; disabled?: boolean;
}>; }>;
MenuControlItem: RC<{ MenuControlItem: AnyComponentTypeWithChildren<{
id: string; id: string;
interactive?: boolean; interactive?: boolean;
}>; }>;
MenuSliderControl: RC<{ MenuSliderControl: AnyComponentTypeWithChildren<{
minValue: number, minValue: number,
maxValue: number, maxValue: number,
value: number, value: number,
onChange(value: number): void, onChange(value: number): void,
renderValue?(value: number): string, renderValue?(value: number): string,
}>; }>;
MenuSearchControl: RC<{ MenuSearchControl: AnyComponentTypeWithChildren<{
query: string query: string;
onChange(query: string): void; onChange(query: string): void;
placeholder?: string; placeholder?: string;
}>; }>;

View file

@ -241,7 +241,7 @@ export interface Constants {
export type ActiveView = LiteralUnion<"emoji" | "gif" | "sticker" | "soundboard", string>; export type ActiveView = LiteralUnion<"emoji" | "gif" | "sticker" | "soundboard", string>;
export interface ExpressionPickerStoreState extends Record<PropertyKey, any> { export interface ExpressionPickerStoreState extends AnyRecord {
activeView: ActiveView | null; activeView: ActiveView | null;
lastActiveView: ActiveView | null; lastActiveView: ActiveView | null;
activeViewType: any | null; activeViewType: any | null;

View file

@ -72,7 +72,7 @@ export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & {
// * }); // * });
// * @returns fromObject // * @returns fromObject
// */ // */
// es: (this: WebpackRequire, fromObject: Record<PropertyKey, any>, toObject: Record<PropertyKey, any>) => Record<PropertyKey, any>; // es: (this: WebpackRequire, fromObject: AnyRecord, toObject: AnyRecord) => AnyRecord;
/** /**
* Creates an async module. A module that exports something that is a Promise, or requires an export from an async module. * Creates an async module. A module that exports something that is a Promise, or requires an export from an async module.
* *
@ -132,7 +132,7 @@ export type WebpackRequire = ((moduleId: PropertyKey) => ModuleExports) & {
* } * }
* // exports is now { exportName: someExportedValue } (but each value is actually a getter) * // exports is now { exportName: someExportedValue } (but each value is actually a getter)
*/ */
d: (this: WebpackRequire, exports: Record<PropertyKey, any>, definiton: Record<PropertyKey, any>) => void; d: (this: WebpackRequire, exports: AnyRecord, definiton: AnyRecord) => void;
/** The chunk handlers, which are used to ensure the files of the chunks are loaded, or load if necessary */ /** The chunk handlers, which are used to ensure the files of the chunks are loaded, or load if necessary */
f: ChunkHandlers; f: ChunkHandlers;
/** /**