Message click actions (yes, again) (#710)

* fix(MessageClickActions): stop sticky modifiers caused by not cleaning up

* fix(MessageClickActions): double clicks now double click double as consistently

* she make on my range till i marker
This commit is contained in:
Key 2026-01-31 04:30:49 +00:00 • committed by GitHub
parent fb8c81a8cf
commit 63dcad499b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -10,7 +10,7 @@ import NoReplyMentionPlugin from "@plugins/noReplyMention";
import { Devs, EquicordDevs } from "@utils/constants";
import { copyWithToast, insertTextIntoChatInputBox } from "@utils/discord";
import { Logger } from "@utils/Logger";
import definePlugin, { OptionType } from "@utils/types";
import definePlugin, { makeRange, OptionType } from "@utils/types";
import type { Channel, 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";
@ -93,7 +93,20 @@ const focusChanged = () => {
let lastMouseDownTime = 0;
const onMouseDown = () => {
lastMouseDownTime = Date.now();
const now = Date.now();
// TODO: this logic is messy but it works so eh
if (mouseDownCount >= 1 && now - lastMouseDownTime < settings.store.clickTimeout) {
if (singleClickTimeout) {
clearTimeout(singleClickTimeout);
singleClickTimeout = null;
}
doubleClickDetected = true;
secondMouseDownTime = now;
}
mouseDownCount++;
lastMouseDownTime = now;
};
function modifierFromKey(e: KeyboardEvent): Modifier | null {
@ -111,6 +124,11 @@ function isModifierPressed(modifier: Modifier): boolean {
let doubleClickTimeout: ReturnType<typeof setTimeout> | null = null;
let singleClickTimeout: ReturnType<typeof setTimeout> | null = null;
let pendingDoubleClickAction: (() => void) | null = null;
let doubleClickFired = false;
let mouseDownCount = 0;
let doubleClickDetected = false;
let secondMouseDownTime = 0;
const settings = definePluginSettings({
reactEmoji: {
@ -173,11 +191,19 @@ const settings = definePluginSettings({
clickTimeout: {
type: OptionType.NUMBER,
description: "Timeout to distinguish double/triple clicks (ms)",
markers: makeRange(100, 500, 50),
default: 300
},
doubleClickHoldThreshold: {
type: OptionType.NUMBER,
description: "Max hold time for double-click actions (ms). Holding longer allows text selection",
markers: makeRange(50, 500, 50),
default: 150
},
selectionHoldTimeout: {
type: OptionType.NUMBER,
description: "Timeout to allow text selection (ms)",
markers: makeRange(100, 1000, 100),
default: 300
},
quoteWithReply: {
@ -468,6 +494,9 @@ export default definePlugin({
singleClickTimeout = null;
}
pendingDoubleClickAction = null;
mouseDownCount = 0;
doubleClickDetected = false;
secondMouseDownTime = 0;
},
onMessageClick(msg, channel, event) {
@ -495,7 +524,13 @@ export default definePlugin({
const isDoubleClick = event.detail === 2;
const isTripleClick = event.detail === 3;
if (Date.now() - lastMouseDownTime > settings.store.selectionHoldTimeout) return;
if (Date.now() - lastMouseDownTime > settings.store.selectionHoldTimeout) {
pressedModifiers.clear();
mouseDownCount = 0;
doubleClickDetected = false;
secondMouseDownTime = 0;
return;
}
if (singleClickTimeout) {
clearTimeout(singleClickTimeout);
@ -511,7 +546,12 @@ export default definePlugin({
if (isModifierPressed(tripleClickModifier) && tripleClickAction !== "NONE") {
executeAction(tripleClickAction, msg, channel, event);
pressedModifiers.clear();
}
doubleClickFired = false;
mouseDownCount = 0;
doubleClickDetected = false;
secondMouseDownTime = 0;
return;
}
@ -519,16 +559,20 @@ export default definePlugin({
const canTripleClick = isModifierPressed(tripleClickModifier) && tripleClickAction !== "NONE";
if (isDoubleClick) {
doubleClickFired = true;
if (singleClickTimeout) {
clearTimeout(singleClickTimeout);
singleClickTimeout = null;
}
const isQuickDoubleClick = !doubleClickDetected || (Date.now() - secondMouseDownTime < settings.store.doubleClickHoldThreshold);
const executeDoubleClick = () => {
if (!canSend(channel)) return;
if (msg.deleted === true) return;
if (canDoubleClick) {
if (canDoubleClick && isQuickDoubleClick) {
executeAction(doubleClickAction, msg, channel, event);
pressedModifiers.clear();
}
};
@ -545,19 +589,32 @@ export default definePlugin({
} else {
executeDoubleClick();
}
event.preventDefault();
if (isQuickDoubleClick) {
event.preventDefault();
}
mouseDownCount = 0;
doubleClickDetected = false;
secondMouseDownTime = 0;
return;
}
if (isSingleClick) {
const shouldExecuteSingle = isModifierPressed(singleClickModifier) && singleClickAction !== "NONE";
doubleClickFired = false;
const executeSingleClick = () => {
if (shouldExecuteSingle) {
if (!doubleClickFired && !doubleClickDetected && isModifierPressed(singleClickModifier) && singleClickAction !== "NONE") {
executeAction(singleClickAction, msg, channel, event);
pressedModifiers.clear();
}
mouseDownCount = 0;
doubleClickDetected = false;
secondMouseDownTime = 0;
};
if (canDoubleClick && shouldExecuteSingle && singleClickModifier === "NONE") {
const hasDoubleClickAction = settings.store.doubleClickAction !== "NONE" || settings.store.doubleClickOthersAction !== "NONE";
if (hasDoubleClickAction && singleClickModifier === "NONE") {
singleClickTimeout = setTimeout(() => {
executeSingleClick();
singleClickTimeout = null;