diff --git a/package.json b/package.json
index 076b2999d..5ffac7ff5 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "vencord",
"private": "true",
- "version": "1.6.7",
+ "version": "1.6.9",
"description": "The cutest Discord client mod",
"homepage": "https://github.com/Vendicated/Vencord#readme",
"bugs": {
diff --git a/src/api/Notifications/notificationLog.tsx b/src/api/Notifications/notificationLog.tsx
index 9535fb62c..6f79ef70a 100644
--- a/src/api/Notifications/notificationLog.tsx
+++ b/src/api/Notifications/notificationLog.tsx
@@ -21,7 +21,7 @@ import { Settings } from "@api/Settings";
import { classNameFactory } from "@api/Styles";
import { closeModal, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalProps, ModalRoot, ModalSize, openModal } from "@utils/modal";
import { useAwaiter } from "@utils/react";
-import { Alerts, Button, Forms, moment, React, Text, Timestamp, useEffect, useReducer, useState } from "@webpack/common";
+import { Alerts, Button, Forms, React, Text, Timestamp, useEffect, useReducer, useState } from "@webpack/common";
import { nanoid } from "nanoid";
import type { DispatchWithoutAction } from "react";
@@ -129,7 +129,7 @@ function NotificationEntry({ data }: { data: PersistentNotificationData; }) {
richBody={
{data.body}
-
+
}
/>
diff --git a/src/plugins/crashHandler/index.ts b/src/plugins/crashHandler/index.ts
index 9d38b7d17..f8c76d7f7 100644
--- a/src/plugins/crashHandler/index.ts
+++ b/src/plugins/crashHandler/index.ts
@@ -25,7 +25,6 @@ import definePlugin, { OptionType } from "@utils/types";
import { maybePromptToUpdate } from "@utils/updater";
import { filters, findBulk, proxyLazyWebpack } from "@webpack";
import { FluxDispatcher, NavigationRouter, SelectedChannelStore } from "@webpack/common";
-import type { ReactElement } from "react";
const CrashHandlerLogger = new Logger("CrashHandler");
const { ModalStack, DraftManager, DraftType, closeExpressionPicker } = proxyLazyWebpack(() => {
@@ -57,13 +56,13 @@ const settings = definePluginSettings({
}
});
-let crashCount: number = 0;
-let lastCrashTimestamp: number = 0;
-let shouldAttemptNextHandle = false;
+let hasCrashedOnce = false;
+let isRecovering = false;
+let shouldAttemptRecover = true;
export default definePlugin({
name: "CrashHandler",
- description: "Utility plugin for handling and possibly recovering from Crashes without a restart",
+ description: "Utility plugin for handling and possibly recovering from crashes without a restart",
authors: [Devs.Nuckyz],
enabledByDefault: true,
@@ -73,61 +72,67 @@ export default definePlugin({
{
find: ".Messages.ERRORS_UNEXPECTED_CRASH",
replacement: {
- match: /(?=this\.setState\()/,
- replace: "$self.handleCrash(this)||"
+ match: /this\.setState\((.+?)\)/,
+ replace: "$self.handleCrash(this,$1);"
}
}
],
- handleCrash(_this: ReactElement & { forceUpdate: () => void; }) {
- if (Date.now() - lastCrashTimestamp <= 1_000 && !shouldAttemptNextHandle) return true;
+ handleCrash(_this: any, errorState: any) {
+ _this.setState(errorState);
- shouldAttemptNextHandle = false;
+ // Already recovering, prevent error which happens more than once too fast to trigger another recover
+ if (isRecovering) return;
+ isRecovering = true;
- if (++crashCount > 5) {
+ // 1 ms timeout to avoid react breaking when re-rendering
+ setTimeout(() => {
try {
- showNotification({
- color: "#eed202",
- title: "Discord has crashed!",
- body: "Awn :( Discord has crashed more than five times, not attempting to recover.",
- noPersist: true,
- });
+ // Prevent a crash loop with an error that could not be handled
+ if (!shouldAttemptRecover) {
+ try {
+ showNotification({
+ color: "#eed202",
+ title: "Discord has crashed!",
+ body: "Awn :( Discord has crashed two times rapidly, not attempting to recover.",
+ noPersist: true
+ });
+ } catch { }
+
+ return;
+ }
+
+ shouldAttemptRecover = false;
+ // This is enough to avoid a crash loop
+ setTimeout(() => shouldAttemptRecover = true, 500);
} catch { }
- lastCrashTimestamp = Date.now();
- return false;
- }
+ try {
+ if (!hasCrashedOnce) {
+ hasCrashedOnce = true;
+ maybePromptToUpdate("Uh oh, Discord has just crashed... but good news, there is a Vencord update available that might fix this issue! Would you like to update now?", true);
+ }
+ } catch { }
- setTimeout(() => crashCount--, 60_000);
-
- try {
- if (crashCount === 1) maybePromptToUpdate("Uh oh, Discord has just crashed... but good news, there is a Vencord update available that might fix this issue! Would you like to update now?", true);
-
- if (settings.store.attemptToPreventCrashes) {
- this.handlePreventCrash(_this);
- return true;
+ try {
+ if (settings.store.attemptToPreventCrashes) {
+ this.handlePreventCrash(_this);
+ }
+ } catch (err) {
+ CrashHandlerLogger.error("Failed to handle crash", err);
}
-
- return false;
- } catch (err) {
- CrashHandlerLogger.error("Failed to handle crash", err);
- return false;
- } finally {
- lastCrashTimestamp = Date.now();
- }
+ }, 1);
},
- handlePreventCrash(_this: ReactElement & { forceUpdate: () => void; }) {
- if (Date.now() - lastCrashTimestamp >= 1_000) {
- try {
- showNotification({
- color: "#eed202",
- title: "Discord has crashed!",
- body: "Attempting to recover...",
- noPersist: true,
- });
- } catch { }
- }
+ handlePreventCrash(_this: any) {
+ try {
+ showNotification({
+ color: "#eed202",
+ title: "Discord has crashed!",
+ body: "Attempting to recover...",
+ noPersist: true
+ });
+ } catch { }
try {
const channelId = SelectedChannelStore.getChannelId();
@@ -176,9 +181,12 @@ export default definePlugin({
}
}
+
+ // Set isRecovering to false before setting the state to allow us to handle the next crash error correcty, in case it happens
+ setImmediate(() => isRecovering = false);
+
try {
- shouldAttemptNextHandle = true;
- _this.forceUpdate();
+ _this.setState({ error: null, info: null });
} catch (err) {
CrashHandlerLogger.debug("Failed to update crash handler component.", err);
}
diff --git a/src/plugins/disableDMCallIdle/index.ts b/src/plugins/disableCallIdle/index.ts
similarity index 70%
rename from src/plugins/disableDMCallIdle/index.ts
rename to src/plugins/disableCallIdle/index.ts
index bce119d01..904d53b03 100644
--- a/src/plugins/disableDMCallIdle/index.ts
+++ b/src/plugins/disableCallIdle/index.ts
@@ -16,18 +16,27 @@
* along with this program. If not, see .
*/
+import { migratePluginSettings } from "@api/Settings";
import { Devs } from "@utils/constants";
import definePlugin from "@utils/types";
+migratePluginSettings("DisableCallIdle", "DisableDMCallIdle");
export default definePlugin({
- name: "DisableDMCallIdle",
- description: "Disables automatically getting kicked from a DM voice call after 3 minutes.",
+ name: "DisableCallIdle",
+ description: "Disables automatically getting kicked from a DM voice call after 3 minutes and being moved to an AFK voice channel.",
authors: [Devs.Nuckyz],
patches: [
{
find: ".Messages.BOT_CALL_IDLE_DISCONNECT",
replacement: {
- match: /(?<=function \i\(\){)(?=.{1,120}\.Messages\.BOT_CALL_IDLE_DISCONNECT)/,
+ match: /,?(?=this\.idleTimeout=new \i\.Timeout)/,
+ replace: ";return;"
+ }
+ },
+ {
+ find: "handleIdleUpdate(){",
+ replacement: {
+ match: /(?<=_initialize\(\){)/,
replace: "return;"
}
}
diff --git a/src/plugins/fakeNitro/index.ts b/src/plugins/fakeNitro/index.ts
index 6107df00e..560cae381 100644
--- a/src/plugins/fakeNitro/index.ts
+++ b/src/plugins/fakeNitro/index.ts
@@ -108,6 +108,7 @@ const enum FakeNoticeType {
const fakeNitroEmojiRegex = /\/emojis\/(\d+?)\.(png|webp|gif)/;
const fakeNitroStickerRegex = /\/stickers\/(\d+?)\./;
const fakeNitroGifStickerRegex = /\/attachments\/\d+?\/\d+?\/(\d+?)\.gif/;
+const hyperLinkRegex = /\[.+?\]\((https?:\/\/.+?)\)/;
const settings = definePluginSettings({
enableEmojiBypass: {
@@ -156,6 +157,11 @@ const settings = definePluginSettings({
type: OptionType.BOOLEAN,
default: true,
restartNeeded: true
+ },
+ useHyperLinks: {
+ description: "Whether to use hyperlinks when sending fake emojis and stickers",
+ type: OptionType.BOOLEAN,
+ default: true
}
});
@@ -345,7 +351,7 @@ export default definePlugin({
predicate: () => settings.store.transformEmojis,
replacement: {
// Add the fake nitro emoji notice
- match: /(?<=isDiscoverable:\i,emojiComesFromCurrentGuild:\i,.+?}=(\i).+?;)(.+?return )(.{0,1000}\.Messages\.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION.+?)(?=},)/,
+ match: /(?<=isDiscoverable:\i,emojiComesFromCurrentGuild:\i,.+?}=(\i).+?;)(.*?return )(.{0,1000}\.Messages\.EMOJI_POPOUT_UNJOINED_DISCOVERABLE_GUILD_DESCRIPTION.+?)(?=},)/,
replace: (_, props, rest, reactNode) => `let{fakeNitroNode}=${props};${rest}$self.addFakeNotice(${FakeNoticeType.Emoji},${reactNode},!!fakeNitroNode?.fake)`
}
},
@@ -447,13 +453,23 @@ export default definePlugin({
trimContent(content: Array) {
const firstContent = content[0];
- if (typeof firstContent === "string") content[0] = firstContent.trimStart();
- if (content[0] === "") content.shift();
+ if (typeof firstContent === "string") {
+ content[0] = firstContent.trimStart();
+ content[0] || content.shift();
+ } else if (firstContent?.type === "span") {
+ firstContent.props.children = firstContent.props.children.trimStart();
+ firstContent.props.children || content.shift();
+ }
const lastIndex = content.length - 1;
const lastContent = content[lastIndex];
- if (typeof lastContent === "string") content[lastIndex] = lastContent.trimEnd();
- if (content[lastIndex] === "") content.pop();
+ if (typeof lastContent === "string") {
+ content[lastIndex] = lastContent.trimEnd();
+ content[lastIndex] || content.pop();
+ } else if (lastContent?.type === "span") {
+ lastContent.props.children = lastContent.props.children.trimEnd();
+ lastContent.props.children || content.pop();
+ }
},
clearEmptyArrayItems(array: Array) {
@@ -465,7 +481,7 @@ export default definePlugin({
},
patchFakeNitroEmojisOrRemoveStickersLinks(content: Array, inline: boolean) {
- // If content has more than one child or it's a single ReactElement like a header or list
+ // If content has more than one child or it's a single ReactElement like a header, list or span
if ((content.length > 1 || typeof content[0]?.type === "string") && !settings.store.transformCompoundSentence) return content;
let nextIndex = content.length;
@@ -574,7 +590,7 @@ export default definePlugin({
itemsToMaybePush.push(...message.attachments.filter(attachment => attachment.content_type === "image/gif").map(attachment => attachment.url));
for (const item of itemsToMaybePush) {
- if (!settings.store.transformCompoundSentence && !item.startsWith("http")) continue;
+ if (!settings.store.transformCompoundSentence && !item.startsWith("http") && !hyperLinkRegex.test(item)) continue;
const imgMatch = item.match(fakeNitroStickerRegex);
if (imgMatch) {
@@ -619,8 +635,7 @@ export default definePlugin({
case "image": {
if (
!settings.store.transformCompoundSentence
- && !contentItems.includes(embed.url!)
- && !contentItems.includes(embed.image?.proxyURL!)
+ && !contentItems.some(item => item === embed.url! || item.match(hyperLinkRegex)?.[1] === embed.url!)
) return false;
if (settings.store.transformEmojis) {
@@ -698,7 +713,7 @@ export default definePlugin({
},
getStickerLink(stickerId: string) {
- return `https://media.discordapp.net/stickers/${stickerId}.png?size=${Settings.plugins.FakeNitro.stickerSize}`;
+ return `https://media.discordapp.net/stickers/${stickerId}.png?size=${settings.store.stickerSize}`;
},
async sendAnimatedSticker(stickerLink: string, stickerId: string, channelId: string) {
@@ -795,12 +810,16 @@ export default definePlugin({
if (sticker.format_type === StickerType.GIF && link.includes(".png")) {
link = link.replace(".png", ".gif");
}
+
if (sticker.format_type === StickerType.APNG) {
this.sendAnimatedSticker(link, sticker.id, channelId);
return { cancel: true };
} else {
+ const url = new URL(link);
+ url.searchParams.set("name", sticker.name);
+
+ messageObj.content += `${getWordBoundary(messageObj.content, messageObj.content.length - 1)}${s.useHyperLinks ? `[${sticker.name}](${url})` : url}`;
extra.stickers!.length = 0;
- messageObj.content += ` ${link}&name=${encodeURIComponent(sticker.name)}`;
}
}
@@ -813,12 +832,13 @@ export default definePlugin({
if (emoji.guildId === guildId && !emoji.animated) continue;
const emojiString = `<${emoji.animated ? "a" : ""}:${emoji.originalName || emoji.name}:${emoji.id}>`;
- const url = emoji.url.replace(/\?size=\d+/, "?" + new URLSearchParams({
- size: Settings.plugins.FakeNitro.emojiSize,
- name: encodeURIComponent(emoji.name)
- }));
+
+ const url = new URL(emoji.url);
+ url.searchParams.set("size", s.emojiSize.toString());
+ url.searchParams.set("name", emoji.name);
+
messageObj.content = messageObj.content.replace(emojiString, (match, offset, origStr) => {
- return `${getWordBoundary(origStr, offset - 1)}${url}${getWordBoundary(origStr, offset + match.length)}`;
+ return `${getWordBoundary(origStr, offset - 1)}${s.useHyperLinks ? `[${emoji.name}](${url})` : url}${getWordBoundary(origStr, offset + match.length)}`;
});
}
}
@@ -840,11 +860,11 @@ export default definePlugin({
if (emoji.available !== false && canUseEmotes) return emojiStr;
if (emoji.guildId === guildId && !emoji.animated) return emojiStr;
- const url = emoji.url.replace(/\?size=\d+/, "?" + new URLSearchParams({
- size: Settings.plugins.FakeNitro.emojiSize,
- name: encodeURIComponent(emoji.name)
- }));
- return `${getWordBoundary(origStr, offset - 1)}${url}${getWordBoundary(origStr, offset + emojiStr.length)}`;
+ const url = new URL(emoji.url);
+ url.searchParams.set("size", s.emojiSize.toString());
+ url.searchParams.set("name", emoji.name);
+
+ return `${getWordBoundary(origStr, offset - 1)}${s.useHyperLinks ? `[${emoji.name}](${url})` : url}${getWordBoundary(origStr, offset + emojiStr.length)}`;
});
});
},
diff --git a/src/plugins/fixYoutubeEmbeds.desktop/native.ts b/src/plugins/fixYoutubeEmbeds.desktop/native.ts
index 5419d4ad8..d5c2df363 100644
--- a/src/plugins/fixYoutubeEmbeds.desktop/native.ts
+++ b/src/plugins/fixYoutubeEmbeds.desktop/native.ts
@@ -17,7 +17,7 @@ app.on("browser-window-created", (_, win) => {
frame.executeJavaScript(`
new MutationObserver(() => {
if(
- document.querySelector('div.ytp-error-content-wrap-subreason a[href^="https://www.youtube.com/watch?v="]')
+ document.querySelector('div.ytp-error-content-wrap-subreason a[href*="www.youtube.com/watch?v="]')
) location.reload()
}).observe(document.body, { childList: true, subtree:true });
`);
diff --git a/src/plugins/imageZoom/components/Magnifier.tsx b/src/plugins/imageZoom/components/Magnifier.tsx
index 6a3fc05a0..816717350 100644
--- a/src/plugins/imageZoom/components/Magnifier.tsx
+++ b/src/plugins/imageZoom/components/Magnifier.tsx
@@ -123,14 +123,13 @@ export const Magnifier: React.FC = ({ instance, size: initialSiz
waitFor(() => instance.state.readyState === "READY", () => {
const elem = document.getElementById(ELEMENT_ID) as HTMLDivElement;
element.current = elem;
- elem.firstElementChild!.setAttribute("draggable", "false");
+ elem.querySelector("img,video")?.setAttribute("draggable", "false");
if (instance.props.animated) {
originalVideoElementRef.current = elem!.querySelector("video")!;
originalVideoElementRef.current.addEventListener("timeupdate", syncVideos);
- setReady(true);
- } else {
- setReady(true);
}
+
+ setReady(true);
});
document.addEventListener("keydown", onKeyDown);
document.addEventListener("keyup", onKeyUp);
@@ -155,7 +154,9 @@ export const Magnifier: React.FC = ({ instance, size: initialSiz
if (!ready) return null;
- const box = element.current!.getBoundingClientRect();
+ const box = element.current?.getBoundingClientRect();
+
+ if (!box) return null;
return (
id && idList.includes(id));
+ const isListed = [linkedChannel.guild_id, channelID, message.author.id].some(id => id && idList.includes(id));
if (listMode === "blacklist" && isListed) continue;
if (listMode === "whitelist" && !isListed) continue;
@@ -265,8 +267,7 @@ function MessageEmbedAccessory({ message }: { message: Message; }) {
const messageProps: MessageEmbedProps = {
message: withEmbeddedBy(linkedMessage, [...embeddedBy, message.id]),
- channel: linkedChannel,
- guildID
+ channel: linkedChannel
};
const type = settings.store.automodEmbeds;
@@ -280,59 +281,64 @@ function MessageEmbedAccessory({ message }: { message: Message; }) {
return accessories.length ? <>{accessories}> : null;
}
-function ChannelMessageEmbedAccessory({ message, channel, guildID }: MessageEmbedProps): JSX.Element | null {
- const isDM = guildID === "@me";
+function getChannelLabelAndIconUrl(channel: Channel) {
+ if (channel.isDM()) return ["Direct Message", IconUtils.getUserAvatarURL(UserStore.getUser(channel.recipients[0]))];
+ if (channel.isGroupDM()) return ["Group DM", IconUtils.getChannelIconURL(channel)];
+ return ["Server", IconUtils.getGuildIconURL(GuildStore.getGuild(channel.guild_id))];
+}
- const guild = !isDM && GuildStore.getGuild(channel.guild_id);
+function ChannelMessageEmbedAccessory({ message, channel }: MessageEmbedProps): JSX.Element | null {
const dmReceiver = UserStore.getUser(ChannelStore.getChannel(channel.id).recipients?.[0]);
+ const [channelLabel, iconUrl] = getChannelLabelAndIconUrl(channel);
- return
diff --git a/src/plugins/showHiddenChannels/index.tsx b/src/plugins/showHiddenChannels/index.tsx
index 906bed504..919f3f3c5 100644
--- a/src/plugins/showHiddenChannels/index.tsx
+++ b/src/plugins/showHiddenChannels/index.tsx
@@ -29,7 +29,7 @@ import type { Channel, Role } from "discord-types/general";
import HiddenChannelLockScreen from "./components/HiddenChannelLockScreen";
-const ChannelListClasses = findByPropsLazy("channelEmoji", "unread", "icon");
+const ChannelListClasses = findByPropsLazy("modeMuted", "modeSelected", "unread", "icon");
const enum ShowMode {
LockIcon,
@@ -162,7 +162,7 @@ export default definePlugin({
},
// Add the hidden eye icon if the channel is hidden
{
- match: /\i\.children.+?:null(?<=,channel:(\i).+?)/,
+ match: /\.name\),.{0,120}\.children.+?:null(?<=,channel:(\i).+?)/,
replace: (m, channel) => `${m},$self.isHiddenChannel(${channel})?$self.HiddenChannelIcon():null`
},
// Make voice channels also appear as muted if they are muted
diff --git a/src/plugins/typingIndicator/index.tsx b/src/plugins/typingIndicator/index.tsx
index 171c560d8..280301480 100644
--- a/src/plugins/typingIndicator/index.tsx
+++ b/src/plugins/typingIndicator/index.tsx
@@ -133,7 +133,7 @@ export default definePlugin({
{
find: "UNREAD_IMPORTANT:",
replacement: {
- match: /channel:(\i).{0,100}?channelEmoji,.{0,250}?\.children.{0,50}?:null/,
+ match: /\.name\),.{0,120}\.children.+?:null(?<=,channel:(\i).+?)/,
replace: "$&,$self.TypingIndicator($1.id)"
}
},
diff --git a/src/plugins/userVoiceShow/index.tsx b/src/plugins/userVoiceShow/index.tsx
index c95307cc4..935ff1c5d 100644
--- a/src/plugins/userVoiceShow/index.tsx
+++ b/src/plugins/userVoiceShow/index.tsx
@@ -96,7 +96,7 @@ export default definePlugin({
patches: [
// above message box
{
- find: ".lastEditedByContainer",
+ find: ".popularApplicationCommandIds,",
replacement: {
match: /\(0,\i\.jsx\)\(\i\.\i,{user:\i,setNote/,
replace: "$self.patchPopout(arguments[0]),$&",
diff --git a/src/plugins/viewIcons/index.tsx b/src/plugins/viewIcons/index.tsx
index 2da3e21c2..0920c4185 100644
--- a/src/plugins/viewIcons/index.tsx
+++ b/src/plugins/viewIcons/index.tsx
@@ -22,11 +22,9 @@ import { ImageIcon } from "@components/Icons";
import { Devs } from "@utils/constants";
import { openImageModal } from "@utils/discord";
import definePlugin, { OptionType } from "@utils/types";
-import { findByPropsLazy } from "@webpack";
-import { GuildMemberStore, Menu } from "@webpack/common";
+import { GuildMemberStore, IconUtils, Menu } from "@webpack/common";
import type { Channel, Guild, User } from "discord-types/general";
-const BannerStore = findByPropsLazy("getGuildBannerURL");
interface UserContextProps {
channel: Channel;
@@ -91,19 +89,19 @@ const UserContext: NavContextMenuPatchCallback = (children, { user, guildId }: U
openImage(BannerStore.getUserAvatarURL(user, true))}
+ action={() => openImage(IconUtils.getUserAvatarURL(user, true))}
icon={ImageIcon}
/>
{memberAvatar && (
openImage(BannerStore.getGuildMemberAvatarURLSimple({
+ action={() => openImage(IconUtils.getGuildMemberAvatarURLSimple({
userId: user.id,
avatar: memberAvatar,
- guildId,
+ guildId: guildId!,
canAnimate: true
- }, true))}
+ }))}
icon={ImageIcon}
/>
)}
@@ -124,11 +122,11 @@ const GuildContext: NavContextMenuPatchCallback = (children, { guild }: GuildCon
id="view-icon"
label="View Icon"
action={() =>
- openImage(BannerStore.getGuildIconURL({
+ openImage(IconUtils.getGuildIconURL({
id,
icon,
canAnimate: true
- }))
+ })!)
}
icon={ImageIcon}
/>
@@ -138,10 +136,7 @@ const GuildContext: NavContextMenuPatchCallback = (children, { guild }: GuildCon
id="view-banner"
label="View Banner"
action={() =>
- openImage(BannerStore.getGuildBannerURL({
- id,
- banner,
- }, true))
+ openImage(IconUtils.getGuildBannerURL(guild, true)!)
}
icon={ImageIcon}
/>
diff --git a/src/plugins/webContextMenus.web/index.ts b/src/plugins/webContextMenus.web/index.ts
index 5f6beca2c..faa240783 100644
--- a/src/plugins/webContextMenus.web/index.ts
+++ b/src/plugins/webContextMenus.web/index.ts
@@ -47,18 +47,23 @@ const settings = definePluginSettings({
});
const MEDIA_PROXY_URL = "https://media.discordapp.net";
-const CDN_URL = "https://cdn.discordapp.com";
+const CDN_URL = "cdn.discordapp.com";
-function fixImageUrl(urlString: string, explodeWebp: boolean) {
+function fixImageUrl(urlString: string) {
const url = new URL(urlString);
- if (url.origin === CDN_URL) return urlString;
- if (url.origin === MEDIA_PROXY_URL) return CDN_URL + url.pathname;
+ if (url.host === CDN_URL) return urlString;
url.searchParams.delete("width");
url.searchParams.delete("height");
- url.searchParams.set("quality", "lossless");
- if (explodeWebp && url.searchParams.get("format") === "webp")
- url.searchParams.set("format", "png");
+
+ if (url.origin === MEDIA_PROXY_URL) {
+ url.host = CDN_URL;
+ url.searchParams.delete("size");
+ url.searchParams.delete("quality");
+ url.searchParams.delete("format");
+ } else {
+ url.searchParams.set("quality", "lossless");
+ }
return url.toString();
}
@@ -199,7 +204,7 @@ export default definePlugin({
],
async copyImage(url: string) {
- url = fixImageUrl(url, true);
+ url = fixImageUrl(url);
let imageData = await fetch(url).then(r => r.blob());
if (imageData.type !== "image/png") {
@@ -231,7 +236,7 @@ export default definePlugin({
},
async saveImage(url: string) {
- url = fixImageUrl(url, false);
+ url = fixImageUrl(url);
const data = await fetchImage(url);
if (!data) return;
diff --git a/src/webpack/common/types/components.d.ts b/src/webpack/common/types/components.d.ts
index b9bc434c6..72a8a69b4 100644
--- a/src/webpack/common/types/components.d.ts
+++ b/src/webpack/common/types/components.d.ts
@@ -16,7 +16,6 @@
* along with this program. If not, see .
*/
-import type { Moment } from "moment";
import type { ComponentType, CSSProperties, FunctionComponent, HtmlHTMLAttributes, HTMLProps, KeyboardEvent, MouseEvent, PropsWithChildren, PropsWithRef, ReactNode, Ref } from "react";
export type TextVariant = "heading-sm/normal" | "heading-sm/medium" | "heading-sm/semibold" | "heading-sm/bold" | "heading-md/normal" | "heading-md/medium" | "heading-md/semibold" | "heading-md/bold" | "heading-lg/normal" | "heading-lg/medium" | "heading-lg/semibold" | "heading-lg/bold" | "heading-xl/normal" | "heading-xl/medium" | "heading-xl/bold" | "heading-xxl/normal" | "heading-xxl/medium" | "heading-xxl/bold" | "eyebrow" | "heading-deprecated-14/normal" | "heading-deprecated-14/medium" | "heading-deprecated-14/bold" | "text-xxs/normal" | "text-xxs/medium" | "text-xxs/semibold" | "text-xxs/bold" | "text-xs/normal" | "text-xs/medium" | "text-xs/semibold" | "text-xs/bold" | "text-sm/normal" | "text-sm/medium" | "text-sm/semibold" | "text-sm/bold" | "text-md/normal" | "text-md/medium" | "text-md/semibold" | "text-md/bold" | "text-lg/normal" | "text-lg/medium" | "text-lg/semibold" | "text-lg/bold" | "display-sm" | "display-md" | "display-lg" | "code";
@@ -154,7 +153,7 @@ export type Switch = ComponentType>;
export type Timestamp = ComponentType.
*/
+import { Guild, GuildMember } from "discord-types/general";
import type { ReactNode } from "react";
import type { FluxEvents } from "./fluxEvents";
@@ -182,3 +183,47 @@ export interface NavigationRouter {
getLastRouteChangeSource(): any;
getLastRouteChangeSourceLocationStack(): any;
}
+
+export interface IconUtils {
+ getUserAvatarURL(user: User, canAnimate?: boolean, size?: number, format?: string): string;
+ getDefaultAvatarURL(id: string, discriminator?: string): string;
+ getUserBannerURL(data: { id: string, banner: string, canAnimate?: boolean, size: number; }): string | undefined;
+ getAvatarDecorationURL(dara: { avatarDecoration: string, size: number; canCanimate?: boolean; }): string | undefined;
+
+ getGuildMemberAvatarURL(member: GuildMember, canAnimate?: string): string | null;
+ getGuildMemberAvatarURLSimple(data: { guildId: string, userId: string, avatar: string, canAnimate?: boolean; size?: number; }): string;
+ getGuildMemberBannerURL(data: { id: string, guildId: string, banner: string, canAnimate?: boolean, size: number; }): string | undefined;
+
+ getGuildIconURL(data: { id: string, icon?: string, size?: number, canAnimate?: boolean; }): string | undefined;
+ getGuildBannerURL(guild: Guild, canAnimate?: boolean): string | null;
+
+ getChannelIconURL(data: { id: string; icon?: string; applicationId?: string; size?: number; }): string | undefined;
+ getEmojiURL(data: { id: string, animated: boolean, size: number, forcePNG?: boolean; }): string;
+
+ hasAnimatedGuildIcon(guild: Guild): boolean;
+ isAnimatedIconHash(hash: string): boolean;
+
+ getGuildSplashURL: any;
+ getGuildDiscoverySplashURL: any;
+ getGuildHomeHeaderURL: any;
+ getResourceChannelIconURL: any;
+ getNewMemberActionIconURL: any;
+ getGuildTemplateIconURL: any;
+ getApplicationIconURL: any;
+ getGameAssetURL: any;
+ getVideoFilterAssetURL: any;
+
+ getGuildMemberAvatarSource: any;
+ getUserAvatarSource: any;
+ getGuildSplashSource: any;
+ getGuildDiscoverySplashSource: any;
+ makeSource: any;
+ getGameAssetSource: any;
+ getGuildIconSource: any;
+ getGuildTemplateIconSource: any;
+ getGuildBannerSource: any;
+ getGuildHomeHeaderSource: any;
+ getChannelIconSource: any;
+ getApplicationIconSource: any;
+ getAnimatableSourceWithFallback: any;
+}
diff --git a/src/webpack/common/utils.ts b/src/webpack/common/utils.ts
index c62f745a9..7060573d5 100644
--- a/src/webpack/common/utils.ts
+++ b/src/webpack/common/utils.ts
@@ -137,3 +137,5 @@ export const { persist: zustandPersist }: typeof import("zustand/middleware") =
export const MessageActions = findByPropsLazy("editMessage", "sendMessage");
export const UserProfileActions = findByPropsLazy("openUserProfileModal", "closeUserProfileModal");
export const InviteActions = findByPropsLazy("resolveInvite");
+
+export const IconUtils: t.IconUtils = findByPropsLazy("getGuildBannerURL", "getUserAvatarURL");