diff --git a/README.md b/README.md index 4950e342..8fb706d8 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch - MessageLoggerEnhanced by Aria - MessageTranslate by Samwich - ModalFade by Kyuuhachi +- MoreStickers by Leko & Arjix - NewPluginsManager by Sqaaakoi - NoAppsAllowed by kvba - NoBulletPoints by Samwich diff --git a/src/equicordplugins/moreStickers/components/PickerCategoriesSidebar.tsx b/src/equicordplugins/moreStickers/components/PickerCategoriesSidebar.tsx deleted file mode 100644 index 6618c4d6..00000000 --- a/src/equicordplugins/moreStickers/components/PickerCategoriesSidebar.tsx +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2023 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 { ModalContent, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal"; -import { React, Text } from "@webpack/common"; - -import { CategoryImage } from "./categoryImage"; -import { CategoryScroller } from "./categoryScroller"; -import { CategoryWrapper } from "./categoryWrapper"; -import { CogIcon, RecentlyUsedIcon } from "./icons"; -import { RECENT_STICKERS_ID, RECENT_STICKERS_TITLE } from "./recent"; -import { Settings } from "./settings"; -import { StickerCategory } from "./stickerCategory"; -import { cl, clPicker } from "../utils"; - -export interface StickerCategory { - id: string; - name: string; - iconUrl?: string; -} - -export interface SidebarProps { - packMetas: StickerCategory[]; - onPackSelect: (category: StickerCategory) => void; -} - -export const RecentPack = { - id: RECENT_STICKERS_ID, - name: RECENT_STICKERS_TITLE, -} as StickerCategory; - -export const PickerSidebar = ({ packMetas, onPackSelect }: SidebarProps) => { - const [activePack, setActivePack] = React.useState(RecentPack); - const [hovering, setHovering] = React.useState(false); - - return ( - - - { - if (activePack === RecentPack) return; - - onPackSelect(RecentPack); - setActivePack(RecentPack); - }} - > - - - { - ...packMetas.map(pack => { - return ( - { - if (activePack?.id === pack.id) return; - - onPackSelect(pack); - setActivePack(pack); - }} - isActive={activePack?.id === pack.id} - > - - - ); - }) - } - -
- -
-
- ); -}; diff --git a/src/equicordplugins/moreStickers/components/PickerContent.tsx b/src/equicordplugins/moreStickers/components/PickerContent.tsx deleted file mode 100644 index c7d81181..00000000 --- a/src/equicordplugins/moreStickers/components/PickerContent.tsx +++ /dev/null @@ -1,402 +0,0 @@ -/* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2023 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 { React } from "@webpack/common"; - -import { Sticker, StickerPack } from "../types"; -import { sendSticker } from "../upload"; -import { RecentlyUsedIcon } from "./icons"; -import { addRecentSticker, getRecentStickers, RECENT_STICKERS_ID, RECENT_STICKERS_TITLE } from "./recent"; -import { clPicker, FFmpegStateContext } from "../utils"; - -export interface PickerContent { - stickerPacks: StickerPack[]; - selectedStickerPackId?: string | null; - setSelectedStickerPackId: React.Dispatch>; - channelId: string; - closePopout: () => void; - query?: string; -} - -export interface PickerContentHeader { - image: string | React.ReactNode; - title: string; - children?: React.ReactNode; - isSelected?: boolean; - afterScroll?: () => void; - beforeScroll?: () => void; -} - -export interface PickerContentRow { - rowIndex: number; - grid1: PickerContentRowGrid; - grid2?: PickerContentRowGrid; - grid3?: PickerContentRowGrid; - channelId: string; -} - -export interface PickerContentRowGrid { - rowIndex: number; - colIndex: number; - sticker: Sticker; - onHover: (sticker: Sticker | null) => void; - isHovered?: boolean; - channelId?: string; - onSend?: (sticker?: Sticker, shouldClose?: boolean) => void; -} - -function PickerContentRowGrid({ - rowIndex, - colIndex, - sticker, - onHover, - channelId, - onSend = () => { }, - isHovered = false -}: PickerContentRowGrid) { - if (FFmpegStateContext === undefined) { - return
FFmpegStateContext is undefined
; - } - - const ffmpegState = React.useContext(FFmpegStateContext); - - return ( -
onHover(sticker)} - onClick={e => { - if (!channelId) return; - - sendSticker({ channelId, sticker, ctrlKey: e.ctrlKey, shiftKey: e.shiftKey, ffmpegState }); - addRecentSticker(sticker); - onSend(sticker, e.ctrlKey); - }} - > -
- {sticker.title} - -
-
- ); -} - -function PickerContentRow({ rowIndex, grid1, grid2, grid3, channelId }: PickerContentRow) { - return ( -
- - {grid2 && } - {grid3 && } -
- ); -} - - -function HeaderCollapseIcon({ isExpanded }: { isExpanded: boolean; }) { - return ( - - - - ); -} - -export function PickerContentHeader({ - image, - title, - children, - isSelected = false, - afterScroll = () => { }, - beforeScroll = () => { } -}: PickerContentHeader) { - - const [isExpand, setIsExpand] = React.useState(true); - const headerElem = React.useRef(null); - React.useEffect(() => { - if (isSelected && headerElem.current) { - beforeScroll(); - - headerElem.current.scrollIntoView({ - behavior: "smooth", - block: "start", - }); - - afterScroll(); - } - }, [isSelected]); - - return ( - -
-
{ - setIsExpand(e => !e); - }} - > -
-
- {typeof image === "string" ? - - {title} - - - : image} -
-
- - {title} - - -
-
- {isExpand ? children : null} -
- ); -} - -export function PickerContent({ stickerPacks, selectedStickerPackId, setSelectedStickerPackId, channelId, closePopout, query }: PickerContent) { - const [currentSticker, setCurrentSticker] = ( - React.useState(( - stickerPacks.length && stickerPacks[0].stickers.length) ? - stickerPacks[0].stickers[0] : - null - ) - ); - - const [currentStickerPack, setCurrentStickerPack] = React.useState(stickerPacks.length ? stickerPacks[0] : null); - const [recentStickers, setRecentStickers] = React.useState([]); - - const stickerPacksElemRef = React.useRef(null); - const scrollerRef = React.useRef(null); - - function queryFilter(stickers: Sticker[]): Sticker[] { - if (!query) return stickers; - return stickers.filter(sticker => sticker.title.toLowerCase().includes(query.toLowerCase())); - } - - async function fetchRecentStickers() { - const recentStickers = await getRecentStickers(); - setRecentStickers(recentStickers); - } - - React.useEffect(() => { - fetchRecentStickers(); - }, []); - - React.useEffect(() => { - if (currentStickerPack?.id !== currentSticker?.stickerPackId) { - setCurrentStickerPack(stickerPacks.find(p => p.id === currentSticker?.stickerPackId) ?? currentStickerPack); - } - }, [currentSticker]); - - const stickersToRows = (stickers: Sticker[]): JSX.Element[] => stickers - .reduce((acc, sticker, i) => { - if (i % 3 === 0) { - acc.push([]); - } - acc[acc.length - 1].push(sticker); - return acc; - }, [] as Sticker[][]) - .map((stickers, i) => ( - { !s && closePopout(); }, - isHovered: currentSticker?.id === stickers[0].id - }} - grid2={ - stickers.length > 1 ? { - rowIndex: i, - colIndex: 2, - sticker: stickers[1], - onHover: setCurrentSticker, - onSend: (_, s) => { !s && closePopout(); }, - isHovered: currentSticker?.id === stickers[1].id - } : undefined - } - grid3={ - stickers.length > 2 ? { - rowIndex: i, - colIndex: 3, - sticker: stickers[2], - onHover: setCurrentSticker, - onSend: (_, s) => { !s && closePopout(); }, - isHovered: currentSticker?.id === stickers[2].id - } : undefined - } - /> - )); - - return ( -
-
-
-
-
- - } - title={RECENT_STICKERS_TITLE} - isSelected={RECENT_STICKERS_ID === selectedStickerPackId} - beforeScroll={() => { - scrollerRef.current?.scrollTo({ - top: 0, - }); - }} - afterScroll={() => { setSelectedStickerPackId(null); }} - > - { - ...stickersToRows( - queryFilter(recentStickers) - ) - } - - { - stickerPacks.map(sp => { - const rows = stickersToRows(queryFilter(sp.stickers)); - return ( - { - scrollerRef.current?.scrollTo({ - top: 0, - }); - }} - afterScroll={() => { setSelectedStickerPackId(null); }} - > - {...rows} - - ); - }) - } -
-
-
-
-
- -
-
{currentSticker?.title ?? ""}
-
- {currentStickerPack?.title ? "from " : ""} - {currentStickerPack?.title ?? ""} -
-
- -
-
-
- ); -} diff --git a/src/equicordplugins/moreStickers/components/PickerHeader.tsx b/src/equicordplugins/moreStickers/components/PickerHeader.tsx deleted file mode 100644 index ce8e1ede..00000000 --- a/src/equicordplugins/moreStickers/components/PickerHeader.tsx +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2023 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 { debounce } from "@shared/debounce"; -import { React, TextInput } from "@webpack/common"; - -import { Header } from "./header"; -import { IconContainer } from "./iconContainer"; -import { CancelIcon, SearchIcon } from "./icons"; -import { clPicker } from "../utils"; - -export interface PickerHeaderProps { - onQueryChange: (query: string) => void; -} - -const debounceQueryChange = debounce((cb: Function, ...args: any) => cb(...args), 150); - -export const PickerHeader = ({ onQueryChange }: PickerHeaderProps) => { - const [query, setQuery] = React.useState(); - - const setQueryDebounced = (value: string, immediate = false) => { - setQuery(value); - if (immediate) onQueryChange(value); - else debounceQueryChange(onQueryChange, value); - }; - - return ( -
-
-
-
- setQueryDebounced(value)} - /> -
-
- - { - (query && query.length > 0) ? - setQueryDebounced("", true)} /> : - - } - -
-
-
-
- ); -}; diff --git a/src/equicordplugins/moreStickers/components/categoryImage.tsx b/src/equicordplugins/moreStickers/components/categoryImage.tsx deleted file mode 100644 index 00a996b3..00000000 --- a/src/equicordplugins/moreStickers/components/categoryImage.tsx +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2023 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 { cl } from "../utils"; - -export interface CategoryImageProps { - src: string; - alt?: string; - isActive?: boolean; -} - -export function CategoryImage({ src, alt, isActive }: CategoryImageProps) { - return ( -
- - - {alt} - - -
- ); -} diff --git a/src/equicordplugins/moreStickers/components/categoryScroller.tsx b/src/equicordplugins/moreStickers/components/categoryScroller.tsx deleted file mode 100644 index c7d77601..00000000 --- a/src/equicordplugins/moreStickers/components/categoryScroller.tsx +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2023 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 { cl } from "../utils"; - -export function CategoryScroller(props: { children: React.ReactNode, categoryLength: number; }) { - const children = Array.isArray(props.children) ? props.children : [props.children]; - - return ( -
-
{ - children.map(child => ( -
- {child} -
- )) - }
-
- -
- ); -} diff --git a/src/equicordplugins/moreStickers/components/categoryWrapper.tsx b/src/equicordplugins/moreStickers/components/categoryWrapper.tsx deleted file mode 100644 index fd6a2559..00000000 --- a/src/equicordplugins/moreStickers/components/categoryWrapper.tsx +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2023 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 { cl } from "../utils"; - -export function CategoryWrapper(props: { children: JSX.Element | JSX.Element[]; }) { - return ( -
- {props.children} -
- ); -} diff --git a/src/equicordplugins/moreStickers/components/header.tsx b/src/equicordplugins/moreStickers/components/header.tsx deleted file mode 100644 index f9257834..00000000 --- a/src/equicordplugins/moreStickers/components/header.tsx +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2023 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 { cl } from "../utils"; - -export function Header(props: { children: JSX.Element | JSX.Element[]; }) { - return ( -
- {props.children} -
- ); -} diff --git a/src/equicordplugins/moreStickers/components/iconContainer.tsx b/src/equicordplugins/moreStickers/components/iconContainer.tsx deleted file mode 100644 index 3bd8e6d6..00000000 --- a/src/equicordplugins/moreStickers/components/iconContainer.tsx +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2023 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 . -*/ - -export function IconContainer(props: { children: JSX.Element | JSX.Element[]; }) { - return ( -
- {props.children} -
- ); -} diff --git a/src/equicordplugins/moreStickers/components/recent.ts b/src/equicordplugins/moreStickers/components/recent.ts deleted file mode 100644 index 012c4287..00000000 --- a/src/equicordplugins/moreStickers/components/recent.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2023 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 * as DataStore from "@api/DataStore"; - -import { Sticker } from "../types"; -import { Mutex } from "../utils"; - -const mutex = new Mutex(); - -// The ID of recent sticker and recent sticker pack -export const RECENT_STICKERS_ID = "recent"; -export const RECENT_STICKERS_TITLE = "Recently Used"; - -const KEY = "Vencord-MoreStickers-RecentStickers"; - -export async function getRecentStickers(): Promise { - return (await DataStore.get(KEY)) ?? []; -} - -export async function setRecentStickers(stickers: Sticker[]): Promise { - const unlock = await mutex.lock(); - try { - await DataStore.set(KEY, stickers); - } finally { - unlock(); - } -} - -export async function addRecentSticker(sticker: Sticker): Promise { - const stickers = await getRecentStickers(); - const index = stickers.findIndex(s => s.id === sticker.id); - if (index !== -1) { - stickers.splice(index, 1); - } - stickers.unshift(sticker); - while (stickers.length > 16) { - stickers.pop(); - } - await setRecentStickers(stickers); -} - -export async function removeRecentStickerByPackId(packId: string): Promise { - const stickers = await getRecentStickers(); - await setRecentStickers(stickers.filter(s => s.stickerPackId !== packId)); -} diff --git a/src/equicordplugins/moreStickers/components/settings.tsx b/src/equicordplugins/moreStickers/components/settings.tsx deleted file mode 100644 index 4773c659..00000000 --- a/src/equicordplugins/moreStickers/components/settings.tsx +++ /dev/null @@ -1,397 +0,0 @@ -/* - * Vencord, a modification for Discord's desktop app - * Copyright (c) 2023 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 { CheckedTextInput } from "@components/CheckedTextInput"; -import { Flex } from "@components/Flex"; -import { Button, Forms, React, TabBar, Text, TextArea, Toasts } from "@webpack/common"; - -import { convert as convertLineSP, getIdFromUrl as getLineStickerPackIdFromUrl, getStickerPackById as getLineStickerPackById, parseHtml as getLineSPFromHtml, isLineStickerPackHtml } from "../lineStickers"; -import { convert as convertLineEP, getIdFromUrl as getLineEmojiPackIdFromUrl, getStickerPackById as getLineEmojiPackById, parseHtml as getLineEPFromHtml, isLineEmojiPackHtml } from "../lineEmojis"; -import { deleteStickerPack, getStickerPackMetas, saveStickerPack } from "../stickers"; -import { StickerPack, StickerPackMeta } from "../types"; -import { cl, clPicker } from "../utils"; - -enum SettingsTabsKey { - ADD_STICKER_PACK_URL = "Add from URL", - ADD_STICKER_PACK_HTML = "Add from HTML", - ADD_STICKER_PACK_FILE = "Add from File", -} - -const noDrag = { - onMouseDown: e => { e.preventDefault(); return false; }, - onDragStart: e => { e.preventDefault(); return false; } -}; - -const StickerPackMetadata = ({ meta, hoveredStickerPackId, setHoveredStickerPackId, refreshStickerPackMetas }: - { meta: StickerPackMeta, [key: string]: any; } -) => { - return ( -
setHoveredStickerPackId(meta.id)} - onMouseLeave={() => setHoveredStickerPackId(null)} - > -
- - - {meta.title} -
- ); -}; - -export const Settings = () => { - const [stickerPackMetas, setstickerPackMetas] = React.useState([]); - const [addStickerUrl, setAddStickerUrl] = React.useState(""); - const [addStickerHtml, setAddStickerHtml] = React.useState(""); - const [tab, setTab] = React.useState(SettingsTabsKey.ADD_STICKER_PACK_URL); - const [hoveredStickerPackId, setHoveredStickerPackId] = React.useState(null); - - async function refreshStickerPackMetas() { - setstickerPackMetas(await getStickerPackMetas()); - } - React.useEffect(() => { - refreshStickerPackMetas(); - }, []); - - return ( -
- - { - Object.values(SettingsTabsKey).map(k => ( - - {k} - - )) - } - - - {tab === SettingsTabsKey.ADD_STICKER_PACK_URL && -
- Add Sticker Pack from URL - -

- Currently LINE stickers supported only.
- Telegram stickers support is planned, but due to the lack of a public API, it is most likely to be provided by sticker pack files instead of adding by URL. -

-
- - - { - try { - getLineStickerPackIdFromUrl(v); - return true; - } catch (e: any) { } - try { - getLineEmojiPackIdFromUrl(v); - return true; - } catch (e: any) { } - - return "Invalid URL"; - }} - placeholder="Sticker Pack URL" - /> - - - -
- } - {tab === SettingsTabsKey.ADD_STICKER_PACK_HTML && -
- Add Sticker Pack from HTML - -

- When encountering errors while adding a sticker pack, you can try to add it using the HTML source code of the sticker pack page.
- This applies to stickers which are region locked / OS locked / etc.
- The region LINE recognized may vary from the region you are in due to the CORS proxy we're using. -

-
- - -