diff --git a/.gitignore b/.gitignore
index f24a7218..7bd751cb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,6 @@ lerna-debug.log*
*.tsbuildinfo
src/userplugins
+
+ExtensionCache/
+settings/
diff --git a/src/api/MessagePopover.ts b/src/api/MessagePopover.ts
new file mode 100644
index 00000000..85dff9cf
--- /dev/null
+++ b/src/api/MessagePopover.ts
@@ -0,0 +1,69 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2022 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 Logger from "@utils/Logger";
+import { Channel, Message } from "discord-types/general";
+import type { MouseEventHandler } from "react";
+
+const logger = new Logger("MessagePopover");
+
+export interface ButtonItem {
+ key?: string,
+ label: string,
+ icon: React.ComponentType,
+ message: Message,
+ channel: Channel,
+ onClick?: MouseEventHandler,
+ onContextMenu?: MouseEventHandler;
+}
+
+export type getButtonItem = (message: Message) => ButtonItem | null;
+
+export const buttons = new Map();
+
+export function addButton(
+ identifier: string,
+ item: getButtonItem,
+) {
+ buttons.set(identifier, item);
+}
+
+export function removeButton(identifier: string) {
+ buttons.delete(identifier);
+}
+
+export function _buildPopoverElements(
+ msg: Message,
+ makeButton: (item: ButtonItem) => React.ComponentType
+) {
+ const items = [] as React.ComponentType[];
+
+ for (const [identifier, getItem] of buttons.entries()) {
+ try {
+ const item = getItem(msg);
+ if (item) {
+ item.key ??= identifier;
+ items.push(makeButton(item));
+ }
+ } catch (err) {
+ logger.error(`[${identifier}]`, err);
+ }
+ }
+
+ return items;
+}
diff --git a/src/api/index.ts b/src/api/index.ts
index 98fc6a4a..b74da6e3 100644
--- a/src/api/index.ts
+++ b/src/api/index.ts
@@ -21,6 +21,7 @@ import * as $Commands from "./Commands";
import * as $DataStore from "./DataStore";
import * as $MessageAccessories from "./MessageAccessories";
import * as $MessageEventsAPI from "./MessageEvents";
+import * as $MessagePopover from "./MessagePopover";
import * as $Notices from "./Notices";
import * as $ServerList from "./ServerList";
@@ -59,6 +60,10 @@ const DataStore = $DataStore;
* An API allowing you to add custom components as message accessories
*/
const MessageAccessories = $MessageAccessories;
+/**
+ * An API allowing you to add custom buttons in the message popover
+ */
+const MessagePopover = $MessagePopover;
/**
* An API allowing you to add badges to user profiles
*/
@@ -68,4 +73,4 @@ const Badges = $Badges;
*/
const ServerList = $ServerList;
-export { Badges, Commands, DataStore, MessageAccessories, MessageEvents, Notices, ServerList };
+export { Badges, Commands, DataStore, MessageAccessories, MessageEvents, MessagePopover, Notices, ServerList };
diff --git a/src/plugins/apiMessagePopover.ts b/src/plugins/apiMessagePopover.ts
new file mode 100644
index 00000000..95814e05
--- /dev/null
+++ b/src/plugins/apiMessagePopover.ts
@@ -0,0 +1,33 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2022 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 { Devs } from "@utils/constants";
+import definePlugin from "@utils/types";
+
+export default definePlugin({
+ name: "MessagePopoverAPI",
+ description: "API to add buttons to message popovers.",
+ authors: [Devs.KingFish],
+ patches: [{
+ find: "Messages.MESSAGE_UTILITIES_A11Y_LABEL",
+ replacement: {
+ match: /(message:(.).{0,100}Fragment,\{children:\[)(.{0,90}renderPopout:.{0,200}message_reaction_emoji_picker.+?return (.{1,3})\(.{0,30}"add-reaction")/,
+ replace: "$1...Vencord.Api.MessagePopover._buildPopoverElements($2,$4),$3"
+ }
+ }],
+});
diff --git a/src/plugins/HideAttachments.tsx b/src/plugins/hideAttachments.tsx
similarity index 83%
rename from src/plugins/HideAttachments.tsx
rename to src/plugins/hideAttachments.tsx
index 2c1a0d4d..944da653 100644
--- a/src/plugins/HideAttachments.tsx
+++ b/src/plugins/hideAttachments.tsx
@@ -17,11 +17,10 @@
*/
import { get, set } from "@api/DataStore";
+import { addButton, removeButton } from "@api/MessagePopover";
import { Devs } from "@utils/constants";
-import Logger from "@utils/Logger";
import definePlugin from "@utils/types";
import { ChannelStore, FluxDispatcher } from "@webpack/common";
-import { Message } from "discord-types/general";
let style: HTMLStyleElement;
@@ -49,13 +48,7 @@ export default definePlugin({
name: "HideAttachments",
description: "Hide attachments and Embeds for individual messages via hover button",
authors: [Devs.Ven],
- patches: [{
- find: "Messages.MESSAGE_UTILITIES_A11Y_LABEL",
- replacement: {
- match: /(message:(.).{0,100}Fragment,\{children:\[)(.{0,40}renderPopout:.{0,200}message_reaction_emoji_picker.+?return (.{1,3})\(.{0,30}"add-reaction")/,
- replace: "$1Vencord.Plugins.plugins.HideAttachments.renderButton($2, $4),$3"
- }
- }],
+ dependencies: ["MessagePopoverAPI"],
async start() {
style = document.createElement("style");
@@ -64,11 +57,26 @@ export default definePlugin({
await getHiddenMessages();
await this.buildCss();
+
+ addButton("HideAttachments", msg => {
+ if (!msg.attachments.length && !msg.embeds.length) return null;
+
+ const isHidden = hiddenMessages.has(msg.id);
+
+ return {
+ label: isHidden ? "Show Attachments" : "Hide Attachments",
+ icon: isHidden ? ImageVisible : ImageInvisible,
+ message: msg,
+ channel: ChannelStore.getChannel(msg.channel_id),
+ onClick: () => this.toggleHide(msg.id)
+ };
+ });
},
stop() {
style.remove();
hiddenMessages.clear();
+ removeButton("HideAttachments");
},
async buildCss() {
@@ -86,26 +94,6 @@ export default definePlugin({
`;
},
- renderButton(msg: Message, makeItem: (data: any) => React.ComponentType) {
- try {
- if (!msg.attachments.length && !msg.embeds.length) return null;
-
- const isHidden = hiddenMessages.has(msg.id);
-
- return makeItem({
- key: "HideAttachments",
- label: isHidden ? "Show Attachments" : "Hide Attachments",
- icon: isHidden ? ImageVisible : ImageInvisible,
- message: msg,
- channel: ChannelStore.getChannel(msg.channel_id),
- onClick: () => this.toggleHide(msg.id)
- });
- } catch (err) {
- new Logger("HideAttachments").error(err);
- return null;
- }
- },
-
async toggleHide(id: string) {
const ids = await getHiddenMessages();
if (!ids.delete(id))
diff --git a/src/plugins/quickMention.tsx b/src/plugins/quickMention.tsx
index 1c0a6c6c..6e00dd0c 100644
--- a/src/plugins/quickMention.tsx
+++ b/src/plugins/quickMention.tsx
@@ -16,9 +16,11 @@
* along with this program. If not, see .
*/
+import { addButton, removeButton } from "@api/MessagePopover";
import { Devs } from "@utils/constants";
import definePlugin from "@utils/types";
import { findLazy } from "@webpack";
+import { ChannelStore } from "@webpack/common";
const ComponentDispatch = findLazy(m => m.emitter?._events?.INSERT_TEXT);
@@ -26,29 +28,22 @@ export default definePlugin({
name: "QuickMention",
authors: [Devs.kemo],
description: "Adds a quick mention button to the message actions bar",
+ dependencies: ["MessagePopoverAPI"],
- patches: [
- {
- find: "Messages.MESSAGE_UTILITIES_A11Y_LABEL",
- replacement: {
- match: /(null,)(.{1,3}&&!.{1,3}\?(.{1,3})\(\{key:"reply",label:.{1,10}\.Messages\.MESSAGE_ACTION_REPLY,icon:.{1,10},channel:(.+?),message:(.+?),onClick:.+?\}\))/,
- replace: (m, post, og, functionName, channelVar, messageVar) => {
-
- const functionSig =
- `${functionName}({
- key: "QuickMention",
- label: "Mention",
- icon: Vencord.Plugins.plugins.QuickMention.Icon,
- channel: ${channelVar},
- message: ${messageVar},
- onClick: ()=> Vencord.Plugins.plugins.QuickMention.onClick(${messageVar})
- })`;
-
- return `${post}${functionSig},${og}`;
- }
- }
- }
- ],
+ start() {
+ addButton("QuickMention", msg => {
+ return {
+ label: "Quick Mention",
+ icon: this.Icon,
+ message: msg,
+ channel: ChannelStore.getChannel(msg.channel_id),
+ onClick: () => ComponentDispatch.dispatchToLastSubscribed("INSERT_TEXT", { rawText: `<@${msg.author.id}> ` })
+ };
+ });
+ },
+ stop() {
+ removeButton("QuickMention");
+ },
Icon: () => (
),
-
- onClick: (message: any) => ComponentDispatch.dispatchToLastSubscribed("INSERT_TEXT", { rawText: `<@${message.author.id}> ` })
});
diff --git a/src/plugins/viewRaw.tsx b/src/plugins/viewRaw.tsx
new file mode 100644
index 00000000..c49180b8
--- /dev/null
+++ b/src/plugins/viewRaw.tsx
@@ -0,0 +1,147 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2022 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 { addButton, removeButton } from "@api/MessagePopover";
+import ErrorBoundary from "@components/ErrorBoundary";
+import { Flex } from "@components/Flex";
+import { Devs } from "@utils/constants";
+import { copyWithToast } from "@utils/misc";
+import { closeModal, ModalCloseButton, ModalContent, ModalHeader, ModalRoot, ModalSize, openModal } from "@utils/modal";
+import definePlugin from "@utils/types";
+import { Button, ChannelStore, Forms, Margins, Parser } from "@webpack/common";
+import { Message } from "discord-types/general";
+
+
+const CopyIcon = () => {
+ return ;
+};
+
+function sortObject(obj: T): T {
+ return Object.fromEntries(Object.entries(obj).sort(([k1], [k2]) => k1.localeCompare(k2))) as T;
+}
+
+function cleanMessage(msg: Message) {
+ const clone = sortObject(JSON.parse(JSON.stringify(msg)));
+ for (const key in clone.author) {
+ switch (key) {
+ case "id":
+ case "username":
+ case "usernameNormalized":
+ case "discriminator":
+ case "avatar":
+ case "bot":
+ case "system":
+ case "publicFlags":
+ break;
+ default:
+ // phone number, email, etc
+ delete clone.author[key];
+ }
+ }
+
+ // message logger added properties
+ const cloneAny = clone as any;
+ delete cloneAny.editHistory;
+ delete cloneAny.deleted;
+ cloneAny.attachments?.forEach(a => delete a.deleted);
+
+ return clone;
+}
+
+function CodeBlock(props: { content: string, lang: string; }) {
+ return (
+ // make text selectable
+