/* * Vencord, a Discord client mod * Copyright (c) 2024 Vendicated and contributors * SPDX-License-Identifier: GPL-3.0-or-later */ import "./style.css"; import { FFmpeg } from "@ffmpeg/ffmpeg"; import { Devs, EquicordDevs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; import { React } from "@webpack/common"; import { Channel } from "discord-types/general"; import { PickerContent, PickerHeader, PickerSidebar, Settings, Wrapper } from "./components"; import { getStickerPack, getStickerPackMetas } from "./stickers"; import { StickerPack, StickerPackMeta } from "./types"; import { cl, FFmpegStateContext, loadFFmpeg } from "./utils"; export default definePlugin({ name: "MoreStickers", description: "Adds sticker packs from other social media platforms. (e.g. LINE)", authors: [EquicordDevs.Leko, Devs.Arjix], options: { settings: { type: OptionType.COMPONENT, description: "Why is this here? Who is going to read this on a custom component? It isn't even rendered? What is its purpose?", component: Settings } }, patches: [ { find: "STICKER_BUTTON_LABEL,", replacement: [{ match: /(children:\(0,\w\.jsx\)\()([\w.]+?)(,{innerClassName.{10,30}\.stickerButton)/, replace: (_, head, button, tail) => { const isMoreStickers = "arguments[0]?.stickersType"; return `${head}${isMoreStickers}?$self.stickerButton:${button}${tail}`; } }, { match: /(\w=)(\w\.useCallback\(\(\)=>\{\(0,\w+\.\w+\)\([\w.]*?\.STICKER,.*?);/, replace: (_, decl, cb) => { const newCb = cb.replace(/(?<=\(\)=>\{\(.*?\)\().+?\.STICKER/, "\"stickers+\""); return `${decl}arguments[0]?.stickersType?${newCb}:${cb};`; } }, { match: /(\w)=((\w)===\w+?\.\w+?\.STICKER)/, replace: (_, isActive, isStickerTab, currentTab) => { const c = "arguments[0].stickersType"; return `${isActive}=${c}?(${currentTab}===${c}):(${isStickerTab})`; } }] }, { find: ".gifts)", replacement: { match: /,\(null===\(\w=\w\.stickers\)\|\|void 0.*?(\w)\.push\((\(0,\w\.jsx\))\((.+?),{disabled:\w,type:(\w)},"sticker"\)\)/, replace: (m, _, jsx, compo, type) => { const c = "arguments[0].type"; return `${m},${c}?.submit?.button&&${_}.push(${jsx}(${compo},{disabled:!${c}?.submit?.button,type:${type},stickersType:"stickers+"},"stickers+"))`; } } }, { find: ".Messages.EXPRESSION_PICKER_GIF", replacement: { match: /role:"tablist",.+?\.Messages\.EXPRESSION_PICKER_CATEGORIES_A11Y_LABEL,children:(\[.*?\)\]}\)}\):null,)(.*?closePopout:\w.*?:null)/s, replace: m => { const stickerTabRegex = /(\w+?)\?(\([^()]+?\))\((.{1,2}),{.{0,128},isActive:(.{1,2})===.{1,150},children:(.{1,10}Messages.EXPRESSION_PICKER_STICKER).*?:null/s; const res = m.replace(stickerTabRegex, (_m, canUseStickers, jsx, tabHeaderComp, currentTab, stickerText) => { const isActive = `${currentTab}==="stickers+"`; return ( `${_m},${canUseStickers}?` + `${jsx}(${tabHeaderComp},{id:"stickers+-picker-tab","aria-controls":"more-stickers-picker-tab-panel","aria-selected":${isActive},isActive:${isActive},autoFocus:true,viewType:"stickers+",children:${jsx}("div",{children:${stickerText}+"+"})})` + ":null" ); }); return res.replace(/:null,((.{1,200})===.{1,30}\.STICKER&&\w+\?(\([^()]{1,10}\)).{1,15}?(\{.*?,onSelectSticker:.*?\})\):null)/s, (_, _m, currentTab, jsx, props) => { return `:null,${currentTab}==="stickers+"?${jsx}($self.moreStickersComponent,${props}):null,${_m}`; }); } } }, { find: '==="remove_text"', replacement: { match: /,\w\.insertText=\w=>{[\w ;]*?1===\w\.length&&.+?==="remove_text"/, replace: ",$self.textEditor=arguments[0]$&" } } ], stickerButton({ innerClassName, isActive, onClick }) { return ( ); }, moreStickersComponent({ channel, closePopout }: { channel: Channel, closePopout: () => void; }) { if (FFmpegStateContext === undefined) { return
FFmpegStateContext is undefined
; } const [query, setQuery] = React.useState(); const [stickerPackMetas, setStickerPackMetas] = React.useState([]); const [stickerPacks, setStickerPacks] = React.useState([]); const [counter, setCounter] = React.useState(0); const [selectedStickerPackId, setSelectedStickerPackId] = React.useState(null); const ffmpegLoaded = React.useState(false); const ffmpeg = React.useState(new FFmpeg()); const getMetasSignature = (m: StickerPackMeta[]) => m.map(x => x.id).sort().join(","); React.useEffect(() => { (async () => { console.log("Updating sticker packs...", counter); setCounter(counter + 1); const sps = (await Promise.all( stickerPackMetas.map(meta => getStickerPack(meta.id)) )) .filter((x): x is Exclude => x !== null); setStickerPacks(sps); })(); }, [stickerPackMetas]); React.useEffect(() => { (async () => { const metas = await getStickerPackMetas(); if (getMetasSignature(metas) !== getMetasSignature(stickerPackMetas)) { setStickerPackMetas(metas); } })(); }, []); React.useEffect(() => { if (ffmpegLoaded[0]) return; loadFFmpeg(ffmpeg[0], () => { ffmpegLoaded[1](true); }); }, []); return ( ({ id: meta.id, name: meta.title, iconUrl: meta.logo.image })) } onPackSelect={pack => { setSelectedStickerPackId(pack.id); }} > ); } });