From 89367e3b2a9e02767f8aca56e3ac7c34ac0d285e Mon Sep 17 00:00:00 2001 From: Vendicated Date: Thu, 15 Feb 2024 09:20:27 +0100 Subject: [PATCH 1/4] WebContextMenus: fix copying images it broke because of the new url expiry parameters --- src/plugins/webContextMenus.web/index.ts | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) 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; From bc0a55053d709533143f66a2bc4b6914326e907d Mon Sep 17 00:00:00 2001 From: Vendicated Date: Thu, 15 Feb 2024 10:12:33 +0100 Subject: [PATCH 2/4] MessageLinkEmbeds: fix erroring on some invalid message links --- src/plugins/messageLinkEmbeds/index.tsx | 35 ++++++++++--------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/src/plugins/messageLinkEmbeds/index.tsx b/src/plugins/messageLinkEmbeds/index.tsx index 762829992..56facf2e7 100644 --- a/src/plugins/messageLinkEmbeds/index.tsx +++ b/src/plugins/messageLinkEmbeds/index.tsx @@ -31,13 +31,14 @@ import { GuildStore, MessageStore, Parser, + PermissionsBits, PermissionStore, RestAPI, Text, TextAndImagesSettingsStores, UserStore } from "@webpack/common"; -import { Channel, Guild, Message } from "discord-types/general"; +import { Channel, Message } from "discord-types/general"; const messageCache = new Map 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 +265,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,10 +279,8 @@ function MessageEmbedAccessory({ message }: { message: Message; }) { return accessories.length ? <>{accessories} : null; } -function ChannelMessageEmbedAccessory({ message, channel, guildID }: MessageEmbedProps): JSX.Element | null { - const isDM = guildID === "@me"; - - const guild = !isDM && GuildStore.getGuild(channel.guild_id); +function ChannelMessageEmbedAccessory({ message, channel }: MessageEmbedProps): JSX.Element | null { + const guild = !channel.isDM() && GuildStore.getGuild(channel.guild_id); const dmReceiver = UserStore.getUser(ChannelStore.getChannel(channel.id).recipients?.[0]); @@ -293,11 +290,8 @@ function ChannelMessageEmbedAccessory({ message, channel, guildID }: MessageEmbe color: "var(--background-secondary)", author: { name: - {isDM ? "Direct Message - " : (guild as Guild).name + " - "} - {isDM - ? Parser.parse(`<@${dmReceiver.id}>`) - : Parser.parse(`<#${channel.id}>`) - } + {channel.isDM() && Direct Message - } + {Parser.parse(channel.isDM() ? `<@${dmReceiver.id}>` : `<#${channel.id}>`)} , iconProxyURL: guild ? `https://${window.GLOBAL_ENV.CDN_HOST}/icons/${guild.id}/${guild.icon}.png` @@ -318,9 +312,8 @@ function ChannelMessageEmbedAccessory({ message, channel, guildID }: MessageEmbe } function AutomodEmbedAccessory(props: MessageEmbedProps): JSX.Element | null { - const { message, channel, guildID } = props; + const { message, channel } = props; const compact = TextAndImagesSettingsStores.MessageDisplayCompact.useSetting(); - const isDM = guildID === "@me"; const images = getImages(message); const { parse } = Parser; @@ -328,11 +321,11 @@ function AutomodEmbedAccessory(props: MessageEmbedProps): JSX.Element | null { channel={channel} childrenAccessories={ - {isDM + {channel.isDM() ? parse(`<@${ChannelStore.getChannel(channel.id).recipients[0]}>`) : parse(`<#${channel.id}>`) } - {isDM ? " - Direct Message" : " - " + GuildStore.getGuild(channel.guild_id)?.name} + {channel.isDM() && - Direct Message} } compact={compact} From a501da692f5c6584ae202fade81794e9fee00ef4 Mon Sep 17 00:00:00 2001 From: Vendicated Date: Thu, 15 Feb 2024 10:36:01 +0100 Subject: [PATCH 3/4] MessageLinkEmbeds: fix group dm support, improve ui --- src/plugins/messageLinkEmbeds/index.tsx | 79 +++++++++++-------- src/plugins/mutualGroupDMs/index.tsx | 5 +- .../serverProfile/GuildProfileModal.tsx | 12 +-- src/plugins/viewIcons/index.tsx | 21 ++--- src/webpack/common/types/utils.d.ts | 45 +++++++++++ src/webpack/common/utils.ts | 2 + 6 files changed, 107 insertions(+), 57 deletions(-) diff --git a/src/plugins/messageLinkEmbeds/index.tsx b/src/plugins/messageLinkEmbeds/index.tsx index 56facf2e7..50c09ec9f 100644 --- a/src/plugins/messageLinkEmbeds/index.tsx +++ b/src/plugins/messageLinkEmbeds/index.tsx @@ -29,6 +29,7 @@ import { ChannelStore, FluxDispatcher, GuildStore, + IconUtils, MessageStore, Parser, PermissionsBits, @@ -50,6 +51,7 @@ const AutoModEmbed = findComponentByCodeLazy(".withFooter]:", "childrenMessageCo const ChannelMessage = findComponentByCodeLazy("renderSimpleAccessories)"); const SearchResultClasses = findByPropsLazy("message", "searchResult"); +const EmbedClasses = findByPropsLazy("embedAuthorIcon", "embedAuthor", "embedAuthor"); const messageLinkRegex = /(?{accessories} : null; } +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))]; +} + function ChannelMessageEmbedAccessory({ message, channel }: MessageEmbedProps): JSX.Element | null { - const guild = !channel.isDM() && GuildStore.getGuild(channel.guild_id); const dmReceiver = UserStore.getUser(ChannelStore.getChannel(channel.id).recipients?.[0]); + const [channelLabel, iconUrl] = getChannelLabelAndIconUrl(channel); - return - {channel.isDM() && Direct Message - } - {Parser.parse(channel.isDM() ? `<@${dmReceiver.id}>` : `<#${channel.id}>`)} - , - iconProxyURL: guild - ? `https://${window.GLOBAL_ENV.CDN_HOST}/icons/${guild.id}/${guild.icon}.png` - : `https://${window.GLOBAL_ENV.CDN_HOST}/avatars/${dmReceiver.id}/${dmReceiver.avatar}` - } - }} - renderDescription={() => ( -
- -
- )} - />; + return ( + + {channelLabel} - + {Parser.parse(channel.isDM() ? `<@${dmReceiver.id}>` : `<#${channel.id}>`)} + , + iconProxyURL: iconUrl + } + }} + renderDescription={() => ( +
+ +
+ )} + /> + ); } function AutomodEmbedAccessory(props: MessageEmbedProps): JSX.Element | null { @@ -317,15 +325,20 @@ function AutomodEmbedAccessory(props: MessageEmbedProps): JSX.Element | null { const images = getImages(message); const { parse } = Parser; + const [channelLabel, iconUrl] = getChannelLabelAndIconUrl(channel); + return - {channel.isDM() - ? parse(`<@${ChannelStore.getChannel(channel.id).recipients[0]}>`) - : parse(`<#${channel.id}>`) - } - {channel.isDM() && - Direct Message} + + {iconUrl && } + + {channelLabel} - + {channel.isDM() + ? Parser.parse(`<@${ChannelStore.getChannel(channel.id).recipients[0]}>`) + : Parser.parse(`<#${channel.id}>`) + } + } compact={compact} diff --git a/src/plugins/mutualGroupDMs/index.tsx b/src/plugins/mutualGroupDMs/index.tsx index 226d000f1..40d5201cb 100644 --- a/src/plugins/mutualGroupDMs/index.tsx +++ b/src/plugins/mutualGroupDMs/index.tsx @@ -20,11 +20,10 @@ import { Devs } from "@utils/constants"; import { isNonNullish } from "@utils/guards"; import definePlugin from "@utils/types"; import { findByPropsLazy } from "@webpack"; -import { Avatar, ChannelStore, Clickable, RelationshipStore, ScrollerThin, UserStore } from "@webpack/common"; +import { Avatar, ChannelStore, Clickable, IconUtils, RelationshipStore, ScrollerThin, UserStore } from "@webpack/common"; import { Channel, User } from "discord-types/general"; const SelectedChannelActionCreators = findByPropsLazy("selectPrivateChannel"); -const AvatarUtils = findByPropsLazy("getChannelIconURL"); const UserUtils = findByPropsLazy("getGlobalName"); const ProfileListClasses = findByPropsLazy("emptyIconFriends", "emptyIconGuilds"); @@ -71,7 +70,7 @@ export default definePlugin({ }} > diff --git a/src/plugins/serverProfile/GuildProfileModal.tsx b/src/plugins/serverProfile/GuildProfileModal.tsx index be1f2cd54..834367e0a 100644 --- a/src/plugins/serverProfile/GuildProfileModal.tsx +++ b/src/plugins/serverProfile/GuildProfileModal.tsx @@ -12,10 +12,9 @@ import { classes } from "@utils/misc"; import { ModalRoot, ModalSize, openModal } from "@utils/modal"; import { useAwaiter } from "@utils/react"; import { findByPropsLazy, findExportedComponentLazy } from "@webpack"; -import { FluxDispatcher, Forms, GuildChannelStore, GuildMemberStore, Parser, PresenceStore, RelationshipStore, ScrollerThin, SnowflakeUtils, TabBar, Timestamp, useEffect, UserStore, UserUtils, useState, useStateFromStores } from "@webpack/common"; +import { FluxDispatcher, Forms, GuildChannelStore, GuildMemberStore, IconUtils, Parser, PresenceStore, RelationshipStore, ScrollerThin, SnowflakeUtils, TabBar, Timestamp, useEffect, UserStore, UserUtils, useState, useStateFromStores } from "@webpack/common"; import { Guild, User } from "discord-types/general"; -const IconUtils = findByPropsLazy("getGuildBannerURL"); const IconClasses = findByPropsLazy("icon", "acronym", "childWrapper"); const FriendRow = findExportedComponentLazy("FriendRow"); @@ -65,10 +64,7 @@ function GuildProfileModal({ guild }: GuildProps) { const [currentTab, setCurrentTab] = useState(Tabs.ServerInfo); - const bannerUrl = guild.banner && IconUtils.getGuildBannerURL({ - id: guild.id, - banner: guild.banner - }, true).replace(/\?size=\d+$/, "?size=1024"); + const bannerUrl = guild.banner && IconUtils.getGuildBannerURL(guild, true)!.replace(/\?size=\d+$/, "?size=1024"); const iconUrl = guild.icon && IconUtils.getGuildIconURL({ id: guild.id, @@ -89,7 +85,7 @@ function GuildProfileModal({ guild }: GuildProps) { )}
- {guild.icon + {iconUrl ? 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/webpack/common/types/utils.d.ts b/src/webpack/common/types/utils.d.ts index 246659146..2005581a1 100644 --- a/src/webpack/common/types/utils.d.ts +++ b/src/webpack/common/types/utils.d.ts @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +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"); From 7b960716439cba093202f9ba37dd6b258be39017 Mon Sep 17 00:00:00 2001 From: Syncx <47534062+Syncxv@users.noreply.github.com> Date: Fri, 16 Feb 2024 17:52:09 +1100 Subject: [PATCH 4/4] fix ImageZoom patch (#2181) --- src/plugins/imageZoom/components/Magnifier.tsx | 11 ++++++----- src/plugins/imageZoom/index.tsx | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) 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 (