Compare commits

...

17 Commits

Author SHA1 Message Date
Vendicated
897a8b615d [skip ci] add react linting 2023-10-31 23:43:28 +01:00
V
522fdcd15d WebKeyBinds: Fix & make available on ArmCord 2023-10-28 23:51:04 +02:00
Nuckyz
af135b9245 Fix AlwaysAnimate 2023-10-28 17:43:27 -03:00
Nuckyz
4b958d17fd Fix BetterFolders freeze and add new options (#1923) 2023-10-28 20:18:00 +00:00
Nuckyz
bfb48b4faf BetterFolders: Option to choose whether to keep guild icons (#1918) 2023-10-28 04:00:17 +00:00
Hugo C
9dd8e72245 fix: PiP replacing video download button (#1910) 2023-10-28 02:22:04 +02:00
AutumnVN
aae790f1c1 vencordToolbox: correct hover color + oneko (#1913) 2023-10-28 02:21:35 +02:00
AutumnVN
7f17e70697 noSystemBadge: fix (#1914) 2023-10-28 02:20:29 +02:00
Vendicated
b3311c6f12 BetterGifAltText: Fix displaying undefined 2023-10-28 02:19:43 +02:00
Nuckyz
bc09225258 Add missing patches predicates to BetterFolders 2023-10-27 20:10:44 -03:00
Nuckyz
9ce923d4d7 Fix BetterFolders 2023-10-27 19:56:11 -03:00
Nuckyz
7845af0802 Remove hacks to support no longer active versions of Discord 2023-10-27 19:55:39 -03:00
Nuckyz
89672882b9 PermViewer: Fix incorrectly displaying some role names as Unknown Role 2023-10-27 14:33:18 -03:00
Nuckyz
e05c630a54 SHC: Make Chat Input Bar channel list include hidden channels 2023-10-27 14:29:25 -03:00
Nuckyz
38834ef7ac Fix git updater 2023-10-27 13:03:52 -03:00
Luna
98d49af728 Fix git updater for other branches (#1915) 2023-10-27 12:09:39 -03:00
Susheel Thapa
0afe319141 fix typo in multiple files (#1911) 2023-10-27 12:09:38 -03:00
55 changed files with 1289 additions and 390 deletions

View File

@ -9,6 +9,7 @@
"unused-imports", "unused-imports",
"path-alias" "path-alias"
], ],
"extends": ["plugin:react/recommended", "plugin:react/jsx-runtime"],
"settings": { "settings": {
"import/resolver": { "import/resolver": {
"alias": { "alias": {
@ -20,6 +21,9 @@
["@components", "./src/components"] ["@components", "./src/components"]
] ]
} }
},
"react": {
"version": "18.2"
} }
}, },
"rules": { "rules": {
@ -93,6 +97,12 @@
"unused-imports/no-unused-imports": "error", "unused-imports/no-unused-imports": "error",
"path-alias/no-relative": "error" "path-alias/no-relative": "error",
"react/no-unescaped-entities": "off",
"react/prop-types": "off",
/* we dont target ancient browsers */
"react/jsx-no-target-blank": "off"
} }
} }

View File

@ -57,6 +57,7 @@
"eslint": "^8.46.0", "eslint": "^8.46.0",
"eslint-import-resolver-alias": "^1.1.2", "eslint-import-resolver-alias": "^1.1.2",
"eslint-plugin-path-alias": "^1.0.0", "eslint-plugin-path-alias": "^1.0.0",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-simple-import-sort": "^10.0.0", "eslint-plugin-simple-import-sort": "^10.0.0",
"eslint-plugin-unused-imports": "^2.0.0", "eslint-plugin-unused-imports": "^2.0.0",
"highlight.js": "10.6.0", "highlight.js": "10.6.0",

768
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -289,7 +289,7 @@ function runTime(token: string) {
setTimeout(() => console.log("PUPPETEER_TEST_DONE_SIGNAL"), 1000); setTimeout(() => console.log("PUPPETEER_TEST_DONE_SIGNAL"), 1000);
}, 1000)); }, 1000));
} catch (e) { } catch (e) {
console.error("[PUP_DEBUG]", "A fatal error occured"); console.error("[PUP_DEBUG]", "A fatal error occurred");
console.error("[PUP_DEBUG]", e); console.error("[PUP_DEBUG]", e);
process.exit(1); process.exit(1);
} }

View File

