Improve component finding api and migrate plugins to use them

This commit is contained in:
Nuckyz 2023-11-22 02:49:08 -03:00
parent 371b5b0be8
commit ffe6512693
No known key found for this signature in database
GPG key ID: 440BF8296E1C4AD9
14 changed files with 89 additions and 57 deletions

View file

@ -24,9 +24,9 @@ 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 } from "@utils/modal"; import { ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize } from "@utils/modal";
import { LazyComponent } from "@utils/react"; import { findComponentByCodeLazy } from "@utils/react";
import { OptionType, Plugin } from "@utils/types"; import { OptionType, Plugin } from "@utils/types";
import { findByCode, findByPropsLazy } from "@webpack"; import { findByPropsLazy } from "@webpack";
import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, UserUtils } from "@webpack/common"; import { Button, Clickable, FluxDispatcher, Forms, React, Text, Tooltip, UserStore, 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";
@ -42,7 +42,7 @@ import {
} from "./components"; } from "./components";
import { openContributorModal } from "./ContributorModal"; import { openContributorModal } from "./ContributorModal";
const UserSummaryItem = LazyComponent(() => findByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers")); const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers");
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar"); const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
const UserRecord: Constructor<Partial<User>> = proxyLazy(() => UserStore.getCurrentUser().constructor) as any; const UserRecord: Constructor<Partial<User>> = proxyLazy(() => UserStore.getCurrentUser().constructor) as any;

View file

