1
0
Fork 1
mirror of https://github.com/Vendicated/Vencord.git synced 2025-01-10 09:56:24 +00:00

add support forr user statuses and onboarding

This commit is contained in:
sadan 2025-01-04 04:08:24 -05:00
parent 16a1c44947
commit e139522fa7
No known key found for this signature in database

View file

@ -24,12 +24,18 @@ import { Margins } from "@utils/margins";
import { ModalContent, ModalHeader, ModalRoot, openModalLazy } from "@utils/modal"; import { ModalContent, ModalHeader, ModalRoot, openModalLazy } from "@utils/modal";
import definePlugin from "@utils/types"; import definePlugin from "@utils/types";
import { findByCodeLazy, findStoreLazy } from "@webpack"; import { findByCodeLazy, findStoreLazy } from "@webpack";
import { Constants, EmojiStore, FluxDispatcher, Forms, GuildStore, Menu, PermissionsBits, PermissionStore, React, RestAPI, Toasts, Tooltip, UserStore } from "@webpack/common"; import { Constants, ContextMenuApi, EmojiStore, FluxDispatcher, Forms, GuildStore, Menu, PermissionsBits, PermissionStore, React, RestAPI, Toasts, Tooltip, UserStore } from "@webpack/common";
import { Promisable } from "type-fest"; import { Promisable } from "type-fest";
const StickersStore = findStoreLazy("StickersStore"); const StickersStore = findStoreLazy("StickersStore");
const uploadEmoji = findByCodeLazy(".GUILD_EMOJIS(", "EMOJI_UPLOAD_START"); const uploadEmoji = findByCodeLazy(".GUILD_EMOJIS(", "EMOJI_UPLOAD_START");
interface OnboardingContextMenuProps {
option: {
emoji: { id: string; name: string; animated: boolean; } | { id: null; };
};
}
interface Sticker { interface Sticker {
t: "Sticker"; t: "Sticker";
description: string; description: string;
@ -176,7 +182,6 @@ const getFontSize = (s: string) => {
const sizes = [20, 20, 18, 18, 16, 14, 12]; const sizes = [20, 20, 18, 18, 16, 14, 12];
return sizes[s.length] ?? 4; return sizes[s.length] ?? 4;
}; };
const nameValidator = /^\w+$/i; const nameValidator = /^\w+$/i;
function CloneModal({ data }: { data: Sticker | Emoji; }) { function CloneModal({ data }: { data: Sticker | Emoji; }) {
@ -272,7 +277,11 @@ function CloneModal({ data }: { data: Sticker | Emoji; }) {
); );
} }
function buildMenuItem(type: "Emoji" | "Sticker", fetchData: () => Promisable<Omit<Sticker | Emoji, "t">>) { type Discriminate<
U extends { "t": string; },
K extends U["t"]
> = U extends { "t": K; } ? U : never;
function buildMenuItem<T extends (Emoji | Sticker)["t"]>(type: T, fetchData: () => Promisable<Omit<Discriminate<Sticker | Emoji, T>, "t">>) {
return ( return (
<Menu.MenuItem <Menu.MenuItem
id="emote-cloner" id="emote-cloner"
@ -281,7 +290,7 @@ function buildMenuItem(type: "Emoji" | "Sticker", fetchData: () => Promisable<Om
action={() => action={() =>
openModalLazy(async () => { openModalLazy(async () => {
const res = await fetchData(); const res = await fetchData();
const data = { t: type, ...res } as Sticker | Emoji; const data = { t: type, ...res } as any as Sticker | Emoji;
const url = getUrl(data); const url = getUrl(data);
return modalProps => ( return modalProps => (
@ -309,7 +318,7 @@ function buildMenuItem(type: "Emoji" | "Sticker", fetchData: () => Promisable<Om
); );
} }
function isGifUrl(url: string) { function isEmojiAnimated(url: string) {
const u = new URL(url); const u = new URL(url);
return u.pathname.endsWith(".gif") || u.searchParams.get("animated") === "true"; return u.pathname.endsWith(".gif") || u.searchParams.get("animated") === "true";
} }
@ -330,7 +339,7 @@ const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) =
return buildMenuItem("Emoji", () => ({ return buildMenuItem("Emoji", () => ({
id: favoriteableId, id: favoriteableId,
name, name,
isAnimated: isGifUrl(itemHref ?? itemSrc) isAnimated: isEmojiAnimated(itemHref ?? itemSrc)
})); }));
case "sticker": case "sticker":
const sticker = props.message.stickerItems.find(s => s.id === favoriteableId); const sticker = props.message.stickerItems.find(s => s.id === favoriteableId);
@ -354,20 +363,73 @@ const expressionPickerPatch: NavContextMenuPatchCallback = (children, props: { t
children.push(buildMenuItem("Emoji", () => ({ children.push(buildMenuItem("Emoji", () => ({
id, id,
name, name,
isAnimated: firstChild && isGifUrl(firstChild.src) isAnimated: firstChild && isEmojiAnimated(firstChild.src)
}))); })));
} else if (type === "sticker" && !props.target.className?.includes("lottieCanvas")) { } else if (type === "sticker" && !props.target.className?.includes("lottieCanvas")) {
children.push(buildMenuItem("Sticker", () => fetchSticker(id))); children.push(buildMenuItem("Sticker", () => fetchSticker(id)));
} }
}; };
let emojiUrlRegex: RegExp;
// Patches user statuses with a custom emoji
const imageContextMenuPatch: NavContextMenuPatchCallback = (children, { target, src }: {
target?: HTMLImageElement;
src?: string;
}) => {
const [, id] = src?.match(emojiUrlRegex) ?? [];
if (!id) return;
const name = target?.alt || "EmojiName";
children.push(buildMenuItem("Emoji", () => ({
id,
name,
isAnimated: isEmojiAnimated(src!)
})));
return;
};
export default definePlugin({ export default definePlugin({
name: "EmoteCloner", name: "EmoteCloner",
description: "Allows you to clone Emotes & Stickers to your own server (right click them)", description: "Allows you to clone Emotes & Stickers to your own server (right click them)",
tags: ["StickerCloner"], tags: ["StickerCloner"],
authors: [Devs.Ven, Devs.Nuckyz], authors: [Devs.Ven, Devs.Nuckyz, Devs.sadan],
patches: [
{
find: "emoji.animated||",
replacement: {
match: /(?=onClick:)/,
replace: "onContextMenu:$self.OnboardingContextMenu.bind(null, arguments[0]),"
}
}
],
start() {
const { CDN_HOST } = window.GLOBAL_ENV;
emojiUrlRegex = new RegExp(`^${location.protocol}//${CDN_HOST}/emojis/(\\d+)\\.\\w+.+$`);
},
contextMenus: { contextMenus: {
"message": messageContextMenuPatch, "message": messageContextMenuPatch,
"expression-picker": expressionPickerPatch "expression-picker": expressionPickerPatch,
} "image-context": imageContextMenuPatch
},
OnboardingContextMenu({ option: { emoji } }: OnboardingContextMenuProps, ev: React.MouseEvent) {
// covers no emoji and unicode emojis
if (emoji?.id == null) return;
console.log(emoji);
ContextMenuApi.openContextMenuLazy(ev, async () => {
return () => (<Menu.Menu
navId="onboarding-question-context"
onClose={() => FluxDispatcher.dispatch({ type: "CONTEXT_MENU_CLOSE" })}
>
{buildMenuItem("Emoji", () => ({
...emoji,
isAnimated: emoji.animated
}))}
</Menu.Menu>);
});
},
}); });