ViewIcons: Add format setting & user context menu + cleanup

This commit is contained in:
Vendicated 2023-04-12 03:27:31 +02:00
parent 4d836524c1
commit cfe41ef656
No known key found for this signature in database
GPG key ID: A1DC0CFB5615D905

View file

@ -16,28 +16,56 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * 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 { Devs } from "@utils/constants";
import { LazyComponent } from "@utils/misc"; import { LazyComponent } from "@utils/misc";
import { ModalRoot, ModalSize, openModal } from "@utils/modal"; 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 { find, findByCode, findByPropsLazy } from "@webpack";
import { Menu } from "@webpack/common"; import { GuildMemberStore, Menu } from "@webpack/common";
import type { Guild } from "discord-types/general"; import type { Channel, Guild, User } from "discord-types/general";
const ImageModal = LazyComponent(() => findByCode(".MEDIA_MODAL_CLOSE,")); const ImageModal = LazyComponent(() => findByCode(".MEDIA_MODAL_CLOSE,"));
const MaskedLink = LazyComponent(() => find(m => m.type?.toString().includes("MASKED_LINK)"))); 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;
}
const OPEN_URL = "Vencord.Plugins.plugins.ViewIcons.openImage("; interface GuildContextProps {
export default definePlugin({ guild: Guild;
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 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); const u = new URL(url);
u.searchParams.set("size", "512"); u.searchParams.set("size", "512");
u.pathname = u.pathname.replace(/\.(png|jpe?g|webp)$/, `.${settings.store.format}`);
url = u.toString(); url = u.toString();
openModal(modalProps => ( openModal(modalProps => (
@ -50,6 +78,99 @@ export default definePlugin({
/> />
</ModalRoot> </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>
));
};
export default definePlugin({
name: "ViewIcons",
authors: [Devs.Ven],
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: [ patches: [
@ -57,52 +178,17 @@ export default definePlugin({
find: "onAddFriend:", find: "onAddFriend:",
replacement: { replacement: {
// global because Discord has two components that are 99% identical with one small change ._. // global because Discord has two components that are 99% identical with one small change ._.
match: /\{src:(.{1,2}),avatarDecoration/g, match: /\{src:(\i),avatarDecoration/g,
replace: (_, src) => `{src:${src},onClick:()=>${OPEN_URL}${src}),avatarDecoration` replace: (_, src) => `{src:${src},onClick:()=>$self.openImage(${src}),avatarDecoration`
} }
}, { }, {
find: ".popoutNoBannerPremium", find: ".popoutNoBannerPremium",
replacement: { replacement: {
match: /style:.{0,10}\{\},(.{1,2})\)/, match: /style:.{0,10}\{\},(\i)\)/,
replace: (m, style) => replace: (m, style) =>
`onClick:${style}.backgroundImage&&(${style}.cursor="pointer",` + `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>
);
}
}); });