diff --git a/README.md b/README.md index 0f92fb32..c1fe6b51 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch ### Extra included plugins
-166 additional plugins +167 additional plugins ### All Platforms @@ -38,6 +38,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - CleanChannelName by AutumnVN - ClientSideBlock by Samwich - CommandPalette by Ethan +- CopyStickerLinks by Byeoon - CopyUserMention by Cortex & castdrian - CustomSounds by TheKodeToad & SpikeHD - CustomTimestamps by Rini & nvhrr diff --git a/src/plugins/copyStickerLinks/index.tsx b/src/plugins/copyStickerLinks/index.tsx new file mode 100644 index 00000000..f98a3c96 --- /dev/null +++ b/src/plugins/copyStickerLinks/index.tsx @@ -0,0 +1,147 @@ +/* +* Vencord, a modification for Discord's desktop app +* Copyright (c) 2025 Vendicated and contributors +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; +import { Devs } from "@utils/constants"; +import { copyWithToast } from "@utils/misc"; +import definePlugin from "@utils/types"; +import { findStoreLazy } from "@webpack"; +import { Menu, React } from "@webpack/common"; +import { Promisable } from "type-fest"; + +const StickersStore = findStoreLazy("StickersStore"); + +interface Sticker { + t: "Sticker"; + format_type: number; + id: string; + type: number; +} + +const StickerExt = [, "png", "png", "json", "gif"] as const; + +function getUrl(data: Sticker) { + if (data.format_type === 4) + return `https:${window.GLOBAL_ENV.MEDIA_PROXY_ENDPOINT}/stickers/${data.id}.gif?size=4096&lossless=true`; + + return `https://${window.GLOBAL_ENV.CDN_HOST}/stickers/${data.id}.${StickerExt[data.format_type]}?size=4096&lossless=true`; +} + +function buildMenuItem(Sticker, fetchData: () => Promisable>) { + return ( + <> + + + { + const res = await fetchData(); + const data = { t: Sticker, ...res } as Sticker; + const url = getUrl(data[0]); + copyWithToast(url, "Link copied!"); + } + } + /> + + { + const res = await fetchData(); + const data = { t: Sticker, ...res } as Sticker; + const url = getUrl(data[0]); + VencordNative.native.openExternal(url); + } + } + /> + + ); +} + +function buildMenuExpression(Sticker, fetchData: () => Promisable>) { + return ( + <> + + { + const res = await fetchData(); + const data = { t: Sticker, ...res } as Sticker; + const url = getUrl(data); + copyWithToast(url, "Link copied!"); + } + } + /> + { + const res = await fetchData(); + const data = { t: Sticker, ...res } as Sticker; + const url = getUrl(data); + VencordNative.native.openExternal(url); + } + } + /> + + ); +} + +const messageContextMenuPatch: NavContextMenuPatchCallback = (children, props) => { + const { favoriteableId, favoriteableType } = props ?? {}; + if (!favoriteableId) return; + const menuItem = (() => { + const sticker = props.message.stickerItems.find(s => s.id === favoriteableId); + if (sticker?.format_type === 3) return; + switch (favoriteableType) { + case "sticker": + return buildMenuItem("Sticker", () => props.message.stickerItems); + } + })(); + + if (menuItem) + findGroupChildrenByChildId("devmode-copy-id", children, true)?.push(menuItem); +}; + +const expressionPickerPatch: NavContextMenuPatchCallback = (children, props: { target: HTMLElement; }) => { + const { id } = props?.target?.dataset ?? {}; + if (!id) return; + + if (!props.target.className?.includes("lottieCanvas")) { + const stickerCache = StickersStore.getStickerById(id); + if (stickerCache) { + children.push(buildMenuExpression("Sticker", () => stickerCache)); + } + } +}; + +export default definePlugin({ + name: "CopyStickerLinks", + description: "Adds the ability to copy and open sticker links to your browser", + authors: [Devs.Byeoon], + contextMenus: { + "message": messageContextMenuPatch, + "expression-picker": expressionPickerPatch + } +}); diff --git a/src/utils/constants.ts b/src/utils/constants.ts index e8686a82..f8aabc88 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -494,7 +494,7 @@ export const Devs = /* #__PURE__*/ Object.freeze({ name: "Sqaaakoi", id: 259558259491340288n }, - Byron: { + Byeoon: { name: "byeoon", id: 1167275288036655133n }, @@ -1046,6 +1046,10 @@ export const EquicordDevs = Object.freeze({ name: "ItsAlex", id: 551023598203043840n }, + Byeoon: { + name: "byeoon", + id: 1167275288036655133n + }, } satisfies Record); // iife so #__PURE__ works correctly