ViewIcons: Add format setting & user context menu + cleanup
This commit is contained in:
parent
4d836524c1
commit
cfe41ef656
1 changed files with 146 additions and 60 deletions
|
@ -16,40 +16,161 @@
|
||||||
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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({
|
export default definePlugin({
|
||||||
name: "ViewIcons",
|
name: "ViewIcons",
|
||||||
authors: [Devs.Ven],
|
authors: [Devs.Ven],
|
||||||
description: "Makes Avatars/Banners in user profiles clickable, and adds Guild Context Menu Entries to View Banner/Icon.",
|
description: "Makes Avatars/Banners in user profiles clickable, and adds View Icon/Banner entries in the user and server context menu",
|
||||||
|
|
||||||
openImage(url: string) {
|
settings,
|
||||||
const u = new URL(url);
|
|
||||||
u.searchParams.set("size", "512");
|
|
||||||
url = u.toString();
|
|
||||||
|
|
||||||
openModal(modalProps => (
|
openImage,
|
||||||
<ModalRoot size={ModalSize.DYNAMIC} {...modalProps}>
|
|
||||||
<ImageModal
|
start() {
|
||||||
shouldAnimate={true}
|
addContextMenuPatch("user-context", UserContext);
|
||||||
original={url}
|
addContextMenuPatch("guild-context", GuildContext);
|
||||||
src={url}
|
},
|
||||||
renderLinkComponent={MaskedLink}
|
|
||||||
/>
|
stop() {
|
||||||
</ModalRoot>
|
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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue