Merge branch 'Vendicated:main' into main

This commit is contained in:
Bruno 2024-11-29 05:15:43 -03:00 committed by GitHub
commit 76b2527d7f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
113 changed files with 818 additions and 655 deletions

View file

@ -1,7 +1,7 @@
{ {
"name": "vencord", "name": "vencord",
"private": "true", "private": "true",
"version": "1.10.5", "version": "1.10.7",
"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": {
@ -35,6 +35,7 @@
"testTsc": "tsc --noEmit" "testTsc": "tsc --noEmit"
}, },
"dependencies": { "dependencies": {
"@intrnl/xxhash64": "^0.1.2",
"@sapphi-red/web-noise-suppressor": "0.3.5", "@sapphi-red/web-noise-suppressor": "0.3.5",
"@vap/core": "0.0.12", "@vap/core": "0.0.12",
"@vap/shiki": "0.10.5", "@vap/shiki": "0.10.5",

8
pnpm-lock.yaml generated
View file

@ -16,6 +16,9 @@ importers:
.: .:
dependencies: dependencies:
'@intrnl/xxhash64':
specifier: ^0.1.2
version: 0.1.2
'@sapphi-red/web-noise-suppressor': '@sapphi-red/web-noise-suppressor':
specifier: 0.3.5 specifier: 0.3.5
version: 0.3.5 version: 0.3.5
@ -537,6 +540,9 @@ packages:
resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==} resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==}
engines: {node: '>=18.18'} engines: {node: '>=18.18'}
'@intrnl/xxhash64@0.1.2':
resolution: {integrity: sha512-1+lx7j99fdph+uy3EnjQyr39KQZ7LP56+aWOr6finJWpgYpvb7XrhFUqDwnEk/wpPC98nCjAT6RulpW3crWjlg==}
'@jridgewell/gen-mapping@0.3.5': '@jridgewell/gen-mapping@0.3.5':
resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
engines: {node: '>=6.0.0'} engines: {node: '>=6.0.0'}
@ -2939,6 +2945,8 @@ snapshots:
'@humanwhocodes/retry@0.3.0': {} '@humanwhocodes/retry@0.3.0': {}
'@intrnl/xxhash64@0.1.2': {}
'@jridgewell/gen-mapping@0.3.5': '@jridgewell/gen-mapping@0.3.5':
dependencies: dependencies:
'@jridgewell/set-array': 1.2.1 '@jridgewell/set-array': 1.2.1

View file

@ -225,7 +225,7 @@ page.on("console", async e => {
plugin, plugin,
type, type,
id, id,
match: regex.replace(/\[A-Za-z_\$\]\[\\w\$\]\*/g, "\\i"), match: regex.replace(/\(\?:\[A-Za-z_\$\]\[\\w\$\]\*\)/g, "\\i"),
error: await maybeGetError(e.args()[3]) error: await maybeGetError(e.args()[3])
}); });

View file

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

View file

@ -1,11 +0,0 @@
.vc-expandableheader-center-flex {
display: flex;
place-items: center;
}
.vc-expandableheader-btn {
all: unset;
cursor: pointer;
width: 24px;
height: 24px;
}

View file

@ -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}
</>
);
}

View file

@ -18,9 +18,8 @@
import "./iconStyles.css"; import "./iconStyles.css";
import { getTheme, Theme } from "@utils/discord"; import { getIntlMessage } from "@utils/discord";
import { classes } from "@utils/misc"; import { classes } from "@utils/misc";
import { i18n } from "@webpack/common";
import type { PropsWithChildren } from "react"; import type { PropsWithChildren } from "react";
interface BaseIconProps extends IconProps { interface BaseIconProps extends IconProps {
@ -123,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>
); );
@ -133,7 +132,7 @@ export function InfoIcon(props: IconProps) {
export function OwnerCrownIcon(props: IconProps) { export function OwnerCrownIcon(props: IconProps) {
return ( return (
<Icon <Icon
aria-label={i18n.Messages.GUILD_OWNER} aria-label={getIntlMessage("GUILD_OWNER")}
{...props} {...props}
className={classes(props.className, "vc-owner-crown-icon")} className={classes(props.className, "vc-owner-crown-icon")}
role="img" role="img"
@ -212,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>
); );
@ -407,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>
);
} }

View file

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

View file

