diff --git a/src/api/ContextMenu.ts b/src/api/ContextMenu.ts index 3f73a41e..16f9f32b 100644 --- a/src/api/ContextMenu.ts +++ b/src/api/ContextMenu.ts @@ -122,6 +122,8 @@ export function _patchContextMenu(props: ContextMenuProps) { props.contextMenuApiArguments ??= []; const contextMenuPatches = navPatches.get(props.navId); + if (!Array.isArray(props.children)) props.children = [props.children]; + if (contextMenuPatches) { for (const patch of contextMenuPatches) { try { diff --git a/src/plugins/emoteCloner.tsx b/src/plugins/emoteCloner.tsx index afbb3987..3bcc6944 100644 --- a/src/plugins/emoteCloner.tsx +++ b/src/plugins/emoteCloner.tsx @@ -17,7 +17,6 @@ */ import { addContextMenuPatch, findGroupChildrenByChildId, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu"; -import { migratePluginSettings } from "@api/settings"; import { CheckedTextInput } from "@components/CheckedTextInput"; import { Devs } from "@utils/constants"; import Logger from "@utils/Logger"; @@ -176,74 +175,78 @@ function CloneModal({ id, name: emojiName, isAnimated }: { id: string; name: str ); } +function buildMenuItem(id: string, name: string, isAnimated: boolean) { + return ( + + openModal(modalProps => ( + + + + Clone {name} + + + + + + )) + } + /> + ); +} + +function isGifUrl(url: string) { + return new URL(url).pathname.endsWith(".gif"); +} + const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => { - if (!props) return; - const { favoriteableId, emoteClonerDataAlt, itemHref, itemSrc, favoriteableType } = props; + const { favoriteableId, itemHref, itemSrc, favoriteableType } = props ?? {}; - if (!emoteClonerDataAlt || favoriteableType !== "emoji") return; + if (!favoriteableId || favoriteableType !== "emoji") return; - const name = emoteClonerDataAlt.match(/:(.*)(?:~\d+)?:/)?.[1]; - if (!name || !favoriteableId) return; - - const src = itemHref ?? itemSrc; - const isAnimated = new URL(src).pathname.endsWith(".gif"); + const match = props.message.content.match(RegExp(`|https://cdn\\.discordapp\\.com/emojis/${favoriteableId}\\.`)); + if (!match) return; + const name = match[1] ?? "FakeNitroEmoji"; const group = findGroupChildrenByChildId("copy-link", children); - if (group && !group.some(child => child?.props?.id === "emote-cloner")) { - group.push(( - - openModal(modalProps => ( - - - - Clone {name} - - - - - - )) - } - > - - )); - } + if (group && !group.some(child => child?.props?.id === "emote-cloner")) + group.push(buildMenuItem(favoriteableId, name, isGifUrl(itemHref ?? itemSrc))); +}; + +const expressionPickerPatch: NavContextMenuPatchCallback = (children, props: { target: HTMLElement; }) => { + const { id, name, type } = props?.target?.dataset ?? {}; + if (!id || !name || type !== "emoji") return; + + const firstChild = props.target.firstChild as HTMLImageElement; + + if (!children.some(c => c?.props?.id === "emote-cloner")) + children.push(buildMenuItem(id, name, firstChild && isGifUrl(firstChild.src))); }; -migratePluginSettings("EmoteCloner", "EmoteYoink"); export default definePlugin({ name: "EmoteCloner", description: "Adds a Clone context menu item to emotes to clone them your own server", authors: [Devs.Ven, Devs.Nuckyz], dependencies: ["MenuItemDeobfuscatorAPI", "ContextMenuAPI"], - patches: [ - { - find: ".Messages.MESSAGE_ACTIONS_MENU_LABEL", - replacement: { - match: /favoriteableType:\i,(?<=(\i)\.getAttribute\("data-type"\).+?)/, - replace: (m, target) => `${m}emoteClonerDataAlt:${target}.alt,` - } - } - ], - start() { addContextMenuPatch("message", messageContextMenuPatch); + addContextMenuPatch("expression-picker", expressionPickerPatch); }, stop() { removeContextMenuPatch("message", messageContextMenuPatch); + removeContextMenuPatch("expression-picker", expressionPickerPatch); } });