Add React eslint & update depencenies (#3090)

Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com>
This commit is contained in:
v 2025-01-04 07:24:50 +01:00 committed by GitHub
parent 16a1c44947
commit 7be3a40b7c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
56 changed files with 2025 additions and 1529 deletions

View file

@ -34,6 +34,7 @@ import { makeCodeblock } from "@utils/text";
import definePlugin from "@utils/types";
import { checkForUpdates, isOutdated, update } from "@utils/updater";
import { Alerts, Button, Card, ChannelStore, Forms, GuildMemberStore, Parser, RelationshipStore, showToast, Text, Toasts, UserStore } from "@webpack/common";
import { JSX } from "react";
import gitHash from "~git-hash";
import plugins, { PluginMeta } from "~plugins";

View file

@ -99,7 +99,7 @@ export const UnknownIcon = (props: React.PropsWithChildren<SVGProps<SVGSVGElemen
fill="currentColor"
viewBox="0 0 16 16"
>
<path fill-rule="evenodd" d="M4.475 5.458c-.284 0-.514-.237-.47-.517C4.28 3.24 5.576 2 7.825 2c2.25 0 3.767 1.36 3.767 3.215 0 1.344-.665 2.288-1.79 2.973-1.1.659-1.414 1.118-1.414 2.01v.03a.5.5 0 0 1-.5.5h-.77a.5.5 0 0 1-.5-.495l-.003-.2c-.043-1.221.477-2.001 1.645-2.712 1.03-.632 1.397-1.135 1.397-2.028 0-.979-.758-1.698-1.926-1.698-1.009 0-1.71.529-1.938 1.402-.066.254-.278.461-.54.461h-.777ZM7.496 14c.622 0 1.095-.474 1.095-1.09 0-.618-.473-1.092-1.095-1.092-.606 0-1.087.474-1.087 1.091S6.89 14 7.496 14Z" />
<path fillRule="evenodd" d="M4.475 5.458c-.284 0-.514-.237-.47-.517C4.28 3.24 5.576 2 7.825 2c2.25 0 3.767 1.36 3.767 3.215 0 1.344-.665 2.288-1.79 2.973-1.1.659-1.414 1.118-1.414 2.01v.03a.5.5 0 0 1-.5.5h-.77a.5.5 0 0 1-.5-.495l-.003-.2c-.043-1.221.477-2.001 1.645-2.712 1.03-.632 1.397-1.135 1.397-2.028 0-.979-.758-1.698-1.926-1.698-1.009 0-1.71.529-1.938 1.402-.066.254-.278.461-.54.461h-.777ZM7.496 14c.622 0 1.095-.474 1.095-1.09 0-.618-.473-1.092-1.095-1.092-.606 0-1.087.474-1.087 1.091S6.89 14 7.496 14Z" />
</svg>
);

View file

@ -173,7 +173,7 @@ export default definePlugin({
}
return this;
},
map(render: (item: SettingsEntry) => ReactElement) {
map(render: (item: SettingsEntry) => ReactElement<any>) {
return items
.filter(a => a.items.length > 0)
.map(({ label, items }) => {
@ -181,11 +181,14 @@ export default definePlugin({
if (label) {
return (
<Menu.MenuItem
key={label}
id={label.replace(/\W/, "_")}
label={label}
children={children}
action={children[0].props.action}
/>);
>
{children}
</Menu.MenuItem>
);
} else {
return children;
}

View file

@ -133,7 +133,10 @@ function makeShortcuts() {
});
}
Common.ReactDOM.render(Common.React.createElement(component, props), doc.body.appendChild(document.createElement("div")));
const root = Common.ReactDOM.createRoot(doc.body.appendChild(document.createElement("div")));
root.render(Common.React.createElement(component, props));
doc.addEventListener("close", () => root.unmount(), { once: true });
},
preEnable: (plugin: string) => (Vencord.Settings.plugins[plugin] ??= { enabled: true }).enabled = true,

View file

@ -5,6 +5,7 @@
*/
import { React } from "@webpack/common";
import { JSX } from "react";
import { cl } from "../";

View file

@ -7,6 +7,7 @@
import { classes } from "@utils/misc";
import { findByPropsLazy } from "@webpack";
import { React } from "@webpack/common";
import { JSX } from "react";
import { cl } from "../";
import Grid, { GridProps } from "./Grid";

View file

@ -211,7 +211,7 @@ function CloneModal({ data }: { data: Sticker | Emoji; }) {
alignItems: "center"
}}>
{guilds.map(g => (
<Tooltip text={g.name}>
<Tooltip key={g.id} text={g.name}>
{({ onMouseLeave, onMouseEnter }) => (
<div
onMouseLeave={onMouseLeave}

View file

@ -514,7 +514,7 @@ export default definePlugin({
return array.filter(item => item != null);
},
ensureChildrenIsArray(child: ReactElement) {
ensureChildrenIsArray(child: ReactElement<any>) {
if (!Array.isArray(child.props.children)) child.props.children = [child.props.children];
},
@ -524,7 +524,7 @@ export default definePlugin({
let nextIndex = content.length;
const transformLinkChild = (child: ReactElement) => {
const transformLinkChild = (child: ReactElement<any>) => {
if (settings.store.transformEmojis) {
const fakeNitroMatch = child.props.href.match(fakeNitroEmojiRegex);
if (fakeNitroMatch) {
@ -558,7 +558,7 @@ export default definePlugin({
return child;
};
const transformChild = (child: ReactElement) => {
const transformChild = (child: ReactElement<any>) => {
if (child?.props?.trusted != null) return transformLinkChild(child);
if (child?.props?.children != null) {
if (!Array.isArray(child.props.children)) {
@ -574,7 +574,7 @@ export default definePlugin({
return child;
};
const modifyChild = (child: ReactElement) => {
const modifyChild = (child: ReactElement<any>) => {
const newChild = transformChild(child);
if (newChild?.type === "ul" || newChild?.type === "ol") {
@ -601,7 +601,7 @@ export default definePlugin({
return newChild;
};
const modifyChildren = (children: Array<ReactElement>) => {
const modifyChildren = (children: Array<ReactElement<any>>) => {
for (const [index, child] of children.entries()) children[index] = modifyChild(child);
children = this.clearEmptyArrayItems(children);

View file

@ -29,6 +29,7 @@ import definePlugin, { OptionType } from "@utils/types";
import { extractAndLoadChunksLazy, findComponentByCodeLazy } from "@webpack";
import { Button, Flex, Forms, React, Text, UserProfileStore, UserStore, useState } from "@webpack/common";
import { User } from "discord-types/general";
import { ReactElement } from "react";
import virtualMerge from "virtual-merge";
interface UserProfile extends User {
@ -87,7 +88,7 @@ const settings = definePluginSettings({
interface ColorPickerProps {
color: number | null;
label: React.ReactElement;
label: ReactElement<any>;
showEyeDropper?: boolean;
suggestedColors?: string[];
onChange(value: number | null): void;

View file

@ -24,6 +24,7 @@ import { debounce } from "@shared/debounce";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { Menu, ReactDOM } from "@webpack/common";
import { JSX } from "react";
import type { Root } from "react-dom/client";
import { Magnifier, MagnifierProps } from "./components/Magnifier";

View file

@ -41,6 +41,7 @@ import {
UserStore
} from "@webpack/common";
import { Channel, Message } from "discord-types/general";
import { JSX } from "react";
const messageCache = new Map<string, {
message?: Message;
@ -347,10 +348,10 @@ function AutomodEmbedAccessory(props: MessageEmbedProps): JSX.Element | null {
? parse(message.content)
: [noContent(message.attachments.length, message.embeds.length)]
}
{images.map(a => {
{images.map((a, idx) => {
const { width, height } = computeWidthAndHeight(a.width, a.height);
return (
<div>
<div key={idx}>
<img src={a.url} width={width} height={height} />
</div>
);

View file

@ -69,6 +69,7 @@ export function HistoryModal({ modalProps, message }: { modalProps: ModalProps;
{timestamps.map((timestamp, index) => (
<TabBar.Item
key={index}
className="vc-settings-tab-bar-item"
id={index}
>

View file

@ -169,8 +169,8 @@ export default definePlugin({
return Settings.plugins.MessageLogger.inlineEdits && (
<>
{message.editHistory?.map(edit => (
<div className="messagelogger-edited">
{message.editHistory?.map((edit, idx) => (
<div key={idx} className="messagelogger-edited">
{parseEditContent(edit.content, message)}
<Timestamp
timestamp={edit.timestamp}
@ -304,7 +304,7 @@ export default definePlugin({
{...props}
className={classes("messagelogger-edit-marker", className)}
onClick={() => openHistoryModal(message)}
aria-role="button"
role="button"
>
{children}
</span>

View file

@ -114,7 +114,7 @@ function SettingsComponent() {
return (
<Flex flexDirection="column">
{tags.map(t => (
<Card style={{ padding: "1em 1em 0" }}>
<Card key={t.name} style={{ padding: "1em 1em 0" }}>
<Forms.FormTitle style={{ width: "fit-content" }}>
<Tooltip text={t.description}>
{({ onMouseEnter, onMouseLeave }) => (

View file

@ -24,6 +24,7 @@ import definePlugin from "@utils/types";
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
import { Avatar, ChannelStore, Clickable, IconUtils, RelationshipStore, ScrollerThin, useMemo, UserStore } from "@webpack/common";
import { Channel, User } from "discord-types/general";
import { JSX } from "react";
const SelectedChannelActionCreators = findByPropsLazy("selectPrivateChannel");
const UserUtils = findByPropsLazy("getGlobalName");
@ -55,6 +56,7 @@ function getMutualGDMCountText(user: User) {
function renderClickableGDMs(mutualDms: Channel[], onClose: () => void) {
return mutualDms.map(c => (
<Clickable
key={c.id}
className={ProfileListClasses.listRow}
onClick={() => {
onClose();

View file

@ -113,6 +113,7 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea
return (
<div
key={index}
className={cl("modal-list-item-btn")}
onClick={() => selectItem(index)}
role="button"
@ -178,7 +179,7 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea
<div className={cl("modal-divider")} />
<ScrollerThin className={cl("modal-perms")} orientation="auto">
{Object.values(PermissionsBits).map(bit => (
<div className={cl("modal-perms-item")}>
<div key={bit} className={cl("modal-perms-item")}>
<div className={cl("modal-perms-item-icon")}>
{(() => {
const { permissions, overwriteAllow, overwriteDeny } = selectedItem;

View file

@ -192,6 +192,7 @@ function UserPermissionsComponent({ guild, guildMember, closePopout }: { guild:
<div className={classes(RoleRootClasses.root)}>
{userPermissions.map(({ permission, roleColor, roleName }) => (
<Tooltip
key={permission}
text={<GrantedByTooltip roleName={roleName} roleColor={roleColor} />}
tooltipClassName={cl("granted-by-container")}
tooltipContentClassName={cl("granted-by-content")}

View file

@ -33,7 +33,8 @@ function createPinMenuItem(channelId: string) {
{
categories.map(category => (
<Menu.MenuItem
id={`pin-category-${category.name}`}
key={category.id}
id={`pin-category-${category.id}`}
label={category.name}
action={() => addChannelToCategory(channelId, category.id).then(forceUpdate)}
/>

View file

@ -159,7 +159,7 @@ export default LazyComponent(() => {
onClick={() => openBlockModal()}
/>
)}
{review.sender.badges.map(badge => <ReviewBadge {...badge} />)}
{review.sender.badges.map((badge, idx) => <ReviewBadge key={idx} {...badge} />)}
{
!settings.store.hideTimestamps && review.type !== ReviewType.System && (
@ -170,7 +170,13 @@ export default LazyComponent(() => {
<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.substring(0, 200))}...
<br />
<a onClick={() => setShowAll(true)}>Read more</a>]
</>
)
: Parser.parseGuildEventDescription(review.comment)}
</div>

View file

@ -147,7 +147,7 @@ const ChatBarIcon: ChatBarButton = ({ isMainChat }) => {
viewBox="0 0 24 24"
style={{ scale: "1.2" }}
>
<g fill="none" fill-rule="evenodd">
<g fill="none" fillRule="evenodd">
<path fill="currentColor" d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7v-5z" />
<rect width="24" height="24" />
</g>

View file

@ -247,6 +247,7 @@ function UserList(type: "friends" | "blocked", guild: Guild, ids: string[], setC
<ScrollerThin fade className={cl("scroller")}>
{members.map(id =>
<FriendRow
key={id}
user={UserStore.getUser(id)}
status={PresenceStore.getStatus(id) || "offline"}
onSelect={() => openUserProfile(id)}

View file

@ -17,6 +17,7 @@
*/
import { Clipboard } from "@webpack/common";
import { JSX } from "react";
import { cl } from "../utils/misc";
import { CopyButton } from "./CopyButton";

View file

@ -18,6 +18,7 @@
import type { IThemedToken } from "@vap/shiki";
import { hljs } from "@webpack/common";
import { JSX } from "react";
import { cl } from "../utils/misc";
import { ThemeBase } from "./Highlighter";
@ -41,12 +42,12 @@ export const Code = ({
if (useHljs) {
try {
const { value: hljsHtml } = hljs.highlight(lang!, content, true);
const { value: hljsHtml } = hljs.highlight(content, { language: lang!, ignoreIllegals: true });
lines = hljsHtml
.split("\n")
.map((line, i) => <span key={i} dangerouslySetInnerHTML={{ __html: line }} />);
} catch {
lines = content.split("\n").map(line => <span>{line}</span>);
lines = content.split("\n").map((line, idx) => <span key={idx}>{line}</span>);
}
} else {
const renderTokens =
@ -55,11 +56,11 @@ export const Code = ({
.split("\n")
.map(line => [{ color: theme.plainColor, content: line } as IThemedToken]);
lines = renderTokens.map(line => {
lines = renderTokens.map((line, idx) => {
// [Cynthia] this makes it so when you highlight the codeblock
// empty lines are also selected and copied when you Ctrl+C.
if (line.length === 0) {
return <span>{"\n"}</span>;
return <span key={idx}>{"\n"}</span>;
}
return (

View file

@ -97,7 +97,7 @@ function ConnectionsComponent({ id, theme }: { id: string, theme: string; }) {
gap: getSpacingPx(settings.store.iconSpacing),
flexWrap: "wrap"
}}>
{connections.map(connection => <CompactConnectionComponent connection={connection} theme={theme} />)}
{connections.map(connection => <CompactConnectionComponent connection={connection} theme={theme} key={connection.id} />)}
</Flex>
);
}
@ -137,6 +137,7 @@ function CompactConnectionComponent({ connection, theme }: { connection: Connect
className="vc-user-connection"
href={url}
target="_blank"
rel="noreferrer"
onClick={e => {
if (Vencord.Plugins.isPluginEnabled("OpenInApp")) {
const OpenInApp = Vencord.Plugins.plugins.OpenInApp as any as typeof import("../openInApp").default;

View file

@ -268,7 +268,7 @@ function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) {
<div className="shc-lock-screen-tags-container">
<Text variant="text-lg/bold">Available tags:</Text>
<div className="shc-lock-screen-tags">
{availableTags.map(tag => <TagComponent tag={tag} />)}
{availableTags.map(tag => <TagComponent tag={tag} key={tag.id} />)}
</div>
</div>
}

View file

@ -84,13 +84,12 @@ export default definePlugin({
],
TooltipWrapper: ErrorBoundary.wrap(({ message, children, text }: { message: Message; children: FunctionComponent<any>; text: ReactNode; }) => {
if (settings.store.displayStyle === DisplayStyle.Tooltip) return <Tooltip
children={children}
text={renderTimeout(message, false)}
/>;
if (settings.store.displayStyle === DisplayStyle.Tooltip)
return <Tooltip text={renderTimeout(message, false)}>{children}</Tooltip>;
return (
<div className="vc-std-wrapper">
<Tooltip text={text} children={children} />
<Tooltip text={text}>{children}</Tooltip>
<Text variant="text-md/normal" color="status-danger">
{renderTimeout(message, true)} timeout remaining
</Text>

View file

@ -78,7 +78,7 @@ const SilentMessageToggle: ChatBarButton = ({ isMainChat }) => {
{!enabled && <>
<mask id="vc-silent-msg-mask">
<path fill="#fff" d="M0 0h24v24H0Z" />
<path stroke="#000" stroke-width="5.99068" d="M0 24 24 0" />
<path stroke="#000" strokeWidth="5.99068" d="M0 24 24 0" />
</mask>
<path fill="var(--status-danger)" d="m21.178 1.70703 1.414 1.414L4.12103 21.593l-1.414-1.415L21.178 1.70703Z" />
</>}

View file

@ -120,8 +120,8 @@ function ServerTrace({ trace }: ServerTraceProps) {
<Forms.FormSection title="Server Trace" tag="h2">
<code>
<Flex flexDirection="column" style={{ color: "var(--header-primary)", gap: 5, userSelect: "text" }}>
{lines.map(line => (
<span>{line}</span>
{lines.map((line, idx) => (
<span key={idx}>{line}</span>
))}
</Flex>
</code>

View file

@ -23,6 +23,7 @@ import { openUserProfile } from "@utils/discord";
import definePlugin, { OptionType } from "@utils/types";
import { Avatar, GuildMemberStore, React, RelationshipStore } from "@webpack/common";
import { User } from "discord-types/general";
import { PropsWithChildren } from "react";
const settings = definePluginSettings({
showAvatars: {
@ -100,7 +101,7 @@ export default definePlugin({
{
// Style the indicator and add function call to modify the children before rendering
match: /(?<=children:\[(\i)\.length>0.{0,200}?"aria-atomic":!0,children:)\i(?<=guildId:(\i).+?)/,
replace: "$self.mutateChildren($2,$1,$&),style:$self.TYPING_TEXT_STYLE"
replace: "$self.renderTypingUsers({ users: $1, guildId: $2, children: $& }),style:$self.TYPING_TEXT_STYLE"
},
{
// Changes the indicator to keep the user object when creating the list of typing users
@ -125,7 +126,7 @@ export default definePlugin({
buildSeveralUsers,
mutateChildren(guildId: any, users: User[], children: any) {
renderTypingUsers: ErrorBoundary.wrap(({ guildId, users, children }: PropsWithChildren<{ guildId: string, users: User[]; }>) => {
try {
if (!Array.isArray(children)) {
return children;
@ -133,15 +134,17 @@ export default definePlugin({
let element = 0;
return children.map(c =>
c.type === "strong" || (typeof c !== "string" && !React.isValidElement(c))
? <TypingUser guildId={guildId} user={users[element++]} />
: c
);
return children.map(c => {
if (c.type !== "strong" && !(typeof c !== "string" && !React.isValidElement(c)))
return c;
const user = users[element++];
return <TypingUser key={user.id} guildId={guildId} user={user} />;
});
} catch (e) {
console.error(e);
}
return children;
}
}, { noop: true })
});

View file

@ -25,6 +25,7 @@ import { wordsToTitle } from "@utils/text";
import definePlugin, { OptionType, PluginOptionsItem, ReporterTestable } from "@utils/types";
import { findByPropsLazy } from "@webpack";
import { Button, ChannelStore, Forms, GuildMemberStore, SelectedChannelStore, SelectedGuildStore, useMemo, UserStore } from "@webpack/common";
import { ReactElement } from "react";
interface VoiceState {
userId: string;
@ -289,7 +290,7 @@ export default definePlugin({
description: "Undeafen Message (only self for now)",
default: "{{USER}} undeafened"
}
};
} satisfies Record<string, PluginOptionsItem>;
},
settingsAboutComponent({ tempSettings: s }) {
@ -303,7 +304,7 @@ export default definePlugin({
[],
);
let errorComponent: React.ReactElement | null = null;
let errorComponent: ReactElement<any> | null = null;
if (!hasVoices) {
let error = "No narrator voices found. ";
error += navigator.platform?.toLowerCase().includes("linux")

View file

@ -23,7 +23,7 @@ import { Queue } from "@utils/Queue";
import { useForceUpdater } from "@utils/react";
import definePlugin from "@utils/types";
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
import { ChannelStore, Constants, FluxDispatcher, React, RestAPI, Tooltip } from "@webpack/common";
import { ChannelStore, Constants, FluxDispatcher, React, RestAPI, Tooltip, useEffect, useLayoutEffect } from "@webpack/common";
import { CustomEmoji } from "@webpack/types";
import { Message, ReactionEmoji, User } from "discord-types/general";
@ -134,18 +134,21 @@ export default definePlugin({
renderUsers(props: RootObject) {
return props.message.reactions.length > 10 ? null : (
<ErrorBoundary noop>
<this._renderUsers {...props} />
<this.UsersComponent {...props} />
</ErrorBoundary>
);
},
_renderUsers({ message, emoji, type }: RootObject) {
UsersComponent({ message, emoji, type }: RootObject) {
const forceUpdate = useForceUpdater();
React.useLayoutEffect(() => { // bc need to prevent autoscrolling
useLayoutEffect(() => { // bc need to prevent autoscrolling
if (Scroll?.scrollCounter > 0) {
Scroll.setAutomaticAnchor(null);
}
});
React.useEffect(() => {
useEffect(() => {
const cb = (e: any) => {
if (e.messageId === message.id)
forceUpdate();
@ -153,7 +156,7 @@ export default definePlugin({
FluxDispatcher.subscribe("MESSAGE_REACTION_ADD_USERS", cb);
return () => FluxDispatcher.unsubscribe("MESSAGE_REACTION_ADD_USERS", cb);
}, [message.id]);
}, [message.id, forceUpdate]);
const reactions = getReactionsWithQueue(message, emoji, type);
const users = Object.values(reactions).filter(Boolean) as User[];