aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/api/Notifications/NotificationComponent.tsx8
-rw-r--r--src/api/Notifications/styles.css2
-rw-r--r--src/plugins/crashHandler.ts20
-rw-r--r--src/plugins/devCompanion.dev.tsx14
-rw-r--r--src/plugins/emoteCloner.tsx2
-rw-r--r--src/plugins/fakeNitro.ts24
-rw-r--r--src/plugins/reverseImageSearch.tsx2
-rw-r--r--src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx8
-rw-r--r--src/plugins/showHiddenChannels/index.tsx107
-rw-r--r--src/plugins/showHiddenChannels/style.css1
-rw-r--r--src/plugins/volumeBooster.desktop.ts (renamed from src/plugins/volumeBooster.ts)6
11 files changed, 147 insertions, 47 deletions
diff --git a/src/api/Notifications/NotificationComponent.tsx b/src/api/Notifications/NotificationComponent.tsx
index 29cd68f..53c1b81 100644
--- a/src/api/Notifications/NotificationComponent.tsx
+++ b/src/api/Notifications/NotificationComponent.tsx
@@ -63,7 +63,10 @@ export default ErrorBoundary.wrap(function NotificationComponent({
<button
className="vc-notification-root"
style={position === "bottom-right" ? { bottom: "1rem" } : { top: "3rem" }}
- onClick={onClick}
+ onClick={() => {
+ onClose!();
+ onClick?.();
+ }}
onContextMenu={e => {
e.preventDefault();
e.stopPropagation();
@@ -78,7 +81,7 @@ export default ErrorBoundary.wrap(function NotificationComponent({
<div className="vc-notification-header">
<h2 className="vc-notification-title">{title}</h2>
<button
- style={{ all: "unset", cursor: "pointer" }}
+ className="vc-notification-close-btn"
onClick={e => {
e.preventDefault();
e.stopPropagation();
@@ -86,7 +89,6 @@ export default ErrorBoundary.wrap(function NotificationComponent({
}}
>
<svg
- className="vc-notification-close-btn"
width="24"
height="24"
viewBox="0 0 24 24"
diff --git a/src/api/Notifications/styles.css b/src/api/Notifications/styles.css
index a3db81e..cd37142 100644
--- a/src/api/Notifications/styles.css
+++ b/src/api/Notifications/styles.css
@@ -40,6 +40,8 @@
}
.vc-notification-close-btn {
+ all: unset;
+ cursor: pointer;
color: var(--interactive-normal);
opacity: 0.5;
transition: opacity 0.2s ease-in-out, color 0.2s ease-in-out;
diff --git a/src/plugins/crashHandler.ts b/src/plugins/crashHandler.ts
index e35dfed..6457e09 100644
--- a/src/plugins/crashHandler.ts
+++ b/src/plugins/crashHandler.ts
@@ -42,6 +42,7 @@ const settings = definePluginSettings({
});
let crashCount: number = 0;
+let lastCrashTimestamp: number = 0;
export default definePlugin({
name: "CrashHandler",
@@ -80,6 +81,7 @@ export default definePlugin({
});
} catch { }
+ lastCrashTimestamp = Date.now();
return false;
}
@@ -97,17 +99,21 @@ export default definePlugin({
} catch (err) {
CrashHandlerLogger.error("Failed to handle crash", err);
return false;
+ } finally {
+ lastCrashTimestamp = Date.now();
}
},
handlePreventCrash(_this: ReactElement & { forceUpdate: () => void; }) {
- try {
- showNotification({
- color: "#eed202",
- title: "Discord has crashed!",
- body: "Attempting to recover...",
- });
- } catch { }
+ if (Date.now() - lastCrashTimestamp >= 1_000) {
+ try {
+ showNotification({
+ color: "#eed202",
+ title: "Discord has crashed!",
+ body: "Attempting to recover...",
+ });
+ } catch { }
+ }
try {
FluxDispatcher.dispatch({ type: "CONTEXT_MENU_CLOSE" });
diff --git a/src/plugins/devCompanion.dev.tsx b/src/plugins/devCompanion.dev.tsx
index c3d4d6a..8dbf59e 100644
--- a/src/plugins/devCompanion.dev.tsx
+++ b/src/plugins/devCompanion.dev.tsx
@@ -18,10 +18,11 @@
import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu";
import { showNotification } from "@api/Notifications";
+import { definePluginSettings } from "@api/settings";
import { Devs } from "@utils/constants";
import Logger from "@utils/Logger";
import { canonicalizeMatch, canonicalizeReplace } from "@utils/patches";
-import definePlugin from "@utils/types";
+import definePlugin, { OptionType } from "@utils/types";
import { filters, findAll, search } from "@webpack";
import { Menu } from "@webpack/common";
@@ -65,6 +66,14 @@ interface FindData {
args: Array<StringNode | FunctionNode>;
}
+const settings = definePluginSettings({
+ notifyOnAutoConnect: {
+ description: "Wheter to notify when Dev Companion has automatically connected.",
+ type: OptionType.BOOLEAN,
+ default: true
+ }
+});
+
function parseNode(node: Node) {
switch (node.type) {
case "string":
@@ -91,7 +100,7 @@ function initWs(isManual = false) {
logger.info("Connected to WebSocket");
- showNotification({
+ (settings.store.notifyOnAutoConnect || isManual) && showNotification({
title: "Dev Companion Connected",
body: "Connected to WebSocket"
});
@@ -241,6 +250,7 @@ export default definePlugin({
description: "Dev Companion Plugin",
authors: [Devs.Ven],
dependencies: ["ContextMenuAPI"],
+ settings,
start() {
initWs();
diff --git a/src/plugins/emoteCloner.tsx b/src/plugins/emoteCloner.tsx
index 609ef08..afbb398 100644
--- a/src/plugins/emoteCloner.tsx
+++ b/src/plugins/emoteCloner.tsx
@@ -188,7 +188,7 @@ const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) =
const src = itemHref ?? itemSrc;
const isAnimated = new URL(src).pathname.endsWith(".gif");
- const group = findGroupChildrenByChildId("save-image", children);
+ const group = findGroupChildrenByChildId("copy-link", children);
if (group && !group.some(child => child?.props?.id === "emote-cloner")) {
group.push((
<Menu.MenuItem
diff --git a/src/plugins/fakeNitro.ts b/src/plugins/fakeNitro.ts
index 580d69e..494edc8 100644
--- a/src/plugins/fakeNitro.ts
+++ b/src/plugins/fakeNitro.ts
@@ -87,12 +87,12 @@ export default definePlugin({
replace: (_, intention) => `,fakeNitroIntention=${intention}`
},
{
- match: /(?<=\.(?:canUseEmojisEverywhere|canUseAnimatedEmojis)\(\i)(?=\))/g,
- replace: ',typeof fakeNitroIntention!=="undefined"?fakeNitroIntention:void 0'
+ match: /\.(?:canUseEmojisEverywhere|canUseAnimatedEmojis)\(\i(?=\))/g,
+ replace: '$&,typeof fakeNitroIntention!=="undefined"?fakeNitroIntention:void 0'
},
{
- match: /(?<=&&!\i&&)!(\i)(?=\)return \i\.\i\.DISALLOW_EXTERNAL;)/,
- replace: (_, canUseExternal) => `(!${canUseExternal}&&(typeof fakeNitroIntention==="undefined"||![${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)))`
+ match: /(&&!\i&&!)(\i)(?=\)return \i\.\i\.DISALLOW_EXTERNAL;)/,
+ replace: (_, rest, canUseExternal) => `${rest}(!${canUseExternal}&&(typeof fakeNitroIntention==="undefined"||![${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)))`
}
]
},
@@ -100,16 +100,16 @@ export default definePlugin({
find: "canUseAnimatedEmojis:function",
predicate: () => Settings.plugins.FakeNitro.enableEmojiBypass === true,
replacement: {
- match: /(?<=(?:canUseEmojisEverywhere|canUseAnimatedEmojis):function\(\i)\){(.+?\))/g,
- replace: (_, premiumCheck) => `,fakeNitroIntention){${premiumCheck}||fakeNitroIntention==null||[${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)`
+ match: /((?:canUseEmojisEverywhere|canUseAnimatedEmojis):function\(\i)\){(.+?\))/g,
+ replace: (_, rest, premiumCheck) => `${rest},fakeNitroIntention){${premiumCheck}||fakeNitroIntention==null||[${EmojiIntentions.CHAT},${EmojiIntentions.GUILD_STICKER_RELATED_EMOJI}].includes(fakeNitroIntention)`
}
},
{
find: "canUseStickersEverywhere:function",
predicate: () => Settings.plugins.FakeNitro.enableStickerBypass === true,
replacement: {
- match: /(?<=canUseStickersEverywhere:function\(\i\){)/,
- replace: "return true;"
+ match: /canUseStickersEverywhere:function\(\i\){/,
+ replace: "$&return true;"
},
},
{
@@ -129,8 +129,8 @@ export default definePlugin({
"canStreamMidQuality"
].map(func => {
return {
- match: new RegExp(`(?<=${func}:function\\(\\i\\){)`),
- replace: "return true;"
+ match: new RegExp(`${func}:function\\(\\i\\){`),
+ replace: "$&return true;"
};
})
},
@@ -145,8 +145,8 @@ export default definePlugin({
{
find: "canUseClientThemes:function",
replacement: {
- match: /(?<=canUseClientThemes:function\(\i\){)/,
- replace: "return true;"
+ match: /canUseClientThemes:function\(\i\){/,
+ replace: "$&return true;"
}
}
],
diff --git a/src/plugins/reverseImageSearch.tsx b/src/plugins/reverseImageSearch.tsx
index 88c0b16..7a3d6d9 100644
--- a/src/plugins/reverseImageSearch.tsx
+++ b/src/plugins/reverseImageSearch.tsx
@@ -42,7 +42,7 @@ const imageContextMenuPatch: NavContextMenuPatchCallback = (children, props) =>
const src = itemHref ?? itemSrc;
- const group = findGroupChildrenByChildId("save-image", children);
+ const group = findGroupChildrenByChildId("copy-link", children);
if (group && !group.some(child => child?.props?.id === "search-image")) {
group.push((
<Menu.MenuItem
diff --git a/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx b/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx
index 01bc3a7..d2adbb0 100644
--- a/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx
+++ b/src/plugins/showHiddenChannels/components/HiddenChannelLockScreen.tsx
@@ -20,10 +20,12 @@ import ErrorBoundary from "@components/ErrorBoundary";
import { LazyComponent } from "@utils/misc";
import { formatDuration } from "@utils/text";
import { find, findByPropsLazy } from "@webpack";
-import { FluxDispatcher, GuildMemberStore, GuildStore, moment, Parser, SnowflakeUtils, Text, Timestamp, Tooltip } from "@webpack/common";
-import { Channel } from "discord-types/general";
+import { FluxDispatcher, GuildMemberStore, GuildStore, moment, Parser, PermissionStore, SnowflakeUtils, Text, Timestamp, Tooltip } from "@webpack/common";
+import type { Channel } from "discord-types/general";
import type { ComponentType } from "react";
+import { VIEW_CHANNEL } from "..";
+
enum SortOrderTypes {
LATEST_ACTIVITY = 0,
@@ -167,7 +169,7 @@ function HiddenChannelLockScreen({ channel }: { channel: ExtendedChannel; }) {
<img className="shc-lock-screen-logo" src={HiddenChannelLogo} />
<div className="shc-lock-screen-heading-container">
- <Text variant="heading-xxl/bold">This is a hidden {ChannelTypesToChannelNames[type]} channel.</Text>
+ <Text variant="heading-xxl/bold">This is a {!PermissionStore.can(VIEW_CHANNEL, channel) ? "hidden" : "locked"} {ChannelTypesToChannelNames[type]} channel.</Text>
{channel.isNSFW() &&
<Tooltip text="NSFW">
{({ onMouseLeave, onMouseEnter }) => (
diff --git a/src/plugins/showHiddenChannels/index.tsx b/src/plugins/showHiddenChannels/index.tsx
index 318bad2..38ce52d 100644
--- a/src/plugins/showHiddenChannels/index.tsx
+++ b/src/plugins/showHiddenChannels/index.tsx
@@ -21,6 +21,7 @@ import "./style.css";
import { definePluginSettings } from "@api/settings";
import ErrorBoundary from "@components/ErrorBoundary";
import { Devs } from "@utils/constants";
+import { canonicalizeMatch } from "@utils/patches";
import definePlugin, { OptionType } from "@utils/types";
import { findByPropsLazy } from "@webpack";
import { ChannelStore, PermissionStore, Tooltip } from "@webpack/common";
@@ -30,7 +31,8 @@ import HiddenChannelLockScreen, { setChannelBeginHeaderComponent, setEmojiCompon
const ChannelListClasses = findByPropsLazy("channelName", "subtitle", "modeMuted", "iconContainer");
-const VIEW_CHANNEL = 1n << 10n;
+export const VIEW_CHANNEL = 1n << 10n;
+const CONNECT = 1n << 20n;
enum ShowMode {
LockIcon,
@@ -99,14 +101,14 @@ export default definePlugin({
replace: (_, channel) => `!$self.isHiddenChannel(${channel})&&`
},
{
- // Make Discord think we are connected to a voice channel so it shows us inside it
+ // Prevent Discord from trying to connect to hidden channels
match: /(?=\|\|\i\.default\.selectVoiceChannel\((\i)\.id\))/,
replace: (_, channel) => `||$self.isHiddenChannel(${channel})`
},
{
- // Make Discord think we are connected to a voice channel so it shows us inside it
+ // Make Discord show inside the channel if clicking on a hidden or locked channel
match: /(?<=\|\|\i\.default\.selectVoiceChannel\((\i)\.id\);!__OVERLAY__&&\()/,
- replace: (_, channel) => `$self.isHiddenChannel(${channel})||`
+ replace: (_, channel) => `$self.isHiddenChannel(${channel},true)||`
}
]
},
@@ -249,9 +251,49 @@ export default definePlugin({
replace: (m, component) => `${m}$self.setChannelBeginHeaderComponent(${component});`
},
{
- // Patch the header to only return allowed users and roles if it's a hidden channel (Like when it's used on the HiddenChannelLockScreen)
+ // Change the role permission check to CONNECT if the channel is locked
+ match: /ADMINISTRATOR\)\|\|(?=(.+?\((\i),\i\.\i\.)VIEW_CHANNEL)/,
+ replace: (m, permCheck, channel) => `${m}!Vencord.Webpack.Common.PermissionStore.can(${CONNECT}n,${channel})?${permCheck}CONNECT):`
+ },
+ {
+ // Change the permissionOverwrite check to CONNECT if the channel is locked
+ match: /permissionOverwrites\[.+?\?(\i):.+?\i=(?=(.+?)VIEW_CHANNEL)/,
+ replace: (m, channel, permCheck) => `${m}!Vencord.Webpack.Common.PermissionStore.can(${CONNECT}n,${channel})?${permCheck}CONNECT):`
+ },
+ {
+ // Patch the header to only return allowed users and roles if it's a hidden channel or locked channel (Like when it's used on the HiddenChannelLockScreen)
match: /MANAGE_ROLES.{0,60}?return(?=\(.+?(\(0,\i\.jsxs\)\("div",{className:\i\(\)\.members.+?guildId:(\i)\.guild_id.+?roleColor.+?]}\)))/,
- replace: (m, component, channel) => `${m} $self.isHiddenChannel(${channel})?${component}:`
+ replace: (m, component, channel) => {
+ // Export the channel for the users allowed component patch
+ component = component.replace(canonicalizeMatch(/(?<=users:\i)/), `,channel:${channel}`);
+
+ return `${m} $self.isHiddenChannel(${channel},true)?${component}:`;
+ }
+ }
+ ]
+ },
+ {
+ find: "().avatars),children",
+ replacement: [
+ {
+ // Create a variable for the channel prop
+ match: /=(\i)\.maxUsers,/,
+ replace: (m, props) => `${m}channel=${props}.channel,`
+ },
+ {
+ // Make Discord always render the plus button if the component is used inside the HiddenChannelLockScreen
+ match: /\i>0(?=&&.{0,60}renderPopout)/,
+ replace: m => `($self.isHiddenChannel(typeof channel!=="undefined"?channel:void 0,true)?true:${m})`
+ },
+ {
+ // Prevent Discord from overwriting the last children with the plus button if the overflow amount is <= 0 and the component is used inside the HiddenChannelLockScreen
+ match: /(?<=\.value\(\),(\i)=.+?length-)1(?=\]=.{0,60}renderPopout)/,
+ replace: (_, amount) => `($self.isHiddenChannel(typeof channel!=="undefined"?channel:void 0,true)&&${amount}<=0?0:1)`
+ },
+ {
+ // Show only the plus text without overflowed children amount if the overflow amount is <= 0 and the component is used inside the HiddenChannelLockScreen
+ match: /(?<="\+",)(\i)\+1/,
+ replace: (m, amount) => `$self.isHiddenChannel(typeof channel!=="undefined"?channel:void 0,true)&&${amount}<=0?"":${m}`
}
]
},
@@ -261,22 +303,22 @@ export default definePlugin({
{
// Remove the divider and the open chat button for the HiddenChannelLockScreen
match: /"more-options-popout"\)\);if\((?<=function \i\((\i)\).+?)/,
- replace: (m, props) => `${m}(!$self.isHiddenChannel(${props}.channel)||${props}.inCall)&&`
+ replace: (m, props) => `${m}!${props}.inCall&&$self.isHiddenChannel(${props}.channel,true)){}else if(`
},
{
// Render our HiddenChannelLockScreen component instead of the main voice channel component
match: /this\.renderVoiceChannelEffects.+?children:(?<=renderContent=function.+?)/,
- replace: "$&!this.props.inCall&&$self.isHiddenChannel(this.props.channel)?$self.HiddenChannelLockScreen(this.props.channel):"
+ replace: "$&!this.props.inCall&&$self.isHiddenChannel(this.props.channel,true)?$self.HiddenChannelLockScreen(this.props.channel):"
},
{
// Disable gradients for the HiddenChannelLockScreen of voice channels
match: /this\.renderVoiceChannelEffects.+?disableGradients:(?<=renderContent=function.+?)/,
- replace: "$&!this.props.inCall&&$self.isHiddenChannel(this.props.channel)||"
+ replace: "$&!this.props.inCall&&$self.isHiddenChannel(this.props.channel,true)||"
},
{
// Disable useless components for the HiddenChannelLockScreen of voice channels
match: /(?:{|,)render(?!Header|ExternalHeader).{0,30}?:(?<=renderContent=function.+?)(?!void)/g,
- replace: "$&!this.props.inCall&&$self.isHiddenChannel(this.props.channel)?null:"
+ replace: "$&!this.props.inCall&&$self.isHiddenChannel(this.props.channel,true)?null:"
}
]
},
@@ -338,10 +380,25 @@ export default definePlugin({
},
{
find: '.displayName="GuildChannelStore"',
+ replacement: [
+ {
+ // Make GuildChannelStore contain hidden channels
+ match: /isChannelGated\(.+?\)(?=\|\|)/,
+ replace: m => `${m}||true`
+ },
+ {
+ // Filter hidden channels from GuildChannelStore.getChannels unless told otherwise
+ match: /(?<=getChannels=function\(\i)\).+?(?=return (\i)})/,
+ replace: (rest, channels) => `,shouldIncludeHidden=false${rest}${channels}=$self.resolveGuildChannels(${channels},shouldIncludeHidden);`
+ }
+ ]
+ },
+ {
+ find: ".Messages.FORM_LABEL_MUTED",
replacement: {
- // Make GuildChannelStore contain hidden channels for users in voice channels to appear in the guild tooltip
- match: /isChannelGated\(.+?\)(?=\|\|)/,
- replace: m => `${m}||true`
+ // Make GuildChannelStore.getChannels return hidden channels
+ match: /(?<=getChannels\(\i)(?=\))/,
+ replace: ",true"
}
}
],
@@ -349,13 +406,33 @@ export default definePlugin({
setEmojiComponent,
setChannelBeginHeaderComponent,
- isHiddenChannel(channel: Channel & { channelId?: string; }) {
+ isHiddenChannel(channel: Channel & { channelId?: string; }, checkConnect = false) {
if (!channel) return false;
if (channel.channelId) channel = ChannelStore.getChannel(channel.channelId);
if (!channel || channel.isDM() || channel.isGroupDM() || channel.isMultiUserDM()) return false;
- return !PermissionStore.can(VIEW_CHANNEL, channel);
+ return !PermissionStore.can(VIEW_CHANNEL, channel) || checkConnect && !PermissionStore.can(CONNECT, channel);
+ },
+
+ resolveGuildChannels(channels: Record<string | number, Array<{ channel: Channel; comparator: number; }> | string | number>, shouldIncludeHidden: boolean) {
+ if (shouldIncludeHidden) return channels;
+
+ const res = {};
+ for (const [key, maybeObjChannels] of Object.entries(channels)) {
+ if (!Array.isArray(maybeObjChannels)) {
+ res[key] = maybeObjChannels;
+ continue;
+ }
+
+ res[key] ??= [];
+
+ for (const objChannel of maybeObjChannels) {
+ if (objChannel.channel.id === null || !this.isHiddenChannel(objChannel.channel)) res[key].push(objChannel);
+ }
+ }
+
+ return res;
},
HiddenChannelLockScreen: (channel: any) => <HiddenChannelLockScreen channel={channel} />,
diff --git a/src/plugins/showHiddenChannels/style.css b/src/plugins/showHiddenChannels/style.css
index 0f85b50..01903ce 100644
--- a/src/plugins/showHiddenChannels/style.css
+++ b/src/plugins/showHiddenChannels/style.css
@@ -103,4 +103,5 @@
.shc-lock-screen-allowed-users-and-roles-container > [class^="members"] {
margin-left: 10px;
flex-wrap: wrap;
+ justify-content: center;
}
diff --git a/src/plugins/volumeBooster.ts b/src/plugins/volumeBooster.desktop.ts
index 3f692c7..b77af57 100644
--- a/src/plugins/volumeBooster.ts
+++ b/src/plugins/volumeBooster.desktop.ts
@@ -74,10 +74,10 @@ export default definePlugin({
find: '.displayName="MediaEngineStore"',
replacement: [
{
- match: /(?<=\.settings\.audioContextSettings.+?)(\i\[\i\])=(\i\.volume)(.+?setLocalVolume\(\i,).+?\)/,
- replace: (_, localVolume, syncVolume, rest) => ""
+ match: /(\.settings\.audioContextSettings.+?)(\i\[\i\])=(\i\.volume)(.+?setLocalVolume\(\i,).+?\)/,
+ replace: (_, rest1, localVolume, syncVolume, rest2) => rest1
+ `(${localVolume}>200?void 0:${localVolume}=${syncVolume})`
- + rest
+ + rest2
+ `${localVolume}??${syncVolume})`
}
]