MessageClickActions: add action for message reactions (#590)
* MessageClickActions: add optional triple click action for reacting to messages * fix(MessageClickActions): handle API failures gracefully and log errors * fix(MessageClickActions): regex now matches emoji naming requirements * Fix formatting of IDs in constants file * fix(MessageClickActions): remove unnecessary selection clear after triple click action --------- Co-authored-by: thororen <78185467+thororen1234@users.noreply.github.com>
This commit is contained in:
parent
80bdd440b4
commit
3172fb5997
2 changed files with 107 additions and 38 deletions
|
|
@ -19,11 +19,11 @@
|
|||
import { isPluginEnabled } from "@api/PluginManager";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import NoReplyMentionPlugin from "@plugins/noReplyMention";
|
||||
import { Devs } from "@utils/constants";
|
||||
import { Devs, EquicordDevs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { ApplicationIntegrationType, MessageFlags } from "@vencord/discord-types/enums";
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { AuthenticationStore, FluxDispatcher, MessageTypeSets, PermissionsBits, PermissionStore, WindowStore } from "@webpack/common";
|
||||
import { AuthenticationStore, FluxDispatcher, MessageTypeSets, PermissionsBits, PermissionStore, RestAPI, WindowStore } from "@webpack/common";
|
||||
|
||||
const MessageActions = findByPropsLazy("deleteMessage", "startEditMessage");
|
||||
const EditStore = findByPropsLazy("isEditing", "isEditingAny");
|
||||
|
|
@ -33,6 +33,9 @@ const keydown = (e: KeyboardEvent) => e.key === "Backspace" && (isDeletePressed
|
|||
const keyup = (e: KeyboardEvent) => e.key === "Backspace" && (isDeletePressed = false);
|
||||
const focusChanged = () => !WindowStore.isFocused() && (isDeletePressed = false);
|
||||
|
||||
let doubleClickTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||
let pendingDoubleClickAction: (() => void) | null = null;
|
||||
|
||||
const settings = definePluginSettings({
|
||||
enableDeleteOnClick: {
|
||||
type: OptionType.BOOLEAN,
|
||||
|
|
@ -49,6 +52,16 @@ const settings = definePluginSettings({
|
|||
description: "Enable double click to reply",
|
||||
default: true
|
||||
},
|
||||
enableTripleClickToReact: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Enable triple click to react with an emoji",
|
||||
default: false
|
||||
},
|
||||
reactEmoji: {
|
||||
type: OptionType.STRING,
|
||||
description: "Emoji to react with (e.g. 💀 or pepe:123456789)",
|
||||
default: "💀"
|
||||
},
|
||||
requireModifier: {
|
||||
type: OptionType.BOOLEAN,
|
||||
description: "Only do double click actions when shift/ctrl is held",
|
||||
|
|
@ -56,10 +69,28 @@ const settings = definePluginSettings({
|
|||
}
|
||||
});
|
||||
|
||||
async function react(channelId: string, messageId: string, emoji: string) {
|
||||
const trimmed = emoji.trim();
|
||||
if (!trimmed) return;
|
||||
|
||||
const customMatch = trimmed.match(/^:?([\w-]+):(\d+)$/);
|
||||
const emojiParam = customMatch
|
||||
? `${customMatch[1]}:${customMatch[2]}`
|
||||
: encodeURIComponent(trimmed);
|
||||
|
||||
try {
|
||||
await RestAPI.put({
|
||||
url: `/channels/${channelId}/messages/${messageId}/reactions/${emojiParam}/@me`
|
||||
});
|
||||
} catch (e) {
|
||||
console.error("[MessageClickActions] Failed to add reaction:", e);
|
||||
}
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
name: "MessageClickActions",
|
||||
description: "Hold Backspace and click to delete, double click to edit/reply",
|
||||
authors: [Devs.Ven],
|
||||
description: "Hold Backspace and click to delete, double click to edit/reply, triple click to react",
|
||||
authors: [Devs.Ven, EquicordDevs.keircn],
|
||||
|
||||
settings,
|
||||
|
||||
|
|
@ -79,20 +110,50 @@ export default definePlugin({
|
|||
const myId = AuthenticationStore.getId();
|
||||
const isMe = msg.author.id === myId;
|
||||
const isSelfInvokedUserApp = msg.interactionMetadata?.authorizing_integration_owners[ApplicationIntegrationType.USER_INSTALL] === myId;
|
||||
if (!isDeletePressed) {
|
||||
if (event.detail < 2) return;
|
||||
if (settings.store.requireModifier && !event.ctrlKey && !event.shiftKey) return;
|
||||
if (channel.guild_id && !PermissionStore.can(PermissionsBits.SEND_MESSAGES, channel)) return;
|
||||
if (msg.deleted === true) return;
|
||||
|
||||
if (isDeletePressed) {
|
||||
if (!settings.store.enableDeleteOnClick) return;
|
||||
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();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.detail === 3) {
|
||||
if (doubleClickTimeout) {
|
||||
clearTimeout(doubleClickTimeout);
|
||||
doubleClickTimeout = null;
|
||||
pendingDoubleClickAction = null;
|
||||
}
|
||||
|
||||
if (settings.store.enableTripleClickToReact) {
|
||||
react(channel.id, msg.id, settings.store.reactEmoji);
|
||||
event.preventDefault();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.detail !== 2) return;
|
||||
if (settings.store.requireModifier && !event.ctrlKey && !event.shiftKey) return;
|
||||
if (channel.guild_id && !PermissionStore.can(PermissionsBits.SEND_MESSAGES, channel)) return;
|
||||
if (msg.deleted === true) return;
|
||||
|
||||
const executeDoubleClick = () => {
|
||||
if (isMe) {
|
||||
if (!settings.store.enableDoubleClickToEdit || EditStore.isEditing(channel.id, msg.id) || msg.state !== "SENT") return;
|
||||
|
||||
MessageActions.startEditMessage(channel.id, msg.id, msg.content);
|
||||
event.preventDefault();
|
||||
} else {
|
||||
if (!settings.store.enableDoubleClickToReply) return;
|
||||
|
||||
if (!MessageTypeSets.REPLYABLE.has(msg.type) || msg.hasFlag(MessageFlags.EPHEMERAL)) return;
|
||||
|
||||
const isShiftPress = event.shiftKey && !settings.store.requireModifier;
|
||||
|
|
@ -108,17 +169,21 @@ export default definePlugin({
|
|||
showMentionToggle: channel.guild_id !== null
|
||||
});
|
||||
}
|
||||
} else if (settings.store.enableDeleteOnClick && (isMe || PermissionStore.can(PermissionsBits.MANAGE_MESSAGES, channel) || isSelfInvokedUserApp)) {
|
||||
if (msg.deleted) {
|
||||
FluxDispatcher.dispatch({
|
||||
type: "MESSAGE_DELETE",
|
||||
channelId: channel.id,
|
||||
id: msg.id,
|
||||
mlDeleted: true
|
||||
});
|
||||
} else {
|
||||
MessageActions.deleteMessage(channel.id, msg.id);
|
||||
};
|
||||
|
||||
if (settings.store.enableTripleClickToReact) {
|
||||
if (doubleClickTimeout) {
|
||||
clearTimeout(doubleClickTimeout);
|
||||
}
|
||||
pendingDoubleClickAction = executeDoubleClick;
|
||||
doubleClickTimeout = setTimeout(() => {
|
||||
pendingDoubleClickAction?.();
|
||||
pendingDoubleClickAction = null;
|
||||
doubleClickTimeout = null;
|
||||
}, 300);
|
||||
event.preventDefault();
|
||||
} else {
|
||||
executeDoubleClick();
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -615,11 +615,11 @@ export const Devs = /* #__PURE__*/ Object.freeze({
|
|||
},
|
||||
jamesbt365: {
|
||||
name: "jamesbt365",
|
||||
id: 158567567487795200n,
|
||||
id: 158567567487795200n
|
||||
},
|
||||
samsam: {
|
||||
name: "samsam",
|
||||
id: 400482410279469056n,
|
||||
id: 400482410279469056n
|
||||
},
|
||||
Cootshk: {
|
||||
name: "Cootshk",
|
||||
|
|
@ -1031,23 +1031,23 @@ export const EquicordDevs = Object.freeze({
|
|||
},
|
||||
cassie: {
|
||||
name: "cassie",
|
||||
id: 280411966126948353n,
|
||||
id: 280411966126948353n
|
||||
},
|
||||
mochienya: {
|
||||
name: "mochie",
|
||||
id: 1043599230247374869n,
|
||||
id: 1043599230247374869n
|
||||
},
|
||||
okiso: {
|
||||
name: "okiso",
|
||||
id: 274178934143451137n,
|
||||
id: 274178934143451137n
|
||||
},
|
||||
port22exposed: {
|
||||
name: "port",
|
||||
id: 1318383159645311009n,
|
||||
id: 1318383159645311009n
|
||||
},
|
||||
PhoenixAceVFX: {
|
||||
name: "PhoenixAceVFX",
|
||||
id: 1016895892055396484n,
|
||||
id: 1016895892055396484n
|
||||
},
|
||||
TheArmagan: {
|
||||
name: "TheArmagan",
|
||||
|
|
@ -1099,27 +1099,27 @@ export const EquicordDevs = Object.freeze({
|
|||
},
|
||||
Reycko: {
|
||||
name: "Reycko",
|
||||
id: 1123725368004726794n,
|
||||
id: 1123725368004726794n
|
||||
},
|
||||
Campfire: {
|
||||
name: "Campfire",
|
||||
id: 376414446840578081n,
|
||||
id: 376414446840578081n
|
||||
},
|
||||
Cootshk: {
|
||||
name: "Cootshk",
|
||||
id: 921605971577548820n,
|
||||
id: 921605971577548820n
|
||||
},
|
||||
sliwka: {
|
||||
name: "sliwka",
|
||||
id: 1165286199628419129n,
|
||||
id: 1165286199628419129n
|
||||
},
|
||||
bbgaming25k: {
|
||||
name: "bbgaming25k",
|
||||
id: 851222385528274964n,
|
||||
id: 851222385528274964n
|
||||
},
|
||||
davidkra230: {
|
||||
name: "davidkra230",
|
||||
id: 652699312631054356n,
|
||||
id: 652699312631054356n
|
||||
},
|
||||
GroupXyz: {
|
||||
name: "GroupXyz",
|
||||
|
|
@ -1143,7 +1143,7 @@ export const EquicordDevs = Object.freeze({
|
|||
},
|
||||
Etorix: {
|
||||
name: "Etorix",
|
||||
id: 94597845868355584n,
|
||||
id: 94597845868355584n
|
||||
},
|
||||
Johannes7k75: {
|
||||
name: "Johannes7k75",
|
||||
|
|
@ -1171,11 +1171,11 @@ export const EquicordDevs = Object.freeze({
|
|||
},
|
||||
qouesm: {
|
||||
name: "qouesm",
|
||||
id: 130388483494641664n,
|
||||
id: 130388483494641664n
|
||||
},
|
||||
CallMeGii: {
|
||||
name: "gii",
|
||||
id: 156481332652802048n,
|
||||
id: 156481332652802048n
|
||||
},
|
||||
mmeta: {
|
||||
name: "mmeta",
|
||||
|
|
@ -1221,6 +1221,10 @@ export const EquicordDevs = Object.freeze({
|
|||
name: "Benjii",
|
||||
id: 463702169443368970n
|
||||
},
|
||||
keircn: {
|
||||
name: "Key",
|
||||
id: 1230319937155760131n
|
||||
},
|
||||
} satisfies Record<string, Dev>);
|
||||
|
||||
// iife so #__PURE__ works correctly
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue