Also add Emote Cloner to Emote picker rightclick menu (#664)
This commit is contained in:
parent
082ac62eda
commit
8d8cedd72c
|
@ -122,6 +122,8 @@ export function _patchContextMenu(props: ContextMenuProps) {
|
||||||
props.contextMenuApiArguments ??= [];
|
props.contextMenuApiArguments ??= [];
|
||||||
const contextMenuPatches = navPatches.get(props.navId);
|
const contextMenuPatches = navPatches.get(props.navId);
|
||||||
|
|
||||||
|
if (!Array.isArray(props.children)) props.children = [props.children];
|
||||||
|
|
||||||
if (contextMenuPatches) {
|
if (contextMenuPatches) {
|
||||||
for (const patch of contextMenuPatches) {
|
for (const patch of contextMenuPatches) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { addContextMenuPatch, findGroupChildrenByChildId, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu";
|
import { addContextMenuPatch, findGroupChildrenByChildId, NavContextMenuPatchCallback, removeContextMenuPatch } from "@api/ContextMenu";
|
||||||
import { migratePluginSettings } from "@api/settings";
|
|
||||||
import { CheckedTextInput } from "@components/CheckedTextInput";
|
import { CheckedTextInput } from "@components/CheckedTextInput";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import Logger from "@utils/Logger";
|
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 (
|
||||||
|
<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");
|
||||||
|
}
|
||||||
|
|
||||||
const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => {
|
const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => {
|
||||||
if (!props) return;
|
const { favoriteableId, itemHref, itemSrc, favoriteableType } = props ?? {};
|
||||||
const { favoriteableId, emoteClonerDataAlt, itemHref, itemSrc, favoriteableType } = props;
|
|
||||||
|
|
||||||
if (!emoteClonerDataAlt || favoriteableType !== "emoji") return;
|
if (!favoriteableId || favoriteableType !== "emoji") return;
|
||||||
|
|
||||||
const name = emoteClonerDataAlt.match(/:(.*)(?:~\d+)?:/)?.[1];
|
const match = props.message.content.match(RegExp(`<a?:(\\w+)(?:~\\d+)?:${favoriteableId}>|https://cdn\\.discordapp\\.com/emojis/${favoriteableId}\\.`));
|
||||||
if (!name || !favoriteableId) return;
|
if (!match) return;
|
||||||
|
const name = match[1] ?? "FakeNitroEmoji";
|
||||||
const src = itemHref ?? itemSrc;
|
|
||||||
const isAnimated = new URL(src).pathname.endsWith(".gif");
|
|
||||||
|
|
||||||
const group = findGroupChildrenByChildId("copy-link", children);
|
const group = findGroupChildrenByChildId("copy-link", children);
|
||||||
if (group && !group.some(child => child?.props?.id === "emote-cloner")) {
|
if (group && !group.some(child => child?.props?.id === "emote-cloner"))
|
||||||
group.push((
|
group.push(buildMenuItem(favoriteableId, name, isGifUrl(itemHref ?? itemSrc)));
|
||||||
<Menu.MenuItem
|
};
|
||||||
id="emote-cloner"
|
|
||||||
key="emote-cloner"
|
const expressionPickerPatch: NavContextMenuPatchCallback = (children, props: { target: HTMLElement; }) => {
|
||||||
label="Clone"
|
const { id, name, type } = props?.target?.dataset ?? {};
|
||||||
action={() =>
|
if (!id || !name || type !== "emoji") return;
|
||||||
openModal(modalProps => (
|
|
||||||
<ModalRoot {...modalProps}>
|
const firstChild = props.target.firstChild as HTMLImageElement;
|
||||||
<ModalHeader>
|
|
||||||
<img
|
if (!children.some(c => c?.props?.id === "emote-cloner"))
|
||||||
role="presentation"
|
children.push(buildMenuItem(id, name, firstChild && isGifUrl(firstChild.src)));
|
||||||
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>
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
migratePluginSettings("EmoteCloner", "EmoteYoink");
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "EmoteCloner",
|
name: "EmoteCloner",
|
||||||
description: "Adds a Clone context menu item to emotes to clone them your own server",
|
description: "Adds a Clone context menu item to emotes to clone them your own server",
|
||||||
authors: [Devs.Ven, Devs.Nuckyz],
|
authors: [Devs.Ven, Devs.Nuckyz],
|
||||||
dependencies: ["MenuItemDeobfuscatorAPI", "ContextMenuAPI"],
|
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() {
|
start() {
|
||||||
addContextMenuPatch("message", messageContextMenuPatch);
|
addContextMenuPatch("message", messageContextMenuPatch);
|
||||||
|
addContextMenuPatch("expression-picker", expressionPickerPatch);
|
||||||
},
|
},
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
removeContextMenuPatch("message", messageContextMenuPatch);
|
removeContextMenuPatch("message", messageContextMenuPatch);
|
||||||
|
removeContextMenuPatch("expression-picker", expressionPickerPatch);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue