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>
This commit is contained in:
parent
2fa4ad813c
commit
7d228aeafc
1 changed files with 316 additions and 216 deletions
|
|
@ -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<Modifier>();
|
||||
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<typeof setTimeout> | 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();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue