Plugin

HideMessage

Adds a context menu option to hide messages

index.tsx
Download

Source

src/plugins/hideMessage/index.tsx
1/*
2 * FiveCord — a Discord client mod
3 * Copyright (c) 2025 FiveCord
4 * SPDX-License-Identifier: GPL-3.0-or-later
5 */
6
7import "./styles.css";
8
9import { NavContextMenuPatchCallback } from "@api/ContextMenu";
10import { get, set } from "@api/DataStore";
11import { addAccessory, removeAccessory } from "@api/MessageAccessories";
12import { definePluginSettings } from "@api/Settings";
13import { classNameFactory } from "@api/Styles";
14import { Devs } from "@utils/constants";
15import definePlugin, { OptionType } from "@utils/types";
16import { Menu } from "@webpack/common";
17
18import { EyeIcon } from "./EyeIcon";
19import { HideIcon } from "./HideIcon";
20import { HideMessageAccessory } from "./HideMessageAccessory";
21
22let style: HTMLStyleElement;
23
24const KEY = "HideMessage_hiddenMessages";
25
26let hiddenMessages = new Map<string, {
27 id: string;
28 channel_id: string;
29}>();
30
31const patchMessageContextMenu: NavContextMenuPatchCallback = (children, { message }) => {
32 const { deleted, id, channel_id } = message;
33 if (deleted || message.state !== "SENT") return;
34
35 const isHidden = hiddenMessages.has(id);
36 if (isHidden) {
37 return children.push(
38 <Menu.MenuItem
39 id={cl("reveal")}
40 label="Reveal Message"
41 icon={EyeIcon}
42 action={() => revealMessage(id)}
43 />
44 );
45 }
46
47 children.push(<Menu.MenuItem
48 id={cl("hide")}
49 label="Hide Message"
50 color="danger"
51 icon={HideIcon}
52 action={() => {
53 hiddenMessages.set(id, { id, channel_id });
54 if (settings.store.saveHiddenMessages) set(KEY, hiddenMessages);
55
56 buildCss();
57 }}
58 />);
59};
60
61const buildCss = () => {
62 const elements = [...hiddenMessages.values()].map(m => `#chat-messages-${m.channel_id}-${m.id}`).join(",");
63
64 style.textContent = settings.store.showNotice ? `
65 :is(${elements}):not(.messagelogger-deleted) > div {
66 position: relative;
67 background: var(--brand-experiment-05a);
68 }
69 :is(${elements}):not(.messagelogger-deleted) > div:hover {
70 background: var(--brand-experiment-10a);
71 }
72 :is(${elements}):not(.messagelogger-deleted) > div:before {
73 background: var(--brand-experiment);
74 content: "";
75 position: absolute;
76 display: block;
77 top: 0;
78 left: 0;
79 bottom: 0;
80 pointer-events: none;
81 width: 2px;
82 }
83 :is(${elements}) [id^=&#039;message-accessories&#039;] > *:not(.vc-hide-message-accessory),
84 :is(${elements}) [id^=&#039;message-content&#039;] > * {
85 display: none !important;
86 }
87 :is(${elements}) [id^=&#039;message-content&#039;]:empty {
88 display: block !important;
89 }
90 :is(${elements}) [class^=&#039;contents&#039;] [id^=&#039;message-content&#039;]:after {
91 content: "Hidden content";
92 }
93 ` : `
94 :is(${elements}) {
95 display: none !important;
96 }
97 `;
98};
99
100export const revealMessage = (id: string) => {
101 if (hiddenMessages.has(id)) {
102 hiddenMessages.delete(id);
103 buildCss();
104
105 if (settings.store.saveHiddenMessages) set(KEY, hiddenMessages);
106 }
107};
108
109export const cl = classNameFactory("vc-hide-message-");
110
111export const settings = definePluginSettings({
112 showNotice: {
113 type: OptionType.BOOLEAN,
114 description: "Shows a notice when a message is hidden",
115 default: true,
116 onChange: buildCss
117 },
118 saveHiddenMessages: {
119 type: OptionType.BOOLEAN,
120 description: "Persist restarts",
121 default: false,
122 onChange: async (value: boolean) => {
123 if (value) set(KEY, hiddenMessages);
124 else (hiddenMessages = await get(KEY) || hiddenMessages);
125 }
126 },
127});
128
129export default definePlugin({
130 name: "HideMessage",
131 description: "Adds a context menu option to hide messages",
132 authors: [Devs.FiveCord],
133 settings,
134
135 contextMenus: {
136 "message": patchMessageContextMenu
137 },
138
139 async start() {
140 style = document.createElement("style");
141 style.id = "VencordHideMessage";
142 document.head.appendChild(style);
143
144 if (settings.store.saveHiddenMessages) {
145 hiddenMessages = await get(KEY) || hiddenMessages;
146 buildCss();
147 }
148
149 addAccessory("vc-hide-message", ({ message }) => {
150 if (hiddenMessages.has(message.id) && settings.store.showNotice) return <HideMessageAccessory id={message.id} />;
151 return null;
152 });
153 },
154
155 async stop() {
156 for (const id of hiddenMessages.keys()) revealMessage(id);
157
158 removeAccessory("vc-hide-message");
159
160 style.remove();
161 hiddenMessages.clear();
162 }
163});
164