diff options
Diffstat (limited to 'src/plugins/viewIcons.tsx')
-rw-r--r-- | src/plugins/viewIcons.tsx | 210 |
1 files changed, 148 insertions, 62 deletions
diff --git a/src/plugins/viewIcons.tsx b/src/plugins/viewIcons.tsx index b60fb75..8b3d31d 100644 --- a/src/plugins/viewIcons.tsx +++ b/src/plugins/viewIcons.tsx @@ -16,40 +16,161 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ +import { addContextMenuPatch, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; +import { definePluginSettings } from "@api/settings"; import { Devs } from "@utils/constants"; import { LazyComponent } from "@utils/misc"; import { ModalRoot, ModalSize, openModal } from "@utils/modal"; -import definePlugin from "@utils/types"; +import definePlugin, { OptionType } from "@utils/types"; import { find, findByCode, findByPropsLazy } from "@webpack"; -import { Menu } from "@webpack/common"; -import type { Guild } from "discord-types/general"; +import { GuildMemberStore, Menu } from "@webpack/common"; +import type { Channel, Guild, User } from "discord-types/general"; const ImageModal = LazyComponent(() => findByCode(".MEDIA_MODAL_CLOSE,")); const MaskedLink = LazyComponent(() => find(m => m.type?.toString().includes("MASKED_LINK)"))); +const BannerStore = findByPropsLazy("getGuildBannerURL"); -const GuildBannerStore = findByPropsLazy("getGuildBannerURL"); +interface UserContextProps { + channel: Channel; + guildId?: string; + user: User; +} + +interface GuildContextProps { + guild: Guild; +} + +const settings = definePluginSettings({ + format: { + type: OptionType.SELECT, + description: "Choose the image format to use for non animated images. Animated images will always use .gif", + options: [ + { + label: "webp", + value: "webp", + default: true + }, + { + label: "png", + value: "png", + }, + { + label: "jpg", + value: "jpg", + } + ] + } +}); + +function openImage(url: string) { + const u = new URL(url); + u.searchParams.set("size", "512"); + u.pathname = u.pathname.replace(/\.(png|jpe?g|webp)$/, `.${settings.store.format}`); + url = u.toString(); + + openModal(modalProps => ( + <ModalRoot size={ModalSize.DYNAMIC} {...modalProps}> + <ImageModal + shouldAnimate={true} + original={url} + src={url} + renderLinkComponent={MaskedLink} + /> + </ModalRoot> + )); +} + +const seen = new WeakSet(); + +const UserContext: NavContextMenuPatchCallback = (children, { user, guildId }: UserContextProps) => { + if (seen.has(children)) return; + seen.add(children); + + const memberAvatar = GuildMemberStore.getMember(guildId!, user.id)?.avatar || null; + + children.splice(1, 0, ( + <Menu.MenuGroup> + <Menu.MenuItem + id="view-avatar" + label="View Avatar" + action={() => openImage(BannerStore.getUserAvatarURL(user, true, 512))} + /> + {memberAvatar && ( + <Menu.MenuItem + id="view-server-avatar" + label="View Server Avatar" + action={() => openImage(BannerStore.getGuildMemberAvatarURLSimple({ + userId: user.id, + avatar: memberAvatar, + guildId + }, true))} + /> + )} + </Menu.MenuGroup> + )); +}; + +const GuildContext: NavContextMenuPatchCallback = (children, { guild: { id, icon, banner } }: GuildContextProps) => { + if (seen.has(children)) return; + seen.add(children); + + if (!banner && !icon) return; + + // before copy id (if it exists) + const idx = children.length + + children[children.length - 1]?.props?.children?.props?.id === "devmode-copy-id" + ? -2 + : -1; + + children.splice(idx, 0, ( + <Menu.MenuGroup> + {icon ? ( + <Menu.MenuItem + id="view-icon" + label="View Icon" + action={() => + openImage(BannerStore.getGuildIconURL({ + id, + icon, + size: 512, + canAnimate: true + })) + } + /> + ) : null} + {banner ? ( + <Menu.MenuItem + id="view-banner" + label="View Banner" + action={() => + openImage(BannerStore.getGuildBannerURL({ + id, + banner, + }, true)) + } + /> + ) : null} + </Menu.MenuGroup> + )); +}; -const OPEN_URL = "Vencord.Plugins.plugins.ViewIcons.openImage("; export default definePlugin({ name: "ViewIcons", authors: [Devs.Ven], - description: "Makes Avatars/Banners in user profiles clickable, and adds Guild Context Menu Entries to View Banner/Icon.", - - openImage(url: string) { - const u = new URL(url); - u.searchParams.set("size", "512"); - url = u.toString(); - - openModal(modalProps => ( - <ModalRoot size={ModalSize.DYNAMIC} {...modalProps}> - <ImageModal - shouldAnimate={true} - original={url} - src={url} - renderLinkComponent={MaskedLink} - /> - </ModalRoot> - )); + description: "Makes Avatars/Banners in user profiles clickable, and adds View Icon/Banner entries in the user and server context menu", + + settings, + + openImage, + + start() { + addContextMenuPatch("user-context", UserContext); + addContextMenuPatch("guild-context", GuildContext); + }, + + stop() { + removeContextMenuPatch("user-context", UserContext); + removeContextMenuPatch("guild-context", GuildContext); }, patches: [ @@ -57,52 +178,17 @@ export default definePlugin({ find: "onAddFriend:", replacement: { // global because Discord has two components that are 99% identical with one small change ._. - match: /\{src:(.{1,2}),avatarDecoration/g, - replace: (_, src) => `{src:${src},onClick:()=>${OPEN_URL}${src}),avatarDecoration` + match: /\{src:(\i),avatarDecoration/g, + replace: (_, src) => `{src:${src},onClick:()=>$self.openImage(${src}),avatarDecoration` } }, { find: ".popoutNoBannerPremium", replacement: { - match: /style:.{0,10}\{\},(.{1,2})\)/, + match: /style:.{0,10}\{\},(\i)\)/, replace: (m, style) => `onClick:${style}.backgroundImage&&(${style}.cursor="pointer",` + - `()=>${OPEN_URL}${style}.backgroundImage.replace("url(", ""))),${m}` + `()=>$self.openImage(${style}.backgroundImage.replace("url(", ""))),${m}` } - }, { - find: '"GuildContextMenu:', - replacement: [ - { - match: /\w=(\w)\.id/, - replace: "_guild=$1,$&" - }, - { - match: /(id:"leave-guild".{0,200}),(\(0,.{1,3}\.jsxs?\).{0,200}function)/, - replace: "$1,$self.buildGuildContextMenuEntries(_guild),$2" - } - ] } - ], - - buildGuildContextMenuEntries(guild: Guild) { - return ( - <Menu.MenuGroup> - {guild.banner && ( - <Menu.MenuItem - id="view-banner" - key="view-banner" - label="View Banner" - action={() => this.openImage(GuildBannerStore.getGuildBannerURL(guild))} - /> - )} - {guild.icon && ( - <Menu.MenuItem - id="view-icon" - key="view-icon" - label="View Icon" - action={() => this.openImage(guild.getIconURL(0, true))} - /> - )} - </Menu.MenuGroup> - ); - } + ] }); |