Compare commits
23 commits
f5ac9304ae
...
76b4e72917
Author | SHA1 | Date | |
---|---|---|---|
76b4e72917 | |||
|
97c0face2f | ||
|
1a1d9b07e8 | ||
|
4a2def03e7 | ||
|
544edce9f9 | ||
|
e4485165d0 | ||
|
fada76ec81 | ||
|
f659c46031 | ||
|
ae1dc4eab0 | ||
|
fe60a72b80 | ||
|
5a0b2ee3f5 | ||
|
6c1b8b0d8a | ||
|
b2a1410a96 | ||
|
c25c95eecd | ||
|
d94418f42f | ||
|
da1a8cdd67 | ||
|
5d7ede34d8 | ||
|
cd61354998 | ||
|
a452945ac8 | ||
|
b577660800 | ||
|
4f57c7eded | ||
|
e3e5da10a9 | ||
|
998ce72f3b |
30 changed files with 253 additions and 107 deletions
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "vencord",
|
||||
"private": "true",
|
||||
"version": "1.5.6",
|
||||
"version": "1.5.8",
|
||||
"description": "The cutest Discord client mod",
|
||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||
"bugs": {
|
||||
|
|
|
@ -43,7 +43,7 @@ const nodeCommonOpts = {
|
|||
format: "cjs",
|
||||
platform: "node",
|
||||
target: ["esnext"],
|
||||
external: ["electron", ...commonOpts.external],
|
||||
external: ["electron", "original-fs", ...commonOpts.external],
|
||||
define: defines,
|
||||
};
|
||||
|
||||
|
|
|
@ -61,6 +61,13 @@ const report = {
|
|||
otherErrors: [] as string[]
|
||||
};
|
||||
|
||||
const IGNORED_DISCORD_ERRORS = [
|
||||
"KeybindStore: Looking for callback action",
|
||||
"Unable to process domain list delta: Client revision number is null",
|
||||
"Downloading the full bad domains file",
|
||||
/\[GatewaySocket\].{0,110}Cannot access '/
|
||||
] as Array<string | RegExp>;
|
||||
|
||||
function toCodeBlock(s: string) {
|
||||
s = s.replace(/```/g, "`\u200B`\u200B`");
|
||||
return "```" + s + " ```";
|
||||
|
@ -86,6 +93,8 @@ async function printReport() {
|
|||
console.log(` - Error: ${toCodeBlock(p.error)}`);
|
||||
});
|
||||
|
||||
report.otherErrors = report.otherErrors.filter(e => !IGNORED_DISCORD_ERRORS.some(regex => e.match(regex)));
|
||||
|
||||
console.log("## Discord Errors");
|
||||
report.otherErrors.forEach(e => {
|
||||
console.log(`- ${toCodeBlock(e)}`);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
font-size: 20px;
|
||||
height: 20px;
|
||||
position: relative;
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
|
||||
.vc-author-modal-name::before {
|
||||
|
|
|
@ -25,7 +25,7 @@ import { Margins } from "@utils/margins";
|
|||
import { classes } from "@utils/misc";
|
||||
import { showItemInFolder } from "@utils/native";
|
||||
import { useAwaiter } from "@utils/react";
|
||||
import { findByCodeLazy, findByPropsLazy, findLazy } from "@webpack";
|
||||
import { findByPropsLazy, findLazy } from "@webpack";
|
||||
import { Button, Card, FluxDispatcher, Forms, React, showToast, TabBar, TextArea, useEffect, useRef, useState } from "@webpack/common";
|
||||
import { UserThemeHeader } from "main/themes";
|
||||
import type { ComponentType, Ref, SyntheticEvent } from "react";
|
||||
|
@ -41,7 +41,7 @@ type FileInput = ComponentType<{
|
|||
}>;
|
||||
|
||||
const InviteActions = findByPropsLazy("resolveInvite");
|
||||
const FileInput: FileInput = findByCodeLazy("activateUploadDialogue=");
|
||||
const FileInput: FileInput = findLazy(m => m.prototype?.activateUploadDialogue && m.prototype.setRef);
|
||||
const TextAreaProps = findLazy(m => typeof m.textarea === "string");
|
||||
|
||||
const cl = classNameFactory("vc-settings-theme-");
|
||||
|
|
|
@ -62,6 +62,10 @@ if (IS_VESKTOP || !IS_VANILLA) {
|
|||
} catch { }
|
||||
|
||||
|
||||
const findHeader = (headers: Record<string, string[]>, headerName: Lowercase<string>) => {
|
||||
return Object.keys(headers).find(h => h.toLowerCase() === headerName);
|
||||
};
|
||||
|
||||
// Remove CSP
|
||||
type PolicyResult = Record<string, string[]>;
|
||||
|
||||
|
@ -73,6 +77,7 @@ if (IS_VESKTOP || !IS_VANILLA) {
|
|||
result[directiveKey] = directiveValue;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
const stringifyPolicy = (policy: PolicyResult): string =>
|
||||
|
@ -81,31 +86,39 @@ if (IS_VESKTOP || !IS_VANILLA) {
|
|||
.map(directive => directive.flat().join(" "))
|
||||
.join("; ");
|
||||
|
||||
function patchCsp(headers: Record<string, string[]>, header: string) {
|
||||
if (header in headers) {
|
||||
const patchCsp = (headers: Record<string, string[]>) => {
|
||||
const header = findHeader(headers, "content-security-policy");
|
||||
|
||||
if (header) {
|
||||
const csp = parsePolicy(headers[header][0]);
|
||||
|
||||
for (const directive of ["style-src", "connect-src", "img-src", "font-src", "media-src", "worker-src"]) {
|
||||
csp[directive] = ["*", "blob:", "data:", "vencord:", "'unsafe-inline'"];
|
||||
csp[directive] ??= [];
|
||||
csp[directive].push("*", "blob:", "data:", "vencord:", "'unsafe-inline'");
|
||||
}
|
||||
|
||||
// TODO: Restrict this to only imported packages with fixed version.
|
||||
// Perhaps auto generate with esbuild
|
||||
csp["script-src"] ??= [];
|
||||
csp["script-src"].push("'unsafe-eval'", "https://unpkg.com", "https://cdnjs.cloudflare.com");
|
||||
headers[header] = [stringifyPolicy(csp)];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
session.defaultSession.webRequest.onHeadersReceived(({ responseHeaders, resourceType }, cb) => {
|
||||
if (responseHeaders) {
|
||||
if (resourceType === "mainFrame")
|
||||
patchCsp(responseHeaders, "content-security-policy");
|
||||
patchCsp(responseHeaders);
|
||||
|
||||
// Fix hosts that don't properly set the css content type, such as
|
||||
// raw.githubusercontent.com
|
||||
if (resourceType === "stylesheet")
|
||||
responseHeaders["content-type"] = ["text/css"];
|
||||
if (resourceType === "stylesheet") {
|
||||
const header = findHeader(responseHeaders, "content-type");
|
||||
if (header)
|
||||
responseHeaders[header] = ["text/css"];
|
||||
}
|
||||
}
|
||||
|
||||
cb({ cancel: false, responseHeaders });
|
||||
});
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
import { app } from "electron";
|
||||
import { existsSync, mkdirSync, readdirSync, renameSync, statSync, writeFileSync } from "fs";
|
||||
import { existsSync, mkdirSync, readdirSync, renameSync, statSync, writeFileSync } from "original-fs";
|
||||
import { basename, dirname, join } from "path";
|
||||
|
||||
function isNewer($new: string, old: string) {
|
||||
|
|
|
@ -27,8 +27,8 @@ export default definePlugin({
|
|||
{
|
||||
find: ".withMentionPrefix",
|
||||
replacement: {
|
||||
match: /(.roleDot.{10,50}{children:.{1,2})}\)/,
|
||||
replace: "$1.concat(Vencord.Api.MessageDecorations.__addDecorationsToMessage(arguments[0]))})"
|
||||
match: /currentUserIsPremium:.{0,70}{children:\i(?=}\))/,
|
||||
replace: "$&.concat(Vencord.Api.MessageDecorations.__addDecorationsToMessage(arguments[0]))"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
@ -63,26 +63,26 @@ export default definePlugin({
|
|||
replacement: {
|
||||
get match() {
|
||||
switch (Settings.plugins.Settings.settingsLocation) {
|
||||
case "top": return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.USER_SETTINGS\}/;
|
||||
case "aboveNitro": return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.BILLING_SETTINGS\}/;
|
||||
case "belowNitro": return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.APP_SETTINGS\}/;
|
||||
case "belowActivity": return /(?<=\{section:(\i)\.ID\.DIVIDER},)\{section:"changelog"/;
|
||||
case "bottom": return /\{section:(\i)\.ID\.CUSTOM,\s*element:.+?}/;
|
||||
case "top": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.USER_SETTINGS\}/;
|
||||
case "aboveNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.BILLING_SETTINGS\}/;
|
||||
case "belowNitro": return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.APP_SETTINGS\}/;
|
||||
case "belowActivity": return /(?<=\{section:(\i\.\i)\.DIVIDER},)\{section:"changelog"/;
|
||||
case "bottom": return /\{section:(\i\.\i)\.CUSTOM,\s*element:.+?}/;
|
||||
case "aboveActivity":
|
||||
default:
|
||||
return /\{section:(\i)\.ID\.HEADER,\s*label:(\i)\.\i\.Messages\.ACTIVITY_SETTINGS\}/;
|
||||
return /\{section:(\i\.\i)\.HEADER,\s*label:(\i)\.\i\.Messages\.ACTIVITY_SETTINGS\}/;
|
||||
}
|
||||
},
|
||||
replace: "...$self.makeSettingsCategories($1),$&"
|
||||
}
|
||||
}],
|
||||
|
||||
customSections: [] as ((ID: Record<string, unknown>) => any)[],
|
||||
customSections: [] as ((SectionTypes: Record<string, unknown>) => any)[],
|
||||
|
||||
makeSettingsCategories({ ID }: { ID: Record<string, unknown>; }) {
|
||||
makeSettingsCategories(SectionTypes: Record<string, unknown>) {
|
||||
return [
|
||||
{
|
||||
section: ID.HEADER,
|
||||
section: SectionTypes.HEADER,
|
||||
label: "Vencord",
|
||||
className: "vc-settings-header"
|
||||
},
|
||||
|
@ -128,9 +128,9 @@ export default definePlugin({
|
|||
element: require("@components/VencordSettings/PatchHelperTab").default,
|
||||
className: "vc-patch-helper"
|
||||
},
|
||||
...this.customSections.map(func => func(ID)),
|
||||
...this.customSections.map(func => func(SectionTypes)),
|
||||
{
|
||||
section: ID.DIVIDER
|
||||
section: SectionTypes.DIVIDER
|
||||
}
|
||||
].filter(Boolean);
|
||||
},
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
import { Settings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
|
||||
const UserPopoutSectionCssClasses = findByPropsLazy("section", "lastSection");
|
||||
|
||||
export default definePlugin({
|
||||
name: "BetterNotesBox",
|
||||
|
@ -34,12 +37,20 @@ export default definePlugin({
|
|||
match: /hideNote:.+?(?=[,}])/g,
|
||||
replace: "hideNote:true",
|
||||
}
|
||||
}, {
|
||||
},
|
||||
{
|
||||
find: "Messages.NOTE_PLACEHOLDER",
|
||||
replacement: {
|
||||
match: /\.NOTE_PLACEHOLDER,/,
|
||||
replace: "$&spellCheck:!Vencord.Settings.plugins.BetterNotesBox.noSpellCheck,"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: ".Messages.NOTE}",
|
||||
replacement: {
|
||||
match: /(\i)\.hideNote\?null/,
|
||||
replace: "$1.hideNote?$self.patchPadding($1)"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
|
@ -56,5 +67,12 @@ export default definePlugin({
|
|||
disabled: () => Settings.plugins.BetterNotesBox.hide,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
patchPadding(e: any) {
|
||||
if (!e.lastSection) return;
|
||||
return (
|
||||
<div className={UserPopoutSectionCssClasses.lastSection}></div>
|
||||
);
|
||||
}
|
||||
});
|
|
@ -77,15 +77,6 @@ export default definePlugin({
|
|||
}
|
||||
]
|
||||
},
|
||||
// Fix search history being disabled / broken with isStaff
|
||||
{
|
||||
find: 'get("disable_new_search")',
|
||||
predicate: () => settings.store.enableIsStaff,
|
||||
replacement: {
|
||||
match: /(?<=showNewSearch"\);return)\s?!/,
|
||||
replace: "!1&&!"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: 'H1,title:"Experiments"',
|
||||
replacement: {
|
||||
|
|
|
@ -295,7 +295,7 @@ export default definePlugin({
|
|||
},
|
||||
{
|
||||
predicate: () => settings.store.transformStickers,
|
||||
match: /renderAttachments=function\(\i\){var (\i)=\i.attachments.+?;/,
|
||||
match: /renderAttachments=function\(\i\){var \i=this,(\i)=\i.attachments.+?;/,
|
||||
replace: (m, attachments) => `${m}${attachments}=$self.filterAttachments(${attachments});`
|
||||
}
|
||||
]
|
||||
|
@ -329,6 +329,20 @@ export default definePlugin({
|
|||
match: /(?<=\.Messages\.EMOJI_POPOUT_ADDED_PACK_DESCRIPTION.+?return ).{0,1200}\.Messages\.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION.+?(?=}\()/,
|
||||
replace: reactNode => `$self.addFakeNotice(${FakeNoticeType.Emoji},${reactNode},!!arguments[0]?.fakeNitroNode?.fake)`
|
||||
}
|
||||
},
|
||||
{
|
||||
find: "canUsePremiumAppIcons:function",
|
||||
replacement: {
|
||||
match: /canUsePremiumAppIcons:function\(\i\){/,
|
||||
replace: "$&return true;"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: "location:\"AppIconHome\"",
|
||||
replacement: {
|
||||
match: /\i\.\i\.isPremium\(\i\.\i\.getCurrentUser\(\)\)/,
|
||||
replace: "true"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
import { GuildStore } from "@webpack/common";
|
||||
import { Channel, User } from "discord-types/general";
|
||||
|
||||
export default definePlugin({
|
||||
name: "ForceOwnerCrown",
|
||||
|
@ -34,25 +35,15 @@ export default definePlugin({
|
|||
}
|
||||
}
|
||||
],
|
||||
isGuildOwner(props) {
|
||||
// Check if channel is a Group DM, if so return false
|
||||
if (props?.channel?.type === 3) {
|
||||
isGuildOwner(props: { user: User, channel: Channel, guildId?: string; }) {
|
||||
if (!props?.user?.id) return false;
|
||||
if (props.channel?.type === 3 /* GROUP_DM */)
|
||||
return false;
|
||||
}
|
||||
|
||||
// guild id is in props twice, fallback if the first is undefined
|
||||
const guildId = props?.guildId ?? props?.channel?.guild_id;
|
||||
const userId = props?.user?.id;
|
||||
const guildId = props.guildId ?? props.channel?.guild_id;
|
||||
const userId = props.user.id;
|
||||
|
||||
if (guildId && userId) {
|
||||
const guild = GuildStore.getGuild(guildId);
|
||||
if (guild) {
|
||||
return guild.ownerId === userId;
|
||||
}
|
||||
console.error("[ForceOwnerCrown] failed to get guild", { guildId, guild, props });
|
||||
} else {
|
||||
console.error("[ForceOwnerCrown] no guildId or userId", { guildId, userId, props });
|
||||
}
|
||||
return false;
|
||||
return GuildStore.getGuild(guildId)?.ownerId === userId;
|
||||
},
|
||||
});
|
||||
|
|
|
@ -106,7 +106,7 @@ export default definePlugin({
|
|||
}
|
||||
},
|
||||
{
|
||||
find: ".Messages.EMBEDDED_ACTIVITIES_HAVE_PLAYED_ONE_KNOWN",
|
||||
find: ".Messages.EMBEDDED_ACTIVITIES_DEVELOPER_SHELF_SUBTITLE",
|
||||
replacement: [
|
||||
{
|
||||
match: /(?<=\(\)\.activityTitleText.+?children:(\i)\.name.*?}\),)/,
|
||||
|
|
|
@ -360,7 +360,7 @@ export default definePlugin({
|
|||
{
|
||||
// Render editHistory in the deepest div for message content
|
||||
match: /(\)\("div",\{id:.+?children:\[)/,
|
||||
replace: "$1 (arguments[0].message.editHistory.length > 0 ? arguments[0].message.editHistory.map(edit => $self.renderEdit(edit)) : null), "
|
||||
replace: "$1 (arguments[0].message.editHistory?.length > 0 ? arguments[0].message.editHistory.map(edit => $self.renderEdit(edit)) : null), "
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -84,8 +84,8 @@ export default definePlugin({
|
|||
find: "showProgressBadge:",
|
||||
predicate: () => settings.store.hidePremiumOffersCount,
|
||||
replacement: {
|
||||
match: /\(function\(\){return \i\.\i\.getUnacknowledgedOffers\(\i\)\.length}\)/,
|
||||
replace: "(function(){return 0})"
|
||||
match: /=\i\.unviewedTrialCount\+\i\.unviewedDiscountCount/,
|
||||
replace: "=0"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
21
src/plugins/noTypingAnimation/index.ts
Normal file
21
src/plugins/noTypingAnimation/index.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Vencord, a Discord client mod
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
|
||||
export default definePlugin({
|
||||
name: "NoTypingAnimation",
|
||||
authors: [Devs.AutumnVN],
|
||||
description: "Disables the CPU-intensive typing dots animation",
|
||||
patches: [{
|
||||
find: "dotCycle",
|
||||
replacement: {
|
||||
match: /document.hasFocus\(\)/,
|
||||
replace: "false"
|
||||
}
|
||||
}]
|
||||
});
|
|
@ -4,20 +4,44 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
import { ChannelStore, ReadStateStore } from "@webpack/common";
|
||||
import { Message } from "discord-types/general";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { ChannelStore, ReadStateStore, UserStore } from "@webpack/common";
|
||||
import { MessageJSON } from "discord-types/general";
|
||||
|
||||
const enum ChannelType {
|
||||
DM = 1,
|
||||
GROUP_DM = 3
|
||||
}
|
||||
|
||||
const settings = definePluginSettings({
|
||||
channelToAffect: {
|
||||
type: OptionType.SELECT,
|
||||
description: "Select the type of DM for the plugin to affect",
|
||||
options: [
|
||||
{ label: "Both", value: "both_dms", default: true },
|
||||
{ label: "User DMs", value: "user_dm" },
|
||||
{ label: "Group DMs", value: "group_dm" },
|
||||
]
|
||||
},
|
||||
allowMentions: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Receive audio pings for @mentions",
|
||||
default: false,
|
||||
},
|
||||
allowEveryone: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Receive audio pings for @everyone and @here in group DMs",
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
export default definePlugin({
|
||||
name: "OnePingPerDM",
|
||||
description: "If unread messages are sent by a user in DMs multiple times, you'll only receive one audio ping. Read the messages to reset the limit",
|
||||
authors: [Devs.ProffDea],
|
||||
settings,
|
||||
patches: [{
|
||||
find: ".getDesktopType()===",
|
||||
replacement: [{
|
||||
|
@ -29,11 +53,19 @@ export default definePlugin({
|
|||
replace: "sound:!$self.isPrivateChannelRead(arguments[0]?.message)?undefined:$1"
|
||||
}]
|
||||
}],
|
||||
isPrivateChannelRead(message: Message) {
|
||||
isPrivateChannelRead(message: MessageJSON) {
|
||||
const channelType = ChannelStore.getChannel(message.channel_id)?.type;
|
||||
if (channelType !== ChannelType.DM && channelType !== ChannelType.GROUP_DM) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
(channelType === ChannelType.DM && settings.store.channelToAffect === "group_dm") ||
|
||||
(channelType === ChannelType.GROUP_DM && settings.store.channelToAffect === "user_dm") ||
|
||||
(settings.store.allowMentions && message.mentions.some(m => m.id === UserStore.getCurrentUser().id)) ||
|
||||
(settings.store.allowEveryone && message.mention_everyone)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return ReadStateStore.getOldestUnreadMessageId(message.channel_id) === message.id;
|
||||
},
|
||||
});
|
||||
|
|
|
@ -81,7 +81,7 @@ export default definePlugin({
|
|||
// array with the count, or an empty array. Due to spreading, only in the former
|
||||
// case will an element be added to the outer array
|
||||
// Thanks for the fix, Strencher!
|
||||
replace: "$&...$1.props.pinCount,"
|
||||
replace: "$&...($1.props.pinCount ?? []),"
|
||||
},
|
||||
{
|
||||
// Patch renderSection (renders the header) to set the text to "Pinned DMs" instead of "Direct Messages"
|
||||
|
|
|
@ -30,13 +30,13 @@ import { User } from "discord-types/general";
|
|||
const SessionsStore = findStoreLazy("SessionsStore");
|
||||
|
||||
function Icon(path: string, opts?: { viewBox?: string; width?: number; height?: number; }) {
|
||||
return ({ color, tooltip }: { color: string; tooltip: string; }) => (
|
||||
return ({ color, tooltip, small }: { color: string; tooltip: string; small: boolean; }) => (
|
||||
<Tooltip text={tooltip} >
|
||||
{(tooltipProps: any) => (
|
||||
<svg
|
||||
{...tooltipProps}
|
||||
height={opts?.height ?? 20}
|
||||
width={opts?.width ?? 20}
|
||||
height={(opts?.height ?? 20) - (small ? 3 : 0)}
|
||||
width={(opts?.width ?? 20) - (small ? 3 : 0)}
|
||||
viewBox={opts?.viewBox ?? "0 0 24 24"}
|
||||
fill={color}
|
||||
>
|
||||
|
@ -57,16 +57,16 @@ type Platform = keyof typeof Icons;
|
|||
|
||||
const getStatusColor = findByCodeLazy(".TWITCH", ".STREAMING", ".INVISIBLE");
|
||||
|
||||
const PlatformIcon = ({ platform, status }: { platform: Platform, status: string; }) => {
|
||||
const PlatformIcon = ({ platform, status, small }: { platform: Platform, status: string; small: boolean; }) => {
|
||||
const tooltip = platform[0].toUpperCase() + platform.slice(1);
|
||||
const Icon = Icons[platform] ?? Icons.desktop;
|
||||
|
||||
return <Icon color={`var(--${getStatusColor(status)}`} tooltip={tooltip} />;
|
||||
return <Icon color={`var(--${getStatusColor(status)}`} tooltip={tooltip} small={small} />;
|
||||
};
|
||||
|
||||
const getStatus = (id: string): Record<Platform, string> => PresenceStore.getState()?.clientStatuses?.[id];
|
||||
|
||||
const PlatformIndicator = ({ user, wantMargin = true, wantTopMargin = false }: { user: User; wantMargin?: boolean; wantTopMargin?: boolean; }) => {
|
||||
const PlatformIndicator = ({ user, wantMargin = true, wantTopMargin = false, small = false }: { user: User; wantMargin?: boolean; wantTopMargin?: boolean; small?: boolean; }) => {
|
||||
if (!user || user.bot) return null;
|
||||
|
||||
if (user.id === UserStore.getCurrentUser().id) {
|
||||
|
@ -99,6 +99,7 @@ const PlatformIndicator = ({ user, wantMargin = true, wantTopMargin = false }: {
|
|||
key={platform}
|
||||
platform={platform as Platform}
|
||||
status={status}
|
||||
small={small}
|
||||
/>
|
||||
));
|
||||
|
||||
|
@ -137,7 +138,7 @@ const indicatorLocations = {
|
|||
description: "In the member list",
|
||||
onEnable: () => addDecorator("platform-indicator", props =>
|
||||
<ErrorBoundary noop>
|
||||
<PlatformIndicator user={props.user} />
|
||||
<PlatformIndicator user={props.user} small={true} />
|
||||
</ErrorBoundary>
|
||||
),
|
||||
onDisable: () => removeDecorator("platform-indicator")
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { classes } from "@utils/misc";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { UserStore } from "@webpack/common";
|
||||
|
@ -39,17 +40,17 @@ function shouldShow(message: Message): boolean {
|
|||
return true;
|
||||
}
|
||||
|
||||
export function PronounsChatComponentWrapper({ message }: { message: Message; }) {
|
||||
export const PronounsChatComponentWrapper = ErrorBoundary.wrap(({ message }: { message: Message; }) => {
|
||||
return shouldShow(message)
|
||||
? <PronounsChatComponent message={message} />
|
||||
: null;
|
||||
}
|
||||
}, { noop: true });
|
||||
|
||||
export function CompactPronounsChatComponentWrapper({ message }: { message: Message; }) {
|
||||
export const CompactPronounsChatComponentWrapper = ErrorBoundary.wrap(({ message }: { message: Message; }) => {
|
||||
return shouldShow(message)
|
||||
? <CompactPronounsChatComponent message={message} />
|
||||
: null;
|
||||
}
|
||||
}, { noop: true });
|
||||
|
||||
function PronounsChatComponent({ message }: { message: Message; }) {
|
||||
const [result] = useFormattedPronouns(message.author.id);
|
||||
|
@ -63,7 +64,7 @@ function PronounsChatComponent({ message }: { message: Message; }) {
|
|||
: null;
|
||||
}
|
||||
|
||||
export function CompactPronounsChatComponent({ message }: { message: Message; }) {
|
||||
export const CompactPronounsChatComponent = ErrorBoundary.wrap(({ message }: { message: Message; }) => {
|
||||
const [result] = useFormattedPronouns(message.author.id);
|
||||
|
||||
return result
|
||||
|
@ -73,4 +74,4 @@ export function CompactPronounsChatComponent({ message }: { message: Message; })
|
|||
>• {result}</span>
|
||||
)
|
||||
: null;
|
||||
}
|
||||
}, { noop: true });
|
||||
|
|
|
@ -41,7 +41,7 @@ export default definePlugin({
|
|||
find: "showCommunicationDisabledStyles",
|
||||
replacement: {
|
||||
match: /("span",{id:\i,className:\i,children:\i}\))/,
|
||||
replace: "$1, $self.CompactPronounsChatComponentWrapper(e)"
|
||||
replace: "$1, $self.CompactPronounsChatComponentWrapper(arguments[0])"
|
||||
}
|
||||
},
|
||||
// Patch the chat timestamp element (normal mode)
|
||||
|
@ -49,7 +49,7 @@ export default definePlugin({
|
|||
find: "showCommunicationDisabledStyles",
|
||||
replacement: {
|
||||
match: /(?<=return\s*\(0,\i\.jsxs?\)\(.+!\i&&)(\(0,\i.jsxs?\)\(.+?\{.+?\}\))/,
|
||||
replace: "[$1, $self.PronounsChatComponentWrapper(e)]"
|
||||
replace: "[$1, $self.PronounsChatComponentWrapper(arguments[0])]"
|
||||
}
|
||||
},
|
||||
// Patch the profile popout username header to use our pronoun hook instead of Discord's pronouns
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.vc-uvs-button > div {
|
||||
.vc-uvs-button>div {
|
||||
white-space: normal !important;
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
|||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.vc-uvs-popout-margin > [class^="section"] {
|
||||
margin-top: -12px;
|
||||
.vc-uvs-popout-margin-self>[class^="section"] {
|
||||
padding-top: 0;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
|
|
@ -20,14 +20,13 @@ import { definePluginSettings } from "@api/Settings";
|
|||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { findByPropsLazy, findStoreLazy } from "@webpack";
|
||||
import { findStoreLazy } from "@webpack";
|
||||
import { ChannelStore, GuildStore, UserStore } from "@webpack/common";
|
||||
import { User } from "discord-types/general";
|
||||
|
||||
import { VoiceChannelSection } from "./components/VoiceChannelSection";
|
||||
|
||||
const VoiceStateStore = findStoreLazy("VoiceStateStore");
|
||||
const UserPopoutSectionCssClasses = findByPropsLazy("section", "lastSection");
|
||||
|
||||
const settings = definePluginSettings({
|
||||
showInUserProfileModal: {
|
||||
|
@ -88,7 +87,7 @@ export default definePlugin({
|
|||
patchPopout: ({ user }: UserProps) => {
|
||||
const isSelfUser = user.id === UserStore.getCurrentUser().id;
|
||||
return (
|
||||
<div className={isSelfUser ? `vc-uvs-popout-margin ${UserPopoutSectionCssClasses.lastSection}` : ""}>
|
||||
<div className={isSelfUser ? "vc-uvs-popout-margin-self" : ""}>
|
||||
<VoiceChannelField user={user} />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -24,7 +24,7 @@ import { Margins } from "@utils/margins";
|
|||
import { wordsToTitle } from "@utils/text";
|
||||
import definePlugin, { OptionType, PluginOptionsItem } from "@utils/types";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { Button, ChannelStore, Forms, SelectedChannelStore, useMemo, UserStore } from "@webpack/common";
|
||||
import { Button, ChannelStore, Forms, GuildMemberStore, SelectedChannelStore, SelectedGuildStore, useMemo, UserStore } from "@webpack/common";
|
||||
|
||||
interface VoiceState {
|
||||
userId: string;
|
||||
|
@ -70,11 +70,12 @@ function clean(str: string) {
|
|||
.trim();
|
||||
}
|
||||
|
||||
function formatText(str: string, user: string, channel: string, displayName: string) {
|
||||
function formatText(str: string, user: string, channel: string, displayName: string, nickname: string) {
|
||||
return str
|
||||
.replaceAll("{{USER}}", clean(user) || (user ? "Someone" : ""))
|
||||
.replaceAll("{{CHANNEL}}", clean(channel) || "channel")
|
||||
.replaceAll("{{DISPLAY_NAME}}", clean(displayName) || (displayName ? "Someone" : ""));
|
||||
.replaceAll("{{DISPLAY_NAME}}", clean(displayName) || (displayName ? "Someone" : ""))
|
||||
.replaceAll("{{NICKNAME}}", clean(nickname) || (nickname ? "Someone" : ""));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -145,8 +146,9 @@ function updateStatuses(type: string, { deaf, mute, selfDeaf, selfMute, userId,
|
|||
function playSample(tempSettings: any, type: string) {
|
||||
const settings = Object.assign({}, Settings.plugins.VcNarrator, tempSettings);
|
||||
const currentUser = UserStore.getCurrentUser();
|
||||
const myGuildId = SelectedGuildStore.getGuildId();
|
||||
|
||||
speak(formatText(settings[type + "Message"], currentUser.username, "general", (currentUser as any).globalName ?? currentUser.username), settings);
|
||||
speak(formatText(settings[type + "Message"], currentUser.username, "general", (currentUser as any).globalName ?? currentUser.username, GuildMemberStore.getNick(myGuildId, currentUser.id) ?? currentUser.username), settings);
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
|
@ -156,6 +158,7 @@ export default definePlugin({
|
|||
|
||||
flux: {
|
||||
VOICE_STATE_UPDATES({ voiceStates }: { voiceStates: VoiceState[]; }) {
|
||||
const myGuildId = SelectedGuildStore.getGuildId();
|
||||
const myChanId = SelectedChannelStore.getVoiceChannelId();
|
||||
const myId = UserStore.getCurrentUser().id;
|
||||
|
||||
|
@ -175,9 +178,10 @@ export default definePlugin({
|
|||
const template = Settings.plugins.VcNarrator[type + "Message"];
|
||||
const user = isMe && !Settings.plugins.VcNarrator.sayOwnName ? "" : UserStore.getUser(userId).username;
|
||||
const displayName = user && ((UserStore.getUser(userId) as any).globalName ?? user);
|
||||
const nickname = user && (GuildMemberStore.getNick(myGuildId, userId) ?? user);
|
||||
const channel = ChannelStore.getChannel(id).name;
|
||||
|
||||
speak(formatText(template, user, channel, displayName));
|
||||
speak(formatText(template, user, channel, displayName, nickname));
|
||||
|
||||
// updateStatuses(type, state, isMe);
|
||||
}
|
||||
|
@ -189,7 +193,7 @@ export default definePlugin({
|
|||
if (!s) return;
|
||||
|
||||
const event = s.mute || s.selfMute ? "unmute" : "mute";
|
||||
speak(formatText(Settings.plugins.VcNarrator[event + "Message"], "", ChannelStore.getChannel(chanId).name, ""));
|
||||
speak(formatText(Settings.plugins.VcNarrator[event + "Message"], "", ChannelStore.getChannel(chanId).name, "", ""));
|
||||
},
|
||||
|
||||
AUDIO_TOGGLE_SELF_DEAF() {
|
||||
|
@ -198,7 +202,7 @@ export default definePlugin({
|
|||
if (!s) return;
|
||||
|
||||
const event = s.deaf || s.selfDeaf ? "undeafen" : "deafen";
|
||||
speak(formatText(Settings.plugins.VcNarrator[event + "Message"], "", ChannelStore.getChannel(chanId).name, ""));
|
||||
speak(formatText(Settings.plugins.VcNarrator[event + "Message"], "", ChannelStore.getChannel(chanId).name, "", ""));
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -315,8 +319,8 @@ export default definePlugin({
|
|||
You can customise the spoken messages below. You can disable specific messages by setting them to nothing
|
||||
</Forms.FormText>
|
||||
<Forms.FormText>
|
||||
The special placeholders <code>{"{{USER}}"}</code>, <code>{"{{DISPLAY_NAME}}"}</code> and <code>{"{{CHANNEL}}"}</code>{" "}
|
||||
will be replaced with the user's name (nothing if it's yourself), the user's display name and the channel's name respectively
|
||||
The special placeholders <code>{"{{USER}}"}</code>, <code>{"{{DISPLAY_NAME}}"}</code>, <code>{"{{NICKNAME}}"}</code> and <code>{"{{CHANNEL}}"}</code>{" "}
|
||||
will be replaced with the user's name (nothing if it's yourself), the user's display name, the user's nickname on current server and the channel's name respectively
|
||||
</Forms.FormText>
|
||||
{hasEnglishVoices && (
|
||||
<>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { filters, findByCode, mapMangledModuleLazy } from "@webpack";
|
||||
import { filters, findByCode, findByPropsLazy, mapMangledModuleLazy } from "@webpack";
|
||||
import type { ComponentType, PropsWithChildren, ReactNode, Ref } from "react";
|
||||
|
||||
import { LazyComponent } from "./react";
|
||||
|
@ -132,12 +132,7 @@ export const ModalContent = LazyComponent(() => Modals.ModalContent);
|
|||
export const ModalFooter = LazyComponent(() => Modals.ModalFooter);
|
||||
export const ModalCloseButton = LazyComponent(() => Modals.ModalCloseButton);
|
||||
|
||||
const ModalAPI = mapMangledModuleLazy("onCloseRequest:null!=", {
|
||||
openModal: filters.byCode("onCloseRequest:null!="),
|
||||
closeModal: filters.byCode("onCloseCallback&&"),
|
||||
openModalLazy: m => m?.length === 1 && filters.byCode(".apply(this,arguments)")(m),
|
||||
closeAllModals: filters.byCode(".value.key,")
|
||||
});
|
||||
const ModalAPI = findByPropsLazy("openModalLazy");
|
||||
|
||||
/**
|
||||
* Wait for the render promise to resolve, then open a modal with it.
|
||||
|
|
|
@ -16,10 +16,11 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { proxyLazy } from "@utils/lazy";
|
||||
import type * as Stores from "discord-types/stores";
|
||||
|
||||
// eslint-disable-next-line path-alias/no-relative
|
||||
import { filters, findByCodeLazy, findByPropsLazy, mapMangledModuleLazy } from "../webpack";
|
||||
import { filters, findByCode, findByProps, findByPropsLazy, mapMangledModuleLazy } from "../webpack";
|
||||
import { waitForStore } from "./internal";
|
||||
import * as t from "./types/stores";
|
||||
|
||||
|
@ -83,7 +84,14 @@ export const useStateFromStores: <T>(
|
|||
idk?: any,
|
||||
isEqual?: (old: T, newer: T) => boolean
|
||||
) => T
|
||||
= findByCodeLazy("useStateFromStores");
|
||||
// FIXME: hack to support old stable and new canary
|
||||
= proxyLazy(() => {
|
||||
try {
|
||||
return findByProps("useStateFromStores").useStateFromStores;
|
||||
} catch {
|
||||
return findByCode('("useStateFromStores")');
|
||||
}
|
||||
});
|
||||
|
||||
waitForStore("DraftStore", s => DraftStore = s);
|
||||
waitForStore("UserStore", s => UserStore = s);
|
||||
|
|
|
@ -16,10 +16,11 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { proxyLazy } from "@utils/lazy";
|
||||
import type { User } from "discord-types/general";
|
||||
|
||||
// eslint-disable-next-line path-alias/no-relative
|
||||
import { _resolveReady, filters, findByCodeLazy, findByPropsLazy, findLazy, mapMangledModuleLazy, waitFor } from "../webpack";
|
||||
import { _resolveReady, filters, find, findByCodeLazy, findByPropsLazy, findLazy, mapMangledModuleLazy, waitFor } from "../webpack";
|
||||
import type * as t from "./types/utils";
|
||||
|
||||
export let FluxDispatcher: t.FluxDispatcher;
|
||||
|
@ -126,4 +127,11 @@ waitFor("parseTopic", m => Parser = m);
|
|||
export let SettingsRouter: any;
|
||||
waitFor(["open", "saveAccountChanges"], m => SettingsRouter = m);
|
||||
|
||||
export const PermissionsBits: t.PermissionsBits = findLazy(m => typeof m.ADMINISTRATOR === "bigint");
|
||||
// FIXME: hack to support old stable and new canary
|
||||
export const PermissionsBits: t.PermissionsBits = proxyLazy(() => {
|
||||
try {
|
||||
return find(m => m.Permissions?.ADMINISTRATOR).Permissions;
|
||||
} catch {
|
||||
return find(m => typeof m.ADMINISTRATOR === "bigint");
|
||||
}
|
||||
});
|
||||
|
|
|
@ -36,9 +36,8 @@ if (window[WEBPACK_CHUNK]) {
|
|||
Object.defineProperty(window, WEBPACK_CHUNK, {
|
||||
get: () => webpackChunk,
|
||||
set: v => {
|
||||
if (v?.push !== Array.prototype.push) {
|
||||
if (v?.push !== Array.prototype.push && _initWebpack(v)) {
|
||||
logger.info(`Patching ${WEBPACK_CHUNK}.push`);
|
||||
_initWebpack(v);
|
||||
patchPush();
|
||||
// @ts-ignore
|
||||
delete window[WEBPACK_CHUNK];
|
||||
|
@ -85,10 +84,9 @@ function patchPush() {
|
|||
logger.error("Error in patched chunk", err);
|
||||
return void originalMod(module, exports, require);
|
||||
}
|
||||
|
||||
// There are (at the time of writing) 11 modules exporting the window
|
||||
// Make these non enumerable to improve webpack search performance
|
||||
if (module.exports === window) {
|
||||
if (exports === window) {
|
||||
Object.defineProperty(require.c, id, {
|
||||
value: require.c[id],
|
||||
enumerable: false,
|
||||
|
|
|
@ -62,9 +62,50 @@ export type CallbackFn = (mod: any, id: number) => void;
|
|||
export function _initWebpack(instance: typeof window.webpackChunkdiscord_app) {
|
||||
if (cache !== void 0) throw "no.";
|
||||
|
||||
wreq = instance.push([[Symbol("Vencord")], {}, r => r]);
|
||||
instance.push([[Symbol("Vencord")], {}, r => wreq = r]);
|
||||
if (!wreq) return false;
|
||||
|
||||
cache = wreq.c;
|
||||
instance.pop();
|
||||
|
||||
for (const id in cache) {
|
||||
const { exports } = cache[id];
|
||||
if (!exports) continue;
|
||||
|
||||
const numberId = Number(id);
|
||||
|
||||
for (const callback of listeners) {
|
||||
try {
|
||||
callback(exports, numberId);
|
||||
} catch (err) {
|
||||
logger.error("Error in webpack listener", err);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [filter, callback] of subscriptions) {
|
||||
try {
|
||||
if (filter(exports)) {
|
||||
subscriptions.delete(filter);
|
||||
callback(exports, numberId);
|
||||
} else if (typeof exports === "object") {
|
||||
if (exports.default && filter(exports.default)) {
|
||||
subscriptions.delete(filter);
|
||||
callback(exports.default, numberId);
|
||||
}
|
||||
|
||||
for (const nested in exports) if (nested.length <= 3) {
|
||||
if (exports[nested] && filter(exports[nested])) {
|
||||
subscriptions.delete(filter);
|
||||
callback(exports[nested], numberId);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error("Error while firing callback for webpack chunk", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IS_DEV && IS_DISCORD_DESKTOP) {
|
||||
|
|
Loading…
Reference in a new issue