@ -17,8 +17,8 @@
*/ */
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { LazyComponent } from "@utils/react"; import { findComponentByCodeLazy } from "@utils/react";
import { find, findByPropsLazy, findStoreLazy } from "@webpack"; import { findByPropsLazy, findStoreLazy } from "@webpack";
import { useStateFromStores } from "@webpack/common"; import { useStateFromStores } from "@webpack/common";
import type { CSSProperties } from "react"; import type { CSSProperties } from "react";
@ -26,7 +26,7 @@ import { ExpandedGuildFolderStore, settings } from ".";
const ChannelRTCStore = findStoreLazy("ChannelRTCStore"); const ChannelRTCStore = findStoreLazy("ChannelRTCStore");
const Animations = findByPropsLazy("a", "animated", "useTransition"); const Animations = findByPropsLazy("a", "animated", "useTransition");
const GuildsBar = LazyComponent(() => find(m => m.type?.toString().includes('("guildsnav")'))); const GuildsBar = findComponentByCodeLazy('("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

@ -11,12 +11,11 @@ import { Devs } from "@utils/constants";
import { getTheme, Theme } from "@utils/discord"; import { getTheme, Theme } from "@utils/discord";
import { Margins } from "@utils/margins"; import { Margins } from "@utils/margins";
import { classes } from "@utils/misc"; import { classes } from "@utils/misc";
import { LazyComponent } from "@utils/react"; import { findComponentByCodeLazy } from "@utils/react";
import definePlugin, { OptionType, StartAt } from "@utils/types"; import definePlugin, { OptionType, StartAt } from "@utils/types";
import { findByCode } from "@webpack";
import { Button, Forms } from "@webpack/common"; import { Button, Forms } from "@webpack/common";
const ColorPicker = LazyComponent(() => findByCode(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR")); const ColorPicker = findComponentByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR");
const colorPresets = [ const colorPresets = [
"#1E1514", "#172019", "#13171B", "#1C1C28", "#402D2D", "#1E1514", "#172019", "#13171B", "#1C1C28", "#402D2D",

View file

@ -20,12 +20,12 @@ import { definePluginSettings, Settings } from "@api/Settings";
import { Link } from "@components/Link"; import { Link } from "@components/Link";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { isTruthy } from "@utils/guards"; import { isTruthy } from "@utils/guards";
import { useAwaiter } from "@utils/react"; import { findComponentByCodeLazy, useAwaiter } from "@utils/react";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByCodeLazy, findByPropsLazy } from "@webpack"; import { findByPropsLazy } from "@webpack";
import { ApplicationAssetUtils, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common"; import { ApplicationAssetUtils, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common";
const ActivityComponent = findByCodeLazy("onOpenGameProfile"); const ActivityComponent = findComponentByCodeLazy("onOpenGameProfile");
const ActivityClassName = findByPropsLazy("activity", "buttonColor"); const ActivityClassName = findByPropsLazy("activity", "buttonColor");
const Colors = findByPropsLazy("profileColors"); const Colors = findByPropsLazy("profileColors");

View file

@ -19,13 +19,13 @@
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";
import { findComponentByCodeLazy } from "@utils/react";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { findByCodeLazy } from "@webpack";
import { StatusSettingsStores } from "@webpack/common"; import { StatusSettingsStores } from "@webpack/common";
import style from "./style.css?managed"; import style from "./style.css?managed";
const Button = findByCodeLazy("Button.Sizes.NONE,disabled:"); const Button = findComponentByCodeLazy("Button.Sizes.NONE,disabled:");
function makeIcon(showCurrentGame?: boolean) { function makeIcon(showCurrentGame?: boolean) {
return function () { return function () {

View file

@ -22,9 +22,9 @@ import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants.js"; 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 { LazyComponent } from "@utils/react"; import { findComponentByCodeLazy } from "@utils/react";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { find, findByCode, findByPropsLazy } from "@webpack"; import { findByPropsLazy } from "@webpack";
import { import {
Button, Button,
ChannelStore, ChannelStore,
@ -45,9 +45,9 @@ const messageCache = new Map<string, {
fetched: boolean; fetched: boolean;
}>(); }>();
const Embed = LazyComponent(() => findByCode(".inlineMediaEmbed")); const Embed = findComponentByCodeLazy(".inlineMediaEmbed");
const AutoModEmbed = LazyComponent(() => findByCode(".withFooter]:", "childrenMessageContent:")); const AutoModEmbed = findComponentByCodeLazy(".withFooter]:", "childrenMessageContent:");
const ChannelMessage = LazyComponent(() => find(m => m.type?.toString()?.includes("renderSimpleAccessories)"))); const ChannelMessage = findComponentByCodeLazy("renderSimpleAccessories)");
const SearchResultClasses = findByPropsLazy("message", "searchResult"); const SearchResultClasses = findByPropsLazy("message", "searchResult");

View file

@ -10,14 +10,14 @@ import { classNameFactory } from "@api/Styles";
import { openImageModal, openUserProfile } from "@utils/discord"; import { openImageModal, openUserProfile } from "@utils/discord";
import { classes } from "@utils/misc"; import { classes } from "@utils/misc";
import { ModalRoot, ModalSize, openModal } from "@utils/modal"; import { ModalRoot, ModalSize, openModal } from "@utils/modal";
import { LazyComponent, useAwaiter } from "@utils/react"; import { findExportedComponentLazy, useAwaiter } from "@utils/react";
import { findByProps, findByPropsLazy } from "@webpack"; import { findByPropsLazy } from "@webpack";
import { FluxDispatcher, Forms, GuildChannelStore, GuildMemberStore, moment, Parser, PresenceStore, RelationshipStore, ScrollerThin, SnowflakeUtils, TabBar, Timestamp, useEffect, UserStore, UserUtils, useState, useStateFromStores } from "@webpack/common"; import { FluxDispatcher, Forms, GuildChannelStore, GuildMemberStore, moment, Parser, PresenceStore, RelationshipStore, ScrollerThin, SnowflakeUtils, TabBar, Timestamp, useEffect, UserStore, UserUtils, useState, useStateFromStores } from "@webpack/common";
import { Guild, User } from "discord-types/general"; import { Guild, User } from "discord-types/general";
const IconUtils = findByPropsLazy("getGuildBannerURL"); const IconUtils = findByPropsLazy("getGuildBannerURL");
const IconClasses = findByPropsLazy("icon", "acronym", "childWrapper"); const IconClasses = findByPropsLazy("icon", "acronym", "childWrapper");
const FriendRow = LazyComponent(() => findByProps("FriendRow").FriendRow); const FriendRow = findExportedComponentLazy("FriendRow");
const cl = classNameFactory("vc-gp-"); const cl = classNameFactory("vc-gp-");

View file

@ -16,12 +16,12 @@
* 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 { LazyComponent } from "@utils/react"; import { findComponentByCodeLazy } from "@utils/react";
import { findByCode, findLazy } from "@webpack"; import { findLazy } from "@webpack";
import { i18n, useToken } from "@webpack/common"; import { i18n, useToken } from "@webpack/common";
const ColorMap = findLazy(m => m.colors?.INTERACTIVE_MUTED?.css); const ColorMap = findLazy(m => m.colors?.INTERACTIVE_MUTED?.css);
const VerifiedIconComponent = LazyComponent(() => findByCode(".CONNECTIONS_ROLE_OFFICIAL_ICON_TOOLTIP")); const VerifiedIconComponent = findComponentByCodeLazy(".CONNECTIONS_ROLE_OFFICIAL_ICON_TOOLTIP");
export function VerifiedIcon() { export function VerifiedIcon() {
const color = useToken(ColorMap.colors.INTERACTIVE_MUTED).hex(); const color = useToken(ColorMap.colors.INTERACTIVE_MUTED).hex();

View file

@ -24,15 +24,15 @@ import { Flex } from "@components/Flex";
import { CopyIcon, LinkIcon } from "@components/Icons"; import { CopyIcon, LinkIcon } from "@components/Icons";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { copyWithToast } from "@utils/misc"; import { copyWithToast } from "@utils/misc";
import { LazyComponent } from "@utils/react"; import { findComponentByCodeLazy } from "@utils/react";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByCode, findByCodeLazy, findByPropsLazy, findStoreLazy } from "@webpack"; import { findByCodeLazy, findByPropsLazy, findStoreLazy } from "@webpack";
import { Text, Tooltip, UserProfileStore } from "@webpack/common"; import { Text, Tooltip, UserProfileStore } from "@webpack/common";
import { User } from "discord-types/general"; import { User } from "discord-types/general";
import { VerifiedIcon } from "./VerifiedIcon"; import { VerifiedIcon } from "./VerifiedIcon";
const Section = LazyComponent(() => findByCode(".lastSection]:")); const Section = findComponentByCodeLazy(".lastSection]: ");
const ThemeStore = findStoreLazy("ThemeStore"); const ThemeStore = findStoreLazy("ThemeStore");
const platforms: { get(type: string): ConnectionPlatform; } = findByPropsLazy("isSupported", "getByUrl"); const platforms: { get(type: string): ConnectionPlatform; } = findByPropsLazy("isSupported", "getByUrl");
const getTheme: (user: User, displayProfile: any) => any = findByCodeLazy(',"--profile-gradient-primary-color"'); const getTheme: (user: User, displayProfile: any) => any = findByCodeLazy(',"--profile-gradient-primary-color"');

View file

@ -18,9 +18,9 @@
import { Settings } from "@api/Settings"; import { Settings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { LazyComponent } from "@utils/react"; import { findComponentByCodeLazy, findComponentLazy } from "@utils/react";
import { formatDuration } from "@utils/text"; import { formatDuration } from "@utils/text";
import { find, findByCode, findByPropsLazy } from "@webpack"; import { findByPropsLazy } from "@webpack";
import { EmojiStore, FluxDispatcher, GuildMemberStore, GuildStore, moment, Parser, PermissionsBits, PermissionStore, SnowflakeUtils, Text, Timestamp, Tooltip, useEffect, useState } from "@webpack/common"; import { EmojiStore, FluxDispatcher, GuildMemberStore, GuildStore, moment, Parser, PermissionsBits, PermissionStore, SnowflakeUtils, Text, Timestamp, Tooltip, useEffect, useState } from "@webpack/common";
import type { Channel } from "discord-types/general"; import type { Channel } from "discord-types/general";
@ -81,14 +81,14 @@ const enum ChannelFlags {
const ChatScrollClasses = findByPropsLazy("auto", "content", "scrollerBase"); const ChatScrollClasses = findByPropsLazy("auto", "content", "scrollerBase");
const ChatClasses = findByPropsLazy("chat", "content", "noChat", "chatContent"); const ChatClasses = findByPropsLazy("chat", "content", "noChat", "chatContent");
const ChannelBeginHeader = LazyComponent(() => findByCode(".Messages.ROLE_REQUIRED_SINGLE_USER_MESSAGE")); const ChannelBeginHeader = findComponentByCodeLazy(".Messages.ROLE_REQUIRED_SINGLE_USER_MESSAGE");
const TagComponent = LazyComponent(() => find(m => { const TagComponent = findComponentLazy(m => {
if (typeof m !== "function") return false; if (typeof m !== "function") return false;
const code = Function.prototype.toString.call(m); const code = Function.prototype.toString.call(m);
// Get the component which doesn't include increasedActivity // Get the component which doesn't include increasedActivity
return code.includes(".Messages.FORUM_TAG_A11Y_FILTER_BY_TAG") && !code.includes("increasedActivityPill"); return code.includes(".Messages.FORUM_TAG_A11Y_FILTER_BY_TAG") && !code.includes("increasedActivityPill");
})); });
const EmojiParser = findByPropsLazy("convertSurrogateToName"); const EmojiParser = findByPropsLazy("convertSurrogateToName");
const EmojiUtils = findByPropsLazy("getURL", "buildEmojiReactionColorsPlatformed"); const EmojiUtils = findByPropsLazy("getURL", "buildEmojiReactionColorsPlatformed");

View file

@ -16,8 +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 { LazyComponent, useTimer } from "@utils/react"; import { findComponentByCodeLazy, useTimer } from "@utils/react";
import { find } from "@webpack";
import { cl } from "./utils"; import { cl } from "./utils";
@ -25,7 +24,7 @@ interface VoiceMessageProps {
src: string; src: string;
waveform: string; waveform: string;
} }
const VoiceMessage = LazyComponent<VoiceMessageProps>(() => find(m => m.type?.toString().includes("waveform:"))); const VoiceMessage = findComponentByCodeLazy<VoiceMessageProps>("waveform:");
export type VoicePreviewOptions = { export type VoicePreviewOptions = {
src?: string; src?: string;

View file

@ -20,14 +20,14 @@ import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { sleep } from "@utils/misc"; import { sleep } from "@utils/misc";
import { Queue } from "@utils/Queue"; import { Queue } from "@utils/Queue";
import { LazyComponent, useForceUpdater } from "@utils/react"; import { findComponentByCodeLazy, useForceUpdater } from "@utils/react";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { findByCode, findByPropsLazy } from "@webpack"; import { findByPropsLazy } from "@webpack";
import { ChannelStore, FluxDispatcher, React, RestAPI, Tooltip } from "@webpack/common"; import { ChannelStore, FluxDispatcher, React, RestAPI, Tooltip } from "@webpack/common";
import { CustomEmoji } from "@webpack/types"; import { CustomEmoji } from "@webpack/types";
import { Message, ReactionEmoji, User } from "discord-types/general"; import { Message, ReactionEmoji, User } from "discord-types/general";
const UserSummaryItem = LazyComponent(() => findByCode("defaultRenderUser", "showDefaultAvatarsForNullUsers")); const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers");
const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar"); const AvatarStyles = findByPropsLazy("moreUsers", "emptyUser", "avatarContainer", "clickableAvatar");
const queue = new Queue(); const queue = new Queue();

View file

@ -16,6 +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 { FilterFn, filters, find, findByProps } from "@webpack";
import { React, useEffect, useMemo, useReducer, useState } from "@webpack/common"; import { React, useEffect, useMemo, useReducer, useState } from "@webpack/common";
import { makeLazy } from "./lazy"; import { makeLazy } from "./lazy";
@ -77,7 +78,6 @@ interface AwaiterOpts<T> {
* @param fallbackValue The fallback value that will be used until the promise resolved * @param fallbackValue The fallback value that will be used until the promise resolved
* @returns [value, error, isPending] * @returns [value, error, isPending]
*/ */
export function useAwaiter<T>(factory: () => Promise<T>): AwaiterRes<T | null>; export function useAwaiter<T>(factory: () => Promise<T>): AwaiterRes<T | null>;
export function useAwaiter<T>(factory: () => Promise<T>, providedOpts: AwaiterOpts<T>): AwaiterRes<T>; export function useAwaiter<T>(factory: () => Promise<T>, providedOpts: AwaiterOpts<T>): AwaiterRes<T>;
export function useAwaiter<T>(factory: () => Promise<T>, providedOpts?: AwaiterOpts<T | null>): AwaiterRes<T | null> { export function useAwaiter<T>(factory: () => Promise<T>, providedOpts?: AwaiterOpts<T | null>): AwaiterRes<T | null> {
@ -113,31 +113,16 @@ export function useAwaiter<T>(factory: () => Promise<T>, providedOpts?: AwaiterO
return [state.value, state.error, state.pending]; return [state.value, state.error, state.pending];
} }
/** /**
* Returns a function that can be used to force rerender react components * Returns a function that can be used to force rerender react components
*/ */
export function useForceUpdater(): () => void; export function useForceUpdater(): () => void;
export function useForceUpdater(withDep: true): [unknown, () => void]; export function useForceUpdater(withDep: true): [unknown, () => void];
export function useForceUpdater(withDep?: true) { export function useForceUpdater(withDep?: true) {
const r = useReducer(x => x + 1, 0); const r = useReducer(x => x + 1, 0);
return withDep ? r : r[1]; return withDep ? r : r[1];
} }
/**
* A lazy component. The factory method is called on first render. For example useful
* for const Component = LazyComponent(() => findByDisplayName("...").default)
* @param factory Function returning a Component
* @param attempts How many times to try to get the component before giving up
* @returns Result of factory function
*/
export function LazyComponent<T extends object = any>(factory: () => React.ComponentType<T>, attempts = 5) {
const get = makeLazy(factory, attempts);
return (props: T) => {
const Component = get() ?? NoopComponent;
return <Component {...props} />;
};
}
interface TimerOpts { interface TimerOpts {
interval?: number; interval?: number;
@ -159,3 +144,52 @@ export function useTimer({ interval = 1000, deps = [] }: TimerOpts) {
return time; return time;
} }
/**
* Finds the component which includes all the given code. Checks for plain components, memos and forwardRefs
*/
export function findComponentByCode(...code: string[]) {
const filter = filters.byCode(...code);
return find(m => {
if (filter(m)) return true;
if (!m.$$typeof) return false;
if (m.type) return filter(m.type); // memos
if (m.render) return filter(m.render); // forwardRefs
return false;
}) ?? NoopComponent;
}
/**
* Finds the first component that matches the filter, lazily.
*/
export function findComponentLazy<T extends object = any>(filter: FilterFn) {
return LazyComponent<T>(() => find(filter));
}
/**
* Finds the first component that includes all the given code, lazily
*/
export function findComponentByCodeLazy<T extends object = any>(...code: string[]) {
return LazyComponent<T>(() => findComponentByCode(...code));
}
/**
* Finds the first component that is exported by the first prop name, lazily
*/
export function findExportedComponentLazy<T extends object = any>(...props: string[]) {
return LazyComponent<T>(() => findByProps(...props)?.[props[0]]);
}
/**
* A lazy component. The factory method is called on first render.
* @param factory Function returning a Component
* @param attempts How many times to try to get the component before giving up
* @returns Result of factory function
*/
export function LazyComponent<T extends object = any>(factory: () => React.ComponentType<T>, attempts = 5) {
const get = makeLazy(factory, attempts);
return (props: T) => {
const Component = get() ?? NoopComponent;
return <Component {...props} />;
};
}

View file

@ -386,7 +386,7 @@ export function findStore(name: string) {
} }
/** /**
* findByDisplayName but lazy * findStore but lazy
*/ */
export function findStoreLazy(name: string) { export function findStoreLazy(name: string) {
return proxyLazy(() => findStore(name)); return proxyLazy(() => findStore(name));