@ -69,7 +69,7 @@ export function addGlobalContextMenuPatch(patch: GlobalContextMenuPatchCallback)
* Remove a context menu patch * Remove a context menu patch
* @param navId The navId(s) for the context menu(s) to remove the patch * @param navId The navId(s) for the context menu(s) to remove the patch
* @param patch The patch to be removed * @param patch The patch to be removed
* @returns Wheter the patch was sucessfully removed from the context menu(s) * @returns Whether the patch was successfully removed from the context menu(s)
*/ */
export function removeContextMenuPatch<T extends string | Array<string>>(navId: T, patch: NavContextMenuPatchCallback): T extends string ? boolean : Array<boolean> { export function removeContextMenuPatch<T extends string | Array<string>>(navId: T, patch: NavContextMenuPatchCallback): T extends string ? boolean : Array<boolean> {
const navIds = Array.isArray(navId) ? navId : [navId as string]; const navIds = Array.isArray(navId) ? navId : [navId as string];
@ -82,7 +82,7 @@ export function removeContextMenuPatch<T extends string | Array<string>>(navId:
/** /**
* Remove a global context menu patch * Remove a global context menu patch
* @param patch The patch to be removed * @param patch The patch to be removed
* @returns Wheter the patch was sucessfully removed * @returns Whether the patch was successfully removed
*/ */
export function removeGlobalContextMenuPatch(patch: GlobalContextMenuPatchCallback): boolean { export function removeGlobalContextMenuPatch(patch: GlobalContextMenuPatchCallback): boolean {
return globalPatches.delete(patch); return globalPatches.delete(patch);

View File

@ -21,7 +21,7 @@ import "./styles.css";
import { useSettings } from "@api/Settings"; import { useSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { classes } from "@utils/misc"; import { classes } from "@utils/misc";
import { React, useEffect, useMemo, useState, useStateFromStores, WindowStore } from "@webpack/common"; import { useEffect, useMemo, useState, useStateFromStores, WindowStore } from "@webpack/common";
import { NotificationData } from "./Notifications"; import { NotificationData } from "./Notifications";

View File

@ -80,10 +80,7 @@ const ErrorBoundary = LazyComponent(() => {
if (this.props.noop) return null; if (this.props.noop) return null;
if (this.props.fallback) if (this.props.fallback)
return <this.props.fallback return <this.props.fallback {...this.state}>{this.props.children}</this.props.fallback>;
children={this.props.children}
{...this.state}
/>;
const msg = this.props.message || "An error occurred while rendering this Component. More info can be found below and in your console."; const msg = this.props.message || "An error occurred while rendering this Component. More info can be found below and in your console.";

View File

@ -27,7 +27,7 @@ export function Heart() {
> >
<path <path
fill="#db61a2" fill="#db61a2"
fill-rule="evenodd" fillRule="evenodd"
d="M4.25 2.5c-1.336 0-2.75 1.164-2.75 3 0 2.15 1.58 4.144 3.365 5.682A20.565 20.565 0 008 13.393a20.561 20.561 0 003.135-2.211C12.92 9.644 14.5 7.65 14.5 5.5c0-1.836-1.414-3-2.75-3-1.373 0-2.609.986-3.029 2.456a.75.75 0 01-1.442 0C6.859 3.486 5.623 2.5 4.25 2.5zM8 14.25l-.345.666-.002-.001-.006-.003-.018-.01a7.643 7.643 0 01-.31-.17 22.075 22.075 0 01-3.434-2.414C2.045 10.731 0 8.35 0 5.5 0 2.836 2.086 1 4.25 1 5.797 1 7.153 1.802 8 3.02 8.847 1.802 10.203 1 11.75 1 13.914 1 16 2.836 16 5.5c0 2.85-2.045 5.231-3.885 6.818a22.08 22.08 0 01-3.744 2.584l-.018.01-.006.003h-.002L8 14.25zm0 0l.345.666a.752.752 0 01-.69 0L8 14.25z" d="M4.25 2.5c-1.336 0-2.75 1.164-2.75 3 0 2.15 1.58 4.144 3.365 5.682A20.565 20.565 0 008 13.393a20.561 20.561 0 003.135-2.211C12.92 9.644 14.5 7.65 14.5 5.5c0-1.836-1.414-3-2.75-3-1.373 0-2.609.986-3.029 2.456a.75.75 0 01-1.442 0C6.859 3.486 5.623 2.5 4.25 2.5zM8 14.25l-.345.666-.002-.001-.006-.003-.018-.01a7.643 7.643 0 01-.31-.17 22.075 22.075 0 01-3.434-2.414C2.045 10.731 0 8.35 0 5.5 0 2.836 2.086 1 4.25 1 5.797 1 7.153 1.802 8 3.02 8.847 1.802 10.203 1 11.75 1 13.914 1 16 2.836 16 5.5c0 2.85-2.045 5.231-3.885 6.818a22.08 22.08 0 01-3.744 2.584l-.018.01-.006.003h-.002L8 14.25zm0 0l.345.666a.752.752 0 01-.69 0L8 14.25z"
/> />
</svg> </svg>

View File

@ -58,7 +58,7 @@ export function LinkIcon({ height = 24, width = 24, className }: IconProps) {
className={classes(className, "vc-link-icon")} className={classes(className, "vc-link-icon")}
viewBox="0 0 24 24" viewBox="0 0 24 24"
> >
<g fill="none" fill-rule="evenodd"> <g fill="none" fillRule="evenodd">
<path fill="currentColor" d="M10.59 13.41c.41.39.41 1.03 0 1.42-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0 5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.982 2.982 0 0 0 0-4.24 2.982 2.982 0 0 0-4.24 0l-3.53 3.53a2.982 2.982 0 0 0 0 4.24zm2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0 5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.982 2.982 0 0 0 0 4.24 2.982 2.982 0 0 0 4.24 0l3.53-3.53a2.982 2.982 0 0 0 0-4.24.973.973 0 0 1 0-1.42z" /> <path fill="currentColor" d="M10.59 13.41c.41.39.41 1.03 0 1.42-.39.39-1.03.39-1.42 0a5.003 5.003 0 0 1 0-7.07l3.54-3.54a5.003 5.003 0 0 1 7.07 0 5.003 5.003 0 0 1 0 7.07l-1.49 1.49c.01-.82-.12-1.64-.4-2.42l.47-.48a2.982 2.982 0 0 0 0-4.24 2.982 2.982 0 0 0-4.24 0l-3.53 3.53a2.982 2.982 0 0 0 0 4.24zm2.82-4.24c.39-.39 1.03-.39 1.42 0a5.003 5.003 0 0 1 0 7.07l-3.54 3.54a5.003 5.003 0 0 1-7.07 0 5.003 5.003 0 0 1 0-7.07l1.49-1.49c-.01.82.12 1.64.4 2.43l-.47.47a2.982 2.982 0 0 0 0 4.24 2.982 2.982 0 0 0 4.24 0l3.53-3.53a2.982 2.982 0 0 0 0-4.24.973.973 0 0 1 0-1.42z" />
<rect width={width} height={height} /> <rect width={width} height={height} />
</g> </g>

View File

@ -30,7 +30,7 @@ import { ChangeList } from "@utils/ChangeList";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import { Margins } from "@utils/margins"; import { Margins } from "@utils/margins";
import { classes, isObjectEmpty } from "@utils/misc"; import { classes, isObjectEmpty } from "@utils/misc";
import { openModalLazy } from "@utils/modal"; import { openModal } from "@utils/modal";
import { useAwaiter } from "@utils/react"; import { useAwaiter } from "@utils/react";
import { Plugin } from "@utils/types"; import { Plugin } from "@utils/types";
import { findByPropsLazy } from "@webpack"; import { findByPropsLazy } from "@webpack";
@ -95,12 +95,14 @@ export function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, on
const isEnabled = () => settings.enabled ?? false; const isEnabled = () => settings.enabled ?? false;
function openModal() { function openPluginModal() {
openModalLazy(async () => { openModal(modalProps => (
return modalProps => { <PluginModal
return <PluginModal {...modalProps} plugin={plugin} onRestartNeeded={() => onRestartNeeded(plugin.name)} />; {...modalProps}
}; plugin={plugin}
}); onRestartNeeded={() => onRestartNeeded(plugin.name)}
/>
));
} }
function toggleEnabled() { function toggleEnabled() {
@ -159,7 +161,7 @@ export function PluginCard({ plugin, disabled, onRestartNeeded, onMouseEnter, on
onMouseEnter={onMouseEnter} onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave} onMouseLeave={onMouseLeave}
infoButton={ infoButton={
<button role="switch" onClick={() => openModal()} className={classes(ButtonClasses.button, cl("info-button"))}> <button role="switch" onClick={() => openPluginModal()} className={classes(ButtonClasses.button, cl("info-button"))}>
{plugin.options && !isObjectEmpty(plugin.options) {plugin.options && !isObjectEmpty(plugin.options)
? <CogWheel /> ? <CogWheel />
: <InfoIcon />} : <InfoIcon />}
@ -352,7 +354,7 @@ function makeDependencyList(deps: string[]) {
return ( return (
<React.Fragment> <React.Fragment>
<Forms.FormText>This plugin is required by:</Forms.FormText> <Forms.FormText>This plugin is required by:</Forms.FormText>
{deps.map((dep: string) => <Forms.FormText className={cl("dep-text")}>{dep}</Forms.FormText>)} {deps.map((dep: string) => <Forms.FormText key={dep} className={cl("dep-text")}>{dep}</Forms.FormText>)}
</React.Fragment> </React.Fragment>
); );
} }

View File

@ -106,9 +106,9 @@ function ReplacementComponent({ module, match, replacement, setReplacementError
} }
function renderDiff() { function renderDiff() {
return diff?.map(p => { return diff?.map((p, i) => {
const color = p.added ? "lime" : p.removed ? "red" : "grey"; const color = p.added ? "lime" : p.removed ? "red" : "grey";
return <div style={{ color, userSelect: "text" }}>{p.value}</div>; return <div key={i} style={{ color, userSelect: "text" }}>{p.value}</div>;
}); });
} }

View File

@ -26,7 +26,7 @@ import { classes } from "@utils/misc";
import { showItemInFolder } from "@utils/native"; import { showItemInFolder } from "@utils/native";
import { useAwaiter } from "@utils/react"; import { useAwaiter } from "@utils/react";
import { findByPropsLazy, findLazy } from "@webpack"; import { findByPropsLazy, findLazy } from "@webpack";
import { Button, Card, FluxDispatcher, Forms, React, showToast, TabBar, TextArea, useEffect, useRef, useState } from "@webpack/common"; import { Button, Card, FluxDispatcher, Forms, showToast, TabBar, TextArea, useEffect, useRef, useState } from "@webpack/common";
import { UserThemeHeader } from "main/themes"; import { UserThemeHeader } from "main/themes";
import type { ComponentType, Ref, SyntheticEvent } from "react"; import type { ComponentType, Ref, SyntheticEvent } from "react";

View File

@ -46,7 +46,7 @@ function withDispatcher(dispatcher: React.Dispatch<React.SetStateAction<boolean>
if (code === "ENOENT") if (code === "ENOENT")
var err = `Command \`${path}\` not found.\nPlease install it and try again`; var err = `Command \`${path}\` not found.\nPlease install it and try again`;
else { else {
var err = `An error occured while running \`${cmd}\`:\n`; var err = `An error occurred while running \`${cmd}\`:\n`;
err += stderr || `Code \`${code}\`. See the console for more info`; err += stderr || `Code \`${code}\`. See the console for more info`;
} }
@ -57,7 +57,7 @@ function withDispatcher(dispatcher: React.Dispatch<React.SetStateAction<boolean>
title: "Oops!", title: "Oops!",
body: ( body: (
<ErrorCard> <ErrorCard>
{err.split("\n").map(line => <div>{Parser.parse(line)}</div>)} {err.split("\n").map((line, i) => <div key={i}>{Parser.parse(line)}</div>)}
</ErrorCard> </ErrorCard>
) )
}); });
@ -83,7 +83,7 @@ function Changes({ updates, repo, repoPending }: CommonProps & { updates: typeof
return ( return (
<Card style={{ padding: ".5em" }}> <Card style={{ padding: ".5em" }}>
{updates.map(({ hash, author, message }) => ( {updates.map(({ hash, author, message }) => (
<div> <div key={hash}>
<code><HashLink {...{ repo, hash }} disabled={repoPending} /></code> <code><HashLink {...{ repo, hash }} disabled={repoPending} /></code>
<span style={{ <span style={{
marginLeft: "0.5em", marginLeft: "0.5em",

View File

@ -49,7 +49,9 @@ async function getRepo() {
async function calculateGitChanges() { async function calculateGitChanges() {
await git("fetch"); await git("fetch");
const res = await git("log", "HEAD...origin/main", "--pretty=format:%an/%h/%s"); const branch = await git("branch", "--show-current");
const res = await git("log", `HEAD...origin/${branch.stdout.trim()}`, "--pretty=format:%an/%h/%s");
const commits = res.stdout.trim(); const commits = res.stdout.trim();
return commits ? commits.split("\n").map(line => { return commits ? commits.split("\n").map(line => {

View File

@ -21,16 +21,30 @@ import definePlugin from "@utils/types";
export default definePlugin({ export default definePlugin({
name: "AlwaysAnimate", name: "AlwaysAnimate",
description: "Animates anything that can be animated, besides status emojis.", description: "Animates anything that can be animated",
authors: [Devs.FieryFlames], authors: [Devs.FieryFlames],
patches: [ patches: [
{ {
find: ".canAnimate", find: "canAnimate:",
all: true, all: true,
// Some modules match the find but the replacement is returned untouched
noWarn: true,
replacement: { replacement: {
match: /\.canAnimate\b/g, match: /canAnimate:.+?(?=([,}].*?\)))/g,
replace: ".canAnimate || true" replace: (m, rest) => {
const destructuringMatch = rest.match(/}=.+/);
if (destructuringMatch == null) return "canAnimate:!0";
return m;
}
}
},
{
// Status emojis
find: ".Messages.GUILD_OWNER,",
replacement: {
match: /(?<=\.activityEmoji,.+?animate:)\i/,
replace: "!0"
} }
} }
] ]

View File

@ -16,56 +16,35 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { Settings } from "@api/Settings";
import { classNameFactory } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { findByPropsLazy, findStoreLazy } from "@webpack"; import { LazyComponent } from "@utils/react";
import { i18n, React, useStateFromStores } from "@webpack/common"; import { find, findByPropsLazy } from "@webpack";
import { useStateFromStores } from "@webpack/common";
const cl = classNameFactory("vc-bf-"); import { ExpandedGuildFolderStore, settings } from ".";
const classes = findByPropsLazy("sidebar", "guilds");
const Animations = findByPropsLazy("a", "animated", "useTransition"); const Animations = findByPropsLazy("a", "animated", "useTransition");
const ChannelRTCStore = findStoreLazy("ChannelRTCStore"); const GuildsBar = LazyComponent(() => find(m => m.type?.toString().includes('("guildsnav")')));
const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore");
function Guilds(props: { export default ErrorBoundary.wrap(guildsBarProps => {
className: string;
bfGuildFolders: any[];
}) {
// @ts-expect-error
const res = Vencord.Plugins.plugins.BetterFolders.Guilds(props);
// TODO: Make this better
const scrollerProps = res.props.children?.props?.children?.props?.children?.[1]?.props;
if (scrollerProps?.children) {
const servers = scrollerProps.children.find(c => c?.props?.["aria-label"] === i18n.Messages.SERVERS);
if (servers) scrollerProps.children = servers;
}
return res;
}
export default ErrorBoundary.wrap(() => {
const expandedFolders = useStateFromStores([ExpandedGuildFolderStore], () => ExpandedGuildFolderStore.getExpandedFolders()); const expandedFolders = useStateFromStores([ExpandedGuildFolderStore], () => ExpandedGuildFolderStore.getExpandedFolders());
const fullscreen = useStateFromStores([ChannelRTCStore], () => ChannelRTCStore.isFullscreenInContext());
const guilds = document.querySelector(`.${classes.guilds}`);
const visible = !!expandedFolders.size;
const className = cl("folder-sidebar", { fullscreen });
const Sidebar = ( const Sidebar = (
<Guilds <GuildsBar
className={classes.guilds} {...guildsBarProps}
bfGuildFolders={Array.from(expandedFolders)} isBetterFolders={true}
betterFoldersExpandedIds={expandedFolders}
/> />
); );
if (!guilds || !Settings.plugins.BetterFolders.sidebarAnim) const visible = !!expandedFolders.size;
const guilds = document.querySelector(guildsBarProps.className.split(" ").map(c => `.${c}`).join(""));
if (!guilds || !settings.store.sidebarAnim) {
return visible return visible
? <div className={className}>{Sidebar}</div> ? <div style={{ display: "flex " }}>{Sidebar}</div>
: null; : null;
}
return ( return (
<Animations.Transition <Animations.Transition
@ -75,11 +54,13 @@ export default ErrorBoundary.wrap(() => {
leave={{ width: 0 }} leave={{ width: 0 }}
config={{ duration: 200 }} config={{ duration: 200 }}
> >
{(style, show) => show && ( {(style, show) =>
<Animations.animated.div style={style} className={className}> show && (
<Animations.animated.div style={{ ...style, display: "flex" }}>
{Sidebar} {Sidebar}
</Animations.animated.div> </Animations.animated.div>
)} )
}
</Animations.Transition> </Animations.Transition>
); );
}, { noop: true }); }, { noop: true });

View File

@ -1,17 +0,0 @@
.vc-bf-folder-sidebar [class*="wrapper-"] > [class*="listItem-"]:first-of-type,
.vc-bf-folder-sidebar [class*="unreadMentionsIndicator"] {
display: none;
}
.vc-bf-folder-sidebar [class*="expandedFolderBackground-"] {
background-color: transparent;
}
.vc-bf-folder-sidebar {
display: flex;
}
.vc-bf-fullscreen {
width: 0 !important;
visibility: hidden;
}

View File

@ -1,177 +0,0 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2023 Vendicated and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import "./betterFolders.css";
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy, findLazy, findStoreLazy } from "@webpack";
import { FluxDispatcher } from "@webpack/common";
import FolderSideBar from "./FolderSideBar";
const GuildsTree = findLazy(m => m.prototype?.convertToFolder);
const GuildFolderStore = findStoreLazy("SortedGuildStore");
const ExpandedFolderStore = findStoreLazy("ExpandedGuildFolderStore");
const FolderUtils = findByPropsLazy("move", "toggleGuildFolderExpand");
const settings = definePluginSettings({
sidebar: {
type: OptionType.BOOLEAN,
description: "Display servers from folder on dedicated sidebar",
default: true,
},
sidebarAnim: {
type: OptionType.BOOLEAN,
description: "Animate opening the folder sidebar",
default: true,
},
closeAllFolders: {
type: OptionType.BOOLEAN,
description: "Close all folders when selecting a server not in a folder",
default: false,
},
closeAllHomeButton: {
type: OptionType.BOOLEAN,
description: "Close all folders when clicking on the home button",
default: false,
},
closeOthers: {
type: OptionType.BOOLEAN,
description: "Close other folders when opening a folder",
default: false,
},
forceOpen: {
type: OptionType.BOOLEAN,
description: "Force a folder to open when switching to a server of that folder",
default: false,
},
});
export default definePlugin({
name: "BetterFolders",
description: "Shows server folders on dedicated sidebar and adds folder related improvements",
authors: [Devs.juby, Devs.AutumnVN],
patches: [
{
find: '("guildsnav")',
predicate: () => settings.store.sidebar,
replacement: [
{
match: /(\i)\(\){return \i\(\(0,\i\.jsx\)\("div",{className:\i\(\)\.guildSeparator}\)\)}/,
replace: "$&$self.Separator=$1;"
},
// Folder component patch
{
match: /\i\(\(function\(\i,\i,\i\){var \i=\i\.key;return.+\(\i\)},\i\)}\)\)/,
replace: "arguments[0].bfHideServers?null:$&"
},
// BEGIN Guilds component patch
{
match: /(\i)\.themeOverride,(.{15,25}\(function\(\){var \i=)(\i\.\i\.getGuildsTree\(\))/,
replace: "$1.themeOverride,bfPatch=$1.bfGuildFolders,$2bfPatch?$self.getGuildsTree(bfPatch,$3):$3"
},
{
match: /return(\(0,\i\.jsx\))(\(\i,{)(folderNode:\i,setNodeRef:\i\.setNodeRef,draggable:!0,.+},\i\.id\));case/,
replace: "var bfHideServers=typeof bfPatch==='undefined',folder=$1$2bfHideServers,$3;return !bfHideServers&&arguments[1]?[$1($self.Separator,{}),folder]:folder;case"
},
// END
{
match: /\("guildsnav"\);return\(0,\i\.jsx\)\(.{1,6},{navigator:\i,children:\(0,\i\.jsx\)\(/,
replace: "$&$self.Guilds="
}
]
},
{
find: "APPLICATION_LIBRARY,render",
predicate: () => settings.store.sidebar,
replacement: {
match: /(\(0,\i\.jsx\))\(\i\..,{className:\i\(\)\.guilds,themeOverride:\i}\)/,
replace: "$&,$1($self.FolderSideBar,{})"
}
},
{
find: '("guildsnav")',
predicate: () => settings.store.closeAllHomeButton,
replacement: {
match: ",onClick:function(){if(!__OVERLAY__){",
replace: "$&$self.closeFolders();"
}
}
],
settings,
start() {
const getGuildFolder = (id: string) => GuildFolderStore.getGuildFolders().find(f => f.guildIds.includes(id));
FluxDispatcher.subscribe("CHANNEL_SELECT", this.onSwitch = data => {
if (!settings.store.closeAllFolders && !settings.store.forceOpen)
return;
if (this.lastGuildId !== data.guildId) {
this.lastGuildId = data.guildId;
const guildFolder = getGuildFolder(data.guildId);
if (guildFolder?.folderId) {
if (settings.store.forceOpen && !ExpandedFolderStore.isFolderExpanded(guildFolder.folderId))
FolderUtils.toggleGuildFolderExpand(guildFolder.folderId);
} else if (settings.store.closeAllFolders)
this.closeFolders();
}
});
FluxDispatcher.subscribe("TOGGLE_GUILD_FOLDER_EXPAND", this.onToggleFolder = e => {
if (settings.store.closeOthers && !this.dispatching)
FluxDispatcher.wait(() => {
const expandedFolders = ExpandedFolderStore.getExpandedFolders();
if (expandedFolders.size > 1) {
this.dispatching = true;
for (const id of expandedFolders) if (id !== e.folderId)
FolderUtils.toggleGuildFolderExpand(id);
this.dispatching = false;
}
});
});
},
stop() {
FluxDispatcher.unsubscribe("CHANNEL_SELECT", this.onSwitch);
FluxDispatcher.unsubscribe("TOGGLE_GUILD_FOLDER_EXPAND", this.onToggleFolder);
},
FolderSideBar,
getGuildsTree(folders, oldTree) {
const tree = new GuildsTree();
tree.root.children = oldTree.root.children.filter(e => folders.includes(e.id));
tree.nodes = folders.map(id => oldTree.nodes[id]);
return tree;
},
closeFolders() {
for (const id of ExpandedFolderStore.getExpandedFolders())
FolderUtils.toggleGuildFolderExpand(id);
},
});

View File

@ -0,0 +1,307 @@
/*
* Vencord, a modification for Discord's desktop app
* Copyright (c) 2023 Vendicated and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import { proxyLazy } from "@utils/lazy";
import definePlugin, { OptionType } from "@utils/types";
import { findByProps, findByPropsLazy, findStoreLazy } from "@webpack";
import { FluxDispatcher, i18n } from "@webpack/common";
import FolderSideBar from "./FolderSideBar";
enum FolderIconDisplay {
Never,
Always,
MoreThanOneFolderExpanded
}
const GuildsTree = proxyLazy(() => findByProps("GuildsTree").GuildsTree);
const SortedGuildStore = findStoreLazy("SortedGuildStore");
export const ExpandedGuildFolderStore = findStoreLazy("ExpandedGuildFolderStore");
const FolderUtils = findByPropsLazy("move", "toggleGuildFolderExpand");
let lastGuildId = null as string | null;
let dispatchingFoldersClose = false;
function getGuildFolder(id: string) {
return SortedGuildStore.getGuildFolders().find(folder => folder.guildIds.includes(id));
}
function closeFolders() {
for (const id of ExpandedGuildFolderStore.getExpandedFolders())
FolderUtils.toggleGuildFolderExpand(id);
}
export const settings = definePluginSettings({
sidebar: {
type: OptionType.BOOLEAN,
description: "Display servers from folder on dedicated sidebar",
restartNeeded: true,
default: true
},
sidebarAnim: {
type: OptionType.BOOLEAN,
description: "Animate opening the folder sidebar",
default: true
},
closeAllFolders: {
type: OptionType.BOOLEAN,
description: "Close all folders when selecting a server not in a folder",
default: false
},
closeAllHomeButton: {
type: OptionType.BOOLEAN,
description: "Close all folders when clicking on the home button",
restartNeeded: true,
default: false
},
closeOthers: {
type: OptionType.BOOLEAN,
description: "Close other folders when opening a folder",
default: false
},
forceOpen: {
type: OptionType.BOOLEAN,
description: "Force a folder to open when switching to a server of that folder",
default: false
},
keepIcons: {
type: OptionType.BOOLEAN,
description: "Keep showing guild icons in the primary guild bar folder when it's open in the BetterFolders sidebar",
restartNeeded: true,
default: false
},
showFolderIcon: {
type: OptionType.SELECT,
description: "Show the folder icon above the folder guilds in the BetterFolders sidebar",
options: [
{ label: "Never", value: FolderIconDisplay.Never },
{ label: "Always", value: FolderIconDisplay.Always, default: true },
{ label: "When more than one folder is expanded", value: FolderIconDisplay.MoreThanOneFolderExpanded }
],
restartNeeded: true
}
});
export default definePlugin({
name: "BetterFolders",
description: "Shows server folders on dedicated sidebar and adds folder related improvements",
authors: [Devs.juby, Devs.AutumnVN, Devs.Nuckyz],
settings,
patches: [
{
find: '("guildsnav")',
predicate: () => settings.store.sidebar,
replacement: [
// Create the isBetterFolders variable in the GuildsBar component
{
match: /(?<=let{disableAppDownload:\i=\i\.isPlatformEmbedded,isOverlay:.+?)(?=}=\i,)/,
replace: ",isBetterFolders"
},
// If we are rendering the Better Folders sidebar, we filter out guilds that are not in folders and unexpanded folders
{
match: /(useStateFromStoresArray\).{0,25}let \i)=(\i\.\i.getGuildsTree\(\))/,
replace: (_, rest, guildsTree) => `${rest}=$self.getGuildTree(!!arguments[0].isBetterFolders,${guildsTree},arguments[0].betterFoldersExpandedIds)`
},
// If we are rendering the Better Folders sidebar, we filter out everything but the servers and folders from the GuildsBar Guild List children
{
match: /lastTargetNode:\i\[\i\.length-1\].+?Fragment.+?\]}\)\]/,
replace: "$&.filter($self.makeGuildsBarGuildListFilter(!!arguments[0].isBetterFolders))"
},
// If we are rendering the Better Folders sidebar, we filter out everything but the scroller for the guild list from the GuildsBar Tree children
{
match: /unreadMentionsIndicatorBottom,barClassName.+?}\)\]/,
replace: "$&.filter($self.makeGuildsBarTreeFilter(!!arguments[0].isBetterFolders))"
},
// Export the isBetterFolders variable to the folders component
{
match: /(?<=\.Messages\.SERVERS.+?switch\((\i)\.type\){case \i\.\i\.FOLDER:.+?folderNode:\i,)/,
replace: 'isBetterFolders:typeof isBetterFolders!=="undefined"?isBetterFolders:false,'
}
]
},
{
// This is the parent folder component
find: ".MAX_GUILD_FOLDER_NAME_LENGTH,",
predicate: () => settings.store.sidebar && settings.store.showFolderIcon !== FolderIconDisplay.Always,
replacement: [
{
// Modify the expanded state to instead return the list of expanded folders
match: /(useStateFromStores\).{0,20}=>)(\i\.\i)\.isFolderExpanded\(\i\)/,
replace: (_, rest, ExpandedGuildFolderStore) => `${rest}${ExpandedGuildFolderStore}.getExpandedFolders()`,
},
{
// Modify the expanded prop to use the boolean if the above patch fails, or check if the folder is expanded from the list if it succeeds
// Also export the list of expanded folders to the child folder component if the patch above succeeds, else export undefined
match: /(?<=folderNode:(\i),expanded:)\i(?=,)/,
replace: (isExpandedOrExpandedIds, folderNote) => ""
+ `typeof ${isExpandedOrExpandedIds}==="boolean"?${isExpandedOrExpandedIds}:${isExpandedOrExpandedIds}.has(${folderNote}.id),`
+ `betterFoldersExpandedIds:${isExpandedOrExpandedIds} instanceof Set?${isExpandedOrExpandedIds}:void 0`
}
]
},
{
find: ".FOLDER_ITEM_GUILD_ICON_MARGIN);",
predicate: () => settings.store.sidebar,
replacement: [
// We use arguments[0] to access the isBetterFolders variable in this nested folder component (the parent exports all the props so we don't have to patch it)
// If we are rendering the normal GuildsBar sidebar, we make Discord think the folder is always collapsed to show better icons (the mini guild icons) and avoid transitions
{
predicate: () => settings.store.keepIcons,
match: /(?<=let{folderNode:\i,setNodeRef:\i,.+?expanded:(\i),.+?;)(?=let)/,
replace: (_, isExpanded) => `${isExpanded}=!!arguments[0].isBetterFolders&&${isExpanded};`
},
// Disable expanding and collapsing folders transition in the normal GuildsBar sidebar
{
predicate: () => !settings.store.keepIcons,
match: /(?<=\.Messages\.SERVER_FOLDER_PLACEHOLDER.+?useTransition\)\()/,
replace: "!!arguments[0].isBetterFolders&&"
},
// If we are rendering the normal GuildsBar sidebar, we avoid rendering guilds from folders that are expanded
{
predicate: () => !settings.store.keepIcons,
match: /expandedFolderBackground,.+?,(?=\i\(\(\i,\i,\i\)=>{let{key.{0,45}ul)(?<=selected:\i,expanded:(\i),.+?)/,
replace: (m, isExpanded) => `${m}!arguments[0].isBetterFolders&&${isExpanded}?null:`
},
{
// Decide if we should render the expanded folder background if we are rendering the Better Folders sidebar
predicate: () => settings.store.showFolderIcon !== FolderIconDisplay.Always,
match: /(?<=\.wrapper,children:\[)/,
replace: "$self.shouldShowFolderIconAndBackground(!!arguments[0].isBetterFolders,arguments[0].betterFoldersExpandedIds)&&"
},
{
// Decide if we should render the expanded folder icon if we are rendering the Better Folders sidebar
predicate: () => settings.store.showFolderIcon !== FolderIconDisplay.Always,
match: /(?<=\.expandedFolderBackground.+?}\),)(?=\i,)/,
replace: "!$self.shouldShowFolderIconAndBackground(!!arguments[0].isBetterFolders,arguments[0].betterFoldersExpandedIds)?null:"
}
]
},
{
find: "APPLICATION_LIBRARY,render",
predicate: () => settings.store.sidebar,
replacement: {
// Render the Better Folders sidebar
match: /(?<=({className:\i\.guilds,themeOverride:\i})\))/,
replace: ",$self.FolderSideBar($1)"
}
},
{
find: ".Messages.DISCODO_DISABLED",
predicate: () => settings.store.closeAllHomeButton,
replacement: {
// Close all folders when clicking the home button
match: /(?<=onClick:\(\)=>{)(?=.{0,200}"discodo")/,
replace: "$self.closeFolders();"
}
}
],
flux: {
CHANNEL_SELECT(data) {
if (!settings.store.closeAllFolders && !settings.store.forceOpen)
return;
if (lastGuildId !== data.guildId) {
lastGuildId = data.guildId;
const guildFolder = getGuildFolder(data.guildId);
if (guildFolder?.folderId) {
if (settings.store.forceOpen && !ExpandedGuildFolderStore.isFolderExpanded(guildFolder.folderId)) {
FolderUtils.toggleGuildFolderExpand(guildFolder.folderId);
}
} else if (settings.store.closeAllFolders) {
closeFolders();
}
}
},
TOGGLE_GUILD_FOLDER_EXPAND(data) {
if (settings.store.closeOthers && !dispatchingFoldersClose) {
dispatchingFoldersClose = true;
FluxDispatcher.wait(() => {
const expandedFolders = ExpandedGuildFolderStore.getExpandedFolders();
if (expandedFolders.size > 1) {
for (const id of expandedFolders) if (id !== data.folderId)
FolderUtils.toggleGuildFolderExpand(id);
}
dispatchingFoldersClose = false;
});
}
}
},
getGuildTree(isBetterFolders: boolean, oldTree: any, expandedFolderIds?: Set<any>) {
if (!isBetterFolders || expandedFolderIds == null) return oldTree;
const newTree = new GuildsTree();
// Children is every folder and guild which is not in a folder, this filters out only the expanded folders
newTree.root.children = oldTree.root.children.filter(guildOrFolder => expandedFolderIds.has(guildOrFolder.id));
// Nodes is every folder and guild, even if it's in a folder, this filters out only the expanded folders and guilds inside them
newTree.nodes = Object.fromEntries(
Object.entries(oldTree.nodes)
.filter(([_, guildOrFolder]: any[]) => expandedFolderIds.has(guildOrFolder.id) || expandedFolderIds.has(guildOrFolder.parentId))
);
return newTree;
},
makeGuildsBarGuildListFilter(isBetterFolders: boolean) {
return child => {
if (isBetterFolders) {
return child?.props?.["aria-label"] === i18n.Messages.SERVERS;
}
return true;
};
},
makeGuildsBarTreeFilter(isBetterFolders: boolean) {
return child => {
if (isBetterFolders) {
return "onScroll" in child.props;
}
return true;
};
},
shouldShowFolderIconAndBackground(isBetterFolders: boolean, expandedFolderIds?: Set<any>) {
if (!isBetterFolders) return true;
switch (settings.store.showFolderIcon) {
case FolderIconDisplay.Never:
return false;
case FolderIconDisplay.Always:
return true;
case FolderIconDisplay.MoreThanOneFolderExpanded:
return (expandedFolderIds?.size ?? 0) > 1;
default:
return true;
}
},
FolderSideBar: guildsBarProps => <FolderSideBar {...guildsBarProps} />,
closeFolders
});

View File

@ -45,7 +45,8 @@ export default definePlugin({
], ],
altify(props: any) { altify(props: any) {
if (props.alt && props.alt !== "GIF") return props.alt; props.alt ??= "GIF";
if (props.alt !== "GIF") return props.alt;
let url: string = props.original || props.src; let url: string = props.original || props.src;
try { try {

View File

@ -21,7 +21,6 @@ import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { useTimer } from "@utils/react"; import { useTimer } from "@utils/react";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { React } from "@webpack/common";
function formatDuration(ms: number) { function formatDuration(ms: number) {
// here be dragons (moment fucking sucks) // here be dragons (moment fucking sucks)

View File

@ -92,6 +92,7 @@ export default definePlugin({
fakeRenderWin = new WeakRef(win); fakeRenderWin = new WeakRef(win);
win.focus(); win.focus();
// eslint-disable-next-line react/no-deprecated
ReactDOM.render(React.createElement(component, props), win.document.body); ReactDOM.render(React.createElement(component, props), win.document.body);
} }
}; };

View File

@ -23,7 +23,7 @@ import { isTruthy } from "@utils/guards";
import { useAwaiter } from "@utils/react"; import { useAwaiter } from "@utils/react";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByCodeLazy, findByPropsLazy } from "@webpack"; import { findByCodeLazy, findByPropsLazy } from "@webpack";
import { ApplicationAssetUtils, FluxDispatcher, Forms, GuildStore, React, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common"; import { ApplicationAssetUtils, FluxDispatcher, Forms, GuildStore, SelectedChannelStore, SelectedGuildStore, UserStore } from "@webpack/common";
const ActivityComponent = findByCodeLazy("onOpenGameProfile"); const ActivityComponent = findByCodeLazy("onOpenGameProfile");
const ActivityClassName = findByPropsLazy("activity", "buttonColor"); const ActivityClassName = findByPropsLazy("activity", "buttonColor");
@ -395,7 +395,7 @@ export default definePlugin({
return ( return (
<> <>
<Forms.FormText> <Forms.FormText>
Go to <Link href="https://discord.com/developers/applications">Discord Deverloper Portal</Link> to create an application and Go to <Link href="https://discord.com/developers/applications">Discord Developer Portal</Link> to create an application and
get the application ID. get the application ID.
</Forms.FormText> </Forms.FormText>
<Forms.FormText> <Forms.FormText>

View File

@ -205,7 +205,7 @@ function CloneModal({ data }: { data: Sticker | Emoji; }) {
alignItems: "center" alignItems: "center"
}}> }}>
{guilds.map(g => ( {guilds.map(g => (
<Tooltip text={g.name}> <Tooltip key={g.id} text={g.name}>
{({ onMouseLeave, onMouseEnter }) => ( {({ onMouseLeave, onMouseEnter }) => (
<div <div
onMouseLeave={onMouseLeave} onMouseLeave={onMouseLeave}
@ -278,7 +278,8 @@ function buildMenuItem(type: "Emoji" | "Sticker", fetchData: () => Promisable<Om
const data = { t: type, ...res } as Sticker | Emoji; const data = { t: type, ...res } as Sticker | Emoji;
const url = getUrl(data); const url = getUrl(data);
return modalProps => ( return function EmoteClonerModal(modalProps) {
return (
<ModalRoot {...modalProps}> <ModalRoot {...modalProps}>
<ModalHeader> <ModalHeader>
<img <img
@ -297,6 +298,7 @@ function buildMenuItem(type: "Emoji" | "Sticker", fetchData: () => Promisable<Om
</ModalContent> </ModalContent>
</ModalRoot> </ModalRoot>
); );
};
}) })
} }
/> />

View File

@ -28,7 +28,7 @@ import style from "./style.css?managed";
const Button = findByCodeLazy("Button.Sizes.NONE,disabled:"); const Button = findByCodeLazy("Button.Sizes.NONE,disabled:");
function makeIcon(showCurrentGame?: boolean) { function makeIcon(showCurrentGame?: boolean) {
return function () { return function GameActivityToggle() {
return ( return (
<svg <svg
width="20" width="20"

View File

@ -343,10 +343,10 @@ function AutomodEmbedAccessory(props: MessageEmbedProps): JSX.Element | null {
? parse(message.content) ? parse(message.content)
: [noContent(message.attachments.length, message.embeds.length)] : [noContent(message.attachments.length, message.embeds.length)]
} }
{images.map(a => { {images.map((a, i) => {
const { width, height } = computeWidthAndHeight(a.width, a.height); const { width, height } = computeWidthAndHeight(a.width, a.height);
return ( return (
<div> <div key={i}>
<img src={a.url} width={width} height={height} /> <img src={a.url} width={width} height={height} />
</div> </div>
); );

View File

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

View File

@ -64,6 +64,7 @@ export default definePlugin({
renderMutualGDMs(user: User, onClose: () => void) { renderMutualGDMs(user: User, onClose: () => void) {
const entries = ChannelStore.getSortedPrivateChannels().filter(c => c.isGroupDM() && c.recipients.includes(user.id)).map(c => ( const entries = ChannelStore.getSortedPrivateChannels().filter(c => c.isGroupDM() && c.recipients.includes(user.id)).map(c => (
<Clickable <Clickable
key={c.id}
className={ProfileListClasses.listRow} className={ProfileListClasses.listRow}
onClick={() => { onClick={() => {
onClose(); onClose();

View File

@ -25,15 +25,15 @@ export default definePlugin({
authors: [Devs.rushii], authors: [Devs.rushii],
patches: [ patches: [
{ {
find: "setSystemTrayApplications:function", find: ",setSystemTrayApplications",
replacement: [ replacement: [
{ {
match: /setBadge:function.+?},/, match: /setBadge\(\i\).+?},/,
replace: "setBadge:function(){}," replace: "setBadge(){},"
}, },
{ {
match: /setSystemTrayIcon:function.+?},/, match: /setSystemTrayIcon\(\i\).+?},/,
replace: "setSystemTrayIcon:function(){}," replace: "setSystemTrayIcon(){},"
} }
] ]
} }

View File

@ -104,6 +104,7 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea
return ( return (
<button <button
key={permission.id}
className={cl("perms-list-item-btn")} className={cl("perms-list-item-btn")}
onClick={() => selectItem(index)} onClick={() => selectItem(index)}
> >
@ -135,9 +136,9 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea
<Text variant="text-md/normal"> <Text variant="text-md/normal">
{ {
permission.type === PermissionType.Role permission.type === PermissionType.Role
? role?.name || "Unknown Role" ? role?.name ?? "Unknown Role"
: permission.type === PermissionType.User : permission.type === PermissionType.User
? (user && getUniqueUsername(user)) || "Unknown User" ? (user && getUniqueUsername(user)) ?? "Unknown User"
: ( : (
<Flex style={{ gap: "0.2em", justifyItems: "center" }}> <Flex style={{ gap: "0.2em", justifyItems: "center" }}>
@owner @owner
@ -157,7 +158,7 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea
</div> </div>
<div className={cl("perms-perms")}> <div className={cl("perms-perms")}>
{Object.entries(PermissionsBits).map(([permissionName, bit]) => ( {Object.entries(PermissionsBits).map(([permissionName, bit]) => (
<div className={cl("perms-perms-item")}> <div key={permissionName} className={cl("perms-perms-item")}>
<div className={cl("perms-perms-item-icon")}> <div className={cl("perms-perms-item-icon")}>
{(() => { {(() => {
const { permissions, overwriteAllow, overwriteDeny } = selectedItem; const { permissions, overwriteAllow, overwriteDeny } = selectedItem;

View File

@ -109,7 +109,7 @@ function UserPermissionsComponent({ guild, guildMember }: { guild: Guild; guildM
} }
defaultState={settings.store.defaultPermissionsDropdownState} defaultState={settings.store.defaultPermissionsDropdownState}
buttons={[ buttons={[
(<Tooltip text={`Sorting by ${stns.permissionsSortOrder === PermissionsSortOrder.HighestRole ? "Highest Role" : "Lowest Role"}`}> (<Tooltip key="sort" text={`Sorting by ${stns.permissionsSortOrder === PermissionsSortOrder.HighestRole ? "Highest Role" : "Lowest Role"}`}>
{tooltipProps => ( {tooltipProps => (
<button <button
{...tooltipProps} {...tooltipProps}
@ -133,7 +133,7 @@ function UserPermissionsComponent({ guild, guildMember }: { guild: Guild; guildM
{userPermissions.length > 0 && ( {userPermissions.length > 0 && (
<div className={classes(root, roles)}> <div className={classes(root, roles)}>
{userPermissions.map(({ permission, roleColor }) => ( {userPermissions.map(({ permission, roleColor }) => (
<div className={classes(role, rolePill, rolePillBorder)}> <div key={permission} className={classes(role, rolePill, rolePillBorder)}>
<div className={roleRemoveButton}> <div className={roleRemoveButton}>
<span <span
className={roleCircle} className={roleCircle}

View File

@ -8,7 +8,7 @@ import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { React, Tooltip } from "@webpack/common"; import { Tooltip } from "@webpack/common";
const settings = definePluginSettings({ const settings = definePluginSettings({
loop: { loop: {
@ -28,8 +28,8 @@ export default definePlugin({
{ {
find: ".nonMediaAttachment]", find: ".nonMediaAttachment]",
replacement: { replacement: {
match: /\.nonMediaAttachment\].{0,10}children:\[\S/, match: /\.nonMediaAttachment\].{0,10}children:\[(\S)/,
replace: "$&&&$self.renderPiPButton()," replace: "$&,$1&&$self.renderPiPButton(),"
}, },
}, },
], ],

View File

@ -30,7 +30,8 @@ import { User } from "discord-types/general";
const SessionsStore = findStoreLazy("SessionsStore"); const SessionsStore = findStoreLazy("SessionsStore");
function Icon(path: string, opts?: { viewBox?: string; width?: number; height?: number; }) { function Icon(path: string, opts?: { viewBox?: string; width?: number; height?: number; }) {
return ({ color, tooltip, small }: { color: string; tooltip: string; small: boolean; }) => ( return function PlatformIndicator({ color, tooltip, small }: { color: string; tooltip: string; small: boolean; }) {
return (
<Tooltip text={tooltip} > <Tooltip text={tooltip} >
{(tooltipProps: any) => ( {(tooltipProps: any) => (
<svg <svg
@ -45,6 +46,7 @@ function Icon(path: string, opts?: { viewBox?: string; width?: number; height?:
)} )}
</Tooltip> </Tooltip>
); );
};
} }
const Icons = { const Icons = {

View File

@ -19,7 +19,7 @@
import { addServerListElement, removeServerListElement, ServerListRenderPosition } from "@api/ServerList"; import { addServerListElement, removeServerListElement, ServerListRenderPosition } from "@api/ServerList";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { Button, FluxDispatcher, GuildChannelStore, GuildStore, React, ReadStateStore } from "@webpack/common"; import { Button, FluxDispatcher, GuildChannelStore, GuildStore, ReadStateStore } from "@webpack/common";
function onClick() { function onClick() {
const channels: Array<any> = []; const channels: Array<any> = [];

View File

@ -172,7 +172,7 @@ export default definePlugin({
height="24" height="24"
viewBox="0 0 24 24" viewBox="0 0 24 24"
> >
<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" /> <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" /> <rect width="24" height="24" />
</g> </g>

View File

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

View File

@ -46,7 +46,7 @@ export const Code = ({
.split("\n") .split("\n")
.map((line, i) => <span key={i} dangerouslySetInnerHTML={{ __html: line }} />); .map((line, i) => <span key={i} dangerouslySetInnerHTML={{ __html: line }} />);
} catch { } catch {
lines = content.split("\n").map(line => <span>{line}</span>); lines = content.split("\n").map((line, i) => <span key={i}>{line}</span>);
} }
} else { } else {
const renderTokens = const renderTokens =
@ -55,11 +55,11 @@ export const Code = ({
.split("\n") .split("\n")
.map(line => [{ color: theme.plainColor, content: line } as IThemedToken]); .map(line => [{ color: theme.plainColor, content: line } as IThemedToken]);
lines = renderTokens.map(line => { lines = renderTokens.map((line, i) => {
// [Cynthia] this makes it so when you highlight the codeblock // [Cynthia] this makes it so when you highlight the codeblock
// empty lines are also selected and copied when you Ctrl+C. // empty lines are also selected and copied when you Ctrl+C.
if (line.length === 0) { if (line.length === 0) {
return <span>{"\n"}</span>; return <span key={i}>{"\n"}</span>;
} }
return ( return (

View File

@ -18,7 +18,7 @@
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { useAwaiter, useIntersection } from "@utils/react"; import { useAwaiter, useIntersection } from "@utils/react";
import { hljs, React } from "@webpack/common"; import { hljs } from "@webpack/common";
import { resolveLang } from "../api/languages"; import { resolveLang } from "../api/languages";
import { shiki } from "../api/shiki"; import { shiki } from "../api/shiki";

View File

@ -105,7 +105,7 @@ function ConnectionsComponent({ id, theme }: { id: string, theme: string; }) {
gap: getSpacingPx(settings.store.iconSpacing), gap: getSpacingPx(settings.store.iconSpacing),
flexWrap: "wrap" flexWrap: "wrap"
}}> }}>
{connections.map(connection => <CompactConnectionComponent connection={connection} theme={theme} />)} {connections.map(connection => <CompactConnectionComponent connection={connection} theme={theme} key={connection.id} />)}
</Flex> </Flex>
</Section> </Section>
); );

View File

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

View File

@ -414,6 +414,22 @@ export default definePlugin({
} }
] ]
}, },
{
// Make the chat input bar channel list contain hidden channels
find: ",queryStaticRouteChannels(",
replacement: [
{
// Make the getChannels call to GuildChannelStore return hidden channels
match: /(?<=queryChannels\(\i\){.+?getChannels\(\i)(?=\))/,
replace: ",true"
},
{
// Avoid filtering out hidden channels from the channel list
match: /(?<=queryChannels\(\i\){.+?isGuildChannelType\)\((\i)\.type\))(?=&&!\i\.\i\.can\()/,
replace: "&&!$self.isHiddenChannel($1)"
}
]
},
{ {
find: "\"^/guild-stages/(\\\\d+)(?:/)?(\\\\d+)?\"", find: "\"^/guild-stages/(\\\\d+)(?:/)?(\\\\d+)?\"",
replacement: { replacement: {

View File

@ -88,7 +88,7 @@ function SilentMessageToggle(chatBoxProps: {
<g fill="currentColor"> <g fill="currentColor">
<path d="M18 10.7101C15.1085 9.84957 13 7.17102 13 4C13 3.69264 13.0198 3.3899 13.0582 3.093C12.7147 3.03189 12.3611 3 12 3C8.686 3 6 5.686 6 9V14C6 15.657 4.656 17 3 17V18H21V17C19.344 17 18 15.657 18 14V10.7101ZM8.55493 19C9.24793 20.19 10.5239 21 11.9999 21C13.4759 21 14.7519 20.19 15.4449 19H8.55493Z" /> <path d="M18 10.7101C15.1085 9.84957 13 7.17102 13 4C13 3.69264 13.0198 3.3899 13.0582 3.093C12.7147 3.03189 12.3611 3 12 3C8.686 3 6 5.686 6 9V14C6 15.657 4.656 17 3 17V18H21V17C19.344 17 18 15.657 18 14V10.7101ZM8.55493 19C9.24793 20.19 10.5239 21 11.9999 21C13.4759 21 14.7519 20.19 15.4449 19H8.55493Z" />
<path d="M18.2624 5.50209L21 2.5V1H16.0349V2.49791H18.476L16 5.61088V7H21V5.50209H18.2624Z" /> <path d="M18.2624 5.50209L21 2.5V1H16.0349V2.49791H18.476L16 5.61088V7H21V5.50209H18.2624Z" />
{!enabled && <line x1="22" y1="2" x2="2" y2="22" stroke="var(--red-500)" stroke-width="2.5" />} {!enabled && <line x1="22" y1="2" x2="2" y2="22" stroke="var(--red-500)" strokeWidth="2.5" />}
</g> </g>
</svg> </svg>
</div> </div>

View File

@ -21,7 +21,7 @@ import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { Button, ButtonLooks, ButtonWrapperClasses, FluxDispatcher, React, Tooltip } from "@webpack/common"; import { Button, ButtonLooks, ButtonWrapperClasses, FluxDispatcher, Tooltip } from "@webpack/common";
const settings = definePluginSettings({ const settings = definePluginSettings({
showIcon: { showIcon: {
@ -62,7 +62,7 @@ function SilentTypingToggle(chatBoxProps: {
<div className={ButtonWrapperClasses.buttonWrapper}> <div className={ButtonWrapperClasses.buttonWrapper}>
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"> <svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
<path fill="currentColor" d="M528 448H48c-26.51 0-48-21.49-48-48V112c0-26.51 21.49-48 48-48h480c26.51 0 48 21.49 48 48v288c0 26.51-21.49 48-48 48zM128 180v-40c0-6.627-5.373-12-12-12H76c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm-336 96v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm-336 96v-40c0-6.627-5.373-12-12-12H76c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm288 0v-40c0-6.627-5.373-12-12-12H172c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h232c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12z" /> <path fill="currentColor" d="M528 448H48c-26.51 0-48-21.49-48-48V112c0-26.51 21.49-48 48-48h480c26.51 0 48 21.49 48 48v288c0 26.51-21.49 48-48 48zM128 180v-40c0-6.627-5.373-12-12-12H76c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm-336 96v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm-336 96v-40c0-6.627-5.373-12-12-12H76c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12zm288 0v-40c0-6.627-5.373-12-12-12H172c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h232c6.627 0 12-5.373 12-12zm96 0v-40c0-6.627-5.373-12-12-12h-40c-6.627 0-12 5.373-12 12v40c0 6.627 5.373 12 12 12h40c6.627 0 12-5.373 12-12z" />
{isEnabled && <path d="M13 432L590 48" stroke="var(--red-500)" stroke-width="72" stroke-linecap="round" />} {isEnabled && <path d="M13 432L590 48" stroke="var(--red-500)" strokeWidth="72" strokeLinecap="round" />}
</svg> </svg>
</div> </div>
</Button> </Button>

View File

@ -38,7 +38,8 @@ function msToHuman(ms: number) {
} }
function Svg(path: string, label: string) { function Svg(path: string, label: string) {
return () => ( return function Icon() {
return (
<svg <svg
className={classes(cl("button-icon"), cl(label))} className={classes(cl("button-icon"), cl(label))}
height="24" height="24"
@ -51,6 +52,7 @@ function Svg(path: string, label: string) {
<path d={path} /> <path d={path} />
</svg> </svg>
); );
};
} }
// KraXen's icons :yesyes: // KraXen's icons :yesyes:

View File

@ -2,7 +2,7 @@
padding: 0.375rem 0.5rem; padding: 0.375rem 0.5rem;
border-bottom: 1px solid var(--background-modifier-accent); border-bottom: 1px solid var(--background-modifier-accent);
--vc-spotify-green: #1db954; /* so cusotm themes can easily change it */ --vc-spotify-green: #1db954; /* so custom themes can easily change it */
} }
.theme-light #vc-spotify-player { .theme-light #vc-spotify-player {
@ -167,7 +167,7 @@
} }
#vc-spotify-progress-bar > [class^="slider"] [class^="grabber"] { #vc-spotify-progress-bar > [class^="slider"] [class^="grabber"] {
/* these importants are neccessary, it applies a width and height through inline styles */ /* these importants are necessary, it applies a width and height through inline styles */
height: 10px !important; height: 10px !important;
width: 10px !important; width: 10px !important;
background-color: var(--interactive-normal); background-color: var(--interactive-normal);

View File

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

View File

@ -21,7 +21,7 @@ import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { openUserProfile } from "@utils/discord"; import { openUserProfile } from "@utils/discord";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { Avatar, GuildMemberStore, React, RelationshipStore } from "@webpack/common"; import { Avatar, GuildMemberStore, RelationshipStore } from "@webpack/common";
import { User } from "discord-types/general"; import { User } from "discord-types/general";
const settings = definePluginSettings({ const settings = definePluginSettings({
@ -135,7 +135,7 @@ export default definePlugin({
return children.map(c => return children.map(c =>
c.type === "strong" c.type === "strong"
? <TypingUser {...props} user={users[element++]} /> ? <TypingUser {...props} user={users[element++]} key={users[element].id} />
: c : c
); );
} }

View File

@ -1,12 +1,16 @@
.vc-toolbox-btn, .vc-toolbox-btn,
.vc-toolbox-btn svg { .vc-toolbox-btn>svg {
-webkit-app-region: no-drag; -webkit-app-region: no-drag;
} }
.vc-toolbox-btn svg { .vc-toolbox-btn>svg {
color: var(--interactive-normal); color: var(--interactive-normal);
} }
:is(.vc-toolbox-btn:hover, .vc-toolbox-btn[class*="selected"]) svg { .vc-toolbox-btn[class*="selected"]>svg {
color: var(--interactive-active); color: var(--interactive-active);
} }
.vc-toolbox-btn:hover>svg {
color: var(--interactive-hover);
}

View File

@ -89,10 +89,10 @@ function VencordPopout(onClose: () => void) {
); );
} }
function VencordPopoutIcon() { function VencordPopoutIcon(isShown: boolean) {
return ( return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96" width={24} height={24}> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 27 27" width={24} height={24}>
<path fill="currentColor" d="M53 10h7v1h-1v1h-1v1h-1v1h-1v1h-1v1h5v1h-7v-1h1v-1h1v-1h1v-1h1v-1h1v-1h-5m-43 1v32h2v2h2v2h2v2h2v2h2v2h2v2h2v2h2v2h8v-2h2V46h-2v2h-2v2h-4v-2h-2v-2h-2v-2h-2v-2h-2v-2h-2V12m24 0v27h-2v3h4v-6h2v-2h4V12m13 2h5v1h-1v1h-1v1h-1v1h3v1h-5v-1h1v-1h1v-1h1v-1h-3m8 5h1v5h1v-1h1v1h-1v1h1v-1h1v1h-1v3h-1v1h-2v1h-1v1h1v-1h2v-1h1v2h-1v1h-2v1h-1v-1h-1v1h-6v-1h-1v-1h-1v-2h1v1h2v1h3v1h1v-1h-1v-1h-3v-1h-4v-4h1v-2h1v-1h1v-1h1v2h1v1h1v-1h1v1h-1v1h2v-2h1v-2h1v-1h1m-13 4h2v1h-1v4h1v2h1v1h1v1h1v1h4v1h-6v-1h-6v-1h-1v-5h1v-1h1v-2h2m17 3h1v3h-1v1h-1v1h-1v2h-2v-2h2v-1h1v-1h1m1 0h1v3h-1v1h-2v-1h1v-1h1m-30 2v8h-8v32h8v8h32v-8h8v-8H70v8H54V44h16v8h16v-8h-8v-8h-1v1h-7v-1h-2v1h-8v-1" /> <path fill="currentColor" d={isShown ? "M9 0h1v1h1v2h1v2h3V3h1V1h1V0h1v2h1v2h1v7h-1v-1h-3V9h1V6h-1v4h-3v1h1v-1h2v1h3v1h-1v1h-3v2h1v1h1v1h1v3h-1v4h-2v-1h-1v-4h-1v4h-1v1h-2v-4H9v-3h1v-1h1v-1h1v-2H9v-1H8v-1h3V6h-1v3h1v1H8v1H7V4h1V2h1M5 19h2v1h1v1h1v3H4v-1h2v-1H4v-2h1m15-1h2v1h1v2h-2v1h2v1h-5v-3h1v-1h1m4 3h4v1h-4" : "M0 0h7v1H6v1H5v1H4v1H3v1H2v1h5v1H0V6h1V5h1V4h1V3h1V2h1V1H0m13 2h5v1h-1v1h-1v1h-1v1h3v1h-5V7h1V6h1V5h1V4h-3m8 5h1v5h1v-1h1v1h-1v1h1v-1h1v1h-1v3h-1v1h-2v1h-1v1h1v-1h2v-1h1v2h-1v1h-2v1h-1v-1h-1v1h-6v-1h-1v-1h-1v-2h1v1h2v1h3v1h1v-1h-1v-1h-3v-1h-4v-4h1v-2h1v-1h1v-1h1v2h1v1h1v-1h1v1h-1v1h2v-2h1v-2h1v-1h1M8 14h2v1H9v4h1v2h1v1h1v1h1v1h4v1h-6v-1H5v-1H4v-5h1v-1h1v-2h2m17 3h1v3h-1v1h-1v1h-1v2h-2v-2h2v-1h1v-1h1m1 0h1v3h-1v1h-2v-1h1v-1h1"} />
</svg> </svg>
); );
} }
@ -114,7 +114,7 @@ function VencordPopoutButton() {
className="vc-toolbox-btn" className="vc-toolbox-btn"
onClick={() => setShow(v => !v)} onClick={() => setShow(v => !v)}
tooltip={isShown ? null : "Vencord Toolbox"} tooltip={isShown ? null : "Vencord Toolbox"}
icon={VencordPopoutIcon} icon={() => VencordPopoutIcon(isShown)}
selected={isShown} selected={isShown}
/> />
)} )}

View File

@ -18,19 +18,14 @@
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { findLazy, mapMangledModuleLazy } from "@webpack"; import { findByPropsLazy } from "@webpack";
import { ComponentDispatch, FluxDispatcher, NavigationRouter, SelectedGuildStore, SettingsRouter } from "@webpack/common"; import { ComponentDispatch, FluxDispatcher, NavigationRouter, SelectedGuildStore, SettingsRouter } from "@webpack/common";
const GuildNavBinds = mapMangledModuleLazy("mod+alt+down", { const KeyBinds = findByPropsLazy("JUMP_TO_GUILD", "SERVER_NEXT");
CtrlTab: m => m.binds?.at(-1) === "ctrl+tab",
CtrlShiftTab: m => m.binds?.at(-1) === "ctrl+shift+tab",
});
const DigitBinds = findLazy(m => m.binds?.[0] === "mod+1");
export default definePlugin({ export default definePlugin({
name: "WebKeybinds", name: "WebKeybinds",
description: "Re-adds keybinds missing in the web version of Discord: ctrl+t, ctrl+shift+t, ctrl+tab, ctrl+shift+tab, ctrl+1-9, ctrl+,", description: "Re-adds keybinds missing in the web version of Discord: ctrl+t, ctrl+shift+t, ctrl+tab, ctrl+shift+tab, ctrl+1-9, ctrl+,. Only works fully on Vesktop/ArmCord, not inside your browser",
authors: [Devs.Ven], authors: [Devs.Ven],
enabledByDefault: true, enabledByDefault: true,
@ -57,13 +52,13 @@ export default definePlugin({
SettingsRouter.open("My Account"); SettingsRouter.open("My Account");
break; break;
case "Tab": case "Tab":
const handler = e.shiftKey ? GuildNavBinds.CtrlShiftTab : GuildNavBinds.CtrlTab; const handler = e.shiftKey ? KeyBinds.SERVER_PREV : KeyBinds.SERVER_NEXT;
handler.action(e); handler.action(e);
break; break;
default: default:
if (e.key >= "1" && e.key <= "9") { if (e.key >= "1" && e.key <= "9") {
e.preventDefault(); e.preventDefault();
DigitBinds.action(e, `mod+${e.key}`); KeyBinds.JUMP_TO_GUILD.action(e, `mod+${e.key}`);
} }
break; break;
} }

View File

@ -133,7 +133,7 @@ export function useForceUpdater(withDep?: true) {
export function LazyComponent<T extends object = any>(factory: () => React.ComponentType<T>, attempts = 5) { export function LazyComponent<T extends object = any>(factory: () => React.ComponentType<T>, attempts = 5) {
const get = makeLazy(factory, attempts); const get = makeLazy(factory, attempts);
return (props: T) => { return function Lazy(props: T) {
const Component = get() ?? NoopComponent; const Component = get() ?? NoopComponent;
return <Component {...props} />; return <Component {...props} />;
}; };

View File

@ -269,7 +269,7 @@ export interface DefinedSettings<
store: SettingsStore<Def> & PrivateSettings; store: SettingsStore<Def> & PrivateSettings;
/** /**
* React hook for getting the settings for this plugin * React hook for getting the settings for this plugin
* @param filter optional filter to avoid rerenders for irrelavent settings * @param filter optional filter to avoid rerenders for irrelevent settings
*/ */
use<F extends Extract<keyof Def | keyof PrivateSettings, string>>(filter?: F[]): Pick<SettingsStore<Def> & PrivateSettings, F>; use<F extends Extract<keyof Def | keyof PrivateSettings, string>>(filter?: F[]): Pick<SettingsStore<Def> & PrivateSettings, F>;
/** Definitions of each setting */ /** Definitions of each setting */

View File

@ -20,7 +20,7 @@ import { proxyLazy } from "@utils/lazy";
import type * as Stores from "discord-types/stores"; import type * as Stores from "discord-types/stores";
// eslint-disable-next-line path-alias/no-relative // eslint-disable-next-line path-alias/no-relative
import { filters, findByCode, findByProps, findByPropsLazy, mapMangledModuleLazy } from "../webpack"; import { filters, findByProps, findByPropsLazy, mapMangledModuleLazy } from "../webpack";
import { waitForStore } from "./internal"; import { waitForStore } from "./internal";
import * as t from "./types/stores"; import * as t from "./types/stores";
@ -84,14 +84,7 @@ export const useStateFromStores: <T>(
idk?: any, idk?: any,
isEqual?: (old: T, newer: T) => boolean isEqual?: (old: T, newer: T) => boolean
) => T ) => T
// FIXME: hack to support old stable and new canary = proxyLazy(() => findByProps("useStateFromStores").useStateFromStores);
= proxyLazy(() => {
try {
return findByProps("useStateFromStores").useStateFromStores;
} catch {
return findByCode('("useStateFromStores")');
}
});
waitForStore("DraftStore", s => DraftStore = s); waitForStore("DraftStore", s => DraftStore = s);
waitForStore("UserStore", s => UserStore = s); waitForStore("UserStore", s => UserStore = s);

View File

@ -136,11 +136,4 @@ waitFor("parseTopic", m => Parser = m);
export let SettingsRouter: any; export let SettingsRouter: any;
waitFor(["open", "saveAccountChanges"], m => SettingsRouter = m); waitFor(["open", "saveAccountChanges"], m => SettingsRouter = m);
// FIXME: hack to support old stable and new canary export const PermissionsBits: t.PermissionsBits = proxyLazy(() => find(m => typeof m.Permissions?.ADMINISTRATOR === "bigint").Permissions);
export const PermissionsBits: t.PermissionsBits = proxyLazy(() => {
try {
return find(m => m.Permissions?.ADMINISTRATOR).Permissions;
} catch {
return find(m => typeof m.ADMINISTRATOR === "bigint");
}
});

View File

@ -29,7 +29,7 @@ let webpackChunk: any[];
const logger = new Logger("WebpackInterceptor", "#8caaee"); const logger = new Logger("WebpackInterceptor", "#8caaee");
if (window[WEBPACK_CHUNK]) { if (window[WEBPACK_CHUNK]) {
logger.info(`Patching ${WEBPACK_CHUNK}.push (was already existant, likely from cache!)`); logger.info(`Patching ${WEBPACK_CHUNK}.push (was already existent, likely from cache!)`);
_initWebpack(window[WEBPACK_CHUNK]); _initWebpack(window[WEBPACK_CHUNK]);
patchPush(window[WEBPACK_CHUNK]); patchPush(window[WEBPACK_CHUNK]);
} else { } else {