aboutsummaryrefslogtreecommitdiff
path: root/src/plugins/viewIcons.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/viewIcons.tsx')
-rw-r--r--src/plugins/viewIcons.tsx210
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>
- );
- }
+ ]
});