Plugin

MessageClickActions

Hold Backspace and click to delete, double click to edit/reply

Chat Shortcuts
index.ts
Download

Source

src/plugins/messageClickActions/index.ts
1/*
2 * FiveCord — a Discord client mod
3 * Copyright (c) 2025 FiveCord
4 * SPDX-License-Identifier: GPL-3.0-or-later
5 */
6
7import { isPluginEnabled } from "@api/PluginManager";
8import { definePluginSettings } from "@api/Settings";
9import NoReplyMentionPlugin from "@plugins/noReplyMention";
10import { Devs } from "@utils/constants";
11import definePlugin, { OptionType } from "@utils/types";
12import { ApplicationIntegrationType, MessageFlags } from "@vencord/discord-types/enums";
13import { findByPropsLazy } from "@webpack";
14import { AuthenticationStore, FluxDispatcher, MessageTypeSets, PermissionsBits, PermissionStore, WindowStore } from "@webpack/common";
15
16const MessageActions = findByPropsLazy("deleteMessage", "startEditMessage");
17const EditStore = findByPropsLazy("isEditing", "isEditingAny");
18
19let isDeletePressed = false;
20const keydown = (e: KeyboardEvent) => e.key === "Backspace" && (isDeletePressed = true);
21const keyup = (e: KeyboardEvent) => e.key === "Backspace" && (isDeletePressed = false);
22const focusChanged = () => !WindowStore.isFocused() && (isDeletePressed = false);
23
24const settings = definePluginSettings({
25 enableDeleteOnClick: {
26 type: OptionType.BOOLEAN,
27 description: "Enable delete on click while holding backspace",
28 default: true
29 },
30 enableDoubleClickToEdit: {
31 type: OptionType.BOOLEAN,
32 description: "Enable double click to edit",
33 default: true
34 },
35 enableDoubleClickToReply: {
36 type: OptionType.BOOLEAN,
37 description: "Enable double click to reply",
38 default: true
39 },
40 requireModifier: {
41 type: OptionType.BOOLEAN,
42 description: "Only do double click actions when shift/ctrl is held",
43 default: false
44 }
45});
46
47export default definePlugin({
48 name: "MessageClickActions",
49 description: "Hold Backspace and click to delete, double click to edit/reply",
50 tags: ["Chat", "Shortcuts"],
51 authors: [Devs.Ven],
52
53 settings,
54
55 start() {
56 document.addEventListener("keydown", keydown);
57 document.addEventListener("keyup", keyup);
58 WindowStore.addChangeListener(focusChanged);
59 },
60
61 stop() {
62 document.removeEventListener("keydown", keydown);
63 document.removeEventListener("keyup", keyup);
64 WindowStore.removeChangeListener(focusChanged);
65 },
66
67 onMessageClick(msg, channel, event) {
68 const myId = AuthenticationStore.getId();
69 const isMe = msg.author.id === myId;
70 const isSelfInvokedUserApp = msg.interactionMetadata?.authorizing_integration_owners[ApplicationIntegrationType.USER_INSTALL] === myId;
71 if (!isDeletePressed) {
72 if (event.detail < 2) return;
73 if (settings.store.requireModifier && !event.ctrlKey && !event.shiftKey) return;
74 if (channel.guild_id && !PermissionStore.can(PermissionsBits.SEND_MESSAGES, channel)) return;
75 if (msg.deleted === true) return;
76
77 if (isMe) {
78 if (!settings.store.enableDoubleClickToEdit || EditStore.isEditing(channel.id, msg.id) || msg.state !== "SENT") return;
79
80 MessageActions.startEditMessage(channel.id, msg.id, msg.content);
81 event.preventDefault();
82 } else {
83 if (!settings.store.enableDoubleClickToReply) return;
84
85 if (!MessageTypeSets.REPLYABLE.has(msg.type) || msg.hasFlag(MessageFlags.EPHEMERAL)) return;
86
87 const isShiftPress = event.shiftKey && !settings.store.requireModifier;
88 const shouldMention = isPluginEnabled(NoReplyMentionPlugin.name)
89 ? NoReplyMentionPlugin.shouldMention(msg, isShiftPress)
90 : !isShiftPress;
91
92 FluxDispatcher.dispatch({
93 type: "CREATE_PENDING_REPLY",
94 channel,
95 message: msg,
96 shouldMention,
97 showMentionToggle: channel.guild_id !== null
98 });
99 }
100 } else if (settings.store.enableDeleteOnClick && (isMe || PermissionStore.can(PermissionsBits.MANAGE_MESSAGES, channel) || isSelfInvokedUserApp)) {
101 if (msg.deleted) {
102 FluxDispatcher.dispatch({
103 type: "MESSAGE_DELETE",
104 channelId: channel.id,
105 id: msg.id,
106 mlDeleted: true
107 });
108 } else {
109 MessageActions.deleteMessage(channel.id, msg.id);
110 }
111 event.preventDefault();
112 }
113 },
114});
115