From 262336f43643f4b4de6d1b0591b774e164f276da Mon Sep 17 00:00:00 2001 From: thororen1234 <78185467+thororen1234@users.noreply.github.com> Date: Fri, 26 Sep 2025 01:32:01 -0400 Subject: [PATCH] Quoter: Fix & Shrink + Remove Old Finds --- .../settings/tabs/plugins/index.tsx | 3 +- src/equicordplugins/orbolayBridge/index.tsx | 11 - src/equicordplugins/questify/index.tsx | 39 +-- src/equicordplugins/questify/settings.tsx | 2 - src/equicordplugins/quoter/index.tsx | 256 ++++++++---------- src/equicordplugins/quoter/utils.tsx | 87 ++---- src/equicordplugins/randomVoice/index.tsx | 2 +- src/equicordplugins/replyPingControl/index.ts | 2 +- src/equicordplugins/statsfmPresence/index.tsx | 3 - src/equicordplugins/tosuRPC/index.ts | 1 - src/plugins/lastfmRichPresence/index.tsx | 3 - src/plugins/messageLogger/index.tsx | 2 +- src/plugins/messageTags/index.ts | 1 - src/plugins/noBlockedMessages/index.ts | 2 +- src/plugins/pinDms/data.ts | 5 - .../components/HiddenChannelLockScreen.tsx | 1 - src/plugins/showMeYourName/index.tsx | 10 +- src/plugins/silentTyping/index.tsx | 22 +- src/plugins/userVoiceShow/components.tsx | 6 +- src/plugins/whoReacted/index.tsx | 2 - src/plugins/xsOverlay/index.tsx | 1 - 21 files changed, 161 insertions(+), 300 deletions(-) diff --git a/src/components/settings/tabs/plugins/index.tsx b/src/components/settings/tabs/plugins/index.tsx index 20a747505..02c152034 100644 --- a/src/components/settings/tabs/plugins/index.tsx +++ b/src/components/settings/tabs/plugins/index.tsx @@ -40,13 +40,12 @@ import { openWarningModal } from "./PluginModal"; import { StockPluginsCard, UserPluginsCard } from "./PluginStatCards"; // Avoid circular dependency -const { startDependenciesRecursive, startPlugin, stopPlugin } = proxyLazy(() => require("plugins")); +const { stopPlugin } = proxyLazy(() => require("plugins")); export const cl = classNameFactory("vc-plugins-"); export const logger = new Logger("PluginSettings", "#a6d189"); const InputStyles = findByPropsLazy("inputWrapper", "inputError", "error"); -const ButtonClasses = findByPropsLazy("button", "disabled", "enabled"); function showErrorToast(message: string) { Toasts.show({ diff --git a/src/equicordplugins/orbolayBridge/index.tsx b/src/equicordplugins/orbolayBridge/index.tsx index e94b6e95b..e2aaebb29 100644 --- a/src/equicordplugins/orbolayBridge/index.tsx +++ b/src/equicordplugins/orbolayBridge/index.tsx @@ -22,17 +22,6 @@ import definePlugin, { OptionType } from "@utils/types"; import { VoiceState } from "@vencord/discord-types"; import { ChannelStore, FluxDispatcher, GuildMemberStore, Toasts, UserStore, VoiceStateStore } from "@webpack/common"; -interface ChannelState { - userId: string; - channelId: string; - deaf: boolean; - mute: boolean; - stream: boolean; - selfDeaf: boolean; - selfMute: boolean; - selfStream: boolean; -} - const settings = definePluginSettings({ port: { type: OptionType.NUMBER, diff --git a/src/equicordplugins/questify/index.tsx b/src/equicordplugins/questify/index.tsx index ae0fe1cbf..5d7402e2f 100644 --- a/src/equicordplugins/questify/index.tsx +++ b/src/equicordplugins/questify/index.tsx @@ -178,12 +178,10 @@ function shouldHideBadgeOnUserProfiles(): boolean { function shouldHideQuestPopup(quest: Quest | null): boolean { const { disableQuestsPopupAboveAccountPanel, - disableQuestsEverything, - triggerQuestsRerender + disableQuestsEverything } = settings.use([ "disableQuestsPopupAboveAccountPanel", - "disableQuestsEverything", - "triggerQuestsRerender" + "disableQuestsEverything" ]); const noProgress = !quest?.userStatus?.progress || Object.keys(quest?.userStatus?.progress || {}).length === 0; @@ -251,16 +249,12 @@ function QuestTileContextMenu(children: React.ReactNode[], props: { quest: any; export function getQuestTileClasses(originalClasses: string, quest: Quest, color: number | null | undefined, gradient: string | undefined): string { const { - ignoredQuestIDs, - ignoredQuestProfile, restyleQuestsUnclaimed, restyleQuestsClaimed, restyleQuestsIgnored, restyleQuestsExpired, restyleQuestsGradient } = settings.use([ - "ignoredQuestIDs", - "ignoredQuestProfile", "restyleQuestsUnclaimed", "restyleQuestsClaimed", "restyleQuestsIgnored", @@ -326,7 +320,7 @@ export function getQuestTileClasses(originalClasses: string, quest: Quest, color } function makeDesktopCompatible(quests: Quest[]): void { - const { makeMobileQuestsDesktopCompatible, triggerQuestsRerender } = settings.use(["makeMobileQuestsDesktopCompatible", "triggerQuestsRerender"]); + const { makeMobileQuestsDesktopCompatible } = settings.use(["makeMobileQuestsDesktopCompatible", "triggerQuestsRerender"]); if (makeMobileQuestsDesktopCompatible) { quests.forEach(quest => { @@ -358,27 +352,17 @@ function makeDesktopCompatible(quests: Quest[]): void { function sortQuests(quests: Quest[], skip?: boolean): Quest[] { const { - ignoredQuestIDs, - ignoredQuestProfile, reorderQuests, unclaimedSubsort, claimedSubsort, ignoredSubsort, - expiredSubsort, - completeVideoQuestsInBackground, - completeGameQuestsInBackground, - triggerQuestsRerender + expiredSubsort } = settings.use([ - "ignoredQuestIDs", - "ignoredQuestProfile", "reorderQuests", "unclaimedSubsort", "claimedSubsort", "ignoredSubsort", - "expiredSubsort", - "completeVideoQuestsInBackground", - "completeGameQuestsInBackground", - "triggerQuestsRerender" + "expiredSubsort" ]); makeDesktopCompatible(quests); @@ -468,16 +452,6 @@ function sortQuests(quests: Quest[], skip?: boolean): Quest[] { } export function getQuestTileStyle(quest: Quest | null): Record { - const { - restyleQuests, - ignoredQuestIDs, - ignoredQuestProfile - } = settings.use([ - "restyleQuests", - "ignoredQuestIDs", - "ignoredQuestProfile" - ]); - const style: Record = {}; let themeColor: RGB | null = null; @@ -801,7 +775,6 @@ function disguiseHomeButton(location: string): boolean { } function useQuestRerender(): number { - const { triggerQuestsRerender } = settings.use(["triggerQuestsRerender"]); const [renderTrigger, setRenderTrigger] = useState(0); useEffect(() => addRerenderCallback(() => setRenderTrigger(prev => prev + 1)), []); return renderTrigger; @@ -818,12 +791,10 @@ function getLastFilterChoices(): { group: string; filter: string; }[] | null { } function setLastSortChoice(sort: string): void { - const { rememberQuestPageFilters } = settings.use(["rememberQuestPageFilters"]); settings.store.lastQuestPageSort = sort; } function setLastFilterChoices(filters: { group: string; filter: string; }[]): void { - const { rememberQuestPageFilters } = settings.use(["rememberQuestPageFilters"]); if (!filters || !Object.keys(filters).length || !Object.values(filters).every(f => f.group && f.filter)) { return; } settings.store.lastQuestPageFilters = JSON.parse(JSON.stringify(filters)).reduce((acc, item) => ({ ...acc, [item.filter]: item }), {}); } diff --git a/src/equicordplugins/questify/settings.tsx b/src/equicordplugins/questify/settings.tsx index c6c4321a1..bbc01ee62 100644 --- a/src/equicordplugins/questify/settings.tsx +++ b/src/equicordplugins/questify/settings.tsx @@ -364,7 +364,6 @@ function validateQuestButtonSetting() { function validateDisableQuestSetting() { const { - disableQuestsEverything, disableQuestsDiscoveryTab, disableQuestsFetchingQuests, disableQuestsPopupAboveAccountPanel, @@ -372,7 +371,6 @@ function validateDisableQuestSetting() { disableQuestsGiftInventoryRelocationNotice, disableFriendsListActiveNowPromotion } = settings.use([ - "disableQuestsEverything", "disableQuestsDiscoveryTab", "disableQuestsFetchingQuests", "disableQuestsPopupAboveAccountPanel", diff --git a/src/equicordplugins/quoter/index.tsx b/src/equicordplugins/quoter/index.tsx index 9e2feeba3..d82b9d04d 100644 --- a/src/equicordplugins/quoter/index.tsx +++ b/src/equicordplugins/quoter/index.tsx @@ -1,10 +1,10 @@ /* * Vencord, a Discord client mod - * Copyright (c) 2024 Vendicated and contributors + * Copyright (c) 2025 Vendicated and contributors * SPDX-License-Identifier: GPL-3.0-or-later */ -import { findGroupChildrenByChildId, NavContextMenuPatchCallback } from "@api/ContextMenu"; +import { findGroupChildrenByChildId } from "@api/ContextMenu"; import { definePluginSettings } from "@api/Settings"; import { Devs } from "@utils/constants"; import { getCurrentChannel } from "@utils/discord"; @@ -20,44 +20,28 @@ enum ImageStyle { inspirational } -const messagePatch: NavContextMenuPatchCallback = (children, { message }) => { - recentmessage = message; - if (!message.content) return; - - const buttonElement = - { - openModal(props => ); - }} - />; - - const group = findGroupChildrenByChildId("copy-text", children); - if (!group) { - children.push(buttonElement); - return; - } - - group.splice( - group.findIndex(c => c?.props?.id === "copy-text") + 1, 0, buttonElement - ); -}; - -let recentmessage: Message; -let grayscale; -let setStyle: ImageStyle = ImageStyle.inspirational; -let customMessage: string = ""; - enum userIDOptions { displayName, userName, userId } + +interface QuoteImageOptions { + avatarUrl: string; + quoteOld: string; + grayScale: boolean; + imageStyle: ImageStyle; + author: { + username: string; + globalName?: string; + id: string; + }; + userIdentifier: userIDOptions; +} + + const settings = definePluginSettings({ - userIdentifier: - { + userIdentifier: { type: OptionType.SELECT, description: "What the author's name should be displayed as", options: [ @@ -71,14 +55,28 @@ const settings = definePluginSettings({ export default definePlugin({ name: "Quoter", description: "Adds the ability to create an inspirational quote image from a message", - authors: [Devs.Samwich], + authors: [Devs.Samwich, Devs.thororen], + settings, contextMenus: { - "message": messagePatch - }, - settings + "message": (children, { message }) => { + if (!message.content) return; + const buttonElement = ( + openModal(props => )} + /> + ); + + const group = findGroupChildrenByChildId("copy-text", children); + if (!group) children.push(buttonElement); + else group.splice(group.findIndex(c => c?.props?.id === "copy-text") + 1, 0, buttonElement); + } + } }); -function sizeUpgrade(url) { +function sizeUpgrade(url: string) { const u = new URL(url); u.searchParams.set("size", "512"); return u.toString(); @@ -87,68 +85,49 @@ function sizeUpgrade(url) { const preparingSentence: string[] = []; const lines: string[] = []; -async function createQuoteImage(avatarUrl: string, quoteOld: string, grayScale: boolean): Promise { +async function createQuoteImage(options: QuoteImageOptions): Promise { + const { avatarUrl, quoteOld, grayScale, imageStyle, author, userIdentifier } = options; + const quote = FixUpQuote(quoteOld); const canvas = document.createElement("canvas"); const ctx = canvas.getContext("2d"); + if (!ctx) throw new Error("Cant get 2d rendering context :("); - if (!ctx) { - throw new Error("Cant get 2d rendering context :("); - } - - let name: string = ""; - - switch (settings.store.userIdentifier) { + let name = ""; + switch (userIdentifier) { case userIDOptions.displayName: - // @ts-ignore - const meow = recentmessage.author.globalName; - if (meow) { - name = meow; - } - else { - name = recentmessage.author.username; - } + name = author.globalName || author.username; break; case userIDOptions.userName: - name = recentmessage.author.username; + name = author.username; break; case userIDOptions.userId: - name = recentmessage.author.id; + name = author.id; break; default: - name = "MAN WTF HAPPENED"; - break; + name = "Unknown"; } - switch (setStyle) { + switch (imageStyle) { case ImageStyle.inspirational: - const cardWidth = 1200; const cardHeight = 600; - canvas.width = cardWidth; canvas.height = cardHeight; ctx.fillStyle = "#000"; - ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.fillRect(0, 0, cardWidth, cardHeight); const avatarBlob = await fetchImageAsBlob(avatarUrl); - const fadeBlob = await fetchImageAsBlob("https://raw.githubusercontent.com/Equicord/Equibored/refs/heads/main/icons/quoter/quoter.png"); + const fadeBlob = await fetchImageAsBlob("https://raw.githubusercontent.com/Equicord/Equibored/main/icons/quoter/quoter.png"); const avatar = new Image(); const fade = new Image(); - const avatarPromise = new Promise(resolve => { - avatar.onload = () => resolve(); - avatar.src = URL.createObjectURL(avatarBlob); - }); - - const fadePromise = new Promise(resolve => { - fade.onload = () => resolve(); - fade.src = URL.createObjectURL(fadeBlob); - }); - - await Promise.all([avatarPromise, fadePromise]); + await Promise.all([ + new Promise(resolve => { avatar.onload = () => resolve(); avatar.src = URL.createObjectURL(avatarBlob); }), + new Promise(resolve => { fade.onload = () => resolve(); fade.src = URL.createObjectURL(fadeBlob); }) + ]); ctx.drawImage(avatar, 0, 0, cardHeight, cardHeight); @@ -164,42 +143,69 @@ async function createQuoteImage(avatarUrl: string, quoteOld: string, grayScale: ctx.fillStyle = "#fff"; ctx.font = "italic 20px Georgia"; const quoteWidth = cardWidth / 2 - 50; - const quoteX = ((cardWidth - cardHeight)); + const quoteX = cardWidth - cardHeight; const quoteY = cardHeight / 2 - 10; wrapText(ctx, `"${quote}"`, quoteX, quoteY, quoteWidth, 20, preparingSentence, lines); const wrappedTextHeight = lines.length * 25; ctx.font = "bold 16px Georgia"; - const authorNameX = (cardHeight * 1.5) - (ctx.measureText(`- ${name}`).width / 2) - 30; + const authorNameX = cardHeight * 1.5 - ctx.measureText(`- ${name}`).width / 2 - 30; const authorNameY = quoteY + wrappedTextHeight + 30; - ctx.fillText(`- ${name}`, authorNameX, authorNameY); + preparingSentence.length = 0; lines.length = 0; + return await canvasToBlob(canvas); } } -function registerStyleChange(style) { - setStyle = style; - GeneratePreview(); +function generateFileNamePreview(message: string) { + const words = message.split(" "); + return words.length >= 6 ? words.slice(0, 6).join(" ") : words.join(" "); } -function QuoteModal(props: ModalProps) { +function QuoteModal({ message, ...props }: ModalProps & { message: Message; }) { const [gray, setGray] = useState(true); - useEffect(() => { - grayscale = gray; - GeneratePreview(); - }, [gray]); + const [style, setStyle] = useState(ImageStyle.inspirational); + const [quoteImage, setQuoteImage] = useState(null); + const { userIdentifier } = settings.store; + const safeContent = message.content ? message.content : ""; - const safeContent = recentmessage && recentmessage.content ? recentmessage.content : ""; + const generateImage = async () => { + const image = await createQuoteImage({ + avatarUrl: sizeUpgrade(message.author.getAvatarURL()), + quoteOld: safeContent, + grayScale: gray, + imageStyle: style, + author: message.author, + userIdentifier + }); + setQuoteImage(image); + document.getElementById("quoterPreview")?.setAttribute("src", URL.createObjectURL(image)); + }; - const [custom, setCustom] = useState(safeContent); - useEffect(() => { - customMessage = custom; - GeneratePreview(); - }, [custom]); + useEffect(() => { generateImage(); }, [gray, style, safeContent]); + + const Export = () => { + if (!quoteImage) return; + const link = document.createElement("a"); + const preview = generateFileNamePreview(safeContent); + link.href = URL.createObjectURL(quoteImage); + link.download = `${preview} - ${message.author.username}.png`; + link.click(); + link.remove(); + }; + + const SendInChat = () => { + if (!quoteImage) return; + const preview = generateFileNamePreview(safeContent); + const file = new File([quoteImage], `${preview} - ${message.author.username}.png`, { type: "image/png" }); + // @ts-expect-error typing issue + UploadHandler.promptToUpload([file], getCurrentChannel(), 0); + props.onClose?.(); + }; return ( @@ -210,59 +216,27 @@ function QuoteModal(props: ModalProps) { - -



+ +

+

Grayscale - +