Plugin
MessageClickActions
Hold Backspace and click to delete, double click to edit/reply
1
/*2
* FiveCord — a Discord client mod3
* Copyright (c) 2025 FiveCord4
* SPDX-License-Identifier: GPL-3.0-or-later5
*/6
7
import { isPluginEnabled } from "@api/PluginManager";8
import { definePluginSettings } from "@api/Settings";9
import NoReplyMentionPlugin from "@plugins/noReplyMention";10
import { Devs } from "@utils/constants";11
import definePlugin, { OptionType } from "@utils/types";12
import { ApplicationIntegrationType, MessageFlags } from "@vencord/discord-types/enums";13
import { findByPropsLazy } from "@webpack";14
import { AuthenticationStore, FluxDispatcher, MessageTypeSets, PermissionsBits, PermissionStore, WindowStore } from "@webpack/common";15
16
const MessageActions = findByPropsLazy("deleteMessage", "startEditMessage");17
const EditStore = findByPropsLazy("isEditing", "isEditingAny");18
19
let isDeletePressed = false;20
const keydown = (e: KeyboardEvent) => e.key === "Backspace" && (isDeletePressed = true);21
const keyup = (e: KeyboardEvent) => e.key === "Backspace" && (isDeletePressed = false);22
const focusChanged = () => !WindowStore.isFocused() && (isDeletePressed = false);23
24
const settings = definePluginSettings({25
enableDeleteOnClick: {26
type: OptionType.BOOLEAN,27
description: "Enable delete on click while holding backspace",28
default: true29
},30
enableDoubleClickToEdit: {31
type: OptionType.BOOLEAN,32
description: "Enable double click to edit",33
default: true34
},35
enableDoubleClickToReply: {36
type: OptionType.BOOLEAN,37
description: "Enable double click to reply",38
default: true39
},40
requireModifier: {41
type: OptionType.BOOLEAN,42
description: "Only do double click actions when shift/ctrl is held",43
default: false44
}45
});46
47
export 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 !== null98
});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: true107
});108
} else {109
MessageActions.deleteMessage(channel.id, msg.id);110
}111
event.preventDefault();112
}113
},114
});115