diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/api/ContextMenu.ts | 2 | ||||
-rw-r--r-- | src/plugins/emoteCloner.tsx | 105 |
2 files changed, 56 insertions, 51 deletions
diff --git a/src/api/ContextMenu.ts b/src/api/ContextMenu.ts index 3f73a41..16f9f32 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 afbb398..3bcc694 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 ); } -const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => { - if (!props) return; - const { favoriteableId, emoteClonerDataAlt, itemHref, itemSrc, favoriteableType } = props; +function buildMenuItem(id: string, name: string, isAnimated: boolean) { + return ( + <Menu.MenuItem + id="emote-cloner" + key="emote-cloner" + label="Clone Emote" + action={() => + openModal(modalProps => ( + <ModalRoot {...modalProps}> + <ModalHeader> + <img + role="presentation" + aria-hidden + src={`${location.protocol}//${window.GLOBAL_ENV.CDN_HOST}/emojis/${id}.${isAnimated ? "gif" : "png"}`} + alt="" + height={24} + width={24} + style={{ marginRight: "0.5em" }} + /> + <Forms.FormText>Clone {name}</Forms.FormText> + </ModalHeader> + <ModalContent> + <CloneModal id={id} name={name} isAnimated={isAnimated} /> + </ModalContent> + </ModalRoot> + )) + } + /> + ); +} + +function isGifUrl(url: string) { + return new URL(url).pathname.endsWith(".gif"); +} - if (!emoteClonerDataAlt || favoriteableType !== "emoji") return; +const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => { + const { favoriteableId, itemHref, itemSrc, favoriteableType } = props ?? {}; - const name = emoteClonerDataAlt.match(/:(.*)(?:~\d+)?:/)?.[1]; - if (!name || !favoriteableId) return; + if (!favoriteableId || favoriteableType !== "emoji") return; - const src = itemHref ?? itemSrc; - const isAnimated = new URL(src).pathname.endsWith(".gif"); + const match = props.message.content.match(RegExp(`<a?:(\\w+)(?:~\\d+)?:${favoriteableId}>|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(( - <Menu.MenuItem - id="emote-cloner" - key="emote-cloner" - label="Clone" - action={() => - openModal(modalProps => ( - <ModalRoot {...modalProps}> - <ModalHeader> - <img - role="presentation" - aria-hidden - src={`${location.protocol}//${window.GLOBAL_ENV.CDN_HOST}/emojis/${favoriteableId}.${isAnimated ? "gif" : "png"}`} - alt="" - height={24} - width={24} - style={{ marginRight: "0.5em" }} - /> - <Forms.FormText>Clone {name}</Forms.FormText> - </ModalHeader> - <ModalContent> - <CloneModal id={favoriteableId} name={name} isAnimated={isAnimated} /> - </ModalContent> - </ModalRoot> - )) - } - > - </Menu.MenuItem> - )); - } + 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); } }); |