Merge remote-tracking branch 'upstream/main'
This commit is contained in:
commit
3c846afd1e
37 changed files with 292 additions and 396 deletions
|
@ -31,6 +31,7 @@ Before starting your plugin:
|
||||||
- No FakeDeafen or FakeMute
|
- No FakeDeafen or FakeMute
|
||||||
- No StereoMic
|
- No StereoMic
|
||||||
- No plugins that simply hide or redesign ui elements. This can be done with CSS
|
- No plugins that simply hide or redesign ui elements. This can be done with CSS
|
||||||
|
- No plugins that interact with specific Discord bots (official Discord apps like Youtube WatchTogether are okay)
|
||||||
- No selfbots or API spam (animated status, message pruner, auto reply, nitro snipers, etc)
|
- No selfbots or API spam (animated status, message pruner, auto reply, nitro snipers, etc)
|
||||||
- No untrusted third party APIs. Popular services like Google or GitHub are fine, but absolutely no self hosted ones
|
- No untrusted third party APIs. Popular services like Google or GitHub are fine, but absolutely no self hosted ones
|
||||||
- No plugins that require the user to enter their own API key
|
- No plugins that require the user to enter their own API key
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "vencord",
|
"name": "vencord",
|
||||||
"private": "true",
|
"private": "true",
|
||||||
"version": "1.10.7",
|
"version": "1.10.8",
|
||||||
"description": "The cutest Discord client mod",
|
"description": "The cutest Discord client mod",
|
||||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
|
|
|
@ -99,7 +99,8 @@ export interface ChatBarButtonProps {
|
||||||
tooltip: string;
|
tooltip: string;
|
||||||
onClick: MouseEventHandler<HTMLButtonElement>;
|
onClick: MouseEventHandler<HTMLButtonElement>;
|
||||||
onContextMenu?: MouseEventHandler<HTMLButtonElement>;
|
onContextMenu?: MouseEventHandler<HTMLButtonElement>;
|
||||||
buttonProps?: Omit<HTMLProps<HTMLButtonElement>, "size" | "onClick" | "onContextMenu">;
|
onAuxClick?: MouseEventHandler<HTMLButtonElement>;
|
||||||
|
buttonProps?: Omit<HTMLProps<HTMLButtonElement>, "size" | "onClick" | "onContextMenu" | "onAuxClick">;
|
||||||
}
|
}
|
||||||
export const ChatBarButton = ErrorBoundary.wrap((props: ChatBarButtonProps) => {
|
export const ChatBarButton = ErrorBoundary.wrap((props: ChatBarButtonProps) => {
|
||||||
return (
|
return (
|
||||||
|
@ -115,6 +116,7 @@ export const ChatBarButton = ErrorBoundary.wrap((props: ChatBarButtonProps) => {
|
||||||
innerClassName={`${ButtonWrapperClasses.button} ${ChannelTextAreaClasses?.button}`}
|
innerClassName={`${ButtonWrapperClasses.button} ${ChannelTextAreaClasses?.button}`}
|
||||||
onClick={props.onClick}
|
onClick={props.onClick}
|
||||||
onContextMenu={props.onContextMenu}
|
onContextMenu={props.onContextMenu}
|
||||||
|
onAuxClick={props.onAuxClick}
|
||||||
{...props.buttonProps}
|
{...props.buttonProps}
|
||||||
>
|
>
|
||||||
<div className={ButtonWrapperClasses.buttonWrapper}>
|
<div className={ButtonWrapperClasses.buttonWrapper}>
|
||||||
|
|
|
@ -54,5 +54,5 @@ export function sendBotMessage(channelId: string, message: PartialDeep<Message>)
|
||||||
export function findOption<T>(args: Argument[], name: string): T & {} | undefined;
|
export function findOption<T>(args: Argument[], name: string): T & {} | undefined;
|
||||||
export function findOption<T>(args: Argument[], name: string, fallbackValue: T): T & {};
|
export function findOption<T>(args: Argument[], name: string, fallbackValue: T): T & {};
|
||||||
export function findOption(args: Argument[], name: string, fallbackValue?: any) {
|
export function findOption(args: Argument[], name: string, fallbackValue?: any) {
|
||||||
return (args.find(a => a.name === name)?.value || fallbackValue) as any;
|
return (args.find(a => a.name === name)?.value ?? fallbackValue) as any;
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,6 +110,7 @@ function registerSubCommands(cmd: Command, plugin: string) {
|
||||||
const subCmd = {
|
const subCmd = {
|
||||||
...cmd,
|
...cmd,
|
||||||
...o,
|
...o,
|
||||||
|
options: o.options !== undefined ? o.options : undefined,
|
||||||
type: ApplicationCommandType.CHAT_INPUT,
|
type: ApplicationCommandType.CHAT_INPUT,
|
||||||
name: `${cmd.name} ${o.name}`,
|
name: `${cmd.name} ${o.name}`,
|
||||||
id: `${o.name}-${cmd.id}`,
|
id: `${o.name}-${cmd.id}`,
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
.vc-expandableheader-center-flex {
|
|
||||||
display: flex;
|
|
||||||
place-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vc-expandableheader-btn {
|
|
||||||
all: unset;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
|
@ -1,121 +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 "./ExpandableHeader.css";
|
|
||||||
|
|
||||||
import { classNameFactory } from "@api/Styles";
|
|
||||||
import { Text, Tooltip, useState } from "@webpack/common";
|
|
||||||
|
|
||||||
const cl = classNameFactory("vc-expandableheader-");
|
|
||||||
|
|
||||||
export interface ExpandableHeaderProps {
|
|
||||||
onMoreClick?: () => void;
|
|
||||||
moreTooltipText?: string;
|
|
||||||
onDropDownClick?: (state: boolean) => void;
|
|
||||||
defaultState?: boolean;
|
|
||||||
headerText: string;
|
|
||||||
children: React.ReactNode;
|
|
||||||
buttons?: React.ReactNode[];
|
|
||||||
forceOpen?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ExpandableHeader({
|
|
||||||
children,
|
|
||||||
onMoreClick,
|
|
||||||
buttons,
|
|
||||||
moreTooltipText,
|
|
||||||
onDropDownClick,
|
|
||||||
headerText,
|
|
||||||
defaultState = false,
|
|
||||||
forceOpen = false,
|
|
||||||
}: ExpandableHeaderProps) {
|
|
||||||
const [showContent, setShowContent] = useState(defaultState || forceOpen);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div style={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
alignItems: "center",
|
|
||||||
marginBottom: "8px"
|
|
||||||
}}>
|
|
||||||
<Text
|
|
||||||
tag="h2"
|
|
||||||
variant="eyebrow"
|
|
||||||
style={{
|
|
||||||
color: "var(--header-primary)",
|
|
||||||
display: "inline"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{headerText}
|
|
||||||
</Text>
|
|
||||||
|
|
||||||
<div className={cl("center-flex")}>
|
|
||||||
{
|
|
||||||
buttons ?? null
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
onMoreClick && // only show more button if callback is provided
|
|
||||||
<Tooltip text={moreTooltipText}>
|
|
||||||
{tooltipProps => (
|
|
||||||
<button
|
|
||||||
{...tooltipProps}
|
|
||||||
className={cl("btn")}
|
|
||||||
onClick={onMoreClick}>
|
|
||||||
<svg
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
|
||||||
<path fill="var(--text-normal)" d="M7 12.001C7 10.8964 6.10457 10.001 5 10.001C3.89543 10.001 3 10.8964 3 12.001C3 13.1055 3.89543 14.001 5 14.001C6.10457 14.001 7 13.1055 7 12.001ZM14 12.001C14 10.8964 13.1046 10.001 12 10.001C10.8954 10.001 10 10.8964 10 12.001C10 13.1055 10.8954 14.001 12 14.001C13.1046 14.001 14 13.1055 14 12.001ZM19 10.001C20.1046 10.001 21 10.8964 21 12.001C21 13.1055 20.1046 14.001 19 14.001C17.8954 14.001 17 13.1055 17 12.001C17 10.8964 17.8954 10.001 19 10.001Z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</Tooltip>
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
<Tooltip text={showContent ? "Hide " + headerText : "Show " + headerText}>
|
|
||||||
{tooltipProps => (
|
|
||||||
<button
|
|
||||||
{...tooltipProps}
|
|
||||||
className={cl("btn")}
|
|
||||||
onClick={() => {
|
|
||||||
setShowContent(v => !v);
|
|
||||||
onDropDownClick?.(showContent);
|
|
||||||
}}
|
|
||||||
disabled={forceOpen}
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
transform={showContent ? "scale(1 -1)" : "scale(1 1)"}
|
|
||||||
>
|
|
||||||
<path fill="var(--text-normal)" d="M16.59 8.59003L12 13.17L7.41 8.59003L6 10L12 16L18 10L16.59 8.59003Z" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{showContent && children}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
import "./iconStyles.css";
|
import "./iconStyles.css";
|
||||||
|
|
||||||
import { getIntlMessage, getTheme, Theme } from "@utils/discord";
|
import { getIntlMessage } from "@utils/discord";
|
||||||
import { classes } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
import type { PropsWithChildren } from "react";
|
import type { PropsWithChildren } from "react";
|
||||||
|
|
||||||
|
@ -122,8 +122,8 @@ export function InfoIcon(props: IconProps) {
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
transform="translate(2 2)"
|
fill-rule="evenodd"
|
||||||
d="M9,7 L11,7 L11,5 L9,5 L9,7 Z M10,18 C5.59,18 2,14.41 2,10 C2,5.59 5.59,2 10,2 C14.41,2 18,5.59 18,10 C18,14.41 14.41,18 10,18 L10,18 Z M10,4.4408921e-16 C4.4771525,-1.77635684e-15 4.4408921e-16,4.4771525 0,10 C-1.33226763e-15,12.6521649 1.0535684,15.195704 2.92893219,17.0710678 C4.80429597,18.9464316 7.3478351,20 10,20 C12.6521649,20 15.195704,18.9464316 17.0710678,17.0710678 C18.9464316,15.195704 20,12.6521649 20,10 C20,7.3478351 18.9464316,4.80429597 17.0710678,2.92893219 C15.195704,1.0535684 12.6521649,2.22044605e-16 10,0 L10,4.4408921e-16 Z M9,15 L11,15 L11,9 L9,9 L9,15 L9,15 Z"
|
d="M23 12a11 11 0 1 1-22 0 11 11 0 0 1 22 0Zm-9.5-4.75a1.25 1.25 0 1 1-2.5 0 1.25 1.25 0 0 1 2.5 0Zm-.77 3.96a1 1 0 1 0-1.96-.42l-1.04 4.86a2.77 2.77 0 0 0 4.31 2.83l.24-.17a1 1 0 1 0-1.16-1.62l-.24.17a.77.77 0 0 1-1.2-.79l1.05-4.86Z" clip-rule="evenodd"
|
||||||
/>
|
/>
|
||||||
</Icon>
|
</Icon>
|
||||||
);
|
);
|
||||||
|
@ -211,9 +211,10 @@ export function CogWheel(props: IconProps) {
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
clipRule="evenodd"
|
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
d="M19.738 10H22V14H19.739C19.498 14.931 19.1 15.798 18.565 16.564L20 18L18 20L16.565 18.564C15.797 19.099 14.932 19.498 14 19.738V22H10V19.738C9.069 19.498 8.203 19.099 7.436 18.564L6 20L4 18L5.436 16.564C4.901 15.799 4.502 14.932 4.262 14H2V10H4.262C4.502 9.068 4.9 8.202 5.436 7.436L4 6L6 4L7.436 5.436C8.202 4.9 9.068 4.502 10 4.262V2H14V4.261C14.932 4.502 15.797 4.9 16.565 5.435L18 3.999L20 5.999L18.564 7.436C19.099 8.202 19.498 9.069 19.738 10ZM12 16C14.2091 16 16 14.2091 16 12C16 9.79086 14.2091 8 12 8C9.79086 8 8 9.79086 8 12C8 14.2091 9.79086 16 12 16Z"
|
fill-rule="evenodd"
|
||||||
|
d="M10.56 1.1c-.46.05-.7.53-.64.98.18 1.16-.19 2.2-.98 2.53-.8.33-1.79-.15-2.49-1.1-.27-.36-.78-.52-1.14-.24-.77.59-1.45 1.27-2.04 2.04-.28.36-.12.87.24 1.14.96.7 1.43 1.7 1.1 2.49-.33.8-1.37 1.16-2.53.98-.45-.07-.93.18-.99.64a11.1 11.1 0 0 0 0 2.88c.06.46.54.7.99.64 1.16-.18 2.2.19 2.53.98.33.8-.14 1.79-1.1 2.49-.36.27-.52.78-.24 1.14.59.77 1.27 1.45 2.04 2.04.36.28.87.12 1.14-.24.7-.95 1.7-1.43 2.49-1.1.8.33 1.16 1.37.98 2.53-.07.45.18.93.64.99a11.1 11.1 0 0 0 2.88 0c.46-.06.7-.54.64-.99-.18-1.16.19-2.2.98-2.53.8-.33 1.79.14 2.49 1.1.27.36.78.52 1.14.24.77-.59 1.45-1.27 2.04-2.04.28-.36.12-.87-.24-1.14-.96-.7-1.43-1.7-1.1-2.49.33-.8 1.37-1.16 2.53-.98.45.07.93-.18.99-.64a11.1 11.1 0 0 0 0-2.88c-.06-.46-.54-.7-.99-.64-1.16.18-2.2-.19-2.53-.98-.33-.8.14-1.79 1.1-2.49.36-.27.52-.78.24-1.14a11.07 11.07 0 0 0-2.04-2.04c-.36-.28-.87-.12-1.14.24-.7.96-1.7 1.43-2.49 1.1-.8-.33-1.16-1.37-.98-2.53.07-.45-.18-.93-.64-.99a11.1 11.1 0 0 0-2.88 0ZM16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0Z"
|
||||||
|
clip-rule="evenodd"
|
||||||
/>
|
/>
|
||||||
</Icon>
|
</Icon>
|
||||||
);
|
);
|
||||||
|
@ -406,23 +407,30 @@ export function PencilIcon(props: IconProps) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const WebsiteIconDark = "/assets/e1e96d89e192de1997f73730db26e94f.svg";
|
export function GithubIcon(props: IconProps) {
|
||||||
const WebsiteIconLight = "/assets/730f58bcfd5a57a5e22460c445a0c6cf.svg";
|
return (
|
||||||
const GithubIconLight = "/assets/3ff98ad75ac94fa883af5ed62d17c459.svg";
|
<Icon
|
||||||
const GithubIconDark = "/assets/6a853b4c87fce386cbfef4a2efbacb09.svg";
|
{...props}
|
||||||
|
viewBox="-3 -3 30 30"
|
||||||
export function GithubIcon(props: ImageProps) {
|
>
|
||||||
const src = getTheme() === Theme.Light
|
<path
|
||||||
? GithubIconLight
|
fill={props.fill || "currentColor"}
|
||||||
: GithubIconDark;
|
d="M12 0C5.37 0 0 5.37 0 12c0 5.3 3.438 9.8 8.205 11.385.6.11.82-.26.82-.577v-2.17c-3.338.726-4.042-1.61-4.042-1.61-.546-1.387-1.333-1.757-1.333-1.757-1.09-.745.083-.73.083-.73 1.205.084 1.84 1.237 1.84 1.237 1.07 1.835 2.807 1.305 3.492.998.108-.775.42-1.305.763-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.467-2.38 1.235-3.22-.123-.303-.535-1.523.117-3.176 0 0 1.008-.322 3.3 1.23.957-.266 1.98-.398 3-.403 1.02.005 2.043.137 3 .403 2.29-1.552 3.297-1.23 3.297-1.23.653 1.653.24 2.873.118 3.176.77.84 1.233 1.91 1.233 3.22 0 4.61-2.803 5.625-5.475 5.92.43.37.823 1.102.823 2.222v3.293c0 .32.218.694.825.577C20.565 21.797 24 17.298 24 12c0-6.63-5.37-12-12-12z"
|
||||||
|
/>
|
||||||
return <img {...props} src={src} />;
|
</Icon>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function WebsiteIcon(props: ImageProps) {
|
export function WebsiteIcon(props: IconProps) {
|
||||||
const src = getTheme() === Theme.Light
|
return (
|
||||||
? WebsiteIconLight
|
<Icon
|
||||||
: WebsiteIconDark;
|
{...props}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
return <img {...props} src={src} />;
|
>
|
||||||
|
<path
|
||||||
|
fill={props.fill || "currentColor"}
|
||||||
|
d="M12 2C6.486 2 2 6.486 2 12s4.486 10 10 10 10-4.486 10-10S17.514 2 12 2zM4 12c0-.899.156-1.762.431-2.569L6 11l2 2v2l2 2 1 1v1.931C7.061 19.436 4 16.072 4 12zm14.33 4.873C17.677 16.347 16.687 16 16 16v-1a2 2 0 0 0-2-2h-4v-3a2 2 0 0 0 2-2V7h1a2 2 0 0 0 2-2v-.411C17.928 5.778 20 8.65 20 12a7.947 7.947 0 0 1-1.67 4.873z"
|
||||||
|
/>
|
||||||
|
</Icon>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,16 +6,19 @@
|
||||||
|
|
||||||
import "./LinkIconButton.css";
|
import "./LinkIconButton.css";
|
||||||
|
|
||||||
|
import { getTheme, Theme } from "@utils/discord";
|
||||||
import { MaskedLink, Tooltip } from "@webpack/common";
|
import { MaskedLink, Tooltip } from "@webpack/common";
|
||||||
|
|
||||||
import { GithubIcon, WebsiteIcon } from "..";
|
import { GithubIcon, WebsiteIcon } from "..";
|
||||||
|
|
||||||
export function GithubLinkIcon() {
|
export function GithubLinkIcon() {
|
||||||
return <GithubIcon aria-hidden className={"vc-settings-modal-link-icon"} />;
|
const theme = getTheme() === Theme.Light ? "#000000" : "#FFFFFF";
|
||||||
|
return <GithubIcon aria-hidden fill={theme} className={"vc-settings-modal-link-icon"} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function WebsiteLinkIcon() {
|
export function WebsiteLinkIcon() {
|
||||||
return <WebsiteIcon aria-hidden className={"vc-settings-modal-link-icon"} />;
|
const theme = getTheme() === Theme.Light ? "#000000" : "#FFFFFF";
|
||||||
|
return <WebsiteIcon aria-hidden fill={theme} className={"vc-settings-modal-link-icon"} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
|
@ -10,7 +10,6 @@ export * from "./CodeBlock";
|
||||||
export * from "./DonateButton";
|
export * from "./DonateButton";
|
||||||
export { default as ErrorBoundary } from "./ErrorBoundary";
|
export { default as ErrorBoundary } from "./ErrorBoundary";
|
||||||
export * from "./ErrorCard";
|
export * from "./ErrorCard";
|
||||||
export * from "./ExpandableHeader";
|
|
||||||
export * from "./Flex";
|
export * from "./Flex";
|
||||||
export * from "./Heart";
|
export * from "./Heart";
|
||||||
export * from "./Icons";
|
export * from "./Icons";
|
||||||
|
|
5
src/plugins/_api/badges/fixDiscordBadgePadding.css
Normal file
5
src/plugins/_api/badges/fixDiscordBadgePadding.css
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
/* the profile popout badge container(s) */
|
||||||
|
[class*="biteSize_"] [class*="tags_"] [class*="container_"] {
|
||||||
|
/* Discord has padding set to 2px instead of 1px, which causes the 12th badge to wrap to a new line. */
|
||||||
|
padding: 0 1px;
|
||||||
|
}
|
|
@ -16,6 +16,8 @@
|
||||||
* 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 "./fixDiscordBadgePadding.css";
|
||||||
|
|
||||||
import { _getBadges, BadgePosition, BadgeUserArgs, ProfileBadge } from "@api/Badges";
|
import { _getBadges, BadgePosition, BadgeUserArgs, ProfileBadge } from "@api/Badges";
|
||||||
import DonateButton from "@components/DonateButton";
|
import DonateButton from "@components/DonateButton";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
|
|
|
@ -23,11 +23,14 @@ export default definePlugin({
|
||||||
name: "MessagePopoverAPI",
|
name: "MessagePopoverAPI",
|
||||||
description: "API to add buttons to message popovers.",
|
description: "API to add buttons to message popovers.",
|
||||||
authors: [Devs.KingFish, Devs.Ven, Devs.Nuckyz],
|
authors: [Devs.KingFish, Devs.Ven, Devs.Nuckyz],
|
||||||
patches: [{
|
patches: [
|
||||||
find: "#{intl::MESSAGE_UTILITIES_A11Y_LABEL}",
|
{
|
||||||
replacement: {
|
find: "#{intl::MESSAGE_UTILITIES_A11Y_LABEL}",
|
||||||
match: /\.jsx\)\((\i\.\i),\{label:\i\.\i\.string\(\i\.\i#{intl::MESSAGE_ACTION_REPLY}.{0,200}?"reply-self".{0,50}?\}\):null(?=,.+?message:(\i))/,
|
replacement: {
|
||||||
replace: "$&,Vencord.Api.MessagePopover._buildPopoverElements($1,$2)"
|
match: /(?<=:null),(.{0,40}togglePopout:.+?}\))\]}\):null,(?<=\((\i\.\i),{label:.+?:null,(\i&&!\i)\?\(0,\i\.jsxs?\)\(\i\.Fragment.+?message:(\i).+?)/,
|
||||||
|
replace: (_, ReactButton, ButtonComponent, showReactButton, message) => "" +
|
||||||
|
`]}):null,Vencord.Api.MessagePopover._buildPopoverElements(${ButtonComponent},${message}),${showReactButton}?${ReactButton}:null,`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}],
|
]
|
||||||
});
|
});
|
||||||
|
|
|
@ -51,7 +51,7 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: "bitbucket.org",
|
find: "bitbucket.org",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /function \i\(\i\){(?=.{0,60}\.parse\(\i\))/,
|
match: /function \i\(\i\){(?=.{0,30}pathname:\i)/,
|
||||||
replace: "$&return null;"
|
replace: "$&return null;"
|
||||||
},
|
},
|
||||||
predicate: () => settings.store.file
|
predicate: () => settings.store.file
|
||||||
|
|
|
@ -123,7 +123,7 @@ export default definePlugin({
|
||||||
},
|
},
|
||||||
// If we are rendering the Better Folders sidebar, we filter out everything but the servers and folders from the GuildsBar Guild List children
|
// 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.+?\]}\)\]/,
|
match: /lastTargetNode:\i\[\i\.length-1\].+?}\)\](?=}\))/,
|
||||||
replace: "$&.filter($self.makeGuildsBarGuildListFilter(!!arguments[0]?.isBetterFolders))"
|
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
|
// If we are rendering the Better Folders sidebar, we filter out everything but the scroller for the guild list from the GuildsBar Tree children
|
||||||
|
@ -275,24 +275,30 @@ export default definePlugin({
|
||||||
},
|
},
|
||||||
|
|
||||||
makeGuildsBarGuildListFilter(isBetterFolders: boolean) {
|
makeGuildsBarGuildListFilter(isBetterFolders: boolean) {
|
||||||
return child => {
|
return child => {
|
||||||
if (isBetterFolders) {
|
if (!isBetterFolders) return true;
|
||||||
try {
|
|
||||||
return child?.props?.["aria-label"] === getIntlMessage("SERVERS");
|
try {
|
||||||
} catch (e) {
|
return child?.props?.["aria-label"] === getIntlMessage("SERVERS");
|
||||||
console.error(e);
|
} catch (e) {
|
||||||
}
|
console.error(e);
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
};
|
return true;
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
makeGuildsBarTreeFilter(isBetterFolders: boolean) {
|
makeGuildsBarTreeFilter(isBetterFolders: boolean) {
|
||||||
return child => {
|
return child => {
|
||||||
if (isBetterFolders) {
|
if (!isBetterFolders) return true;
|
||||||
return child?.props?.onScroll != null;
|
|
||||||
|
if (child?.props?.className?.includes("itemsContainer") && child.props.children != null) {
|
||||||
|
// Filter out everything but the scroller for the guild list
|
||||||
|
child.props.children = child.props.children.filter(child => child?.props?.onScroll != null);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -110,7 +110,7 @@ const settings = definePluginSettings({
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "ClientTheme",
|
name: "ClientTheme",
|
||||||
authors: [Devs.F53, Devs.Nuckyz],
|
authors: [Devs.Nuckyz],
|
||||||
description: "Recreation of the old client theme experiment. Add a color to your Discord client theme",
|
description: "Recreation of the old client theme experiment. Add a color to your Discord client theme",
|
||||||
settings,
|
settings,
|
||||||
|
|
||||||
|
|
|
@ -130,27 +130,6 @@ export default definePlugin({
|
||||||
replace: ""
|
replace: ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// Zustand section
|
|
||||||
{
|
|
||||||
find: "[DEPRECATED] Passing a vanilla store will be unsupported in a future version. Instead use `import { useStore } from 'zustand'`.",
|
|
||||||
replacement: [
|
|
||||||
{
|
|
||||||
match: /&&console\.warn\("\[DEPRECATED\] Passing a vanilla store will be unsupported in a future version\. Instead use `import { useStore } from 'zustand'`\."\)/,
|
|
||||||
replace: ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
match: /console\.warn\("\[DEPRECATED\] Use `createWithEqualityFn` instead of `create` or use `useStoreWithEqualityFn` instead of `useStore`\. They can be imported from 'zustand\/traditional'\. https:\/\/github\.com\/pmndrs\/zustand\/discussions\/1937"\),/,
|
|
||||||
replace: ""
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
find: "[DEPRECATED] `getStorage`, `serialize` and `deserialize` options are deprecated. Use `storage` option instead.",
|
|
||||||
replacement: {
|
|
||||||
match: /console\.warn\("\[DEPRECATED\] `getStorage`, `serialize` and `deserialize` options are deprecated\. Use `storage` option instead\."\),/,
|
|
||||||
replace: ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Patches discords generic logger function
|
// Patches discords generic logger function
|
||||||
{
|
{
|
||||||
find: "Σ:",
|
find: "Σ:",
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { openInviteModal } from "@utils/discord";
|
||||||
import { Margins } from "@utils/margins";
|
import { Margins } from "@utils/margins";
|
||||||
import { classes, copyWithToast } from "@utils/misc";
|
import { classes, copyWithToast } from "@utils/misc";
|
||||||
import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
import { closeAllModals, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||||
|
import { Queue } from "@utils/Queue";
|
||||||
import { findComponentByCodeLazy } from "@webpack";
|
import { findComponentByCodeLazy } from "@webpack";
|
||||||
import { Alerts, Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, Parser, Text, Tooltip, useEffect, UserStore, UserUtils, useState } from "@webpack/common";
|
import { Alerts, Button, FluxDispatcher, Forms, GuildStore, NavigationRouter, Parser, Text, Tooltip, useEffect, UserStore, UserUtils, useState } from "@webpack/common";
|
||||||
import { User } from "discord-types/general";
|
import { User } from "discord-types/general";
|
||||||
|
@ -49,6 +50,8 @@ interface SectionHeaderProps {
|
||||||
section: Section;
|
section: Section;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fetchAuthorsQueue = new Queue();
|
||||||
|
|
||||||
function SectionHeader({ section }: SectionHeaderProps) {
|
function SectionHeader({ section }: SectionHeaderProps) {
|
||||||
const hasSubtitle = typeof section.subtitle !== "undefined";
|
const hasSubtitle = typeof section.subtitle !== "undefined";
|
||||||
const hasAuthorIds = typeof section.authorIds !== "undefined";
|
const hasAuthorIds = typeof section.authorIds !== "undefined";
|
||||||
|
@ -56,17 +59,18 @@ function SectionHeader({ section }: SectionHeaderProps) {
|
||||||
const [authors, setAuthors] = useState<User[]>([]);
|
const [authors, setAuthors] = useState<User[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
fetchAuthorsQueue.push(async () => {
|
||||||
if (!section.authorIds) return;
|
if (!section.authorIds) return;
|
||||||
|
|
||||||
for (const authorId of section.authorIds) {
|
for (const authorId of section.authorIds) {
|
||||||
const author = UserStore.getUser(authorId) ?? await UserUtils.getUser(authorId);
|
const author = UserStore.getUser(authorId) ?? await UserUtils.getUser(authorId).catch(() => null);
|
||||||
|
if (author == null) continue;
|
||||||
|
|
||||||
setAuthors(authors => [...authors, author]);
|
setAuthors(authors => [...authors, author]);
|
||||||
}
|
}
|
||||||
})();
|
});
|
||||||
}, [section.authorIds]);
|
}, [section.authorIds]);
|
||||||
|
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
<Flex>
|
<Flex>
|
||||||
<Forms.FormTitle style={{ flexGrow: 1 }}>{section.title}</Forms.FormTitle>
|
<Forms.FormTitle style={{ flexGrow: 1 }}>{section.title}</Forms.FormTitle>
|
||||||
|
|
|
@ -119,7 +119,7 @@ function CreateDecorationModal(props: ModalProps) {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<HelpMessage messageType={HelpMessageTypes.INFO}>
|
<HelpMessage messageType={HelpMessageTypes.INFO} className={Margins.bottom8}>
|
||||||
To receive updates on your decoration's review, join <Link
|
To receive updates on your decoration's review, join <Link
|
||||||
href={`https://discord.gg/${INVITE_KEY}`}
|
href={`https://discord.gg/${INVITE_KEY}`}
|
||||||
onClick={async e => {
|
onClick={async e => {
|
||||||
|
|
|
@ -310,7 +310,8 @@ function buildMenuItem(type: "Emoji" | "Sticker", fetchData: () => Promisable<Om
|
||||||
}
|
}
|
||||||
|
|
||||||
function isGifUrl(url: string) {
|
function isGifUrl(url: string) {
|
||||||
return new URL(url).pathname.endsWith(".gif");
|
const u = new URL(url);
|
||||||
|
return u.pathname.endsWith(".gif") || u.searchParams.get("animated") === "true";
|
||||||
}
|
}
|
||||||
|
|
||||||
const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => {
|
const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => {
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { FluxDispatcher, React, useRef, useState } from "@webpack/common";
|
import { FluxDispatcher, useLayoutEffect, useRef, useState } from "@webpack/common";
|
||||||
|
|
||||||
import { ELEMENT_ID } from "../constants";
|
import { ELEMENT_ID } from "../constants";
|
||||||
import { settings } from "../index";
|
import { settings } from "../index";
|
||||||
|
@ -55,7 +55,7 @@ export const Magnifier = ErrorBoundary.wrap<MagnifierProps>(({ instance, size: i
|
||||||
const imageRef = useRef<HTMLImageElement | null>(null);
|
const imageRef = useRef<HTMLImageElement | null>(null);
|
||||||
|
|
||||||
// since we accessing document im gonna use useLayoutEffect
|
// since we accessing document im gonna use useLayoutEffect
|
||||||
React.useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
const onKeyDown = (e: KeyboardEvent) => {
|
const onKeyDown = (e: KeyboardEvent) => {
|
||||||
if (e.key === "Shift") {
|
if (e.key === "Shift") {
|
||||||
isShiftDown.current = true;
|
isShiftDown.current = true;
|
||||||
|
@ -135,12 +135,14 @@ export const Magnifier = ErrorBoundary.wrap<MagnifierProps>(({ instance, size: i
|
||||||
|
|
||||||
setReady(true);
|
setReady(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener("keydown", onKeyDown);
|
document.addEventListener("keydown", onKeyDown);
|
||||||
document.addEventListener("keyup", onKeyUp);
|
document.addEventListener("keyup", onKeyUp);
|
||||||
document.addEventListener("mousemove", updateMousePosition);
|
document.addEventListener("mousemove", updateMousePosition);
|
||||||
document.addEventListener("mousedown", onMouseDown);
|
document.addEventListener("mousedown", onMouseDown);
|
||||||
document.addEventListener("mouseup", onMouseUp);
|
document.addEventListener("mouseup", onMouseUp);
|
||||||
document.addEventListener("wheel", onWheel);
|
document.addEventListener("wheel", onWheel);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener("keydown", onKeyDown);
|
document.removeEventListener("keydown", onKeyDown);
|
||||||
document.removeEventListener("keyup", onKeyUp);
|
document.removeEventListener("keyup", onKeyUp);
|
||||||
|
@ -148,14 +150,16 @@ export const Magnifier = ErrorBoundary.wrap<MagnifierProps>(({ instance, size: i
|
||||||
document.removeEventListener("mousedown", onMouseDown);
|
document.removeEventListener("mousedown", onMouseDown);
|
||||||
document.removeEventListener("mouseup", onMouseUp);
|
document.removeEventListener("mouseup", onMouseUp);
|
||||||
document.removeEventListener("wheel", onWheel);
|
document.removeEventListener("wheel", onWheel);
|
||||||
|
|
||||||
if (settings.store.saveZoomValues) {
|
|
||||||
settings.store.zoom = zoom.current;
|
|
||||||
settings.store.size = size.current;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useLayoutEffect(() => () => {
|
||||||
|
if (settings.store.saveZoomValues) {
|
||||||
|
settings.store.zoom = zoom.current;
|
||||||
|
settings.store.size = size.current;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (!ready) return null;
|
if (!ready) return null;
|
||||||
|
|
||||||
const box = element.current?.getBoundingClientRect();
|
const box = element.current?.getBoundingClientRect();
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { isNonNullish } from "@utils/guards";
|
import { isNonNullish } from "@utils/guards";
|
||||||
|
import { Logger } from "@utils/Logger";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
import { findByPropsLazy, findComponentByCodeLazy } from "@webpack";
|
||||||
import { Avatar, ChannelStore, Clickable, IconUtils, RelationshipStore, ScrollerThin, useMemo, UserStore } from "@webpack/common";
|
import { Avatar, ChannelStore, Clickable, IconUtils, RelationshipStore, ScrollerThin, useMemo, UserStore } from "@webpack/common";
|
||||||
|
@ -87,7 +88,7 @@ export default definePlugin({
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
match: /\i\.useEffect.{0,100}(\i)\[0\]\.section/,
|
match: /\i\.useEffect.{0,100}(\i)\[0\]\.section/,
|
||||||
replace: "$self.pushSection($1, arguments[0].user);$&"
|
replace: "$self.pushSection($1,arguments[0].user);$&"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /\(0,\i\.jsx\)\(\i,\{items:\i,section:(\i)/,
|
match: /\(0,\i\.jsx\)\(\i,\{items:\i,section:(\i)/,
|
||||||
|
@ -97,26 +98,46 @@ export default definePlugin({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
find: 'section:"MUTUAL_FRIENDS"',
|
find: 'section:"MUTUAL_FRIENDS"',
|
||||||
replacement: {
|
replacement: [
|
||||||
match: /\.openUserProfileModal.+?\)}\)}\)(?<=(\(0,\i\.jsxs?\)\(\i\.\i,{className:(\i)\.divider}\)).+?)/,
|
{
|
||||||
replace: "$&,$self.renderDMPageList({user: arguments[0].user, Divider: $1, listStyle: $2.list})"
|
match: /\i\|\|\i(?=\?\(0,\i\.jsxs?\)\(\i\.\i\.Overlay,)/,
|
||||||
}
|
replace: "$&||$self.getMutualGroupDms(arguments[0].user.id).length>0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
match: /\.openUserProfileModal.+?\)}\)}\)(?<=,(\i)&&(\i)&&(\(0,\i\.jsxs?\)\(\i\.\i,{className:(\i)\.divider}\)).+?)/,
|
||||||
|
replace: (m, hasMutualGuilds, hasMutualFriends, Divider, classes) => "" +
|
||||||
|
`${m},$self.renderDMPageList({user:arguments[0].user,hasDivider:${hasMutualGuilds}||${hasMutualFriends},Divider:${Divider},listStyle:${classes}.list})`
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
pushSection(sections: any[], user: User) {
|
getMutualGroupDms(userId: string) {
|
||||||
if (isBotOrSelf(user) || sections[IS_PATCHED]) return;
|
try {
|
||||||
|
return getMutualGroupDms(userId);
|
||||||
|
} catch (e) {
|
||||||
|
new Logger("MutualGroupDMs").error("Failed to get mutual group dms:", e);
|
||||||
|
}
|
||||||
|
|
||||||
sections[IS_PATCHED] = true;
|
return [];
|
||||||
sections.push({
|
},
|
||||||
section: "MUTUAL_GDMS",
|
|
||||||
text: getMutualGDMCountText(user)
|
pushSection(sections: any[], user: User) {
|
||||||
});
|
try {
|
||||||
|
if (isBotOrSelf(user) || sections[IS_PATCHED]) return;
|
||||||
|
|
||||||
|
sections[IS_PATCHED] = true;
|
||||||
|
sections.push({
|
||||||
|
section: "MUTUAL_GDMS",
|
||||||
|
text: getMutualGDMCountText(user)
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
new Logger("MutualGroupDMs").error("Failed to push mutual group dms section:", e);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
renderMutualGDMs: ErrorBoundary.wrap(({ user, onClose }: { user: User, onClose: () => void; }) => {
|
renderMutualGDMs: ErrorBoundary.wrap(({ user, onClose }: { user: User, onClose: () => void; }) => {
|
||||||
const mutualGDms = useMemo(() => getMutualGroupDms(user.id), [user.id]);
|
const mutualGDms = useMemo(() => getMutualGroupDms(user.id), [user.id]);
|
||||||
|
|
||||||
const entries = renderClickableGDMs(mutualGDms, onClose);
|
const entries = renderClickableGDMs(mutualGDms, onClose);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -138,14 +159,13 @@ export default definePlugin({
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
renderDMPageList: ErrorBoundary.wrap(({ user, Divider, listStyle }: { user: User, Divider: JSX.Element, listStyle: string; }) => {
|
renderDMPageList: ErrorBoundary.wrap(({ user, hasDivider, Divider, listStyle }: { user: User, hasDivider: boolean, Divider: JSX.Element, listStyle: string; }) => {
|
||||||
const mutualGDms = getMutualGroupDms(user.id);
|
const mutualGDms = getMutualGroupDms(user.id);
|
||||||
if (mutualGDms.length === 0) return null;
|
if (mutualGDms.length === 0) return null;
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{Divider}
|
{hasDivider && Divider}
|
||||||
<ExpandableList
|
<ExpandableList
|
||||||
listClassName={listStyle}
|
listClassName={listStyle}
|
||||||
header={"Mutual Groups"}
|
header={"Mutual Groups"}
|
||||||
|
|
|
@ -87,7 +87,7 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: "trackAnnouncementMessageLinkClicked({",
|
find: "trackAnnouncementMessageLinkClicked({",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /function (\i\(\i,\i\)\{)(?=.{0,100}trusted:)/,
|
match: /function (\i\(\i,\i\)\{)(?=.{0,150}trusted:)/,
|
||||||
replace: "async function $1 if(await $self.handleLink(...arguments)) return;"
|
replace: "async function $1 if(await $self.handleLink(...arguments)) return;"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -22,12 +22,12 @@ import { InfoIcon, OwnerCrownIcon } from "@components/Icons";
|
||||||
import { getIntlMessage, getUniqueUsername } from "@utils/discord";
|
import { getIntlMessage, getUniqueUsername } from "@utils/discord";
|
||||||
import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
import { ModalCloseButton, ModalContent, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
|
||||||
import { findByCodeLazy } from "@webpack";
|
import { findByCodeLazy } from "@webpack";
|
||||||
import { Clipboard, ContextMenuApi, FluxDispatcher, GuildMemberStore, GuildStore, Menu, PermissionsBits, ScrollerThin, Text, Tooltip, useEffect, UserStore, useState, useStateFromStores } from "@webpack/common";
|
import { Clipboard, ContextMenuApi, FluxDispatcher, GuildMemberStore, GuildStore, i18n, Menu, PermissionsBits, ScrollerThin, Text, Tooltip, useEffect, useMemo, UserStore, useState, useStateFromStores } from "@webpack/common";
|
||||||
import { UnicodeEmoji } from "@webpack/types";
|
import { UnicodeEmoji } from "@webpack/types";
|
||||||
import type { Guild, Role, User } from "discord-types/general";
|
import type { Guild, Role, User } from "discord-types/general";
|
||||||
|
|
||||||
import { settings } from "..";
|
import { settings } from "..";
|
||||||
import { cl, getPermissionDescription, getPermissionString } from "../utils";
|
import { cl, getGuildPermissionSpecMap } from "../utils";
|
||||||
import { PermissionAllowedIcon, PermissionDefaultIcon, PermissionDeniedIcon } from "./icons";
|
import { PermissionAllowedIcon, PermissionDefaultIcon, PermissionDeniedIcon } from "./icons";
|
||||||
|
|
||||||
export const enum PermissionType {
|
export const enum PermissionType {
|
||||||
|
@ -56,7 +56,7 @@ function getRoleIconSrc(role: Role) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, header }: { permissions: Array<RoleOrUserPermission>; guild: Guild; modalProps: ModalProps; header: string; }) {
|
function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, header }: { permissions: Array<RoleOrUserPermission>; guild: Guild; modalProps: ModalProps; header: string; }) {
|
||||||
permissions.sort((a, b) => a.type - b.type);
|
const guildPermissionSpecMap = useMemo(() => getGuildPermissionSpecMap(guild), [guild.id]);
|
||||||
|
|
||||||
useStateFromStores(
|
useStateFromStores(
|
||||||
[GuildMemberStore],
|
[GuildMemberStore],
|
||||||
|
@ -65,6 +65,10 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea
|
||||||
(old, current) => old.length === current.length
|
(old, current) => old.length === current.length
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
permissions.sort((a, b) => a.type - b.type);
|
||||||
|
}, [permissions]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const usersToRequest = permissions
|
const usersToRequest = permissions
|
||||||
.filter(p => p.type === PermissionType.User && !GuildMemberStore.isMember(guild.id, p.id!))
|
.filter(p => p.type === PermissionType.User && !GuildMemberStore.isMember(guild.id, p.id!))
|
||||||
|
@ -173,7 +177,7 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea
|
||||||
</ScrollerThin>
|
</ScrollerThin>
|
||||||
<div className={cl("modal-divider")} />
|
<div className={cl("modal-divider")} />
|
||||||
<ScrollerThin className={cl("modal-perms")} orientation="auto">
|
<ScrollerThin className={cl("modal-perms")} orientation="auto">
|
||||||
{Object.entries(PermissionsBits).map(([permissionName, bit]) => (
|
{Object.values(PermissionsBits).map(bit => (
|
||||||
<div className={cl("modal-perms-item")}>
|
<div className={cl("modal-perms-item")}>
|
||||||
<div className={cl("modal-perms-item-icon")}>
|
<div className={cl("modal-perms-item-icon")}>
|
||||||
{(() => {
|
{(() => {
|
||||||
|
@ -192,9 +196,14 @@ function RolesAndUsersPermissionsComponent({ permissions, guild, modalProps, hea
|
||||||
return PermissionDefaultIcon();
|
return PermissionDefaultIcon();
|
||||||
})()}
|
})()}
|
||||||
</div>
|
</div>
|
||||||
<Text variant="text-md/normal">{getPermissionString(permissionName)}</Text>
|
<Text variant="text-md/normal">{guildPermissionSpecMap[String(bit)].title}</Text>
|
||||||
|
|
||||||
<Tooltip text={getPermissionDescription(permissionName) || "No Description"}>
|
<Tooltip text={
|
||||||
|
(() => {
|
||||||
|
const { description } = guildPermissionSpecMap[String(bit)];
|
||||||
|
return typeof description === "function" ? i18n.intl.format(description, {}) : description;
|
||||||
|
})()
|
||||||
|
}>
|
||||||
{props => <InfoIcon {...props} />}
|
{props => <InfoIcon {...props} />}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { ExpandableHeader } from "@components/ExpandableHeader";
|
|
||||||
import { getIntlMessage } from "@utils/discord";
|
import { getIntlMessage } from "@utils/discord";
|
||||||
import { classes } from "@utils/misc";
|
import { classes } from "@utils/misc";
|
||||||
import { filters, findBulk, proxyLazyWebpack } from "@webpack";
|
import { filters, findBulk, proxyLazyWebpack } from "@webpack";
|
||||||
|
@ -25,7 +24,7 @@ import { PermissionsBits, Text, Tooltip, useMemo, UserStore } from "@webpack/com
|
||||||
import type { Guild, GuildMember } from "discord-types/general";
|
import type { Guild, GuildMember } from "discord-types/general";
|
||||||
|
|
||||||
import { PermissionsSortOrder, settings } from "..";
|
import { PermissionsSortOrder, settings } from "..";
|
||||||
import { cl, getPermissionString, getSortedRoles, sortUserRoles } from "../utils";
|
import { cl, getGuildPermissionSpecMap, getSortedRoles, sortUserRoles } from "../utils";
|
||||||
import openRolesAndUsersPermissionsModal, { PermissionType, type RoleOrUserPermission } from "./RolesAndUsersPermissions";
|
import openRolesAndUsersPermissionsModal, { PermissionType, type RoleOrUserPermission } from "./RolesAndUsersPermissions";
|
||||||
|
|
||||||
interface UserPermission {
|
interface UserPermission {
|
||||||
|
@ -87,9 +86,11 @@ function GrantedByTooltip({ roleName, roleColor }: GrantedByTooltipProps) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function UserPermissionsComponent({ guild, guildMember, forceOpen = false }: { guild: Guild; guildMember: GuildMember; forceOpen?: boolean; }) {
|
function UserPermissionsComponent({ guild, guildMember, closePopout }: { guild: Guild; guildMember: GuildMember; closePopout: () => void; }) {
|
||||||
const { permissionsSortOrder } = settings.use(["permissionsSortOrder"]);
|
const { permissionsSortOrder } = settings.use(["permissionsSortOrder"]);
|
||||||
|
|
||||||
|
const guildPermissionSpecMap = useMemo(() => getGuildPermissionSpecMap(guild), [guild.id]);
|
||||||
|
|
||||||
const [rolePermissions, userPermissions] = useMemo(() => {
|
const [rolePermissions, userPermissions] = useMemo(() => {
|
||||||
const userPermissions: UserPermissions = [];
|
const userPermissions: UserPermissions = [];
|
||||||
|
|
||||||
|
@ -106,7 +107,7 @@ function UserPermissionsComponent({ guild, guildMember, forceOpen = false }: { g
|
||||||
permissions: Object.values(PermissionsBits).reduce((prev, curr) => prev | curr, 0n)
|
permissions: Object.values(PermissionsBits).reduce((prev, curr) => prev | curr, 0n)
|
||||||
});
|
});
|
||||||
|
|
||||||
const OWNER = getIntlMessage("GUILD_OWNER") || "Server Owner";
|
const OWNER = getIntlMessage("GUILD_OWNER") ?? "Server Owner";
|
||||||
userPermissions.push({
|
userPermissions.push({
|
||||||
permission: OWNER,
|
permission: OWNER,
|
||||||
roleName: "Owner",
|
roleName: "Owner",
|
||||||
|
@ -117,11 +118,11 @@ function UserPermissionsComponent({ guild, guildMember, forceOpen = false }: { g
|
||||||
|
|
||||||
sortUserRoles(userRoles);
|
sortUserRoles(userRoles);
|
||||||
|
|
||||||
for (const [permission, bit] of Object.entries(PermissionsBits)) {
|
for (const bit of Object.values(PermissionsBits)) {
|
||||||
for (const { permissions, colorString, position, name } of userRoles) {
|
for (const { permissions, colorString, position, name } of userRoles) {
|
||||||
if ((permissions & bit) === bit) {
|
if ((permissions & bit) === bit) {
|
||||||
userPermissions.push({
|
userPermissions.push({
|
||||||
permission: getPermissionString(permission),
|
permission: guildPermissionSpecMap[String(bit)].title,
|
||||||
roleName: name,
|
roleName: name,
|
||||||
roleColor: colorString || "var(--primary-300)",
|
roleColor: colorString || "var(--primary-300)",
|
||||||
rolePosition: position
|
rolePosition: position
|
||||||
|
@ -137,26 +138,15 @@ function UserPermissionsComponent({ guild, guildMember, forceOpen = false }: { g
|
||||||
return [rolePermissions, userPermissions];
|
return [rolePermissions, userPermissions];
|
||||||
}, [permissionsSortOrder]);
|
}, [permissionsSortOrder]);
|
||||||
|
|
||||||
return (
|
return <div>
|
||||||
<ExpandableHeader
|
<div className={cl("user-header-container")}>
|
||||||
forceOpen={forceOpen}
|
<Text variant="eyebrow">Permissions</Text>
|
||||||
headerText="Permissions"
|
<div className={cl("user-header-btns")}>
|
||||||
moreTooltipText="Role Details"
|
|
||||||
onMoreClick={() =>
|
|
||||||
openRolesAndUsersPermissionsModal(
|
|
||||||
rolePermissions,
|
|
||||||
guild,
|
|
||||||
guildMember.nick || UserStore.getUser(guildMember.userId).username
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onDropDownClick={state => settings.store.defaultPermissionsDropdownState = !state}
|
|
||||||
defaultState={settings.store.defaultPermissionsDropdownState}
|
|
||||||
buttons={[
|
|
||||||
<Tooltip text={`Sorting by ${permissionsSortOrder === PermissionsSortOrder.HighestRole ? "Highest Role" : "Lowest Role"}`}>
|
<Tooltip text={`Sorting by ${permissionsSortOrder === PermissionsSortOrder.HighestRole ? "Highest Role" : "Lowest Role"}`}>
|
||||||
{tooltipProps => (
|
{tooltipProps => (
|
||||||
<div
|
<div
|
||||||
{...tooltipProps}
|
{...tooltipProps}
|
||||||
className={cl("user-sortorder-btn")}
|
className={cl("user-header-btn")}
|
||||||
role="button"
|
role="button"
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -164,8 +154,8 @@ function UserPermissionsComponent({ guild, guildMember, forceOpen = false }: { g
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
width="20"
|
width="24"
|
||||||
height="20"
|
height="24"
|
||||||
viewBox="0 96 960 960"
|
viewBox="0 96 960 960"
|
||||||
transform={permissionsSortOrder === PermissionsSortOrder.HighestRole ? "scale(1 1)" : "scale(1 -1)"}
|
transform={permissionsSortOrder === PermissionsSortOrder.HighestRole ? "scale(1 1)" : "scale(1 -1)"}
|
||||||
>
|
>
|
||||||
|
@ -174,24 +164,46 @@ function UserPermissionsComponent({ guild, guildMember, forceOpen = false }: { g
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
]}>
|
<Tooltip text="Role Details">
|
||||||
{userPermissions.length > 0 && (
|
{tooltipProps => (
|
||||||
<div className={classes(RoleRootClasses.root)}>
|
<div
|
||||||
{userPermissions.map(({ permission, roleColor, roleName }) => (
|
{...tooltipProps}
|
||||||
<Tooltip
|
className={cl("user-header-btn")}
|
||||||
text={<GrantedByTooltip roleName={roleName} roleColor={roleColor} />}
|
role="button"
|
||||||
tooltipClassName={cl("granted-by-container")}
|
tabIndex={0}
|
||||||
tooltipContentClassName={cl("granted-by-content")}
|
onClick={() => {
|
||||||
|
closePopout();
|
||||||
|
openRolesAndUsersPermissionsModal(rolePermissions, guild, guildMember.nick || UserStore.getUser(guildMember.userId).username);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{tooltipProps => (
|
<svg
|
||||||
<FakeRole {...tooltipProps} text={permission} color={roleColor} />
|
width="24"
|
||||||
)}
|
height="24"
|
||||||
</Tooltip>
|
viewBox="0 0 24 24"
|
||||||
))}
|
>
|
||||||
</div>
|
<path fill="var(--text-normal)" d="M7 12.001C7 10.8964 6.10457 10.001 5 10.001C3.89543 10.001 3 10.8964 3 12.001C3 13.1055 3.89543 14.001 5 14.001C6.10457 14.001 7 13.1055 7 12.001ZM14 12.001C14 10.8964 13.1046 10.001 12 10.001C10.8954 10.001 10 10.8964 10 12.001C10 13.1055 10.8954 14.001 12 14.001C13.1046 14.001 14 13.1055 14 12.001ZM19 10.001C20.1046 10.001 21 10.8964 21 12.001C21 13.1055 20.1046 14.001 19 14.001C17.8954 14.001 17 13.1055 17 12.001C17 10.8964 17.8954 10.001 19 10.001Z" />
|
||||||
)}
|
</svg>
|
||||||
</ExpandableHeader>
|
</div>
|
||||||
);
|
)}
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{userPermissions.length > 0 && (
|
||||||
|
<div className={classes(RoleRootClasses.root)}>
|
||||||
|
{userPermissions.map(({ permission, roleColor, roleName }) => (
|
||||||
|
<Tooltip
|
||||||
|
text={<GrantedByTooltip roleName={roleName} roleColor={roleColor} />}
|
||||||
|
tooltipClassName={cl("granted-by-container")}
|
||||||
|
tooltipContentClassName={cl("granted-by-content")}
|
||||||
|
>
|
||||||
|
{tooltipProps => (
|
||||||
|
<FakeRole {...tooltipProps} text={permission} color={roleColor} />
|
||||||
|
)}
|
||||||
|
</Tooltip>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ErrorBoundary.wrap(UserPermissionsComponent, { noop: true });
|
export default ErrorBoundary.wrap(UserPermissionsComponent, { noop: true });
|
||||||
|
|
|
@ -56,11 +56,6 @@ export const settings = definePluginSettings({
|
||||||
{ label: "Lowest Role", value: PermissionsSortOrder.LowestRole }
|
{ label: "Lowest Role", value: PermissionsSortOrder.LowestRole }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
defaultPermissionsDropdownState: {
|
|
||||||
description: "Whether the permissions dropdown on user popouts should be open by default",
|
|
||||||
type: OptionType.BOOLEAN,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function MenuItem(guildId: string, id?: string, type?: MenuItemParentType) {
|
function MenuItem(guildId: string, id?: string, type?: MenuItemParentType) {
|
||||||
|
@ -182,9 +177,9 @@ export default definePlugin({
|
||||||
<Popout
|
<Popout
|
||||||
position="bottom"
|
position="bottom"
|
||||||
align="center"
|
align="center"
|
||||||
renderPopout={() => (
|
renderPopout={({ closePopout }) => (
|
||||||
<Dialog className={PopoutClasses.container} style={{ width: "500px" }}>
|
<Dialog className={PopoutClasses.container} style={{ width: "500px" }}>
|
||||||
<UserPermissions guild={guild} guildMember={guildMember} forceOpen />
|
<UserPermissions guild={guild} guildMember={guildMember} closePopout={closePopout} />
|
||||||
</Dialog>
|
</Dialog>
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,12 +1,22 @@
|
||||||
/* User Permissions Component */
|
/* User Permissions Component */
|
||||||
|
|
||||||
.vc-permviewer-user-sortorder-btn {
|
.vc-permviewer-user-header-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-permviewer-user-header-btns {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vc-permviewer-user-header-btn {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RolesAndUsersPermissions Component */
|
/* RolesAndUsersPermissions Component */
|
||||||
|
|
|
@ -17,57 +17,17 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { classNameFactory } from "@api/Styles";
|
import { classNameFactory } from "@api/Styles";
|
||||||
import { getIntlMessage } from "@utils/discord";
|
import { findByPropsLazy } from "@webpack";
|
||||||
import { wordsToTitle } from "@utils/text";
|
import { GuildStore } from "@webpack/common";
|
||||||
import { GuildStore, Parser } from "@webpack/common";
|
|
||||||
import { Guild, GuildMember, Role } from "discord-types/general";
|
import { Guild, GuildMember, Role } from "discord-types/general";
|
||||||
import type { ReactNode } from "react";
|
|
||||||
|
|
||||||
import { PermissionsSortOrder, settings } from ".";
|
import { PermissionsSortOrder, settings } from ".";
|
||||||
import { PermissionType } from "./components/RolesAndUsersPermissions";
|
import { PermissionType } from "./components/RolesAndUsersPermissions";
|
||||||
|
|
||||||
|
export const { getGuildPermissionSpecMap } = findByPropsLazy("getGuildPermissionSpecMap");
|
||||||
|
|
||||||
export const cl = classNameFactory("vc-permviewer-");
|
export const cl = classNameFactory("vc-permviewer-");
|
||||||
|
|
||||||
function formatPermissionWithoutMatchingString(permission: string) {
|
|
||||||
return wordsToTitle(permission.toLowerCase().split("_"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// because discord is unable to be consistent with their names
|
|
||||||
const PermissionKeyMap = {
|
|
||||||
MANAGE_GUILD: "MANAGE_SERVER",
|
|
||||||
MANAGE_GUILD_EXPRESSIONS: "MANAGE_EXPRESSIONS",
|
|
||||||
CREATE_GUILD_EXPRESSIONS: "CREATE_EXPRESSIONS",
|
|
||||||
MODERATE_MEMBERS: "MODERATE_MEMBER", // HELLOOOO ??????
|
|
||||||
STREAM: "VIDEO",
|
|
||||||
SEND_VOICE_MESSAGES: "ROLE_PERMISSIONS_SEND_VOICE_MESSAGE",
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export function getPermissionString(permission: string) {
|
|
||||||
permission = PermissionKeyMap[permission] || permission;
|
|
||||||
|
|
||||||
return getIntlMessage(permission) ||
|
|
||||||
// shouldn't get here but just in case
|
|
||||||
formatPermissionWithoutMatchingString(permission);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getPermissionDescription(permission: string): ReactNode {
|
|
||||||
// DISCORD PLEEEEEEEEAAAAASE IM BEGGING YOU :(
|
|
||||||
if (permission === "USE_APPLICATION_COMMANDS")
|
|
||||||
permission = "USE_APPLICATION_COMMANDS_GUILD";
|
|
||||||
else if (permission === "SEND_VOICE_MESSAGES")
|
|
||||||
permission = "SEND_VOICE_MESSAGE_GUILD";
|
|
||||||
else if (permission !== "STREAM")
|
|
||||||
permission = PermissionKeyMap[permission] || permission;
|
|
||||||
|
|
||||||
const msg = getIntlMessage(`ROLE_PERMISSIONS_${permission}_DESCRIPTION`) as any;
|
|
||||||
if (msg?.hasMarkdown)
|
|
||||||
return Parser.parse(msg.message);
|
|
||||||
|
|
||||||
if (typeof msg === "string") return msg;
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getSortedRoles({ id }: Guild, member: GuildMember) {
|
export function getSortedRoles({ id }: Guild, member: GuildMember) {
|
||||||
const roles = GuildStore.getRoles(id);
|
const roles = GuildStore.getRoles(id);
|
||||||
|
|
||||||
|
|
|
@ -156,7 +156,7 @@ export default definePlugin({
|
||||||
find: "#{intl::MESSAGE_EDITED}",
|
find: "#{intl::MESSAGE_EDITED}",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?<=isUnsupported\]:(\i)\.isUnsupported\}\),)(?=children:\[)/,
|
match: /(?<=isUnsupported\]:(\i)\.isUnsupported\}\),)(?=children:\[)/,
|
||||||
replace: "style:$self.useMessageColorStyle($1),"
|
replace: "style:$self.useMessageColorsStyle($1),"
|
||||||
},
|
},
|
||||||
predicate: () => settings.store.colorChatMessages
|
predicate: () => settings.store.colorChatMessages
|
||||||
}
|
}
|
||||||
|
@ -188,13 +188,19 @@ export default definePlugin({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
useMessageColor(message: any) {
|
useMessageColorsStyle(message: any) {
|
||||||
try {
|
try {
|
||||||
const { messageSaturation } = settings.use(["messageSaturation"]);
|
const { messageSaturation } = settings.use(["messageSaturation"]);
|
||||||
const author = useMessageAuthor(message);
|
const author = useMessageAuthor(message);
|
||||||
|
|
||||||
if (author.colorString != null && messageSaturation !== 0) {
|
if (author.colorString != null && messageSaturation !== 0) {
|
||||||
return `color-mix(in oklab, ${author.colorString} ${messageSaturation}%, var(--text-normal))`;
|
const value = `color-mix(in oklab, ${author.colorString} ${messageSaturation}%, var({DEFAULT}))`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
color: value.replace("{DEFAULT}", "--text-normal"),
|
||||||
|
"--header-primary": value.replace("{DEFAULT}", "--header-primary"),
|
||||||
|
"--text-muted": value.replace("{DEFAULT}", "--text-muted")
|
||||||
|
};
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
new Logger("RoleColorEverywhere").error("Failed to get message color", e);
|
new Logger("RoleColorEverywhere").error("Failed to get message color", e);
|
||||||
|
@ -203,14 +209,6 @@ export default definePlugin({
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
useMessageColorStyle(message: any) {
|
|
||||||
const color = this.useMessageColor(message);
|
|
||||||
|
|
||||||
return color && {
|
|
||||||
color
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
RoleGroupColor: ErrorBoundary.wrap(({ id, count, title, guildId, label }: { id: string; count: number; title: string; guildId: string; label: string; }) => {
|
RoleGroupColor: ErrorBoundary.wrap(({ id, count, title, guildId, label }: { id: string; count: number; title: string; guildId: string; label: string; }) => {
|
||||||
const role = GuildStore.getRole(guildId, id);
|
const role = GuildStore.getRole(guildId, id);
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,9 @@ import { addServerListElement, removeServerListElement, ServerListRenderPosition
|
||||||
import { Settings } from "@api/Settings";
|
import { Settings } from "@api/Settings";
|
||||||
import ErrorBoundary from "@components/ErrorBoundary";
|
import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import { useForceUpdater } from "@utils/react";
|
|
||||||
import definePlugin, { OptionType } from "@utils/types";
|
import definePlugin, { OptionType } from "@utils/types";
|
||||||
import { GuildStore, PresenceStore, RelationshipStore } from "@webpack/common";
|
import { findStoreLazy } from "@webpack";
|
||||||
|
import { GuildStore, PresenceStore, RelationshipStore, useStateFromStores } from "@webpack/common";
|
||||||
|
|
||||||
const enum IndicatorType {
|
const enum IndicatorType {
|
||||||
SERVER = 1 << 0,
|
SERVER = 1 << 0,
|
||||||
|
@ -30,13 +30,24 @@ const enum IndicatorType {
|
||||||
BOTH = SERVER | FRIEND,
|
BOTH = SERVER | FRIEND,
|
||||||
}
|
}
|
||||||
|
|
||||||
let onlineFriends = 0;
|
const UserGuildJoinRequestStore = findStoreLazy("UserGuildJoinRequestStore");
|
||||||
let guildCount = 0;
|
|
||||||
let forceUpdateFriendCount: () => void;
|
|
||||||
let forceUpdateGuildCount: () => void;
|
|
||||||
|
|
||||||
function FriendsIndicator() {
|
function FriendsIndicator() {
|
||||||
forceUpdateFriendCount = useForceUpdater();
|
const onlineFriendsCount = useStateFromStores([RelationshipStore, PresenceStore], () => {
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
const friendIds = RelationshipStore.getFriendIDs();
|
||||||
|
for (const id of friendIds) {
|
||||||
|
const status = PresenceStore.getStatus(id) ?? "offline";
|
||||||
|
if (status === "offline") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span id="vc-friendcount" style={{
|
<span id="vc-friendcount" style={{
|
||||||
|
@ -48,13 +59,19 @@ function FriendsIndicator() {
|
||||||
textTransform: "uppercase",
|
textTransform: "uppercase",
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
}}>
|
}}>
|
||||||
{onlineFriends} online
|
{onlineFriendsCount} online
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ServersIndicator() {
|
function ServersIndicator() {
|
||||||
forceUpdateGuildCount = useForceUpdater();
|
const guildCount = useStateFromStores([GuildStore, UserGuildJoinRequestStore], () => {
|
||||||
|
const guildJoinRequests: string[] = UserGuildJoinRequestStore.computeGuildIds();
|
||||||
|
const guilds = GuildStore.getGuilds();
|
||||||
|
|
||||||
|
// Filter only pending guild join requests
|
||||||
|
return GuildStore.getGuildCount() + guildJoinRequests.filter(id => guilds[id] == null).length;
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span id="vc-guildcount" style={{
|
<span id="vc-guildcount" style={{
|
||||||
|
@ -71,24 +88,6 @@ function ServersIndicator() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePresenceUpdate() {
|
|
||||||
onlineFriends = 0;
|
|
||||||
const relations = RelationshipStore.getRelationships();
|
|
||||||
for (const id of Object.keys(relations)) {
|
|
||||||
const type = relations[id];
|
|
||||||
// FRIEND relationship type
|
|
||||||
if (type === 1 && PresenceStore.getStatus(id) !== "offline") {
|
|
||||||
onlineFriends += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
forceUpdateFriendCount?.();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleGuildUpdate() {
|
|
||||||
guildCount = GuildStore.getGuildCount();
|
|
||||||
forceUpdateGuildCount?.();
|
|
||||||
}
|
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "ServerListIndicators",
|
name: "ServerListIndicators",
|
||||||
description: "Add online friend count or server count in the server list",
|
description: "Add online friend count or server count in the server list",
|
||||||
|
@ -117,18 +116,8 @@ export default definePlugin({
|
||||||
</ErrorBoundary>;
|
</ErrorBoundary>;
|
||||||
},
|
},
|
||||||
|
|
||||||
flux: {
|
|
||||||
PRESENCE_UPDATES: handlePresenceUpdate,
|
|
||||||
GUILD_CREATE: handleGuildUpdate,
|
|
||||||
GUILD_DELETE: handleGuildUpdate,
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
addServerListElement(ServerListRenderPosition.Above, this.renderIndicator);
|
addServerListElement(ServerListRenderPosition.Above, this.renderIndicator);
|
||||||
|
|
||||||
handlePresenceUpdate();
|
|
||||||
handleGuildUpdate();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
|
|
|
@ -103,7 +103,7 @@ export default definePlugin({
|
||||||
replacement: [
|
replacement: [
|
||||||
{
|
{
|
||||||
// Do not show confirmation to join a voice channel when already connected to another if clicking on a hidden voice channel
|
// Do not show confirmation to join a voice channel when already connected to another if clicking on a hidden voice channel
|
||||||
match: /(?<=getBlockedUsersForVoiceChannel\((\i)\.id\);return\()/,
|
match: /(?<=getIgnoredUsersForVoiceChannel\((\i)\.id\);return\()/,
|
||||||
replace: (_, channel) => `!$self.isHiddenChannel(${channel})&&`
|
replace: (_, channel) => `!$self.isHiddenChannel(${channel})&&`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -75,6 +75,15 @@ export default definePlugin({
|
||||||
replace: "$1$2arguments[0].member.highestRoleId]",
|
replace: "$1$2arguments[0].member.highestRoleId]",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// allows you to open mod view on yourself
|
||||||
|
{
|
||||||
|
find: ".MEMBER_SAFETY,{modViewPanel:",
|
||||||
|
predicate: () => settings.store.showModView,
|
||||||
|
replacement: {
|
||||||
|
match: /\i(?=\?null)/,
|
||||||
|
replace: "false"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
find: "prod_discoverable_guilds",
|
find: "prod_discoverable_guilds",
|
||||||
predicate: () => settings.store.disableDiscoveryFilters,
|
predicate: () => settings.store.disableDiscoveryFilters,
|
||||||
|
@ -107,7 +116,7 @@ export default definePlugin({
|
||||||
predicate: () => settings.store.disableDisallowedDiscoveryFilters,
|
predicate: () => settings.store.disableDisallowedDiscoveryFilters,
|
||||||
all: true,
|
all: true,
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /\i\.\i\.get\(\{url:\i\.\i\.GUILD_DISCOVERY_VALID_TERM,query:\{term:\i\},oldFormErrors:!0\}\)/g,
|
match: /\i\.\i\.get\(\{url:\i\.\i\.GUILD_DISCOVERY_VALID_TERM,query:\{term:\i\},oldFormErrors:!0,rejectWithError:!1\}\)/g,
|
||||||
replace: "Promise.resolve({ body: { valid: true } })"
|
replace: "Promise.resolve({ body: { valid: true } })"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,9 +54,17 @@ const SilentTypingToggle: ChatBarButton = ({ isMainChat }) => {
|
||||||
tooltip={isEnabled ? "Disable Silent Typing" : "Enable Silent Typing"}
|
tooltip={isEnabled ? "Disable Silent Typing" : "Enable Silent Typing"}
|
||||||
onClick={toggle}
|
onClick={toggle}
|
||||||
>
|
>
|
||||||
<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 24 24" style={{ scale: "1.2" }}>
|
||||||
<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" mask="url(#silent-typing-msg-mask)" d="M18.333 15.556H1.667a1.667 1.667 0 0 1 -1.667 -1.667v-10a1.667 1.667 0 0 1 1.667 -1.667h16.667a1.667 1.667 0 0 1 1.667 1.667v10a1.667 1.667 0 0 1 -1.667 1.667M4.444 6.25V4.861a0.417 0.417 0 0 0 -0.417 -0.417H2.639a0.417 0.417 0 0 0 -0.417 0.417V6.25a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m3.333 0V4.861a0.417 0.417 0 0 0 -0.417 -0.417H5.973a0.417 0.417 0 0 0 -0.417 0.417V6.25a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m3.333 0V4.861a0.417 0.417 0 0 0 -0.417 -0.417h-1.389a0.417 0.417 0 0 0 -0.417 0.417V6.25a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m3.333 0V4.861a0.417 0.417 0 0 0 -0.417 -0.417h-1.389a0.417 0.417 0 0 0 -0.417 0.417V6.25a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m3.333 0V4.861a0.417 0.417 0 0 0 -0.417 -0.417h-1.389a0.417 0.417 0 0 0 -0.417 0.417V6.25a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m-11.667 3.333V8.194a0.417 0.417 0 0 0 -0.417 -0.417H4.306a0.417 0.417 0 0 0 -0.417 0.417V9.583a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m3.333 0V8.194a0.417 0.417 0 0 0 -0.417 -0.417H7.639a0.417 0.417 0 0 0 -0.417 0.417V9.583a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m3.333 0V8.194a0.417 0.417 0 0 0 -0.417 -0.417h-1.389a0.417 0.417 0 0 0 -0.417 0.417V9.583a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m3.333 0V8.194a0.417 0.417 0 0 0 -0.417 -0.417h-1.389a0.417 0.417 0 0 0 -0.417 0.417V9.583a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m-11.667 3.333v-1.389a0.417 0.417 0 0 0 -0.417 -0.417H2.639a0.417 0.417 0 0 0 -0.417 0.417V12.917a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417m10 0v-1.389a0.417 0.417 0 0 0 -0.417 -0.417H5.973a0.417 0.417 0 0 0 -0.417 0.417V12.917a0.417 0.417 0 0 0 0.417 0.417h8.056a0.417 0.417 0 0 0 0.417 -0.417m3.333 0v-1.389a0.417 0.417 0 0 0 -0.417 -0.417h-1.389a0.417 0.417 0 0 0 -0.417 0.417V12.917a0.417 0.417 0 0 0 0.417 0.417h1.389a0.417 0.417 0 0 0 0.417 -0.417" transform="translate(2, 3)" />
|
||||||
{isEnabled && <path d="M13 432L590 48" stroke="var(--red-500)" stroke-width="72" stroke-linecap="round" />}
|
{isEnabled && (
|
||||||
|
<>
|
||||||
|
<mask id="silent-typing-msg-mask">
|
||||||
|
<path fill="#fff" d="M0 0h24v24H0Z"></path>
|
||||||
|
<path stroke="#000" strokeWidth="5.99068" d="M0 24 24 0" transform="translate(-2, -3)"></path>
|
||||||
|
</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" />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</svg>
|
</svg>
|
||||||
</ChatBarButton>
|
</ChatBarButton>
|
||||||
);
|
);
|
||||||
|
|
|
@ -216,9 +216,12 @@ export default definePlugin({
|
||||||
replace: "true"
|
replace: "true"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
match: /\i\.\i\.copy/,
|
match: /\i\.\i\.copy(?=\(\i)/,
|
||||||
replace: "Vencord.Webpack.Common.Clipboard.copy"
|
replace: "Vencord.Webpack.Common.Clipboard.copy"
|
||||||
}]
|
}
|
||||||
|
],
|
||||||
|
all: true,
|
||||||
|
noWarn: true
|
||||||
},
|
},
|
||||||
// Automod add filter words
|
// Automod add filter words
|
||||||
{
|
{
|
||||||
|
|
|
@ -267,10 +267,6 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
||||||
name: "arHSM",
|
name: "arHSM",
|
||||||
id: 841509053422632990n
|
id: 841509053422632990n
|
||||||
},
|
},
|
||||||
F53: {
|
|
||||||
name: "Cassie (Code)",
|
|
||||||
id: 280411966126948353n
|
|
||||||
},
|
|
||||||
AutumnVN: {
|
AutumnVN: {
|
||||||
name: "AutumnVN",
|
name: "AutumnVN",
|
||||||
id: 393694671383166998n
|
id: 393694671383166998n
|
||||||
|
|
|
@ -22,6 +22,7 @@ import { findByPropsLazy, waitFor } from "../webpack";
|
||||||
export let React: typeof import("react");
|
export let React: typeof import("react");
|
||||||
export let useState: typeof React.useState;
|
export let useState: typeof React.useState;
|
||||||
export let useEffect: typeof React.useEffect;
|
export let useEffect: typeof React.useEffect;
|
||||||
|
export let useLayoutEffect: typeof React.useLayoutEffect;
|
||||||
export let useMemo: typeof React.useMemo;
|
export let useMemo: typeof React.useMemo;
|
||||||
export let useRef: typeof React.useRef;
|
export let useRef: typeof React.useRef;
|
||||||
export let useReducer: typeof React.useReducer;
|
export let useReducer: typeof React.useReducer;
|
||||||
|
@ -31,5 +32,5 @@ export const ReactDOM: typeof import("react-dom") & typeof import("react-dom/cli
|
||||||
|
|
||||||
waitFor("useState", m => {
|
waitFor("useState", m => {
|
||||||
React = m;
|
React = m;
|
||||||
({ useEffect, useState, useMemo, useRef, useReducer, useCallback } = React);
|
({ useEffect, useState, useLayoutEffect, useMemo, useRef, useReducer, useCallback } = React);
|
||||||
});
|
});
|
||||||
|
|
|
@ -163,8 +163,8 @@ waitFor(["open", "saveAccountChanges"], m => SettingsRouter = m);
|
||||||
|
|
||||||
export const PermissionsBits: t.PermissionsBits = findLazy(m => typeof m.ADMINISTRATOR === "bigint");
|
export const PermissionsBits: t.PermissionsBits = findLazy(m => typeof m.ADMINISTRATOR === "bigint");
|
||||||
|
|
||||||
export const { zustandCreate } = mapMangledModuleLazy(["useSyncExternalStoreWithSelector:", "Object.assign", /(\i)\?(\i)\(\1\):\2/], {
|
export const { zustandCreate } = mapMangledModuleLazy(["useSyncExternalStoreWithSelector:", "Object.assign"], {
|
||||||
zustandCreate: filters.byCode(/(\i)\?(\i)\(\1\):\2/)
|
zustandCreate: filters.byCode(/=>(\i)\?\i\(\1/)
|
||||||
});
|
});
|
||||||
|
|
||||||
export const { zustandPersist } = mapMangledModuleLazy(".onRehydrateStorage)?", {
|
export const { zustandPersist } = mapMangledModuleLazy(".onRehydrateStorage)?", {
|
||||||
|
|
Loading…
Reference in a new issue