From 7d228aeafcdb7e25527ea5421067e02f605f39dc Mon Sep 17 00:00:00 2001 From: Key Date: Mon, 19 Jan 2026 17:50:03 +0000 Subject: [PATCH] Message click actions (#655) * feat(MessageClickAction): add modifier configuration for individual actions * feat(MessageClickAction): add click to copy action * refactor(MessageClickActions): input agnostic actions - Also adds middle click as an input * refactor(MessageClickActions): clean up plugin settings * fix(MessageClickActions): header * feat(MessageClickActions): remove middleClickAction until I fix it * refactor(MessageClickActions): add back useful settings (doubleClickOthersAction, quote, pin, etc) * fix fallback for guild_id Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix fallback for guild_id Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix(MessageClickActions): add missing EDIT_REPLY case to executeAction * fix(MessageClickActions): resolve Copilot review comments (guild_id fallback, focusChanged, double-click condition, permissions, etc.) * add bro back (sorry) * sorry thororen-senpai --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/plugins/messageClickActions/index.ts | 532 ++++++++++++++--------- 1 file changed, 316 insertions(+), 216 deletions(-) diff --git a/src/plugins/messageClickActions/index.ts b/src/plugins/messageClickActions/index.ts index 6cd8f5237..6f52d1aae 100644 --- a/src/plugins/messageClickActions/index.ts +++ b/src/plugins/messageClickActions/index.ts @@ -11,118 +11,141 @@ import { Devs, EquicordDevs } from "@utils/constants"; import { copyWithToast, insertTextIntoChatInputBox } from "@utils/discord"; import { Logger } from "@utils/Logger"; import definePlugin, { OptionType } from "@utils/types"; -import { Channel, Message } from "@vencord/discord-types"; +import type { Message } from "@vencord/discord-types"; import { ApplicationIntegrationType, MessageFlags } from "@vencord/discord-types/enums"; -import { AuthenticationStore, Constants, EditMessageStore, FluxDispatcher, MessageActions, MessageTypeSets, PermissionsBits, PermissionStore, PinActions, RestAPI, Toasts, WindowStore } from "@webpack/common"; +import { AuthenticationStore, Constants, EditMessageStore, FluxDispatcher, MessageActions, MessageTypeSets, PermissionsBits, PermissionStore, PinActions, RestAPI, showToast, Toasts, WindowStore } from "@webpack/common"; -let isKeyPressed = false; +type Modifier = "NONE" | "SHIFT" | "CTRL" | "ALT" | "BACKSPACE"; +type ClickAction = "NONE" | "DELETE" | "COPY_LINK" | "COPY_ID" | "COPY_CONTENT" | "COPY_USER_ID" | "EDIT" | "REPLY" | "REACT" | "OPEN_THREAD" | "OPEN_TAB" | "EDIT_REPLY" | "QUOTE" | "PIN"; + +const actions: { label: string; value: ClickAction; }[] = [ + { label: "None", value: "NONE" }, + { label: "Delete", value: "DELETE" }, + { label: "Copy Link", value: "COPY_LINK" }, + { label: "Copy ID", value: "COPY_ID" }, + { label: "Copy Content", value: "COPY_CONTENT" }, + { label: "Copy User ID", value: "COPY_USER_ID" }, + { label: "Edit", value: "EDIT" }, + { label: "Reply", value: "REPLY" }, + { label: "React", value: "REACT" }, + { label: "Open Thread", value: "OPEN_THREAD" }, + { label: "Open Tab", value: "OPEN_TAB" } +]; + +const doubleClickOwnActions: { label: string; value: ClickAction; }[] = [ + { label: "None", value: "NONE" }, + { label: "Edit", value: "EDIT" }, + { label: "Quote", value: "QUOTE" }, + { label: "Copy Content", value: "COPY_CONTENT" }, + { label: "Copy Link", value: "COPY_LINK" }, + { label: "Copy ID", value: "COPY_ID" }, + { label: "Copy User ID", value: "COPY_USER_ID" }, + { label: "React", value: "REACT" }, + { label: "Pin", value: "PIN" } +]; + +const doubleClickOthersActions: { label: string; value: ClickAction; }[] = [ + { label: "None", value: "NONE" }, + { label: "Reply", value: "REPLY" }, + { label: "Quote", value: "QUOTE" }, + { label: "Copy Content", value: "COPY_CONTENT" }, + { label: "Copy Link", value: "COPY_LINK" }, + { label: "Copy ID", value: "COPY_ID" }, + { label: "Copy User ID", value: "COPY_USER_ID" }, + { label: "React", value: "REACT" }, + { label: "Pin", value: "PIN" } +]; + +const modifiers: { label: string; value: Modifier; }[] = [ + { label: "None", value: "NONE" }, + { label: "Shift", value: "SHIFT" }, + { label: "Ctrl", value: "CTRL" }, + { label: "Alt", value: "ALT" } +]; + +const singleClickModifiers: { label: string; value: Modifier; }[] = [ + { label: "Backspace", value: "BACKSPACE" }, + ...modifiers +]; + +const pressedModifiers = new Set(); const keydown = (e: KeyboardEvent) => { - const key = settings.store.keySelection === "backspace" ? "Backspace" : "Delete"; - if (e.key === key) { - isKeyPressed = true; - } + const mod = modifierFromKey(e); + if (mod) pressedModifiers.add(mod); + if (e.key === "Backspace") pressedModifiers.add("BACKSPACE"); }; const keyup = (e: KeyboardEvent) => { - const key = settings.store.keySelection === "backspace" ? "Backspace" : "Delete"; - if (e.key === key) { - isKeyPressed = false; + const mod = modifierFromKey(e); + if (mod) pressedModifiers.delete(mod); + if (e.key === "Backspace") pressedModifiers.delete("BACKSPACE"); +}; +const focusChanged = () => { + if (!WindowStore.isFocused()) { + pressedModifiers.clear(); } }; -const focusChanged = () => !WindowStore.isFocused() && (isKeyPressed = false); + +function modifierFromKey(e: KeyboardEvent): Modifier | null { + if (e.key === "Shift") return "SHIFT"; + if (e.key === "Control") return "CTRL"; + if (e.key === "Alt") return "ALT"; + return null; +} + +function isModifierPressed(modifier: Modifier): boolean { + return modifier === "NONE" || pressedModifiers.has(modifier); +} let doubleClickTimeout: ReturnType | null = null; let pendingDoubleClickAction: (() => void) | null = null; -enum ClickAction { - NONE = "none", - DELETE = "delete", - EDIT = "edit", - REPLY = "reply", - COPY_CONTENT = "copy_content", - COPY_LINK = "copy_link", - COPY_MESSAGE_ID = "copy_message_id", - COPY_USER_ID = "copy_user_id", - QUOTE = "quote", - REACT = "react", - PIN = "pin" -} - const settings = definePluginSettings({ - backspaceClickAction: { - type: OptionType.SELECT, - description: "Action when holding Backspace and clicking a message", - options: [ - { label: "Delete Message", value: ClickAction.DELETE, default: true }, - { label: "Copy Content", value: ClickAction.COPY_CONTENT }, - { label: "Copy Link", value: ClickAction.COPY_LINK }, - { label: "Copy Message ID", value: ClickAction.COPY_MESSAGE_ID }, - { label: "Copy User ID", value: ClickAction.COPY_USER_ID }, - { label: "None (Disabled)", value: ClickAction.NONE } - ] + reactEmoji: { + type: OptionType.STRING, + description: "Emoji to use for react actions", + default: "💀" }, - keySelection: { + singleClickAction: { type: OptionType.SELECT, - description: "Key to use for click actions (Backspace or Delete)", - options: [ - { label: "Backspace", value: "backspace", default: true }, - { label: "Delete", value: "delete" } - ] + description: "Action on single click with modifier", + options: actions, + default: "DELETE" + }, + singleClickModifier: { + type: OptionType.SELECT, + description: "Modifier required for single click action", + options: singleClickModifiers, + default: "BACKSPACE" }, doubleClickAction: { type: OptionType.SELECT, - description: "Action on double-click (on your messages)", - options: [ - { label: "Edit Message", value: ClickAction.EDIT, default: true }, - { label: "Quote", value: ClickAction.QUOTE }, - { label: "Copy Content", value: ClickAction.COPY_CONTENT }, - { label: "Copy Link", value: ClickAction.COPY_LINK }, - { label: "Copy Message ID", value: ClickAction.COPY_MESSAGE_ID }, - { label: "Copy User ID", value: ClickAction.COPY_USER_ID }, - { label: "React", value: ClickAction.REACT }, - { label: "Pin Message", value: ClickAction.PIN }, - { label: "None (Disabled)", value: ClickAction.NONE } - ] + description: "Action on double-click (your messages)", + options: doubleClickOwnActions, + default: "EDIT" }, doubleClickOthersAction: { type: OptionType.SELECT, - description: "Action on double-click (on others' messages)", - options: [ - { label: "Reply", value: ClickAction.REPLY, default: true }, - { label: "Quote", value: ClickAction.QUOTE }, - { label: "Copy Content", value: ClickAction.COPY_CONTENT }, - { label: "Copy Link", value: ClickAction.COPY_LINK }, - { label: "Copy Message ID", value: ClickAction.COPY_MESSAGE_ID }, - { label: "Copy User ID", value: ClickAction.COPY_USER_ID }, - { label: "React", value: ClickAction.REACT }, - { label: "Pin Message", value: ClickAction.PIN }, - { label: "None (Disabled)", value: ClickAction.NONE } - ] + description: "Action on double-click (others' messages)", + options: doubleClickOthersActions, + default: "REPLY" + }, + doubleClickModifier: { + type: OptionType.SELECT, + description: "Modifier required for double-click action", + options: modifiers, + default: "NONE" }, tripleClickAction: { type: OptionType.SELECT, description: "Action on triple-click", - options: [ - { label: "React", value: ClickAction.REACT, default: true }, - { label: "Edit Message", value: ClickAction.EDIT }, - { label: "Reply", value: ClickAction.REPLY }, - { label: "Quote", value: ClickAction.QUOTE }, - { label: "Copy Content", value: ClickAction.COPY_CONTENT }, - { label: "Copy Link", value: ClickAction.COPY_LINK }, - { label: "Copy Message ID", value: ClickAction.COPY_MESSAGE_ID }, - { label: "Copy User ID", value: ClickAction.COPY_USER_ID }, - { label: "Pin Message", value: ClickAction.PIN }, - { label: "None (Disabled)", value: ClickAction.NONE } - ] + options: actions, + default: "REACT" }, - reactEmoji: { - type: OptionType.STRING, - description: "Emoji to use for react actions (e.g. 💀 or pepe:123456789)", - default: "💀" - }, - requireModifier: { - type: OptionType.BOOLEAN, - description: "Only perform click actions when shift or ctrl is held", - default: false + tripleClickModifier: { + type: OptionType.SELECT, + description: "Modifier required for triple-click action", + options: modifiers, + default: "NONE" }, disableInDms: { type: OptionType.BOOLEAN, @@ -131,12 +154,12 @@ const settings = definePluginSettings({ }, disableInSystemDms: { type: OptionType.BOOLEAN, - description: "Disable all click actions in system DMs (e.g. Clyde, Discord)", + description: "Disable all click actions in system DMs", default: true }, clickTimeout: { type: OptionType.NUMBER, - description: "Timeout in milliseconds to distinguish double/triple clicks", + description: "Timeout to distinguish double/triple clicks (ms)", default: 300 }, quoteWithReply: { @@ -146,7 +169,7 @@ const settings = definePluginSettings({ }, useSelectionForQuote: { type: OptionType.BOOLEAN, - description: "When quoting, use selected text if available (otherwise quotes full message)", + description: "When quoting, use selected text if available", default: false } }); @@ -162,29 +185,11 @@ function showWarning(message: string) { }); } -function executeCommonAction(action: ClickAction, channel: Channel, msg: Message): boolean { - switch (action) { - case ClickAction.COPY_CONTENT: - copyWithToast(msg.content || "", "Message content copied!"); - return true; - case ClickAction.COPY_LINK: - copyWithToast(`https://discord.com/channels/${channel.guild_id ?? "@me"}/${channel.id}/${msg.id}`, "Message link copied!"); - return true; - case ClickAction.COPY_MESSAGE_ID: - copyWithToast(msg.id, "Message ID copied!"); - return true; - case ClickAction.COPY_USER_ID: - copyWithToast(msg.author.id, "User ID copied!"); - return true; - } - return false; -} - function isMessageReplyable(msg: Message) { return MessageTypeSets.REPLYABLE.has(msg.type) && !msg.hasFlag(MessageFlags.EPHEMERAL); } -async function toggleReaction(channelId: string, messageId: string, emoji: string, channel: Channel, msg: Message) { +async function toggleReaction(channelId: string, messageId: string, emoji: string, channel: { id: string; }, msg: Message) { const trimmed = emoji.trim(); if (!trimmed) return; @@ -220,7 +225,37 @@ async function toggleReaction(channelId: string, messageId: string, emoji: strin } } -function togglePin(channel: Channel, msg: Message) { +async function copyMessageLink(msg: Message, channel: { id: string; guild_id?: string | null; }) { + const guildId = channel.guild_id ?? "@me"; + const link = `https://discord.com/channels/${guildId}/${channel.id}/${msg.id}`; + + try { + await navigator.clipboard.writeText(link); + showToast("Message link copied", Toasts.Type.SUCCESS); + } catch (e) { + new Logger("MessageClickActions").error("Failed to copy link:", e); + } +} + +async function copyMessageId(msg: Message) { + try { + await navigator.clipboard.writeText(msg.id); + showToast("Message ID copied", Toasts.Type.SUCCESS); + } catch (e) { + new Logger("MessageClickActions").error("Failed to copy message ID:", e); + } +} + +async function copyUserId(msg: Message) { + try { + await navigator.clipboard.writeText(msg.author.id); + showToast("User ID copied", Toasts.Type.SUCCESS); + } catch (e) { + new Logger("MessageClickActions").error("Failed to copy user ID:", e); + } +} + +function togglePin(channel: { id: string; }, msg: Message) { if (!PermissionStore.can(PermissionsBits.MANAGE_MESSAGES, channel)) { showWarning("Cannot pin: Missing permissions"); return; @@ -233,7 +268,7 @@ function togglePin(channel: Channel, msg: Message) { } } -function quoteMessage(channel: Channel, msg: Message) { +function quoteMessage(channel: { id: string; guild_id?: string | null; isPrivate?: () => boolean; }, msg: Message) { if (!isMessageReplyable(msg)) { showWarning("Cannot quote this message type"); return; @@ -258,14 +293,154 @@ function quoteMessage(channel: Channel, msg: Message) { channel, message: msg, shouldMention: false, - showMentionToggle: !channel.isPrivate() + showMentionToggle: !channel.isPrivate?.() }); } } +function openInNewTab(msg: Message, channel: { id: string; guild_id?: string | null; }) { + const guildId = channel.guild_id ?? "@me"; + const link = `https://discord.com/channels/${guildId}/${channel.id}/${msg.id}`; + VencordNative.native.openExternal(link); +} + +function openInThread(msg: Message, channel: { id: string; }) { + FluxDispatcher.dispatch({ + type: "OPEN_THREAD_FLOW_MODAL", + channelId: channel.id, + messageId: msg.id + }); +} + +async function executeAction( + action: ClickAction, + msg: Message, + channel: { id: string; guild_id?: string | null; isDM?: () => boolean; isSystemDM?: () => boolean; isPrivate?: () => boolean; }, + event: MouseEvent +) { + const myId = AuthenticationStore.getId(); + const isMe = msg.author.id === myId; + const isSelfInvokedUserApp = msg.interactionMetadata?.authorizing_integration_owners?.[ApplicationIntegrationType.USER_INSTALL] === myId; + + switch (action) { + case "DELETE": + if (!(isMe || PermissionStore.can(PermissionsBits.MANAGE_MESSAGES, channel) || isSelfInvokedUserApp)) return; + + if (msg.deleted) { + FluxDispatcher.dispatch({ + type: "MESSAGE_DELETE", + channelId: channel.id, + id: msg.id, + mlDeleted: true + }); + } else { + MessageActions.deleteMessage(channel.id, msg.id); + } + event.preventDefault(); + break; + + case "COPY_LINK": + await copyMessageLink(msg, channel); + event.preventDefault(); + break; + + case "COPY_ID": + await copyMessageId(msg); + event.preventDefault(); + break; + + case "COPY_CONTENT": + copyWithToast(msg.content || "", "Message content copied!"); + event.preventDefault(); + break; + + case "COPY_USER_ID": + await copyUserId(msg); + event.preventDefault(); + break; + + case "EDIT": + if (!isMe) return; + if (EditMessageStore.isEditing(channel.id, msg.id) || msg.state !== "SENT") return; + MessageActions.startEditMessage(channel.id, msg.id, msg.content); + event.preventDefault(); + break; + + case "REPLY": + if (!MessageTypeSets.REPLYABLE.has(msg.type) || msg.hasFlag(MessageFlags.EPHEMERAL)) return; + if (channel.guild_id && !PermissionStore.can(PermissionsBits.SEND_MESSAGES, channel)) return; + + const isShiftPress = event.shiftKey; + const shouldMention = isPluginEnabled(NoReplyMentionPlugin.name) + ? NoReplyMentionPlugin.shouldMention(msg, isShiftPress) + : !isShiftPress; + + FluxDispatcher.dispatch({ + type: "CREATE_PENDING_REPLY", + channel, + message: msg, + shouldMention, + showMentionToggle: channel.guild_id !== null + }); + event.preventDefault(); + break; + + case "EDIT_REPLY": + if (isMe) { + if (EditMessageStore.isEditing(channel.id, msg.id) || msg.state !== "SENT") return; + MessageActions.startEditMessage(channel.id, msg.id, msg.content); + } else { + if (!MessageTypeSets.REPLYABLE.has(msg.type) || msg.hasFlag(MessageFlags.EPHEMERAL)) return; + if (channel.guild_id && !PermissionStore.can(PermissionsBits.SEND_MESSAGES, channel)) return; + + const shouldMentionReply = isPluginEnabled(NoReplyMentionPlugin.name) + ? NoReplyMentionPlugin.shouldMention(msg, false) + : true; + + FluxDispatcher.dispatch({ + type: "CREATE_PENDING_REPLY", + channel, + message: msg, + shouldMention: shouldMentionReply, + showMentionToggle: channel.guild_id !== null + }); + } + event.preventDefault(); + break; + + case "QUOTE": + quoteMessage(channel, msg); + event.preventDefault(); + break; + + case "PIN": + togglePin(channel, msg); + event.preventDefault(); + break; + + case "REACT": + await toggleReaction(channel.id, msg.id, settings.store.reactEmoji, channel, msg); + event.preventDefault(); + break; + + case "OPEN_THREAD": + openInThread(msg, channel); + event.preventDefault(); + break; + + case "OPEN_TAB": + openInNewTab(msg, channel); + event.preventDefault(); + break; + + case "NONE": + break; + } +} + export default definePlugin({ name: "MessageClickActions", - description: "Customize message click actions - choose what happens when you click, double-click, or hold backspace", + description: "Customize click actions on messages.", authors: [Devs.Ven, EquicordDevs.keyages, EquicordDevs.ZcraftElite], isModified: true, @@ -292,131 +467,56 @@ export default definePlugin({ onMessageClick(msg, channel, event) { const myId = AuthenticationStore.getId(); const isMe = msg.author.id === myId; - const isSelfInvokedUserApp = msg.interactionMetadata?.authorizing_integration_owners[ApplicationIntegrationType.USER_INSTALL] === myId; const isDM = channel.isDM(); const isSystemDM = channel.isSystemDM(); if ((settings.store.disableInDms && isDM) || (settings.store.disableInSystemDms && isSystemDM)) return; - if (isKeyPressed) { - const action = settings.store.backspaceClickAction; - if (action === ClickAction.NONE) return; + const singleClickAction = settings.store.singleClickAction as ClickAction; + const doubleClickAction = isMe + ? (settings.store.doubleClickAction as ClickAction) + : (settings.store.doubleClickOthersAction as ClickAction); + const tripleClickAction = settings.store.tripleClickAction as ClickAction; - if (action === ClickAction.DELETE) { - if (!(isMe || PermissionStore.can(PermissionsBits.MANAGE_MESSAGES, channel) || isSelfInvokedUserApp)) { - showWarning("Cannot delete: Missing permissions"); - return; - } + const singleClickModifier = settings.store.singleClickModifier as Modifier; + const doubleClickModifier = settings.store.doubleClickModifier as Modifier; + const tripleClickModifier = settings.store.tripleClickModifier as Modifier; - if (msg.deleted) { - FluxDispatcher.dispatch({ - type: "MESSAGE_DELETE", - channelId: channel.id, - id: msg.id, - mlDeleted: true - }); - } else { - MessageActions.deleteMessage(channel.id, msg.id); - } - } else if (executeCommonAction(action, channel, msg)) { - event.preventDefault(); - return; - } - } + const isSingleClick = event.detail === 1 && event.button === 0; + const isDoubleClick = event.detail === 2; + const isTripleClick = event.detail === 3; - if (event.detail === 3) { + if (isTripleClick) { if (doubleClickTimeout) { clearTimeout(doubleClickTimeout); doubleClickTimeout = null; pendingDoubleClickAction = null; } - const action = settings.store.tripleClickAction; - if (action === ClickAction.NONE) return; - - if (action === ClickAction.REACT) { - toggleReaction(channel.id, msg.id, settings.store.reactEmoji, channel, msg); - } else if (action === ClickAction.EDIT) { - if (!isMe) return; - if (EditMessageStore.isEditing(channel.id, msg.id) || msg.state !== "SENT") return; - MessageActions.startEditMessage(channel.id, msg.id, msg.content); - } else if (action === ClickAction.REPLY) { - if (channel.guild_id && !PermissionStore.can(PermissionsBits.SEND_MESSAGES, channel)) { - showWarning("Cannot reply: Missing permissions"); - return; - } - if (!isMessageReplyable(msg)) { - showWarning("Cannot reply to this message type"); - return; - } - - FluxDispatcher.dispatch({ - type: "CREATE_PENDING_REPLY", - channel, - message: msg, - shouldMention: !isMe, - showMentionToggle: !channel.isPrivate() - }); - } else if (executeCommonAction(action, channel, msg)) { - event.preventDefault(); - return; - } else if (action === ClickAction.QUOTE) { - quoteMessage(channel, msg); - } else if (action === ClickAction.PIN) { - togglePin(channel, msg); + if (isModifierPressed(tripleClickModifier) && tripleClickAction !== "NONE") { + executeAction(tripleClickAction, msg, channel, event); } - event.preventDefault(); return; } - if (event.detail !== 2) return; - if (settings.store.requireModifier && !event.ctrlKey && !event.shiftKey) return; - if (msg.deleted === true) return; + if (!isDoubleClick) { + if (isSingleClick && isModifierPressed(singleClickModifier) && singleClickAction !== "NONE") { + executeAction(singleClickAction, msg, channel, event); + return; + } + } const executeDoubleClick = () => { - const action = isMe ? settings.store.doubleClickAction : settings.store.doubleClickOthersAction; - if (action === ClickAction.NONE) return; - - if (action === ClickAction.EDIT) { - if (!isMe) return; - if (EditMessageStore.isEditing(channel.id, msg.id) || msg.state !== "SENT") return; - MessageActions.startEditMessage(channel.id, msg.id, msg.content); - } else if (action === ClickAction.REPLY) { - if (isMe) return; - if (channel.guild_id && !PermissionStore.can(PermissionsBits.SEND_MESSAGES, channel)) { - showWarning("Cannot reply: Missing permissions"); - return; - } - if (!isMessageReplyable(msg)) { - showWarning("Cannot reply to this message type"); - return; - } - - const isShiftPress = event.shiftKey && !settings.store.requireModifier; - const shouldMention = isPluginEnabled(NoReplyMentionPlugin.name) - ? NoReplyMentionPlugin.shouldMention(msg, isShiftPress) - : !isShiftPress; - - FluxDispatcher.dispatch({ - type: "CREATE_PENDING_REPLY", - channel, - message: msg, - shouldMention, - showMentionToggle: !channel.isPrivate() - }); - } else if (executeCommonAction(action, channel, msg)) { - event.preventDefault(); - return; - } else if (action === ClickAction.QUOTE) { - quoteMessage(channel, msg); - } else if (action === ClickAction.REACT) { - toggleReaction(channel.id, msg.id, settings.store.reactEmoji, channel, msg); - } else if (action === ClickAction.PIN) { - togglePin(channel, msg); + if (channel.guild_id && !PermissionStore.can(PermissionsBits.SEND_MESSAGES, channel)) return; + if (msg.deleted === true) return; + if (doubleClickAction !== "NONE") { + executeAction(doubleClickAction, msg, channel, event); } }; - if (settings.store.tripleClickAction !== ClickAction.NONE) { + const canTripleClick = isModifierPressed(tripleClickModifier) && tripleClickAction !== "NONE"; + + if (canTripleClick) { if (doubleClickTimeout) { clearTimeout(doubleClickTimeout); } @@ -427,7 +527,7 @@ export default definePlugin({ doubleClickTimeout = null; }, settings.store.clickTimeout); event.preventDefault(); - } else { + } else if (isDoubleClick && (isModifierPressed(doubleClickModifier) || doubleClickModifier === "NONE")) { executeDoubleClick(); event.preventDefault(); }