@ -247,7 +247,7 @@ function FullPatchInput({ setFind, setParsedFind, setMatch, setReplacement }: Fu
} }
try { try {
const parsed = (0, eval)(`(${fullPatch})`) as Patch; const parsed = (0, eval)(`([${fullPatch}][0])`) as Patch;
if (!parsed.find) throw new Error("No 'find' field"); if (!parsed.find) throw new Error("No 'find' field");
if (!parsed.replacement) throw new Error("No 'replacement' field"); if (!parsed.replacement) throw new Error("No 'replacement' field");

View file

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

View file

@ -27,13 +27,16 @@ export async function loadLazyChunks() {
const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?)\.then\(\i\.bind\(\i,"?([^)]+?)"?\)\)/g); const LazyChunkRegex = canonicalizeMatch(/(?:(?:Promise\.all\(\[)?(\i\.e\("?[^)]+?"?\)[^\]]*?)(?:\]\))?)\.then\(\i\.bind\(\i,"?([^)]+?)"?\)\)/g);
let foundCssDebuggingLoad = false;
async function searchAndLoadLazyChunks(factoryCode: string) { async function searchAndLoadLazyChunks(factoryCode: string) {
// Workaround to avoid loading the CSS debugging chunk which turns the app pink
const hasCssDebuggingLoad = foundCssDebuggingLoad ? false : (foundCssDebuggingLoad = factoryCode.includes(".cssDebuggingEnabled&&"));
const lazyChunks = factoryCode.matchAll(LazyChunkRegex); const lazyChunks = factoryCode.matchAll(LazyChunkRegex);
const validChunkGroups = new Set<[chunkIds: number[], entryPoint: number]>(); const validChunkGroups = new Set<[chunkIds: number[], entryPoint: number]>();
// Workaround for a chunk that depends on the ChannelMessage component but may be be force loaded before const shouldForceDefer = false;
// the chunk containing the component
const shouldForceDefer = factoryCode.includes(".Messages.GUILD_FEED_UNFEATURE_BUTTON_TEXT");
await Promise.all(Array.from(lazyChunks).map(async ([, rawChunkIds, entryPoint]) => { await Promise.all(Array.from(lazyChunks).map(async ([, rawChunkIds, entryPoint]) => {
const chunkIds = rawChunkIds ? Array.from(rawChunkIds.matchAll(Webpack.ChunkIdsRegex)).map(m => Number(m[1])) : []; const chunkIds = rawChunkIds ? Array.from(rawChunkIds.matchAll(Webpack.ChunkIdsRegex)).map(m => Number(m[1])) : [];
@ -45,6 +48,16 @@ export async function loadLazyChunks() {
let invalidChunkGroup = false; let invalidChunkGroup = false;
for (const id of chunkIds) { for (const id of chunkIds) {
if (hasCssDebuggingLoad) {
if (chunkIds.length > 1) {
throw new Error("Found multiple chunks in factory that loads the CSS debugging chunk");
}
invalidChunks.add(id);
invalidChunkGroup = true;
break;
}
if (wreq.u(id) == null || wreq.u(id) === "undefined.js") continue; if (wreq.u(id) == null || wreq.u(id) === "undefined.js") continue;
const isWorkerAsset = await fetch(wreq.p + wreq.u(id)) const isWorkerAsset = await fetch(wreq.p + wreq.u(id))

View file

@ -31,7 +31,7 @@ export default definePlugin({
match: /let\{[^}]*lostPermissionTooltipText:\i[^}]*\}=(\i),/, match: /let\{[^}]*lostPermissionTooltipText:\i[^}]*\}=(\i),/,
replace: "$&vencordProps=$1," replace: "$&vencordProps=$1,"
}, { }, {
match: /\.Messages\.GUILD_OWNER(?=.+?decorators:(\i)\(\)).+?\1=?\(\)=>.+?children:\[/, match: /#{intl::GUILD_OWNER}(?=.+?decorators:(\i)\(\)).+?\1=?\(\)=>.+?children:\[/,
replace: "$&...(typeof vencordProps=='undefined'?[]:Vencord.Api.MemberListDecorators.__getDecorators(vencordProps))," replace: "$&...(typeof vencordProps=='undefined'?[]:Vencord.Api.MemberListDecorators.__getDecorators(vencordProps)),"
} }
] ]

View file

@ -25,7 +25,7 @@ export default definePlugin({
authors: [Devs.Cyn], authors: [Devs.Cyn],
patches: [ patches: [
{ {
find: ".Messages.REMOVE_ATTACHMENT_BODY", find: "#{intl::REMOVE_ATTACHMENT_BODY}",
replacement: { replacement: {
match: /(?<=.container\)?,children:)(\[.+?\])/, match: /(?<=.container\)?,children:)(\[.+?\])/,
replace: "Vencord.Api.MessageAccessories._modifyAccessories($1,this.props)", replace: "Vencord.Api.MessageAccessories._modifyAccessories($1,this.props)",

View file

@ -27,7 +27,7 @@ export default definePlugin({
{ {
find: '"Message Username"', find: '"Message Username"',
replacement: { replacement: {
match: /\.Messages\.GUILD_COMMUNICATION_DISABLED_BOTTOM_SHEET_TITLE.+?}\),\i(?=\])/, match: /#{intl::GUILD_COMMUNICATION_DISABLED_BOTTOM_SHEET_TITLE}.+?}\),\i(?=\])/,
replace: "$&,...Vencord.Api.MessageDecorations.__addDecorationsToMessage(arguments[0])" replace: "$&,...Vencord.Api.MessageDecorations.__addDecorationsToMessage(arguments[0])"
} }
} }

View file

@ -25,7 +25,7 @@ export default definePlugin({
authors: [Devs.Arjix, Devs.hunt, Devs.Ven], authors: [Devs.Arjix, Devs.hunt, Devs.Ven],
patches: [ patches: [
{ {
find: ".Messages.EDIT_TEXTAREA_HELP", find: "#{intl::EDIT_TEXTAREA_HELP}",
replacement: { replacement: {
match: /(?<=,channel:\i\}\)\.then\().+?(?=return \i\.content!==this\.props\.message\.content&&\i\((.+?)\))/, match: /(?<=,channel:\i\}\)\.then\().+?(?=return \i\.content!==this\.props\.message\.content&&\i\((.+?)\))/,
replace: (match, args) => "" + replace: (match, args) => "" +

View file

@ -24,9 +24,9 @@ export default definePlugin({
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: "Messages.MESSAGE_UTILITIES_A11Y_LABEL", find: "#{intl::MESSAGE_UTILITIES_A11Y_LABEL}",
replacement: { replacement: {
match: /\.jsx\)\((\i\.\i),\{label:\i\.\i\.Messages\.MESSAGE_ACTION_REPLY.{0,200}?"reply-self".{0,50}?\}\):null(?=,.+?message:(\i))/, match: /\.jsx\)\((\i\.\i),\{label:\i\.\i\.string\(\i\.\i#{intl::MESSAGE_ACTION_REPLY}.{0,200}?"reply-self".{0,50}?\}\):null(?=,.+?message:(\i))/,
replace: "$&,Vencord.Api.MessagePopover._buildPopoverElements($1,$2)" replace: "$&,Vencord.Api.MessagePopover._buildPopoverElements($1,$2)"
} }
}], }],

View file

@ -25,16 +25,16 @@ export default definePlugin({
description: "Api required for plugins that modify the server list", description: "Api required for plugins that modify the server list",
patches: [ patches: [
{ {
find: "Messages.DISCODO_DISABLED", find: "#{intl::DISCODO_DISABLED}",
replacement: { replacement: {
match: /(?<=Messages\.DISCODO_DISABLED.+?return)(\(.{0,75}?tutorialContainer.+?}\))(?=}function)/, match: /(?<=#{intl::DISCODO_DISABLED}.+?return)(\(.{0,75}?tutorialContainer.+?}\))(?=}function)/,
replace: "[$1].concat(Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.Above))" replace: "[$1].concat(Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.Above))"
} }
}, },
{ {
find: "Messages.SERVERS,children", find: "#{intl::SERVERS}),children",
replacement: { replacement: {
match: /(?<=Messages\.SERVERS,children:)\i\.map\(\i\)/, match: /(?<=#{intl::SERVERS}\),children:)\i\.map\(\i\)/,
replace: "Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.In).concat($&)" replace: "Vencord.Api.ServerList.renderAll(Vencord.Api.ServerList.ServerListRenderPosition.In).concat($&)"
} }
} }

View file

@ -59,15 +59,7 @@ export default definePlugin({
replace: "$&return;" replace: "$&return;"
} }
] ]
},
{
find: ".installedLogHooks)",
replacement: {
// if getDebugLogging() returns false, the hooks don't get installed.
match: "getDebugLogging(){",
replace: "getDebugLogging(){return false;"
} }
},
], ],
startAt: StartAt.Init, startAt: StartAt.Init,

View file

@ -25,8 +25,9 @@ import ThemesTab from "@components/VencordSettings/ThemesTab";
import UpdaterTab from "@components/VencordSettings/UpdaterTab"; import UpdaterTab from "@components/VencordSettings/UpdaterTab";
import VencordTab from "@components/VencordSettings/VencordTab"; import VencordTab from "@components/VencordSettings/VencordTab";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { getIntlMessage } from "@utils/discord";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { i18n, React } from "@webpack/common"; import { React } from "@webpack/common";
import gitHash from "~git-hash"; import gitHash from "~git-hash";
@ -57,20 +58,20 @@ export default definePlugin({
] ]
}, },
{ {
find: "Messages.ACTIVITY_SETTINGS", find: ".SEARCH_NO_RESULTS&&0===",
replacement: [ replacement: [
{ {
match: /(?<=section:(.{0,50})\.DIVIDER\}\))([,;])(?=.{0,200}(\i)\.push.{0,100}label:(\i)\.header)/, match: /(?<=section:(.{0,50})\.DIVIDER\}\))([,;])(?=.{0,200}(\i)\.push.{0,100}label:(\i)\.header)/,
replace: (_, sectionTypes, commaOrSemi, elements, element) => `${commaOrSemi} $self.addSettings(${elements}, ${element}, ${sectionTypes}) ${commaOrSemi}` replace: (_, sectionTypes, commaOrSemi, elements, element) => `${commaOrSemi} $self.addSettings(${elements}, ${element}, ${sectionTypes}) ${commaOrSemi}`
}, },
{ {
match: /({(?=.+?function (\i).{0,120}(\i)=\i\.useMemo.{0,60}return \i\.useMemo\(\(\)=>\i\(\3).+?function\(\){return )\2(?=})/, match: /({(?=.+?function (\i).{0,160}(\i)=\i\.useMemo.{0,140}return \i\.useMemo\(\(\)=>\i\(\3).+?function\(\){return )\2(?=})/,
replace: (_, rest, settingsHook) => `${rest}$self.wrapSettingsHook(${settingsHook})` replace: (_, rest, settingsHook) => `${rest}$self.wrapSettingsHook(${settingsHook})`
} }
] ]
}, },
{ {
find: "Messages.USER_SETTINGS_ACTIONS_MENU_LABEL", find: "#{intl::USER_SETTINGS_ACTIONS_MENU_LABEL}",
replacement: { replacement: {
match: /(?<=function\((\i),\i\)\{)(?=let \i=Object.values\(\i.\i\).*?(\i\.\i)\.open\()/, match: /(?<=function\((\i),\i\)\{)(?=let \i=Object.values\(\i.\i\).*?(\i\.\i)\.open\()/,
replace: "$2.open($1);return;" replace: "$2.open($1);return;"
@ -148,13 +149,18 @@ export default definePlugin({
if (!header) return; if (!header) return;
try {
const names = { const names = {
top: i18n.Messages.USER_SETTINGS, top: getIntlMessage("USER_SETTINGS"),
aboveNitro: i18n.Messages.BILLING_SETTINGS, aboveNitro: getIntlMessage("BILLING_SETTINGS"),
belowNitro: i18n.Messages.APP_SETTINGS, belowNitro: getIntlMessage("APP_SETTINGS"),
aboveActivity: i18n.Messages.ACTIVITY_SETTINGS aboveActivity: getIntlMessage("ACTIVITY_SETTINGS")
}; };
return header === names[settingsLocation]; return header === names[settingsLocation];
} catch {
return firstChild === "PREMIUM";
}
}, },
patchedSettings: new WeakSet(), patchedSettings: new WeakSet(),

View file

@ -147,9 +147,9 @@ export default definePlugin({
settings, settings,
patches: [{ patches: [{
find: ".BEGINNING_DM.format", find: "#{intl::BEGINNING_DM}",
replacement: { replacement: {
match: /BEGINNING_DM\.format\(\{.+?\}\),(?=.{0,300}(\i)\.isMultiUserDM)/, match: /#{intl::BEGINNING_DM},{.+?}\),(?=.{0,300}(\i)\.isMultiUserDM)/,
replace: "$& $self.renderContributorDmWarningCard({ channel: $1 })," replace: "$& $self.renderContributorDmWarningCard({ channel: $1 }),"
} }
}], }],

View file

@ -69,7 +69,7 @@ export default definePlugin({
patches: [ patches: [
{ {
find: ".Messages.ACCOUNT_SPEAKING_WHILE_MUTED", find: "#{intl::ACCOUNT_SPEAKING_WHILE_MUTED}",
group: true, group: true,
replacement: [ replacement: [
{ {

View file

@ -41,7 +41,7 @@ export default definePlugin({
}, },
{ {
// Status emojis // Status emojis
find: ".Messages.GUILD_OWNER,", find: "#{intl::GUILD_OWNER}",
replacement: { replacement: {
match: /(?<=\.activityEmoji,.+?animate:)\i/, match: /(?<=\.activityEmoji,.+?animate:)\i/,
replace: "!0" replace: "!0"

View file

@ -28,10 +28,18 @@ export default definePlugin({
patches: [ patches: [
{ {
find: 'action:"EXPAND_ROLES"', find: 'action:"EXPAND_ROLES"',
replacement: { replacement: [
{
match: /(roles:\i(?=.+?(\i)\(!0\)[,;]\i\({action:"EXPAND_ROLES"}\)).+?\[\i,\2\]=\i\.useState\()!1\)/, match: /(roles:\i(?=.+?(\i)\(!0\)[,;]\i\({action:"EXPAND_ROLES"}\)).+?\[\i,\2\]=\i\.useState\()!1\)/,
replace: (_, rest, setExpandedRoles) => `${rest}!0)` replace: (_, rest, setExpandedRoles) => `${rest}!0)`
},
{
// Fix not calculating non-expanded roles because the above patch makes the default "expanded",
// which makes the collapse button never show up and calculation never occur
match: /(?<=useLayoutEffect\(\(\)=>{if\()\i/,
replace: isExpanded => "false"
} }
]
} }
] ]
}); });

View file

@ -86,9 +86,9 @@ export default definePlugin({
} }
}, },
{ {
find: ".Messages.ATTACHMENT_UTILITIES_SPOILER", find: "#{intl::ATTACHMENT_UTILITIES_SPOILER}",
replacement: { replacement: {
match: /(?<=children:\[)(?=.{10,80}tooltip:.{0,100}\i\.\i\.Messages\.ATTACHMENT_UTILITIES_SPOILER)/, match: /(?<=children:\[)(?=.{10,80}tooltip:.{0,100}#{intl::ATTACHMENT_UTILITIES_SPOILER})/,
replace: "arguments[0].canEdit!==false?$self.renderIcon(arguments[0]):null," replace: "arguments[0].canEdit!==false?$self.renderIcon(arguments[0]):null,"
}, },
}, },

View file

@ -36,7 +36,7 @@ export default definePlugin({
settings, settings,
patches: [ patches: [
{ {
find: "BAN_CONFIRM_TITLE.", find: "#{intl::BAN_CONFIRM_TITLE}",
replacement: { replacement: {
match: /src:\i\("?\d+"?\)/g, match: /src:\i\("?\d+"?\)/g,
replace: "src:$self.source" replace: "src:$self.source"

View file

@ -18,9 +18,10 @@
import { definePluginSettings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { getIntlMessage } from "@utils/discord";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy, findLazy, findStoreLazy } from "@webpack"; import { findByPropsLazy, findLazy, findStoreLazy } from "@webpack";
import { FluxDispatcher, i18n, useMemo } from "@webpack/common"; import { FluxDispatcher, useMemo } from "@webpack/common";
import FolderSideBar from "./FolderSideBar"; import FolderSideBar from "./FolderSideBar";
@ -122,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
@ -172,7 +173,7 @@ export default definePlugin({
// Disable expanding and collapsing folders transition in the normal GuildsBar sidebar // Disable expanding and collapsing folders transition in the normal GuildsBar sidebar
{ {
predicate: () => !settings.store.keepIcons, predicate: () => !settings.store.keepIcons,
match: /(?<=\.Messages\.SERVER_FOLDER_PLACEHOLDER.+?useTransition\)\()/, match: /(?<=#{intl::SERVER_FOLDER_PLACEHOLDER}.+?useTransition\)\()/,
replace: "$self.shouldShowTransition(arguments[0])&&" replace: "$self.shouldShowTransition(arguments[0])&&"
}, },
// If we are rendering the normal GuildsBar sidebar, we avoid rendering guilds from folders that are expanded // If we are rendering the normal GuildsBar sidebar, we avoid rendering guilds from folders that are expanded
@ -205,7 +206,7 @@ export default definePlugin({
} }
}, },
{ {
find: ".Messages.DISCODO_DISABLED", find: "#{intl::DISCODO_DISABLED}",
predicate: () => settings.store.closeAllHomeButton, predicate: () => settings.store.closeAllHomeButton,
replacement: { replacement: {
// Close all folders when clicking the home button // Close all folders when clicking the home button
@ -275,19 +276,29 @@ export default definePlugin({
makeGuildsBarGuildListFilter(isBetterFolders: boolean) { makeGuildsBarGuildListFilter(isBetterFolders: boolean) {
return child => { return child => {
if (isBetterFolders) { if (!isBetterFolders) return true;
return child?.props?.["aria-label"] === i18n.Messages.SERVERS;
try {
return child?.props?.["aria-label"] === getIntlMessage("SERVERS");
} 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;
}; };
}, },

View file

@ -34,9 +34,9 @@ export default definePlugin({
}, },
}, },
{ {
find: ".Messages.GIF,", find: "#{intl::GIF}",
replacement: { replacement: {
match: /alt:(\i)=(\i\.\i\.Messages\.GIF)(?=,[^}]*\}=(\i))/, match: /alt:(\i)=(\i\.\i\.string\(\i\.\i#{intl::GIF}\))(?=,[^}]*\}=(\i))/,
replace: replace:
// rename prop so we can always use default value // rename prop so we can always use default value
"alt_$$:$1=$self.altify($3)||$2", "alt_$$:$1=$self.altify($3)||$2",

View file

@ -63,9 +63,9 @@ export default definePlugin({
} }
}, },
{ {
find: "Messages.NOTE_PLACEHOLDER", find: "#{intl::NOTE_PLACEHOLDER}",
replacement: { replacement: {
match: /\.NOTE_PLACEHOLDER,/, match: /#{intl::NOTE_PLACEHOLDER}\),/,
replace: "$&spellCheck:!$self.noSpellCheck," replace: "$&spellCheck:!$self.noSpellCheck,"
} }
} }

View file

@ -47,7 +47,7 @@ export default definePlugin({
}, },
{ {
find: ".ADD_ROLE_A11Y_LABEL", find: "#{intl::ADD_ROLE_A11Y_LABEL}",
all: true, all: true,
predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles, predicate: () => Settings.plugins.BetterRoleDot.copyRoleColorInProfilePopout && !Settings.plugins.BetterRoleDot.bothStyles,
noWarn: true, noWarn: true,

View file

@ -60,7 +60,7 @@ export default definePlugin({
patches: [ patches: [
{ {
find: "Messages.AUTH_SESSIONS_SESSION_LOG_OUT", find: "#{intl::AUTH_SESSIONS_SESSION_LOG_OUT}",
replacement: [ replacement: [
// Replace children with a single label with state // Replace children with a single label with state
{ {

View file

@ -5,8 +5,9 @@
*/ */
import { openPluginModal } from "@components/PluginSettings/PluginModal"; import { openPluginModal } from "@components/PluginSettings/PluginModal";
import { getIntlMessage } from "@utils/discord";
import { isObjectEmpty } from "@utils/misc"; import { isObjectEmpty } from "@utils/misc";
import { Alerts, i18n, Menu, useMemo, useState } from "@webpack/common"; import { Alerts, Menu, useMemo, useState } from "@webpack/common";
import Plugins from "~plugins"; import Plugins from "~plugins";
@ -48,7 +49,7 @@ export default function PluginsSubmenu() {
query={query} query={query}
onChange={setQuery} onChange={setQuery}
ref={ref} ref={ref}
placeholder={i18n.Messages.SEARCH} placeholder={getIntlMessage("SEARCH")}
/> />
)} )}
/> />

View file

@ -7,10 +7,11 @@
import { definePluginSettings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { classNameFactory } from "@api/Styles"; import { classNameFactory } from "@api/Styles";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { getIntlMessage } from "@utils/discord";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { waitFor } from "@webpack"; import { waitFor } from "@webpack";
import { ComponentDispatch, FocusLock, i18n, Menu, useEffect, useRef } from "@webpack/common"; import { ComponentDispatch, FocusLock, Menu, useEffect, useRef } from "@webpack/common";
import type { HTMLAttributes, ReactElement } from "react"; import type { HTMLAttributes, ReactElement } from "react";
import PluginsSubmenu from "./PluginsSubmenu"; import PluginsSubmenu from "./PluginsSubmenu";
@ -111,7 +112,7 @@ export default definePlugin({
predicate: () => settings.store.disableFade predicate: () => settings.store.disableFade
}, },
{ // Load menu TOC eagerly { // Load menu TOC eagerly
find: "Messages.USER_SETTINGS_WITH_BUILD_OVERRIDE.format", find: "#{intl::USER_SETTINGS_WITH_BUILD_OVERRIDE}",
replacement: { replacement: {
match: /(\i)\(this,"handleOpenSettingsContextMenu",.{0,100}?null!=\i&&.{0,100}?(await Promise\.all[^};]*?\)\)).*?,(?=\1\(this)/, match: /(\i)\(this,"handleOpenSettingsContextMenu",.{0,100}?null!=\i&&.{0,100}?(await Promise\.all[^};]*?\)\)).*?,(?=\1\(this)/,
replace: "$&(async ()=>$2)()," replace: "$&(async ()=>$2)(),"
@ -119,7 +120,7 @@ export default definePlugin({
predicate: () => settings.store.eagerLoad predicate: () => settings.store.eagerLoad
}, },
{ // Settings cog context menu { // Settings cog context menu
find: "Messages.USER_SETTINGS_ACTIONS_MENU_LABEL", find: "#{intl::USER_SETTINGS_ACTIONS_MENU_LABEL}",
replacement: [ replacement: [
{ {
match: /(EXPERIMENTS:.+?)(\(0,\i.\i\)\(\))(?=\.filter\(\i=>\{let\{section:\i\}=)/, match: /(EXPERIMENTS:.+?)(\(0,\i.\i\)\(\))(?=\.filter\(\i=>\{let\{section:\i\}=)/,
@ -159,7 +160,7 @@ export default definePlugin({
if (item.section === "HEADER") { if (item.section === "HEADER") {
items.push({ label: item.label, items: [] }); items.push({ label: item.label, items: [] });
} else if (item.section === "DIVIDER") { } else if (item.section === "DIVIDER") {
items.push({ label: i18n.Messages.OTHER_OPTIONS, items: [] }); items.push({ label: getIntlMessage("OTHER_OPTIONS"), items: [] });
} else { } else {
items.at(-1)!.items.push(item); items.at(-1)!.items.push(item);
} }

View file

@ -14,7 +14,7 @@ import definePlugin, { OptionType, StartAt } from "@utils/types";
import { findByCodeLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack"; import { findByCodeLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack";
import { Button, Forms, ThemeStore, useStateFromStores } from "@webpack/common"; import { Button, Forms, ThemeStore, useStateFromStores } from "@webpack/common";
const ColorPicker = findComponentByCodeLazy(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); const ColorPicker = findComponentByCodeLazy("#{intl::USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR}", ".BACKGROUND_PRIMARY)");
const colorPresets = [ const colorPresets = [
"#1E1514", "#172019", "#13171B", "#1C1C28", "#402D2D", "#1E1514", "#172019", "#13171B", "#1C1C28", "#402D2D",
@ -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,

View file

@ -147,5 +147,5 @@ export default definePlugin({
replace: "$self.NoopLogger()" replace: "$self.NoopLogger()"
} }
} }
], ]
}); });

View file

@ -18,6 +18,7 @@
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { getCurrentChannel, getCurrentGuild } from "@utils/discord"; import { getCurrentChannel, getCurrentGuild } from "@utils/discord";
import { runtimeHashMessageKey } from "@utils/intlHash";
import { SYM_LAZY_CACHED, SYM_LAZY_GET } from "@utils/lazy"; import { SYM_LAZY_CACHED, SYM_LAZY_GET } from "@utils/lazy";
import { relaunch } from "@utils/native"; import { relaunch } from "@utils/native";
import { canonicalizeMatch, canonicalizeReplace, canonicalizeReplacement } from "@utils/patches"; import { canonicalizeMatch, canonicalizeReplace, canonicalizeReplacement } from "@utils/patches";
@ -104,6 +105,7 @@ function makeShortcuts() {
canonicalizeMatch, canonicalizeMatch,
canonicalizeReplace, canonicalizeReplace,
canonicalizeReplacement, canonicalizeReplacement,
runtimeHashMessageKey,
fakeRender: (component: ComponentType, props: any) => { fakeRender: (component: ComponentType, props: any) => {
const prevWin = fakeRenderWin?.deref(); const prevWin = fakeRenderWin?.deref();
const win = prevWin?.closed === false const win = prevWin?.closed === false

View file

@ -25,7 +25,7 @@ export default definePlugin({
authors: [Devs.Obsidian, Devs.Nuckyz], authors: [Devs.Obsidian, Devs.Nuckyz],
patches: [ patches: [
{ {
find: ".Messages.PREVIEW_BYTES_LEFT.format(", find: "#{intl::PREVIEW_BYTES_LEFT}",
replacement: { replacement: {
match: /\.footerGap.+?url:\i,fileName:\i,fileSize:\i}\),(?<=fileContents:(\i),bytesLeft:(\i).+?)/g, match: /\.footerGap.+?url:\i,fileName:\i,fileSize:\i}\),(?<=fileContents:(\i),bytesLeft:(\i).+?)/g,
replace: "$&$self.addCopyButton({fileContents:$1,bytesLeft:$2})," replace: "$&$self.addCopyButton({fileContents:$1,bytesLeft:$2}),"

View file

@ -67,7 +67,7 @@ export default definePlugin({
patches: [ patches: [
{ {
find: ".Messages.ERRORS_UNEXPECTED_CRASH", find: "#{intl::ERRORS_UNEXPECTED_CRASH}",
replacement: { replacement: {
match: /this\.setState\((.+?)\)/, match: /this\.setState\((.+?)\)/,
replace: "$self.handleCrash(this,$1);" replace: "$self.handleCrash(this,$1);"

View file

@ -41,7 +41,7 @@ export default definePlugin({
{ {
find: "DefaultCustomizationSections", find: "DefaultCustomizationSections",
replacement: { replacement: {
match: /(?<=USER_SETTINGS_AVATAR_DECORATION},"decoration"\),)/, match: /(?<=#{intl::USER_SETTINGS_AVATAR_DECORATION}\)},"decoration"\),)/,
replace: "$self.DecorSection()," replace: "$self.DecorSection(),"
} }
}, },

View file

@ -93,7 +93,7 @@ export const useAuthorizationStore = proxyLazy(() => zustandCreate(
} as AuthorizationState), } as AuthorizationState),
{ {
name: "decor-auth", name: "decor-auth",
getStorage: () => indexedDBStorage, storage: indexedDBStorage,
partialize: state => ({ tokens: state.tokens }), partialize: state => ({ tokens: state.tokens }),
onRehydrateStorage: () => state => state?.init() onRehydrateStorage: () => state => state?.init()
} }

View file

@ -95,10 +95,13 @@ export const useUsersDecorationsStore = proxyLazy(() => zustandCreate((set: any,
} as UsersDecorationsState))); } as UsersDecorationsState)));
export function useUserDecorAvatarDecoration(user?: User): AvatarDecoration | null | undefined { export function useUserDecorAvatarDecoration(user?: User): AvatarDecoration | null | undefined {
try {
const [decorAvatarDecoration, setDecorAvatarDecoration] = useState<string | null>(user ? useUsersDecorationsStore.getState().getAsset(user.id) ?? null : null); const [decorAvatarDecoration, setDecorAvatarDecoration] = useState<string | null>(user ? useUsersDecorationsStore.getState().getAsset(user.id) ?? null : null);
useEffect(() => { useEffect(() => {
const destructor = useUsersDecorationsStore.subscribe( const destructor = (() => {
try {
return useUsersDecorationsStore.subscribe(
state => { state => {
if (!user) return; if (!user) return;
const newDecorAvatarDecoration = state.getAsset(user.id); const newDecorAvatarDecoration = state.getAsset(user.id);
@ -106,13 +109,25 @@ export function useUserDecorAvatarDecoration(user?: User): AvatarDecoration | nu
if (decorAvatarDecoration !== newDecorAvatarDecoration) setDecorAvatarDecoration(newDecorAvatarDecoration); if (decorAvatarDecoration !== newDecorAvatarDecoration) setDecorAvatarDecoration(newDecorAvatarDecoration);
} }
); );
} catch {
return () => { };
}
})();
try {
if (user) { if (user) {
const { fetch: fetchUserDecorAvatarDecoration } = useUsersDecorationsStore.getState(); const { fetch: fetchUserDecorAvatarDecoration } = useUsersDecorationsStore.getState();
fetchUserDecorAvatarDecoration(user.id); fetchUserDecorAvatarDecoration(user.id);
} }
} catch { }
return destructor; return destructor;
}, []); }, []);
return decorAvatarDecoration ? { asset: decorAvatarDecoration, skuId: SKU_ID } : null; return decorAvatarDecoration ? { asset: decorAvatarDecoration, skuId: SKU_ID } : null;
} catch (e) {
console.error(e);
}
return null;
} }

View file

@ -5,7 +5,8 @@
*/ */
import { PlusIcon } from "@components/Icons"; import { PlusIcon } from "@components/Icons";
import { i18n, Text } from "@webpack/common"; import { getIntlMessage } from "@utils/discord";
import { Text } from "@webpack/common";
import { HTMLProps } from "react"; import { HTMLProps } from "react";
import { DecorationGridItem } from "."; import { DecorationGridItem } from ".";
@ -24,7 +25,7 @@ export default function DecorationGridCreate(props: DecorationGridCreateProps) {
variant="text-xs/normal" variant="text-xs/normal"
color="header-primary" color="header-primary"
> >
{i18n.Messages.CREATE} {getIntlMessage("CREATE")}
</Text> </Text>
</DecorationGridItem >; </DecorationGridItem >;
} }

View file

@ -5,7 +5,8 @@
*/ */
import { NoEntrySignIcon } from "@components/Icons"; import { NoEntrySignIcon } from "@components/Icons";
import { i18n, Text } from "@webpack/common"; import { getIntlMessage } from "@utils/discord";
import { Text } from "@webpack/common";
import { HTMLProps } from "react"; import { HTMLProps } from "react";
import { DecorationGridItem } from "."; import { DecorationGridItem } from ".";
@ -24,7 +25,7 @@ export default function DecorationGridNone(props: DecorationGridNoneProps) {
variant="text-xs/normal" variant="text-xs/normal"
color="header-primary" color="header-primary"
> >
{i18n.Messages.NONE} {getIntlMessage("NONE")}
</Text> </Text>
</DecorationGridItem >; </DecorationGridItem >;
} }

View file

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

View file

@ -20,7 +20,7 @@ import { AvatarDecorationModalPreview } from "../components";
const FileUpload = findComponentByCodeLazy("fileUploadInput,"); const FileUpload = findComponentByCodeLazy("fileUploadInput,");
const { HelpMessage, HelpMessageTypes } = mapMangledModuleLazy('POSITIVE=3]="POSITIVE', { const { HelpMessage, HelpMessageTypes } = mapMangledModuleLazy('POSITIVE=3]="POSITIVE', {
HelpMessageTypes: filters.byProps("POSITIVE", "WARNING"), HelpMessageTypes: filters.byProps("POSITIVE", "WARNING", "INFO"),
HelpMessage: filters.byCode(".iconDiv") HelpMessage: filters.byCode(".iconDiv")
}); });
@ -119,8 +119,8 @@ function CreateDecorationModal(props: ModalProps) {
/> />
</div> </div>
</div> </div>
<Forms.FormText type="description" className={Margins.bottom16}> <HelpMessage messageType={HelpMessageTypes.INFO} className={Margins.bottom8}>
<br />You can receive updates on your decoration's review by joining <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 => {
e.preventDefault(); e.preventDefault();
@ -138,8 +138,8 @@ function CreateDecorationModal(props: ModalProps) {
}} }}
> >
Decor's Discord server Decor's Discord server
</Link>. </Link> and allow direct messages.
</Forms.FormText> </HelpMessage>
</ErrorBoundary> </ErrorBoundary>
</ModalContent> </ModalContent>
<ModalFooter className={cl("modal-footer")}> <ModalFooter className={cl("modal-footer")}>

View file

@ -27,7 +27,7 @@ export default definePlugin({
authors: [Devs.Nuckyz], authors: [Devs.Nuckyz],
patches: [ patches: [
{ {
find: ".Messages.BOT_CALL_IDLE_DISCONNECT", find: "#{intl::BOT_CALL_IDLE_DISCONNECT_2}",
replacement: { replacement: {
match: /,?(?=\i\(this,"idleTimeout",new \i\.\i\))/, match: /,?(?=\i\(this,"idleTimeout",new \i\.\i\))/,
replace: ";return;" replace: ";return;"

View file

@ -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) => {

View file

@ -20,11 +20,11 @@ import { addPreEditListener, addPreSendListener, removePreEditListener, removePr
import { definePluginSettings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { ApngBlendOp, ApngDisposeOp, importApngJs } from "@utils/dependencies"; import { ApngBlendOp, ApngDisposeOp, importApngJs } from "@utils/dependencies";
import { getCurrentGuild } from "@utils/discord"; import { getCurrentGuild, getEmojiURL } from "@utils/discord";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import definePlugin, { OptionType, Patch } from "@utils/types"; import definePlugin, { OptionType, Patch } from "@utils/types";
import { findByCodeLazy, findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack"; import { findByCodeLazy, findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack";
import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, Forms, GuildMemberStore, IconUtils, lodash, Parser, PermissionsBits, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common"; import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, Forms, GuildMemberStore, lodash, Parser, PermissionsBits, PermissionStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common";
import type { Emoji } from "@webpack/types"; import type { Emoji } from "@webpack/types";
import type { Message } from "discord-types/general"; import type { Message } from "discord-types/general";
import { applyPalette, GIFEncoder, quantize } from "gifenc"; import { applyPalette, GIFEncoder, quantize } from "gifenc";
@ -285,7 +285,7 @@ export default definePlugin({
}, },
// Remove boost requirements to stream with high quality // Remove boost requirements to stream with high quality
{ {
find: "STREAM_FPS_OPTION.format", find: "#{intl::STREAM_FPS_OPTION}",
predicate: () => settings.store.enableStreamQualityBypass, predicate: () => settings.store.enableStreamQualityBypass,
replacement: { replacement: {
match: /guildPremiumTier:\i\.\i\.TIER_\d,?/g, match: /guildPremiumTier:\i\.\i\.TIER_\d,?/g,
@ -356,7 +356,7 @@ export default definePlugin({
] ]
}, },
{ {
find: ".Messages.STICKER_POPOUT_UNJOINED_PRIVATE_GUILD_DESCRIPTION.format", find: "#{intl::STICKER_POPOUT_UNJOINED_PRIVATE_GUILD_DESCRIPTION}",
predicate: () => settings.store.transformStickers, predicate: () => settings.store.transformStickers,
replacement: [ replacement: [
{ {
@ -381,7 +381,7 @@ export default definePlugin({
} }
}, },
{ {
find: ".Messages.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION", find: "#{intl::EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION}",
predicate: () => settings.store.transformEmojis, predicate: () => settings.store.transformEmojis,
replacement: { replacement: {
// Add the fake nitro emoji notice // Add the fake nitro emoji notice
@ -920,7 +920,7 @@ export default definePlugin({
const emojiString = `<${emoji.animated ? "a" : ""}:${emoji.originalName || emoji.name}:${emoji.id}>`; const emojiString = `<${emoji.animated ? "a" : ""}:${emoji.originalName || emoji.name}:${emoji.id}>`;
const url = new URL(IconUtils.getEmojiURL({ id: emoji.id, animated: emoji.animated, size: s.emojiSize })); const url = new URL(getEmojiURL(emoji.id, emoji.animated, s.emojiSize));
url.searchParams.set("size", s.emojiSize.toString()); url.searchParams.set("size", s.emojiSize.toString());
url.searchParams.set("name", emoji.name); url.searchParams.set("name", emoji.name);
@ -953,7 +953,7 @@ export default definePlugin({
hasBypass = true; hasBypass = true;
const url = new URL(IconUtils.getEmojiURL({ id: emoji.id, animated: emoji.animated, size: s.emojiSize })); const url = new URL(getEmojiURL(emoji.id, emoji.animated, s.emojiSize));
url.searchParams.set("size", s.emojiSize.toString()); url.searchParams.set("size", s.emojiSize.toString());
url.searchParams.set("name", emoji.name); url.searchParams.set("name", emoji.name);

View file

@ -108,10 +108,10 @@ interface ProfileModalProps {
isTryItOutFlow: boolean; isTryItOutFlow: boolean;
} }
const ColorPicker = findComponentByCodeLazy<ColorPickerProps>(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); const ColorPicker = findComponentByCodeLazy<ColorPickerProps>("#{intl::USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR}", ".BACKGROUND_PRIMARY)");
const ProfileModal = findComponentByCodeLazy<ProfileModalProps>("isTryItOutFlow:", "pendingThemeColors:", "pendingAvatarDecoration:", "EDIT_PROFILE_BANNER"); const ProfileModal = findComponentByCodeLazy<ProfileModalProps>("isTryItOutFlow:", "pendingThemeColors:", "pendingAvatarDecoration:", "EDIT_PROFILE_BANNER");
const requireColorPicker = extractAndLoadChunksLazy(["USER_SETTINGS_PROFILE_COLOR_DEFAULT_BUTTON.format"], /createPromise:\(\)=>\i\.\i(\("?.+?"?\)).then\(\i\.bind\(\i,"?(.+?)"?\)\)/); const requireColorPicker = extractAndLoadChunksLazy(["#{intl::USER_SETTINGS_PROFILE_COLOR_DEFAULT_BUTTON}"], /createPromise:\(\)=>\i\.\i(\("?.+?"?\)).then\(\i\.bind\(\i,"?(.+?)"?\)\)/);
export default definePlugin({ export default definePlugin({
name: "FakeProfileThemes", name: "FakeProfileThemes",
@ -126,9 +126,9 @@ export default definePlugin({
} }
}, },
{ {
find: ".USER_SETTINGS_RESET_PROFILE_THEME", find: "#{intl::USER_SETTINGS_RESET_PROFILE_THEME}",
replacement: { replacement: {
match: /RESET_PROFILE_THEME}\)(?<=color:(\i),.{0,500}?color:(\i),.{0,500}?)/, match: /#{intl::USER_SETTINGS_RESET_PROFILE_THEME}\)}\)(?<=color:(\i),.{0,500}?color:(\i),.{0,500}?)/,
replace: "$&,$self.addCopy3y3Button({primary:$1,accent:$2})" replace: "$&,$self.addCopy3y3Button({primary:$1,accent:$2})"
} }
} }

View file

@ -27,7 +27,7 @@ export default definePlugin({
authors: [Devs.D3SOX, Devs.Nickyux], authors: [Devs.D3SOX, Devs.Nickyux],
patches: [ patches: [
{ {
find: ".Messages.GUILD_OWNER,", find: "#{intl::GUILD_OWNER}",
replacement: { replacement: {
match: /,isOwner:(\i),/, match: /,isOwner:(\i),/,
replace: ",_isOwner:$1=$self.isGuildOwner(e)," replace: ",_isOwner:$1=$self.isGuildOwner(e),"

View file

@ -26,7 +26,7 @@ export default definePlugin({
{ {
find: ".PANEL}),nicknameIcons", find: ".PANEL}),nicknameIcons",
replacement: { replacement: {
match: /USER_PROFILE_MEMBER_SINCE,.{0,100}userId:(\i\.id)}\)}\)/, match: /#{intl::USER_PROFILE_MEMBER_SINCE}\),.{0,100}userId:(\i\.id)}\)}\)/,
replace: "$&,$self.FriendsSinceComponent({userId:$1,isSidebar:true})" replace: "$&,$self.FriendsSinceComponent({userId:$1,isSidebar:true})"
} }
}, },
@ -34,7 +34,7 @@ export default definePlugin({
{ {
find: "action:\"PRESS_APP_CONNECTION\"", find: "action:\"PRESS_APP_CONNECTION\"",
replacement: { replacement: {
match: /USER_PROFILE_MEMBER_SINCE,.{0,100}userId:(\i\.id),.{0,100}}\)}\),/, match: /#{intl::USER_PROFILE_MEMBER_SINCE}\),.{0,100}userId:(\i\.id),.{0,100}}\)}\),/,
replace: "$&,$self.FriendsSinceComponent({userId:$1,isSidebar:false})," replace: "$&,$self.FriendsSinceComponent({userId:$1,isSidebar:false}),"
} }
} }

View file

@ -19,10 +19,11 @@
import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu";
import { migratePluginSettings } from "@api/Settings"; import { migratePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { getIntlMessage } from "@utils/discord";
import { NoopComponent } from "@utils/react"; import { NoopComponent } from "@utils/react";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { filters, findByPropsLazy, waitFor } from "@webpack"; import { filters, findByPropsLazy, waitFor } from "@webpack";
import { ChannelStore, ContextMenuApi, i18n, UserStore } from "@webpack/common"; import { ChannelStore, ContextMenuApi, UserStore } from "@webpack/common";
import { Message } from "discord-types/general"; import { Message } from "discord-types/general";
const { useMessageMenu } = findByPropsLazy("useMessageMenu"); const { useMessageMenu } = findByPropsLazy("useMessageMenu");
@ -41,7 +42,7 @@ function MessageMenu({ message, channel, onHeightUpdate }) {
return useMessageMenu({ return useMessageMenu({
navId: "message-actions", navId: "message-actions",
ariaLabel: i18n.Messages.MESSAGE_UTILITIES_A11Y_LABEL, ariaLabel: getIntlMessage("MESSAGE_UTILITIES_A11Y_LABEL"),
message, message,
channel, channel,
@ -72,7 +73,7 @@ const contextMenuPatch: NavContextMenuPatchCallback = (children, props: MessageA
const group = findGroupChildrenByChildId("devmode-copy-id", children, true); const group = findGroupChildrenByChildId("devmode-copy-id", children, true);
group?.push( group?.push(
CopyIdMenuItem({ id: props.message.author.id, label: i18n.Messages.COPY_ID_AUTHOR }) CopyIdMenuItem({ id: props.message.author.id, label: getIntlMessage("COPY_ID_AUTHOR") })
); );
}; };

View file

@ -92,7 +92,7 @@ export default definePlugin({
patches: [ patches: [
{ {
find: ".Messages.ACCOUNT_SPEAKING_WHILE_MUTED", find: "#{intl::ACCOUNT_SPEAKING_WHILE_MUTED}",
replacement: { replacement: {
match: /this\.renderNameZone\(\).+?children:\[/, match: /this\.renderNameZone\(\).+?children:\[/,
replace: "$&$self.GameActivityToggleButton()," replace: "$&$self.GameActivityToggleButton(),"

View file

@ -166,7 +166,7 @@ export default definePlugin({
patches: [ patches: [
{ {
find: "Messages.WELCOME_CTA_LABEL", find: "#{intl::WELCOME_CTA_LABEL}",
replacement: { replacement: {
match: /innerClassName:\i\.welcomeCTAButton,(?<={channel:\i,message:\i}=(\i).{0,400}?)/, match: /innerClassName:\i\.welcomeCTAButton,(?<={channel:\i,message:\i}=(\i).{0,400}?)/,
replace: "$&onContextMenu:(vcEvent)=>$self.pickSticker(vcEvent, $1)," replace: "$&onContextMenu:(vcEvent)=>$self.pickSticker(vcEvent, $1),"

View file

@ -260,9 +260,9 @@ export default definePlugin({
} }
}, },
{ {
find: ".Messages.SETTINGS_GAMES_TOGGLE_OVERLAY", find: "#{intl::SETTINGS_GAMES_TOGGLE_OVERLAY}",
replacement: { replacement: {
match: /\.Messages\.SETTINGS_GAMES_TOGGLE_OVERLAY.+?}\(\),(?<={overlay:\i,.+?=(\i),.+?)(?=!(\i))/, match: /#{intl::SETTINGS_GAMES_TOGGLE_OVERLAY}.+?}\(\),(?<={overlay:\i,.+?=(\i),.+?)(?=!(\i))/,
replace: (m, props, nowPlaying) => `${m}$self.renderToggleGameActivityButton(${props},${nowPlaying}),` replace: (m, props, nowPlaying) => `${m}$self.renderToggleGameActivityButton(${props},${nowPlaying}),`
} }
}, },

View file

@ -32,7 +32,7 @@ export default definePlugin({
patches: [ patches: [
// Counts header // Counts header
{ {
find: ".FRIENDS_ALL_HEADER", find: "#{intl::FRIENDS_ALL_HEADER}",
replacement: { replacement: {
match: /toString\(\)\}\);case (\i\.\i)\.BLOCKED/, match: /toString\(\)\}\);case (\i\.\i)\.BLOCKED/,
replace: 'toString()});case $1.IMPLICIT:return "Implicit — "+arguments[1];case $1.BLOCKED' replace: 'toString()});case $1.IMPLICIT:return "Implicit — "+arguments[1];case $1.BLOCKED'
@ -48,9 +48,9 @@ export default definePlugin({
}, },
// Sections header // Sections header
{ {
find: ".FRIENDS_SECTION_ONLINE", find: "#{intl::FRIENDS_SECTION_ONLINE}",
replacement: { replacement: {
match: /(\(0,\i\.jsx\)\(\i\.TabBar\.Item,\{id:\i\.\i)\.BLOCKED,className:([^\s]+?)\.item,children:\i\.\i\.Messages\.BLOCKED\}\)/, match: /(\(0,\i\.jsx\)\(\i\.TabBar\.Item,\{id:\i\.\i)\.BLOCKED,className:([^\s]+?)\.item,children:\i\.\i\.string\(\i\.\i#{intl::BLOCKED}\)\}\)/,
replace: "$1.IMPLICIT,className:$2.item,children:\"Implicit\"}),$&" replace: "$1.IMPLICIT,className:$2.item,children:\"Implicit\"}),$&"
}, },
}, },
@ -117,7 +117,7 @@ export default definePlugin({
wrapSort(comparator: Function, row: any) { wrapSort(comparator: Function, row: any) {
return row.type === 5 return row.type === 5
? -UserAffinitiesStore.getUserAffinity(row.user.id)?.affinity ?? 0 ? -(UserAffinitiesStore.getUserAffinity(row.user.id)?.affinity ?? 0)
: comparator(row); : comparator(row);
}, },

View file

@ -111,7 +111,7 @@ export default definePlugin({
patches: [ patches: [
{ {
// Indicator // Indicator
find: ".Messages.MESSAGE_EDITED,", find: "#{intl::MESSAGE_EDITED}",
replacement: { replacement: {
match: /let\{className:\i,message:\i[^}]*\}=(\i)/, match: /let\{className:\i,message:\i[^}]*\}=(\i)/,
replace: "try {$1 && $self.INV_REGEX.test($1.message.content) ? $1.content.push($self.indicator()) : null } catch {};$&" replace: "try {$1 && $self.INV_REGEX.test($1.message.content) ? $1.content.push($self.indicator()) : null } catch {};$&"

View file

@ -19,7 +19,7 @@
import * as DataStore from "@api/DataStore"; import * as DataStore from "@api/DataStore";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { ChannelRouter, SelectedChannelStore, SelectedGuildStore } from "@webpack/common"; import { ChannelRouter, ChannelStore, NavigationRouter, SelectedChannelStore, SelectedGuildStore } from "@webpack/common";
export interface LogoutEvent { export interface LogoutEvent {
type: "LOGOUT"; type: "LOGOUT";
@ -45,6 +45,16 @@ export default definePlugin({
description: "Attempt to navigate to the channel you were in before switching accounts or loading Discord.", description: "Attempt to navigate to the channel you were in before switching accounts or loading Discord.",
authors: [Devs.Nuckyz], authors: [Devs.Nuckyz],
patches: [
{
find: '"Switching accounts"',
replacement: {
match: /goHomeAfterSwitching:\i/,
replace: "goHomeAfterSwitching:!1"
}
}
],
flux: { flux: {
LOGOUT(e: LogoutEvent) { LOGOUT(e: LogoutEvent) {
({ isSwitchingAccount } = e); ({ isSwitchingAccount } = e);
@ -55,7 +65,11 @@ export default definePlugin({
isSwitchingAccount = false; isSwitchingAccount = false;
if (previousCache?.channelId) { if (previousCache?.channelId) {
if (ChannelStore.hasChannel(previousCache.channelId)) {
ChannelRouter.transitionToChannel(previousCache.channelId); ChannelRouter.transitionToChannel(previousCache.channelId);
} else {
NavigationRouter.transitionToGuild("@me");
}
} }
}, },

View file

@ -62,7 +62,7 @@ export default definePlugin({
patches: [ patches: [
{ {
find: ".LOADING_DID_YOU_KNOW", find: "#{intl::LOADING_DID_YOU_KNOW}",
replacement: [ replacement: [
{ {
match: /"_loadingText".+?(?=(\i)\[.{0,10}\.random)/, match: /"_loadingText".+?(?=(\i)\[.{0,10}\.random)/,

View file

@ -74,7 +74,7 @@ export default definePlugin({
{ {
find: ".invitesDisabledTooltip", find: ".invitesDisabledTooltip",
replacement: { replacement: {
match: /\.VIEW_AS_ROLES_MENTIONS_WARNING.{0,100}(?=])/, match: /#{intl::VIEW_AS_ROLES_MENTIONS_WARNING}.{0,100}(?=])/,
replace: "$&,$self.renderTooltip(arguments[0].guild)" replace: "$&,$self.renderTooltip(arguments[0].guild)"
}, },
predicate: () => settings.store.toolTip predicate: () => settings.store.toolTip

View file

@ -24,12 +24,12 @@ import { Settings } from "@api/Settings";
import { disableStyle, enableStyle } from "@api/Styles"; import { disableStyle, enableStyle } from "@api/Styles";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { proxyLazy } from "@utils/lazy"; import { getIntlMessage } from "@utils/discord";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import { classes } from "@utils/misc"; import { classes } from "@utils/misc";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByCodeLazy, findByPropsLazy } from "@webpack"; import { findByPropsLazy } from "@webpack";
import { ChannelStore, FluxDispatcher, i18n, Menu, MessageStore, Parser, SelectedChannelStore, Timestamp, UserStore, useStateFromStores } from "@webpack/common"; import { ChannelStore, FluxDispatcher, Menu, MessageStore, Parser, SelectedChannelStore, Timestamp, UserStore, useStateFromStores } from "@webpack/common";
import { Message } from "discord-types/general"; import { Message } from "discord-types/general";
import overlayStyle from "./deleteStyleOverlay.css?managed"; import overlayStyle from "./deleteStyleOverlay.css?managed";
@ -43,7 +43,6 @@ interface MLMessage extends Message {
} }
const styles = findByPropsLazy("edited", "communicationDisabled", "isSystemMessage"); const styles = findByPropsLazy("edited", "communicationDisabled", "isSystemMessage");
const getMessage = findByCodeLazy('replace(/^\\n+|\\n+$/g,"")');
function addDeleteStyle() { function addDeleteStyle() {
if (Settings.plugins.MessageLogger.deleteStyle === "text") { if (Settings.plugins.MessageLogger.deleteStyle === "text") {
@ -178,7 +177,7 @@ export default definePlugin({
isEdited={true} isEdited={true}
isInline={false} isInline={false}
> >
<span className={styles.edited}>{" "}({i18n.Messages.MESSAGE_EDITED})</span> <span className={styles.edited}>{" "}({getIntlMessage("MESSAGE_EDITED")})</span>
</Timestamp> </Timestamp>
</div> </div>
))} ))}
@ -312,9 +311,33 @@ export default definePlugin({
); );
}, },
Messages: proxyLazy(() => ({ // DELETED_MESSAGE_COUNT: getMessage("{count, plural, =0 {No deleted messages} one {{count} deleted message} other {{count} deleted messages}}")
DELETED_MESSAGE_COUNT: getMessage("{count, plural, =0 {No deleted messages} one {{count} deleted message} other {{count} deleted messages}}") // TODO: Find a better way to generate intl messages
})), DELETED_MESSAGE_COUNT: () => ({
ast: [[
6,
"count",
{
"=0": ["No deleted messages"],
one: [
[
1,
"count"
],
" deleted message"
],
other: [
[
1,
"count"
],
" deleted messages"
]
},
0,
"cardinal"
]]
}),
patches: [ patches: [
{ {
@ -445,7 +468,7 @@ export default definePlugin({
{ {
// Message content renderer // Message content renderer
find: "Messages.MESSAGE_EDITED,\")\"", find: "#{intl::MESSAGE_EDITED}",
replacement: [ replacement: [
{ {
// Render editHistory in the deepest div for message content // Render editHistory in the deepest div for message content
@ -497,7 +520,7 @@ export default definePlugin({
}, },
{ {
// Message group rendering // Message group rendering
find: "Messages.NEW_MESSAGES_ESTIMATED_WITH_DATE", find: "#{intl::NEW_MESSAGES_ESTIMATED_WITH_DATE}",
replacement: [ replacement: [
{ {
match: /(\i).type===\i\.\i\.MESSAGE_GROUP_BLOCKED\|\|/, match: /(\i).type===\i\.\i\.MESSAGE_GROUP_BLOCKED\|\|/,
@ -505,7 +528,7 @@ export default definePlugin({
}, },
{ {
match: /(\i).type===\i\.\i\.MESSAGE_GROUP_BLOCKED\?.*?:/, match: /(\i).type===\i\.\i\.MESSAGE_GROUP_BLOCKED\?.*?:/,
replace: '$&$1.type==="MESSAGE_GROUP_DELETED"?$self.Messages.DELETED_MESSAGE_COUNT:', replace: '$&$1.type==="MESSAGE_GROUP_DELETED"?$self.DELETED_MESSAGE_COUNT:',
}, },
], ],
predicate: () => Settings.plugins.MessageLogger.collapseDeleted predicate: () => Settings.plugins.MessageLogger.collapseDeleted

View file

@ -19,6 +19,7 @@
import { definePluginSettings } from "@api/Settings"; import { definePluginSettings } from "@api/Settings";
import { Flex } from "@components/Flex"; import { Flex } from "@components/Flex";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { getIntlMessage } from "@utils/discord";
import { Margins } from "@utils/margins"; import { Margins } from "@utils/margins";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByCodeLazy, findLazy } from "@webpack"; import { findByCodeLazy, findLazy } from "@webpack";
@ -187,13 +188,13 @@ export default definePlugin({
} }
}, },
{ {
find: ".DISCORD_SYSTEM_MESSAGE_BOT_TAG_TOOLTIP_OFFICIAL,", find: "#{intl::DISCORD_SYSTEM_MESSAGE_BOT_TAG_TOOLTIP_OFFICIAL}",
replacement: [ replacement: [
// make the tag show the right text // make the tag show the right text
{ {
match: /(switch\((\i)\){.+?)case (\i(?:\.\i)?)\.BOT:default:(\i)=.{0,40}(\i\.\i\.Messages)\.APP_TAG/, match: /(switch\((\i)\){.+?)case (\i(?:\.\i)?)\.BOT:default:(\i)=(.{0,40}#{intl::APP_TAG}\))/,
replace: (_, origSwitch, variant, tags, displayedText, strings) => replace: (_, origSwitch, variant, tags, displayedText, originalText) =>
`${origSwitch}default:{${displayedText} = $self.getTagText(${tags}[${variant}], ${strings})}` `${origSwitch}default:{${displayedText} = $self.getTagText(${tags}[${variant}],${originalText})}`
}, },
// show OP tags correctly // show OP tags correctly
{ {
@ -217,7 +218,7 @@ export default definePlugin({
}, },
// in the member list // in the member list
{ {
find: ".Messages.GUILD_OWNER,", find: "#{intl::GUILD_OWNER}",
replacement: { replacement: {
match: /(?<type>\i)=\(null==.{0,100}\.BOT;return null!=(?<user>\i)&&\i\.bot/, match: /(?<type>\i)=\(null==.{0,100}\.BOT;return null!=(?<user>\i)&&\i\.bot/,
replace: "$<type> = $self.getTag({user: $<user>, channel: arguments[0].channel, origType: $<user>.bot ? 0 : null, location: 'not-chat' }); return typeof $<type> === 'number'" replace: "$<type> = $self.getTag({user: $<user>, channel: arguments[0].channel, origType: $<user>.bot ? 0 : null, location: 'not-chat' }); return typeof $<type> === 'number'"
@ -232,7 +233,7 @@ export default definePlugin({
} }
}, },
{ {
find: ".Messages.USER_PROFILE_PRONOUNS", find: "#{intl::USER_PROFILE_PRONOUNS}",
replacement: { replacement: {
match: /(?=,hideBotTag:!0)/, match: /(?=,hideBotTag:!0)/,
replace: ",moreTags_channelId:arguments[0].moreTags_channelId" replace: ",moreTags_channelId:arguments[0].moreTags_channelId"
@ -295,22 +296,26 @@ export default definePlugin({
isOPTag: (tag: number) => tag === Tag.Types.ORIGINAL_POSTER || tags.some(t => tag === Tag.Types[`${t.name}-OP`]), isOPTag: (tag: number) => tag === Tag.Types.ORIGINAL_POSTER || tags.some(t => tag === Tag.Types[`${t.name}-OP`]),
getTagText(passedTagName: string, strings: Record<string, string>) { getTagText(passedTagName: string, originalText: string) {
if (!passedTagName) return strings.APP_TAG; try {
const [tagName, variant] = passedTagName.split("-"); const [tagName, variant] = passedTagName.split("-");
if (!passedTagName) return getIntlMessage("APP_TAG");
const tag = tags.find(({ name }) => tagName === name); const tag = tags.find(({ name }) => tagName === name);
if (!tag) return strings.APP_TAG; if (!tag) return getIntlMessage("APP_TAG");
if (variant === "BOT" && tagName !== "WEBHOOK" && this.settings.store.dontShowForBots) return strings.APP_TAG; if (variant === "BOT" && tagName !== "WEBHOOK" && this.settings.store.dontShowForBots) return getIntlMessage("APP_TAG");
const tagText = settings.store.tagSettings?.[tag.name]?.text || tag.displayName; const tagText = settings.store.tagSettings?.[tag.name]?.text || tag.displayName;
switch (variant) { switch (variant) {
case "OP": case "OP":
return `${strings.BOT_TAG_FORUM_ORIGINAL_POSTER}${tagText}`; return `${getIntlMessage("BOT_TAG_FORUM_ORIGINAL_POSTER")}${tagText}`;
case "BOT": case "BOT":
return `${strings.APP_TAG}${tagText}`; return `${getIntlMessage("APP_TAG")}${tagText}`;
default: default:
return tagText; return tagText;
} }
} catch {
return originalText;
}
}, },
getTag({ getTag({

View file

@ -18,17 +18,18 @@
import { Settings } from "@api/Settings"; import { Settings } from "@api/Settings";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { runtimeHashMessageKey } from "@utils/intlHash";
import { Logger } from "@utils/Logger"; import { Logger } from "@utils/Logger";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack"; import { findByPropsLazy } from "@webpack";
import { i18n } from "@webpack/common";
import { Message } from "discord-types/general"; import { Message } from "discord-types/general";
const RelationshipStore = findByPropsLazy("getRelationships", "isBlocked"); const RelationshipStore = findByPropsLazy("getRelationships", "isBlocked");
interface MessageDeleteProps { interface MessageDeleteProps {
collapsedReason: { // Internal intl message for BLOCKED_MESSAGE_COUNT
message: string; collapsedReason: () => any;
};
} }
export default definePlugin({ export default definePlugin({
@ -37,7 +38,7 @@ export default definePlugin({
authors: [Devs.rushii, Devs.Samu], authors: [Devs.rushii, Devs.Samu],
patches: [ patches: [
{ {
find: "Messages.BLOCKED_MESSAGES_HIDE", find: "#{intl::BLOCKED_MESSAGES_HIDE}",
replacement: [ replacement: [
{ {
match: /let\{[^}]*collapsedReason[^}]*\}/, match: /let\{[^}]*collapsedReason[^}]*\}/,
@ -77,6 +78,11 @@ export default definePlugin({
}, },
shouldHide(props: MessageDeleteProps) { shouldHide(props: MessageDeleteProps) {
return !props?.collapsedReason?.message.includes("deleted"); try {
return props.collapsedReason() === i18n.t[runtimeHashMessageKey("BLOCKED_MESSAGE_COUNT")]();
} catch (e) {
console.error(e);
}
return false;
} }
}); });

View file

@ -20,7 +20,7 @@ const settings = definePluginSettings({
export default definePlugin({ export default definePlugin({
name: "NoMosaic", name: "NoMosaic",
authors: [Devs.AutumnVN], authors: [Devs.AutumnVN],
description: "Removes Discord new image mosaic", description: "Removes Discord image mosaic",
tags: ["image", "mosaic", "media"], tags: ["image", "mosaic", "media"],
settings, settings,
@ -29,8 +29,8 @@ export default definePlugin({
{ {
find: '=>"IMAGE"===', find: '=>"IMAGE"===',
replacement: { replacement: {
match: /=>"IMAGE"===\i\|\|"VIDEO"===\i;/, match: /=>"IMAGE"===\i\|\|"VIDEO"===\i(?:\|\|("VISUAL_PLACEHOLDER"===\i))?;/,
replace: "=>false;" replace: (_, visualPlaceholderPred) => visualPlaceholderPred != null ? `=>${visualPlaceholderPred};` : "=>false;"
} }
}, },
{ {

View file

@ -25,7 +25,7 @@ export default definePlugin({
authors: [Devs.nekohaxx], authors: [Devs.nekohaxx],
patches: [ patches: [
{ {
find: "Messages.ONBOARDING_COVER_WELCOME_SUBTITLE", find: "#{intl::ONBOARDING_COVER_WELCOME_SUBTITLE}",
replacement: { replacement: {
match: "3e3", match: "3e3",
replace: "0" replace: "0"

View file

@ -28,21 +28,21 @@ export default definePlugin({
{ {
find: '.id,"Search Results"', find: '.id,"Search Results"',
replacement: { replacement: {
match: /if\(.{1,10}\)(.{1,10}\.show\({.{1,50}UNBLOCK_TO_JUMP_TITLE)/, match: /if\(.{1,10}\)(.{1,10}\.show\({.{1,50}#{intl::UNBLOCK_TO_JUMP_TITLE})/,
replace: "if(false)$1" replace: "if(false)$1"
} }
}, },
{ {
find: "renderJumpButton()", find: "renderJumpButton()",
replacement: { replacement: {
match: /if\(.{1,10}\)(.{1,10}\.show\({.{1,50}UNBLOCK_TO_JUMP_TITLE)/, match: /if\(.{1,10}\)(.{1,10}\.show\({.{1,50}#{intl::UNBLOCK_TO_JUMP_TITLE})/,
replace: "if(false)$1" replace: "if(false)$1"
} }
}, },
{ {
find: "flash:!0,returnMessageId", find: "flash:!0,returnMessageId",
replacement: { replacement: {
match: /.\?(.{1,10}\.show\({.{1,50}UNBLOCK_TO_JUMP_TITLE)/, match: /.\?(.{1,10}\.show\({.{1,50}#{intl::UNBLOCK_TO_JUMP_TITLE})/,
replace: "false?$1" replace: "false?$1"
} }
} }

View file

@ -13,7 +13,7 @@ export default definePlugin({
authors: [Devs.bb010g], authors: [Devs.bb010g],
patches: [ patches: [
{ {
find: ".Messages.COPY_MESSAGE_LINK,", find: "#{intl::COPY_MESSAGE_LINK}",
replacement: { replacement: {
match: /\.concat\(location\.host\)/, match: /\.concat\(location\.host\)/,
replace: ".concat($self.normalizeHost(location.host))", replace: ".concat($self.normalizeHost(location.host))",

View file

@ -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;"
} }
}, },

View file

@ -18,8 +18,9 @@
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { getIntlMessage } from "@utils/discord";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { Constants, GuildStore, i18n, RestAPI } from "@webpack/common"; import { Constants, GuildStore, RestAPI } from "@webpack/common";
function showDisableInvites(guildId: string) { function showDisableInvites(guildId: string) {
// @ts-ignore // @ts-ignore
@ -43,15 +44,15 @@ export default definePlugin({
patches: [ patches: [
{ {
find: "Messages.GUILD_INVITE_DISABLE_ACTION_SHEET_DESCRIPTION", find: "#{intl::GUILD_INVITE_DISABLE_ACTION_SHEET_DESCRIPTION}",
group: true, group: true,
replacement: [ replacement: [
{ {
match: /children:\i\.\i\.\i\.GUILD_INVITE_DISABLE_ACTION_SHEET_DESCRIPTION/, match: /children:\i\.\i\.string\(\i\.\i#{intl::GUILD_INVITE_DISABLE_ACTION_SHEET_DESCRIPTION}\)/,
replace: "children: $self.renderInvitesLabel({guildId:arguments[0].guildId,setChecked})", replace: "children: $self.renderInvitesLabel({guildId:arguments[0].guildId,setChecked})",
}, },
{ {
match: /\.INVITES_DISABLED\)(?=.+?\.Messages\.INVITES_PERMANENTLY_DISABLED_TIP.+?checked:(\i)).+?\[\1,(\i)\]=\i.useState\(\i\)/, match: /\.INVITES_DISABLED\)(?=.+?#{intl::INVITES_PERMANENTLY_DISABLED_TIP}.+?checked:(\i)).+?\[\1,(\i)\]=\i.useState\(\i\)/,
replace: "$&,setChecked=$2" replace: "$&,setChecked=$2"
} }
] ]
@ -61,7 +62,7 @@ export default definePlugin({
renderInvitesLabel: ErrorBoundary.wrap(({ guildId, setChecked }) => { renderInvitesLabel: ErrorBoundary.wrap(({ guildId, setChecked }) => {
return ( return (
<div> <div>
{i18n.Messages.GUILD_INVITE_DISABLE_ACTION_SHEET_DESCRIPTION} {getIntlMessage("GUILD_INVITE_DISABLE_ACTION_SHEET_DESCRIPTION")}
{showDisableInvites(guildId) && <a role="button" onClick={() => { {showDisableInvites(guildId) && <a role="button" onClick={() => {
setChecked(true); setChecked(true);
disableInvites(guildId); disableInvites(guildId);

View file

@ -32,7 +32,7 @@ export default definePlugin({
patches: [ patches: [
// Permission lockout, just set the check to true // Permission lockout, just set the check to true
{ {
find: ".STAGE_CHANNEL_CANNOT_OVERWRITE_PERMISSION", find: "#{intl::STAGE_CHANNEL_CANNOT_OVERWRITE_PERMISSION}",
replacement: [ replacement: [
{ {
match: /case"DENY":.{0,50}if\((?=\i\.\i\.can)/, match: /case"DENY":.{0,50}if\((?=\i\.\i\.can)/,
@ -43,7 +43,7 @@ export default definePlugin({
}, },
// Onboarding, same thing but we need to prevent the check // Onboarding, same thing but we need to prevent the check
{ {
find: ".ONBOARDING_CHANNEL_THRESHOLD_WARNING", find: "#{intl::ONBOARDING_CHANNEL_THRESHOLD_WARNING}",
replacement: [ replacement: [
{ {
match: /{(\i:function\(\){return \i},?){2}}/, match: /{(\i:function\(\){return \i},?){2}}/,

View file

@ -19,15 +19,15 @@
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { Flex } from "@components/Flex"; import { Flex } from "@components/Flex";
import { InfoIcon, OwnerCrownIcon } from "@components/Icons"; import { InfoIcon, OwnerCrownIcon } from "@components/Icons";
import { 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, i18n, 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>
@ -216,7 +225,7 @@ function RoleContextMenu({ guild, roleId, onClose }: { guild: Guild; roleId: str
> >
<Menu.MenuItem <Menu.MenuItem
id={cl("copy-role-id")} id={cl("copy-role-id")}
label={i18n.Messages.COPY_ID_ROLE} label={getIntlMessage("COPY_ID_ROLE")}
action={() => { action={() => {
Clipboard.copy(roleId); Clipboard.copy(roleId);
}} }}
@ -225,7 +234,7 @@ function RoleContextMenu({ guild, roleId, onClose }: { guild: Guild; roleId: str
{(settings.store as any).unsafeViewAsRole && ( {(settings.store as any).unsafeViewAsRole && (
<Menu.MenuItem <Menu.MenuItem
id={cl("view-as-role")} id={cl("view-as-role")}
label={i18n.Messages.VIEW_AS_ROLE} label={getIntlMessage("VIEW_AS_ROLE")}
action={() => { action={() => {
const role = GuildStore.getRole(guild.id, roleId); const role = GuildStore.getRole(guild.id, roleId);
if (!role) return; if (!role) return;
@ -257,7 +266,7 @@ function UserContextMenu({ userId }: { userId: string; }) {
> >
<Menu.MenuItem <Menu.MenuItem
id={cl("copy-user-id")} id={cl("copy-user-id")}
label={i18n.Messages.COPY_ID_USER} label={getIntlMessage("COPY_ID_USER")}
action={() => { action={() => {
Clipboard.copy(userId); Clipboard.copy(userId);
}} }}

View file

@ -17,14 +17,14 @@
*/ */
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { ExpandableHeader } from "@components/ExpandableHeader"; 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";
import { i18n, PermissionsBits, Text, Tooltip, useMemo, UserStore } from "@webpack/common"; import { PermissionsBits, Text, Tooltip, useMemo, UserStore } from "@webpack/common";
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 {
@ -86,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 = [];
@ -105,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 = i18n.Messages.GUILD_OWNER || "Server Owner"; const OWNER = getIntlMessage("GUILD_OWNER") ?? "Server Owner";
userPermissions.push({ userPermissions.push({
permission: OWNER, permission: OWNER,
roleName: "Owner", roleName: "Owner",
@ -116,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
@ -136,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={() => {
@ -163,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)"}
> >
@ -173,7 +164,30 @@ function UserPermissionsComponent({ guild, guildMember, forceOpen = false }: { g
</div> </div>
)} )}
</Tooltip> </Tooltip>
]}> <Tooltip text="Role Details">
{tooltipProps => (
<div
{...tooltipProps}
className={cl("user-header-btn")}
role="button"
tabIndex={0}
onClick={() => {
closePopout();
openRolesAndUsersPermissionsModal(rolePermissions, guild, guildMember.nick || UserStore.getUser(guildMember.userId).username);
}}
>
<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>
</div>
)}
</Tooltip>
</div>
</div>
{userPermissions.length > 0 && ( {userPermissions.length > 0 && (
<div className={classes(RoleRootClasses.root)}> <div className={classes(RoleRootClasses.root)}>
{userPermissions.map(({ permission, roleColor, roleName }) => ( {userPermissions.map(({ permission, roleColor, roleName }) => (
@ -189,8 +203,7 @@ function UserPermissionsComponent({ guild, guildMember, forceOpen = false }: { g
))} ))}
</div> </div>
)} )}
</ExpandableHeader> </div>;
);
} }
export default ErrorBoundary.wrap(UserPermissionsComponent, { noop: true }); export default ErrorBoundary.wrap(UserPermissionsComponent, { noop: true });

View file

@ -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) {
@ -170,7 +165,7 @@ export default definePlugin({
patches: [ patches: [
{ {
find: ".VIEW_ALL_ROLES,", find: "#{intl::VIEW_ALL_ROLES}",
replacement: { replacement: {
match: /\.expandButton,.+?null,/, match: /\.expandButton,.+?null,/,
replace: "$&$self.ViewPermissionsButton(arguments[0])," replace: "$&$self.ViewPermissionsButton(arguments[0]),"
@ -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>
)} )}
> >

View file

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

View file

@ -17,56 +17,17 @@
*/ */
import { classNameFactory } from "@api/Styles"; import { classNameFactory } from "@api/Styles";
import { wordsToTitle } from "@utils/text"; import { findByPropsLazy } from "@webpack";
import { GuildStore, i18n, Parser } from "@webpack/common"; import { GuildStore } 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 i18n.Messages[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 = i18n.Messages[`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);

View file

@ -30,10 +30,10 @@ export default definePlugin({
{ {
find: ".removeMosaicItemHoverButton),", find: ".removeMosaicItemHoverButton),",
replacement: { replacement: {
match: /\.nonMediaMosaicItem\]:!(\i).{0,50}?children:\[\S,(\S)/, match: /\.nonMediaMosaicItem\]:.{0,40}children:\[(?<=showDownload:(\i).+?isVisualMediaType:(\i).+?)/,
replace: "$&,$1&&$2&&$self.renderPiPButton()," replace: "$&$1&&$2&&$self.renderPiPButton(),"
}, }
}, }
], ],
renderPiPButton: ErrorBoundary.wrap(() => { renderPiPButton: ErrorBoundary.wrap(() => {

View file

@ -30,7 +30,7 @@ interface ColorPickerWithSwatchesProps {
renderCustomButton?: () => React.ReactNode; renderCustomButton?: () => React.ReactNode;
} }
const ColorPicker = findComponentByCodeLazy<ColorPickerProps>(".Messages.USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR", ".BACKGROUND_PRIMARY)"); const ColorPicker = findComponentByCodeLazy<ColorPickerProps>("#{intl::USER_SETTINGS_PROFILE_COLOR_SELECT_COLOR}", ".BACKGROUND_PRIMARY)");
const ColorPickerWithSwatches = findExportedComponentLazy<ColorPickerWithSwatchesProps>("ColorPicker", "CustomColorPicker"); const ColorPickerWithSwatches = findExportedComponentLazy<ColorPickerWithSwatchesProps>("ColorPicker", "CustomColorPicker");
export const requireSettingsMenu = extractAndLoadChunksLazy(['name:"UserSettings"'], /createPromise:.{0,20}Promise\.all\((\[\i\.\i\("?.+?"?\).+?\])\).then\(\i\.bind\(\i,"?(.+?)"?\)\).{0,50}"UserSettings"/); export const requireSettingsMenu = extractAndLoadChunksLazy(['name:"UserSettings"'], /createPromise:.{0,20}Promise\.all\((\[\i\.\i\("?.+?"?\).+?\])\).then\(\i\.bind\(\i,"?(.+?)"?\)\).{0,50}"UserSettings"/);

View file

@ -50,6 +50,8 @@ async function runMigrations() {
export async function syncAndRunChecks() { export async function syncAndRunChecks() {
await runMigrations(); await runMigrations();
if (UserStore.getCurrentUser() == null) return;
const [oldGuilds, oldGroups, oldFriends] = await DataStore.getMany([ const [oldGuilds, oldGroups, oldFriends] = await DataStore.getMany([
guildsKey(), guildsKey(),
groupsKey(), groupsKey(),

View file

@ -67,7 +67,7 @@ export default definePlugin({
patches: [ patches: [
{ {
find: ".REPLY_QUOTE_MESSAGE_BLOCKED", find: "#{intl::REPLY_QUOTE_MESSAGE_BLOCKED}",
replacement: { replacement: {
match: /\.onClickReply,.+?}\),(?=\i,\i,\i\])/, match: /\.onClickReply,.+?}\),(?=\i,\i,\i\])/,
replace: "$&$self.ReplyTimestamp(arguments[0])," replace: "$&$self.ReplyTimestamp(arguments[0]),"

View file

@ -108,7 +108,7 @@ export default definePlugin({
patches: [ patches: [
{ {
find: ".Messages.MESSAGE_ACTIONS_MENU_LABEL,shouldHideMediaOptions", find: "#{intl::MESSAGE_ACTIONS_MENU_LABEL}",
replacement: { replacement: {
match: /favoriteableType:\i,(?<=(\i)\.getAttribute\("data-type"\).+?)/, match: /favoriteableType:\i,(?<=(\i)\.getAttribute\("data-type"\).+?)/,
replace: (m, target) => `${m}reverseImageSearchType:${target}.getAttribute("data-role"),` replace: (m, target) => `${m}reverseImageSearchType:${target}.getAttribute("data-role"),`

View file

@ -20,6 +20,7 @@ import { definePluginSettings } from "@api/Settings";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { makeRange } from "@components/PluginSettings/components"; import { makeRange } from "@components/PluginSettings/components";
import { Devs } from "@utils/constants"; import { Devs } from "@utils/constants";
import { Logger } from "@utils/Logger";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findByCodeLazy } from "@webpack"; import { findByCodeLazy } from "@webpack";
import { ChannelStore, GuildMemberStore, GuildStore } from "@webpack/common"; import { ChannelStore, GuildMemberStore, GuildStore } from "@webpack/common";
@ -51,6 +52,12 @@ const settings = definePluginSettings({
description: "Show role colors in the reactors list", description: "Show role colors in the reactors list",
restartNeeded: true restartNeeded: true
}, },
pollResults: {
type: OptionType.BOOLEAN,
default: true,
description: "Show role colors in the poll results",
restartNeeded: true
},
colorChatMessages: { colorChatMessages: {
type: OptionType.BOOLEAN, type: OptionType.BOOLEAN,
default: false, default: false,
@ -62,14 +69,15 @@ const settings = definePluginSettings({
description: "Intensity of message coloring.", description: "Intensity of message coloring.",
markers: makeRange(0, 100, 10), markers: makeRange(0, 100, 10),
default: 30 default: 30
}, }
}); });
export default definePlugin({ export default definePlugin({
name: "RoleColorEverywhere", name: "RoleColorEverywhere",
authors: [Devs.KingFish, Devs.lewisakura, Devs.AutumnVN, Devs.Kyuuhachi], authors: [Devs.KingFish, Devs.lewisakura, Devs.AutumnVN, Devs.Kyuuhachi, Devs.jamesbt365],
description: "Adds the top role color anywhere possible", description: "Adds the top role color anywhere possible",
settings,
patches: [ patches: [
// Chat Mentions // Chat Mentions
{ {
@ -77,82 +85,131 @@ export default definePlugin({
replacement: [ replacement: [
{ {
match: /onContextMenu:\i,color:\i,\.\.\.\i(?=,children:)(?<=user:(\i),channel:(\i).{0,500}?)/, match: /onContextMenu:\i,color:\i,\.\.\.\i(?=,children:)(?<=user:(\i),channel:(\i).{0,500}?)/,
replace: "$&,color:$self.getUserColor($1?.id,{channelId:$2?.id})" replace: "$&,color:$self.getColorInt($1?.id,$2?.id)"
} }
], ],
predicate: () => settings.store.chatMentions, predicate: () => settings.store.chatMentions
}, },
// Slate // Slate
{ {
find: ".userTooltip,children", find: ".userTooltip,children",
replacement: [ replacement: [
{ {
match: /let\{id:(\i),guildId:(\i)[^}]*\}.*?\.\i,{(?=children)/, match: /let\{id:(\i),guildId:\i,channelId:(\i)[^}]*\}.*?\.\i,{(?=children)/,
replace: "$&color:$self.getUserColor($1,{guildId:$2})," replace: "$&color:$self.getColorInt($1,$2),"
} }
], ],
predicate: () => settings.store.chatMentions, predicate: () => settings.store.chatMentions
}, },
// Member List Role Headers
{ {
find: 'tutorialId:"whos-online', find: 'tutorialId:"whos-online',
replacement: [ replacement: [
{ {
match: /null,\i," — ",\i\]/, match: /null,\i," — ",\i\]/,
replace: "null,$self.roleGroupColor(arguments[0])]" replace: "null,$self.RoleGroupColor(arguments[0])]"
}, },
], ],
predicate: () => settings.store.memberList, predicate: () => settings.store.memberList
}, },
{ {
find: ".Messages.THREAD_BROWSER_PRIVATE", find: "#{intl::THREAD_BROWSER_PRIVATE}",
replacement: [ replacement: [
{ {
match: /children:\[\i," — ",\i\]/, match: /children:\[\i," — ",\i\]/,
replace: "children:[$self.roleGroupColor(arguments[0])]" replace: "children:[$self.RoleGroupColor(arguments[0])]"
}, },
], ],
predicate: () => settings.store.memberList, predicate: () => settings.store.memberList
}, },
// Voice Users
{ {
find: "renderPrioritySpeaker", find: "renderPrioritySpeaker(){",
replacement: [ replacement: [
{ {
match: /renderName\(\){.+?usernameSpeaking\]:.+?(?=children)/, match: /renderName\(\){.+?usernameSpeaking\]:.+?(?=children)/,
replace: "$&...$self.getVoiceProps(this.props)," replace: "$&style:$self.getColorStyle(this?.props?.user?.id,this?.props?.guildId),"
} }
], ],
predicate: () => settings.store.voiceUsers, predicate: () => settings.store.voiceUsers
}, },
// Reaction List
{ {
find: ".reactorDefault", find: ".reactorDefault",
replacement: { replacement: {
match: /,onContextMenu:e=>.{0,15}\((\i),(\i),(\i)\).{0,250}tag:"strong"/, match: /,onContextMenu:\i=>.{0,15}\((\i),(\i),(\i)\).{0,250}tag:"strong"/,
replace: "$&,style:{color:$self.getColor($2?.id,$1)}" replace: "$&,style:$self.getColorStyle($2?.id,$1?.channel?.id)"
}, },
predicate: () => settings.store.reactorsList, predicate: () => settings.store.reactorsList,
}, },
// Poll Results
{ {
find: '.Messages.MESSAGE_EDITED,")"', find: ",reactionVoteCounts",
replacement: {
match: /\.nickname,(?=children:)/,
replace: "$&style:$self.getColorStyle(arguments[0]?.user?.id,arguments[0]?.channel?.id),"
},
predicate: () => settings.store.pollResults
},
// Messages
{
find: "#{intl::MESSAGE_EDITED}",
replacement: { replacement: {
match: /(?<=isUnsupported\]:(\i)\.isUnsupported\}\),)(?=children:\[)/, match: /(?<=isUnsupported\]:(\i)\.isUnsupported\}\),)(?=children:\[)/,
replace: "style:{color:$self.useMessageColor($1)}," replace: "style:$self.useMessageColorsStyle($1),"
},
predicate: () => settings.store.colorChatMessages,
}, },
predicate: () => settings.store.colorChatMessages
}
], ],
settings,
getColor(userId: string, { channelId, guildId }: { channelId?: string; guildId?: string; }) { getColorString(userId: string, channelOrGuildId: string) {
if (!(guildId ??= ChannelStore.getChannel(channelId!)?.guild_id)) return null; try {
const guildId = ChannelStore.getChannel(channelOrGuildId)?.guild_id ?? GuildStore.getGuild(channelOrGuildId)?.id;
if (guildId == null) return null;
return GuildMemberStore.getMember(guildId, userId)?.colorString ?? null; return GuildMemberStore.getMember(guildId, userId)?.colorString ?? null;
} catch (e) {
new Logger("RoleColorEverywhere").error("Failed to get color string", e);
}
return null;
}, },
getUserColor(userId: string, ids: { channelId?: string; guildId?: string; }) { getColorInt(userId: string, channelOrGuildId: string) {
const colorString = this.getColor(userId, ids); const colorString = this.getColorString(userId, channelOrGuildId);
return colorString && parseInt(colorString.slice(1), 16); return colorString && parseInt(colorString.slice(1), 16);
}, },
roleGroupColor: ErrorBoundary.wrap(({ id, count, title, guildId, label }: { id: string; count: number; title: string; guildId: string; label: string; }) => { getColorStyle(userId: string, channelOrGuildId: string) {
const colorString = this.getColorString(userId, channelOrGuildId);
return colorString && {
color: colorString
};
},
useMessageColorsStyle(message: any) {
try {
const { messageSaturation } = settings.use(["messageSaturation"]);
const author = useMessageAuthor(message);
if (author.colorString != null && messageSaturation !== 0) {
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) {
new Logger("RoleColorEverywhere").error("Failed to get message color", e);
}
return null;
},
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);
return ( return (
@ -164,25 +221,5 @@ export default definePlugin({
{title ?? label} &mdash; {count} {title ?? label} &mdash; {count}
</span> </span>
); );
}, { noop: true }), }, { noop: true })
getVoiceProps({ user: { id: userId }, guildId }: { user: { id: string; }; guildId: string; }) {
return {
style: {
color: this.getColor(userId, { guildId })
}
};
},
useMessageColor(message: any) {
try {
const { messageSaturation } = settings.use(["messageSaturation"]);
const author = useMessageAuthor(message);
if (author.colorString !== undefined && messageSaturation !== 0)
return `color-mix(in oklab, ${author.colorString} ${messageSaturation}%, var(--text-normal))`;
} catch (e) {
console.error("[RCE] failed to get message color", e);
}
return undefined;
},
}); });

View file

@ -1,6 +1,6 @@
/* /*
* Vencord, a modification for Discord's desktop app * Vencord, a modification for Discord's desktop app
* Copyright (c) 2022 Vendicated and contributors * Copyright (c) 2024 Vendicated and contributors
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -18,9 +18,9 @@
import { IShikiTheme } from "@vap/shiki"; import { IShikiTheme } from "@vap/shiki";
export const SHIKI_REPO = "shikijs/shiki"; export const SHIKI_REPO = "shikijs/textmate-grammars-themes";
export const SHIKI_REPO_COMMIT = "0b28ad8ccfbf2615f2d9d38ea8255416b8ac3043"; export const SHIKI_REPO_COMMIT = "2d87559c7601a928b9f7e0f0dda243d2fb6d4499";
export const shikiRepoTheme = (name: string) => `https://raw.githubusercontent.com/${SHIKI_REPO}/${SHIKI_REPO_COMMIT}/packages/shiki/themes/${name}.json`; export const shikiRepoTheme = (name: string) => `https://raw.githubusercontent.com/${SHIKI_REPO}/${SHIKI_REPO_COMMIT}/packages/tm-themes/themes/${name}.json`;
export const themes = { export const themes = {
// Default // Default
@ -30,33 +30,59 @@ export const themes = {
MaterialCandy: "https://raw.githubusercontent.com/millsp/material-candy/master/material-candy.json", MaterialCandy: "https://raw.githubusercontent.com/millsp/material-candy/master/material-candy.json",
// More from Shiki repo // More from Shiki repo
Andromeeda: shikiRepoTheme("andromeeda"),
AuroraX: shikiRepoTheme("aurora-x"),
AyuDark: shikiRepoTheme("ayu-dark"),
CatppuccinLatte: shikiRepoTheme("catppuccin-latte"),
CatppuccinFrappe: shikiRepoTheme("catppuccin-frappe"),
CatppuccinMacchiato: shikiRepoTheme("catppuccin-macchiato"),
CatppuccinMocha: shikiRepoTheme("catppuccin-mocha"),
DraculaSoft: shikiRepoTheme("dracula-soft"), DraculaSoft: shikiRepoTheme("dracula-soft"),
Dracula: shikiRepoTheme("dracula"), Dracula: shikiRepoTheme("dracula"),
EverforestDark: shikiRepoTheme("everforest-dark"),
EverforestLight: shikiRepoTheme("everforest-light"),
GithubDarkDefault: shikiRepoTheme("github-dark-default"),
GithubDarkDimmed: shikiRepoTheme("github-dark-dimmed"), GithubDarkDimmed: shikiRepoTheme("github-dark-dimmed"),
GithubDarkHighContrast: shikiRepoTheme("github-dark-high-contrast"),
GithubDark: shikiRepoTheme("github-dark"), GithubDark: shikiRepoTheme("github-dark"),
GithubLightDefault: shikiRepoTheme("github-light-default"),
GithubLightHighContrast: shikiRepoTheme("github-light-high-contrast"),
GithubLight: shikiRepoTheme("github-light"), GithubLight: shikiRepoTheme("github-light"),
Houston: shikiRepoTheme("houston"),
KanagawaDragon: shikiRepoTheme("kanagawa-dragon"),
KanagawaLotus: shikiRepoTheme("kanagawa-lotus"),
KanagawaWave: shikiRepoTheme("kanagawa-wave"),
LaserWave: shikiRepoTheme("laserwave"),
LightPlus: shikiRepoTheme("light-plus"), LightPlus: shikiRepoTheme("light-plus"),
MaterialDarker: shikiRepoTheme("material-darker"), MaterialDarker: shikiRepoTheme("material-theme-darker"),
MaterialDefault: shikiRepoTheme("material-default"), MaterialDefault: shikiRepoTheme("material-theme"),
MaterialLighter: shikiRepoTheme("material-lighter"), MaterialLighter: shikiRepoTheme("material-theme-lighter"),
MaterialOcean: shikiRepoTheme("material-ocean"), MaterialOcean: shikiRepoTheme("material-theme-ocean"),
MaterialPalenight: shikiRepoTheme("material-palenight"), MaterialPalenight: shikiRepoTheme("material-theme-palenight"),
MinDark: shikiRepoTheme("min-dark"), MinDark: shikiRepoTheme("min-dark"),
MinLight: shikiRepoTheme("min-light"), MinLight: shikiRepoTheme("min-light"),
Monokai: shikiRepoTheme("monokai"), Monokai: shikiRepoTheme("monokai"),
NightOwl: shikiRepoTheme("night-owl"),
Nord: shikiRepoTheme("nord"), Nord: shikiRepoTheme("nord"),
OneDarkPro: shikiRepoTheme("one-dark-pro"), OneDarkPro: shikiRepoTheme("one-dark-pro"),
OneLight: shikiRepoTheme("one-light"),
Plastic: shikiRepoTheme("plastic"),
Poimandres: shikiRepoTheme("poimandres"), Poimandres: shikiRepoTheme("poimandres"),
Red: shikiRepoTheme("red"),
RosePineDawn: shikiRepoTheme("rose-pine-dawn"), RosePineDawn: shikiRepoTheme("rose-pine-dawn"),
RosePineMoon: shikiRepoTheme("rose-pine-moon"), RosePineMoon: shikiRepoTheme("rose-pine-moon"),
RosePine: shikiRepoTheme("rose-pine"), RosePine: shikiRepoTheme("rose-pine"),
SlackDark: shikiRepoTheme("slack-dark"), SlackDark: shikiRepoTheme("slack-dark"),
SlackOchin: shikiRepoTheme("slack-ochin"), SlackOchin: shikiRepoTheme("slack-ochin"),
SnazzyLight: shikiRepoTheme("snazzy-light"),
SolarizedDark: shikiRepoTheme("solarized-dark"), SolarizedDark: shikiRepoTheme("solarized-dark"),
SolarizedLight: shikiRepoTheme("solarized-light"), SolarizedLight: shikiRepoTheme("solarized-light"),
Synthwave84: shikiRepoTheme("synthwave-84"),
TokyoNight: shikiRepoTheme("tokyo-night"),
Vesper: shikiRepoTheme("vesper"),
VitesseBlack: shikiRepoTheme("vitesse-black"),
VitesseDark: shikiRepoTheme("vitesse-dark"), VitesseDark: shikiRepoTheme("vitesse-dark"),
VitesseLight: shikiRepoTheme("vitesse-light"), VitesseLight: shikiRepoTheme("vitesse-light"),
CssVariables: shikiRepoTheme("css-variables"),
}; };
export const themeCache = new Map<string, IShikiTheme>(); export const themeCache = new Map<string, IShikiTheme>();

View file

@ -46,7 +46,7 @@ export default definePlugin({
} }
}, },
{ {
find: ".PREVIEW_NUM_LINES", find: "#{intl::PREVIEW_NUM_LINES}",
replacement: { replacement: {
match: /(?<=function \i\((\i)\)\{)(?=let\{text:\i,language:)/, match: /(?<=function \i\((\i)\)\{)(?=let\{text:\i,language:)/,
replace: "return $self.renderHighlighter({lang:$1.language,content:$1.text});" replace: "return $self.renderHighlighter({lang:$1.language,content:$1.text});"

View file

@ -26,7 +26,7 @@ export default definePlugin({
patches: [ patches: [
{ {
find: ".Messages.MESSAGE_UTILITIES_A11Y_LABEL", find: "#{intl::MESSAGE_UTILITIES_A11Y_LABEL}",
replacement: { replacement: {
match: /isExpanded:\i&&(.+?),/, match: /isExpanded:\i&&(.+?),/,
replace: "isExpanded:$1," replace: "isExpanded:$1,"

View file

@ -16,11 +16,12 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
import { getIntlMessage } from "@utils/discord";
import { findComponentByCodeLazy, findLazy } from "@webpack"; import { findComponentByCodeLazy, findLazy } from "@webpack";
import { i18n, useToken } from "@webpack/common"; import { useToken } from "@webpack/common";
const ColorMap = findLazy(m => m.colors?.INTERACTIVE_MUTED?.css); const ColorMap = findLazy(m => m.colors?.INTERACTIVE_MUTED?.css);
const VerifiedIconComponent = findComponentByCodeLazy(".CONNECTIONS_ROLE_OFFICIAL_ICON_TOOLTIP"); const VerifiedIconComponent = findComponentByCodeLazy("#{intl::CONNECTIONS_ROLE_OFFICIAL_ICON_TOOLTIP}");
export function VerifiedIcon() { export function VerifiedIcon() {
const color = useToken(ColorMap.colors.INTERACTIVE_MUTED).hex(); const color = useToken(ColorMap.colors.INTERACTIVE_MUTED).hex();
@ -31,7 +32,7 @@ export function VerifiedIcon() {
color={color} color={color}
forcedIconColor={forcedIconColor} forcedIconColor={forcedIconColor}
size={16} size={16}
tooltipText={i18n.Messages.CONNECTION_VERIFIED} tooltipText={getIntlMessage("CONNECTION_VERIFIED")}
/> />
); );
} }

View file

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

View file

@ -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})&&`
}, },
{ {
@ -149,7 +149,7 @@ export default definePlugin({
} }
}, },
{ {
find: ".Messages.CHANNEL_TOOLTIP_DIRECTORY", find: "#{intl::CHANNEL_TOOLTIP_DIRECTORY}",
predicate: () => settings.store.showMode === ShowMode.LockIcon, predicate: () => settings.store.showMode === ShowMode.LockIcon,
replacement: { replacement: {
// Lock Icon // Lock Icon
@ -274,7 +274,7 @@ export default definePlugin({
} }
}, },
{ {
find: ".Messages.ROLE_REQUIRED_SINGLE_USER_MESSAGE", find: "#{intl::ROLE_REQUIRED_SINGLE_USER_MESSAGE}",
replacement: [ replacement: [
{ {
// Change the role permission check to CONNECT if the channel is locked // Change the role permission check to CONNECT if the channel is locked
@ -336,7 +336,7 @@ export default definePlugin({
] ]
}, },
{ {
find: ".Messages.CHANNEL_CALL_CURRENT_SPEAKER.format", find: "#{intl::CHANNEL_CALL_CURRENT_SPEAKER}",
replacement: [ replacement: [
{ {
// Remove the divider and the open chat button for the HiddenChannelLockScreen // Remove the divider and the open chat button for the HiddenChannelLockScreen
@ -351,7 +351,7 @@ export default definePlugin({
] ]
}, },
{ {
find: ".Messages.EMBEDDED_ACTIVITIES_DEVELOPER_ACTIVITY_SHELF_FETCH_ERROR", find: "#{intl::EMBEDDED_ACTIVITIES_DEVELOPER_ACTIVITY_SHELF_FETCH_ERROR}",
replacement: [ replacement: [
{ {
// Render our HiddenChannelLockScreen component instead of the main voice channel component // Render our HiddenChannelLockScreen component instead of the main voice channel component
@ -401,7 +401,7 @@ export default definePlugin({
] ]
}, },
{ {
find: ".Messages.STAGE_FULL_MODERATOR_TITLE", find: "#{intl::STAGE_FULL_MODERATOR_TITLE}",
replacement: [ replacement: [
{ {
// Remove the divider and amount of users in stage channel components for the HiddenChannelLockScreen // Remove the divider and amount of users in stage channel components for the HiddenChannelLockScreen
@ -463,7 +463,7 @@ export default definePlugin({
] ]
}, },
{ {
find: ".Messages.FORM_LABEL_MUTED", find: "#{intl::FORM_LABEL_MUTED}",
replacement: { replacement: {
// Make GuildChannelStore.getChannels return hidden channels // Make GuildChannelStore.getChannels return hidden channels
match: /(?<=getChannels\(\i)(?=\))/, match: /(?<=getChannels\(\i)(?=\))/,

View file

@ -68,13 +68,22 @@ export default definePlugin({
}, },
// fixes a bug where Members page must be loaded to see highest role, why is Discord depending on MemberSafetyStore.getEnhancedMember for something that can be obtained here? // fixes a bug where Members page must be loaded to see highest role, why is Discord depending on MemberSafetyStore.getEnhancedMember for something that can be obtained here?
{ {
find: "Messages.GUILD_MEMBER_MOD_VIEW_PERMISSION_GRANTED_BY_ARIA_LABEL,allowOverflow", find: "#{intl::GUILD_MEMBER_MOD_VIEW_PERMISSION_GRANTED_BY_ARIA_LABEL}",
predicate: () => settings.store.showModView, predicate: () => settings.store.showModView,
replacement: { replacement: {
match: /(role:)\i(?=,guildId.{0,100}role:(\i\[))/, match: /(role:)\i(?=,guildId.{0,100}role:(\i\[))/,
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 } })"
} }
} }

View file

@ -9,13 +9,16 @@ import "./styles.css";
import { definePluginSettings } from "@api/Settings"; 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 { getIntlMessage } from "@utils/discord";
import { canonicalizeMatch } from "@utils/patches";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findComponentLazy } from "@webpack"; import { findComponentLazy } from "@webpack";
import { ChannelStore, GuildMemberStore, i18n, Text, Tooltip } from "@webpack/common"; import { ChannelStore, GuildMemberStore, Text, Tooltip } from "@webpack/common";
import { Message } from "discord-types/general"; import { Message } from "discord-types/general";
import { FunctionComponent, ReactNode } from "react"; import { FunctionComponent, ReactNode } from "react";
const CountDown = findComponentLazy(m => m.prototype?.render?.toString().includes(".MAX_AGE_NEVER")); const countDownFilter = canonicalizeMatch("#{intl::MAX_AGE_NEVER}");
const CountDown = findComponentLazy(m => m.prototype?.render?.toString().includes(countDownFilter));
const enum DisplayStyle { const enum DisplayStyle {
Tooltip = "tooltip", Tooltip = "tooltip",
@ -48,9 +51,14 @@ function renderTimeout(message: Message, inline: boolean) {
/> />
); );
getIntlMessage("GUILD_ENABLE_COMMUNICATION_TIME_REMAINING", {
username: message.author.username,
countdown
});
return inline return inline
? countdown() ? countdown()
: i18n.Messages.GUILD_ENABLE_COMMUNICATION_TIME_REMAINING.format({ : getIntlMessage("GUILD_ENABLE_COMMUNICATION_TIME_REMAINING", {
username: message.author.username, username: message.author.username,
countdown countdown
}); });
@ -65,10 +73,10 @@ export default definePlugin({
patches: [ patches: [
{ {
find: ".GUILD_COMMUNICATION_DISABLED_ICON_TOOLTIP_BODY", find: "#{intl::GUILD_COMMUNICATION_DISABLED_ICON_TOOLTIP_BODY}",
replacement: [ replacement: [
{ {
match: /(\i)\.Tooltip,{(text:.{0,30}\.Messages\.GUILD_COMMUNICATION_DISABLED_ICON_TOOLTIP_BODY)/, match: /(\i)\.Tooltip,{(text:.{0,30}#{intl::GUILD_COMMUNICATION_DISABLED_ICON_TOOLTIP_BODY}\))/,
replace: "$self.TooltipWrapper,{message:arguments[0].message,$2" replace: "$self.TooltipWrapper,{message:arguments[0].message,$2"
} }
] ]

View file

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

View file

@ -45,7 +45,7 @@ export default definePlugin({
replace: "}).sortBy(row => $self.wrapSort(($1), row)).value()" replace: "}).sortBy(row => $self.wrapSort(($1), row)).value()"
} }
}, { }, {
find: ".Messages.FRIEND_REQUEST_CANCEL", find: "#{intl::FRIEND_REQUEST_CANCEL}",
replacement: { replacement: {
predicate: () => settings.store.showDates, predicate: () => settings.store.showDates,
match: /subText:(\i)(?<=user:(\i).+?)/, match: /subText:(\i)(?<=user:(\i).+?)/,

View file

@ -26,7 +26,7 @@ export default definePlugin({
description: "Adds Startup Timings to the Settings menu", description: "Adds Startup Timings to the Settings menu",
authors: [Devs.Megu], authors: [Devs.Megu],
patches: [{ patches: [{
find: "Messages.ACTIVITY_SETTINGS", find: "#{intl::ACTIVITY_SETTINGS}",
replacement: { replacement: {
match: /(?<=}\)([,;])(\i\.settings)\.forEach.+?(\i)\.push.+}\)}\))/, match: /(?<=}\)([,;])(\i\.settings)\.forEach.+?(\i)\.push.+}\)}\))/,
replace: (_, commaOrSemi, settings, elements) => "" + replace: (_, commaOrSemi, settings, elements) => "" +

View file

@ -38,7 +38,7 @@ export default definePlugin({
// add --avatar-url-<resolution> css variable to avatar img elements // add --avatar-url-<resolution> css variable to avatar img elements
// popout profiles // popout profiles
{ {
find: ".LABEL_WITH_ONLINE_STATUS", find: "#{intl::LABEL_WITH_ONLINE_STATUS}",
replacement: { replacement: {
match: /src:null!=\i\?(\i).{1,50}"aria-hidden":!0/, match: /src:null!=\i\?(\i).{1,50}"aria-hidden":!0/,
replace: "$&,style:$self.getAvatarStyles($1)" replace: "$&,style:$self.getAvatarStyles($1)"
@ -55,6 +55,8 @@ export default definePlugin({
], ],
getAvatarStyles(src: string) { getAvatarStyles(src: string) {
if (src.startsWith("data:")) return {};
return Object.fromEntries( return Object.fromEntries(
[128, 256, 512, 1024, 2048, 4096].map(size => [ [128, 256, 512, 1024, 2048, 4096].map(size => [
`--avatar-url-${size}`, `--avatar-url-${size}`,

View file

@ -21,9 +21,10 @@ import "./style.css";
import { definePluginSettings, Settings } from "@api/Settings"; import { definePluginSettings, 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 { getIntlMessage } from "@utils/discord";
import definePlugin, { OptionType } from "@utils/types"; import definePlugin, { OptionType } from "@utils/types";
import { findComponentByCodeLazy, findExportedComponentLazy, findStoreLazy } from "@webpack"; import { findComponentByCodeLazy, findExportedComponentLazy, findStoreLazy } from "@webpack";
import { ChannelStore, GuildMemberStore, i18n, RelationshipStore, SelectedChannelStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common"; import { GuildMemberStore, RelationshipStore, SelectedChannelStore, Tooltip, UserStore, useStateFromStores } from "@webpack/common";
import { buildSeveralUsers } from "../typingTweaks"; import { buildSeveralUsers } from "../typingTweaks";
@ -43,7 +44,7 @@ function getDisplayName(guildId: string, userId: string) {
return GuildMemberStore.getNick(guildId, userId) ?? (user as any).globalName ?? user.username; return GuildMemberStore.getNick(guildId, userId) ?? (user as any).globalName ?? user.username;
} }
function TypingIndicator({ channelId }: { channelId: string; }) { function TypingIndicator({ channelId, guildId }: { channelId: string; guildId: string; }) {
const typingUsers: Record<string, number> = useStateFromStores( const typingUsers: Record<string, number> = useStateFromStores(
[TypingStore], [TypingStore],
() => ({ ...TypingStore.getTypingUsers(channelId) as Record<string, number> }), () => ({ ...TypingStore.getTypingUsers(channelId) as Record<string, number> }),
@ -56,7 +57,6 @@ function TypingIndicator({ channelId }: { channelId: string; }) {
} }
); );
const currentChannelId: string = useStateFromStores([SelectedChannelStore], () => SelectedChannelStore.getChannelId()); const currentChannelId: string = useStateFromStores([SelectedChannelStore], () => SelectedChannelStore.getChannelId());
const guildId = ChannelStore.getChannel(channelId).guild_id;
if (!settings.store.includeMutedChannels) { if (!settings.store.includeMutedChannels) {
const isChannelMuted = UserGuildSettingsStore.isChannelMuted(guildId, channelId); const isChannelMuted = UserGuildSettingsStore.isChannelMuted(guildId, channelId);
@ -75,21 +75,21 @@ function TypingIndicator({ channelId }: { channelId: string; }) {
switch (typingUsersArray.length) { switch (typingUsersArray.length) {
case 0: break; case 0: break;
case 1: { case 1: {
tooltipText = i18n.Messages.ONE_USER_TYPING.format({ a: getDisplayName(guildId, typingUsersArray[0]) }); tooltipText = getIntlMessage("ONE_USER_TYPING", { a: getDisplayName(guildId, typingUsersArray[0]) });
break; break;
} }
case 2: { case 2: {
tooltipText = i18n.Messages.TWO_USERS_TYPING.format({ a: getDisplayName(guildId, typingUsersArray[0]), b: getDisplayName(guildId, typingUsersArray[1]) }); tooltipText = getIntlMessage("TWO_USERS_TYPING", { a: getDisplayName(guildId, typingUsersArray[0]), b: getDisplayName(guildId, typingUsersArray[1]) });
break; break;
} }
case 3: { case 3: {
tooltipText = i18n.Messages.THREE_USERS_TYPING.format({ a: getDisplayName(guildId, typingUsersArray[0]), b: getDisplayName(guildId, typingUsersArray[1]), c: getDisplayName(guildId, typingUsersArray[2]) }); tooltipText = getIntlMessage("THREE_USERS_TYPING", { a: getDisplayName(guildId, typingUsersArray[0]), b: getDisplayName(guildId, typingUsersArray[1]), c: getDisplayName(guildId, typingUsersArray[2]) });
break; break;
} }
default: { default: {
tooltipText = Settings.plugins.TypingTweaks.enabled tooltipText = Settings.plugins.TypingTweaks.enabled
? buildSeveralUsers({ a: getDisplayName(guildId, typingUsersArray[0]), b: getDisplayName(guildId, typingUsersArray[1]), count: typingUsersArray.length - 2 }) ? buildSeveralUsers({ a: getDisplayName(guildId, typingUsersArray[0]), b: getDisplayName(guildId, typingUsersArray[1]), count: typingUsersArray.length - 2 })
: i18n.Messages.SEVERAL_USERS_TYPING; : getIntlMessage("SEVERAL_USERS_TYPING");
break; break;
} }
} }
@ -164,7 +164,7 @@ export default definePlugin({
find: "UNREAD_IMPORTANT:", find: "UNREAD_IMPORTANT:",
replacement: { replacement: {
match: /\.name\),.{0,120}\.children.+?:null(?<=,channel:(\i).+?)/, match: /\.name\),.{0,120}\.children.+?:null(?<=,channel:(\i).+?)/,
replace: "$&,$self.TypingIndicator($1.id)" replace: "$&,$self.TypingIndicator($1.id,$1.getGuildId())"
} }
}, },
// Theads // Theads
@ -173,14 +173,14 @@ export default definePlugin({
find: "M11 9H4C2.89543 9 2 8.10457 2 7V1C2 0.447715 1.55228 0 1 0C0.447715 0 0 0.447715 0 1V7C0 9.20914 1.79086 11 4 11H11C11.5523 11 12 10.5523 12 10C12 9.44771 11.5523 9 11 9Z", find: "M11 9H4C2.89543 9 2 8.10457 2 7V1C2 0.447715 1.55228 0 1 0C0.447715 0 0 0.447715 0 1V7C0 9.20914 1.79086 11 4 11H11C11.5523 11 12 10.5523 12 10C12 9.44771 11.5523 9 11 9Z",
replacement: { replacement: {
match: /mentionsCount:\i.+?null(?<=channel:(\i).+?)/, match: /mentionsCount:\i.+?null(?<=channel:(\i).+?)/,
replace: "$&,$self.TypingIndicator($1.id)" replace: "$&,$self.TypingIndicator($1.id,$1.getGuildId())"
} }
} }
], ],
TypingIndicator: (channelId: string) => ( TypingIndicator: (channelId: string, guildId: string) => (
<ErrorBoundary noop> <ErrorBoundary noop>
<TypingIndicator channelId={channelId} /> <TypingIndicator channelId={channelId} guildId={guildId} />
</ErrorBoundary> </ErrorBoundary>
), ),
}); });

View file

@ -112,8 +112,8 @@ export default definePlugin({
{ {
find: "getCooldownTextStyle", find: "getCooldownTextStyle",
replacement: { replacement: {
match: /(?<=(\i)\.length\?\i.\i\.Messages.THREE_USERS_TYPING\.format\({\i:(\i),(?:\i:)?(\i),\i:\i}\):)\i\.\i\.Messages\.SEVERAL_USERS_TYPING/, match: /(,{a:(\i),b:(\i),c:\i}\):)\i\.\i\.string\(\i\.\i#{intl::SEVERAL_USERS_TYPING}\)(?<=(\i)\.length.+?)/,
replace: (_, users, a, b) => `$self.buildSeveralUsers({ a: ${a}, b: ${b}, count: ${users}.length - 2 })` replace: (_, rest, a, b, users) => `${rest}$self.buildSeveralUsers({ a: ${a}, b: ${b}, count: ${users}.length - 2 })`
}, },
predicate: () => settings.store.alternativeFormatting predicate: () => settings.store.alternativeFormatting
} }
@ -129,14 +129,22 @@ export default definePlugin({
buildSeveralUsers, buildSeveralUsers,
mutateChildren(props: any, users: User[], children: any) { mutateChildren(props: any, users: User[], children: any) {
if (!Array.isArray(children)) return children; try {
if (!Array.isArray(children)) {
return children;
}
let element = 0; let element = 0;
return children.map(c => return children.map(c =>
c.type === "strong" c.type === "strong" || (typeof c !== "string" && !React.isValidElement(c))
? <TypingUser {...props} user={users[element++]} /> ? <TypingUser {...props} user={users[element++]} />
: c : c
); );
} catch (e) {
console.error(e);
}
return children;
} }
}); });

View file

@ -25,7 +25,7 @@ export default definePlugin({
settings, settings,
patches: [ patches: [
{ {
find: ".Messages.AVATAR_UPLOAD_EDIT_MEDIA", find: "#{intl::AVATAR_UPLOAD_EDIT_MEDIA}",
replacement: { replacement: {
match: /maxValue:\d/, match: /maxValue:\d/,
replace: "maxValue:$self.settings.store.zoomMultiplier", replace: "maxValue:$self.settings.store.zoomMultiplier",

View file

@ -18,9 +18,10 @@
import { getUserSettingLazy } from "@api/UserSettings"; import { getUserSettingLazy } from "@api/UserSettings";
import ErrorBoundary from "@components/ErrorBoundary"; import ErrorBoundary from "@components/ErrorBoundary";
import { getIntlMessage } from "@utils/discord";
import { classes } from "@utils/misc"; import { classes } from "@utils/misc";
import { findByPropsLazy } from "@webpack"; import { findByPropsLazy } from "@webpack";
import { i18n, Tooltip, UserStore } from "@webpack/common"; import { Tooltip, UserStore } from "@webpack/common";
import { Message } from "discord-types/general"; import { Message } from "discord-types/general";
import { settings } from "./settings"; import { settings } from "./settings";
@ -44,7 +45,7 @@ function PronounsChatComponent({ message }: { message: Message; }) {
const pronouns = useFormattedPronouns(message.author.id); const pronouns = useFormattedPronouns(message.author.id);
return pronouns && ( return pronouns && (
<Tooltip text={i18n.Messages.USER_PROFILE_PRONOUNS}> <Tooltip text={getIntlMessage("USER_PROFILE_PRONOUNS")}>
{tooltipProps => ( {tooltipProps => (
<span <span
{...tooltipProps} {...tooltipProps}

View file

@ -14,7 +14,7 @@ import { Channel } from "discord-types/general";
const cl = classNameFactory("vc-uvs-"); const cl = classNameFactory("vc-uvs-");
const { selectVoiceChannel } = findByPropsLazy("selectVoiceChannel", "selectChannel"); const { selectVoiceChannel } = findByPropsLazy("selectVoiceChannel", "selectChannel");
const { useChannelName } = mapMangledModuleLazy(".Messages.GROUP_DM_ALONE", { const { useChannelName } = mapMangledModuleLazy("#{intl::GROUP_DM_ALONE}", {
useChannelName: filters.byCode("()=>null==") useChannelName: filters.byCode("()=>null==")
}); });
const getDMChannelIcon = findByCodeLazy(".getChannelIconURL({"); const getDMChannelIcon = findByCodeLazy(".getChannelIconURL({");

View file

@ -57,7 +57,7 @@ export default definePlugin({
patches: [ patches: [
// User Popout, Full Size Profile, Direct Messages Side Profile // User Popout, Full Size Profile, Direct Messages Side Profile
{ {
find: ".Messages.USER_PROFILE_LOAD_ERROR", find: "#{intl::USER_PROFILE_LOAD_ERROR}",
replacement: { replacement: {
match: /(\.fetchError.+?\?)null/, match: /(\.fetchError.+?\?)null/,
replace: (_, rest) => `${rest}$self.VoiceChannelIndicator({userId:arguments[0]?.userId,isProfile:true})` replace: (_, rest) => `${rest}$self.VoiceChannelIndicator({userId:arguments[0]?.userId,isProfile:true})`
@ -78,7 +78,7 @@ export default definePlugin({
{ {
find: "PrivateChannel.renderAvatar", find: "PrivateChannel.renderAvatar",
replacement: { replacement: {
match: /\.Messages\.CLOSE_DM.+?}\)(?=])/, match: /#{intl::CLOSE_DM}.+?}\)(?=])/,
replace: "$&,$self.VoiceChannelIndicator({userId:arguments[0]?.user?.id})" replace: "$&,$self.VoiceChannelIndicator({userId:arguments[0]?.user?.id})"
}, },
predicate: () => settings.store.showVoiceChannelIndicator predicate: () => settings.store.showVoiceChannelIndicator

View file

@ -37,9 +37,9 @@ export default definePlugin({
authors: [Devs.newwares], authors: [Devs.newwares],
patches: [ patches: [
{ {
find: "Messages.REPLY_QUOTE_MESSAGE_NOT_LOADED", find: "#{intl::REPLY_QUOTE_MESSAGE_NOT_LOADED}",
replacement: { replacement: {
match: /Messages\.REPLY_QUOTE_MESSAGE_NOT_LOADED/, match: /#{intl::REPLY_QUOTE_MESSAGE_NOT_LOADED}\)/,
replace: "$&,onMouseEnter:()=>$self.fetchReply(arguments[0])" replace: "$&,onMouseEnter:()=>$self.fetchReply(arguments[0])"
} }
}, },

View file

@ -209,10 +209,11 @@ export default definePlugin({
}, },
// Group DMs top small & large icon // Group DMs top small & large icon
{ {
find: /\.recipients\.length>=2(?!<isMultiUserDM.{0,50})/, find: '["aria-hidden"],"aria-label":',
replacement: { replacement: {
match: /null==\i\.icon\?.+?src:(\(0,\i\.\i\).+?\))(?=[,}])/, match: /null==\i\.icon\?.+?src:(\(0,\i\.\i\).+?\))(?=[,}])/,
replace: (m, iconUrl) => `${m},onClick:()=>$self.openAvatar(${iconUrl})` // We have to check that icon is not an unread GDM in the server bar
replace: (m, iconUrl) => `${m},onClick:()=>arguments[0]?.size!=="SIZE_48"&&$self.openAvatar(${iconUrl})`
} }
}, },
// User DMs top small icon // User DMs top small icon

Some files were not shown because too many files have changed in this diff Show more