Plugin
VoiceChatDoubleClick
Join voice chats via double click instead of single click
1
/*2
* FiveCord — a Discord client mod3
* Copyright (c) 2025 FiveCord4
* SPDX-License-Identifier: GPL-3.0-or-later5
*/6
7
import { Devs } from "@utils/constants";8
import definePlugin from "@utils/types";9
import { ChannelStore, SelectedChannelStore } from "@webpack/common";10
11
const timers = {} as Record<string, {12
timeout?: NodeJS.Timeout;13
i: number;14
}>;15
16
export default definePlugin({17
name: "VoiceChatDoubleClick",18
description: "Join voice chats via double click instead of single click",19
tags: ["Voice"],20
authors: [Devs.Ven, Devs.D3SOX],21
patches: [22
// Stage Channels & Voice Channels23
// the find is for stage channels, but it also handles voice24
// channels because they're both in the same concatenated module25
// the find for voice channels was `.handleVoiceStatusClick`26
{27
find: ".handleClickChat",28
// hack: these are not React onClick, it is a custom prop handled by Discord29
// thus, replacing this with onDoubleClick won't work, and you also cannot check30
// e.detail since instead of the event they pass the channel.31
// do this timer workaround instead32
replacement: [33
{34
match: /onClick:\(\)=>\{this.handleClick\(\)/g,35
replace: "onClick:()=>{$self.schedule(()=>{this.handleClick()},this)",36
},37
]38
},39
{40
// channel mentions41
find: 039;className:"channelMention",children:[null!=039;,42
replacement: {43
match: /onClick:(\i)(?=,.{0,30}className:"channelMention".+?(\i)\.inContent)/,44
replace: (_, onClick, props) => ""45
+ `onClick:(vcDoubleClickEvt)=>$self.shouldRunOnClick(vcDoubleClickEvt,${props})&&${onClick}()`,46
}47
}48
],49
50
shouldRunOnClick(e: MouseEvent, { channelId }) {51
const channel = ChannelStore.getChannel(channelId);52
if (!channel || ![2, 13].includes(channel.type)) return true;53
return e.detail >= 2;54
},55
56
schedule(cb: () => void, e: any) {57
const id = e.props.channel.id as string;58
if (SelectedChannelStore.getVoiceChannelId() === id) {59
cb();60
return;61
}62
// use a different counter for each channel63
const data = (timers[id] ??= { timeout: void 0, i: 0 });64
// clear any existing timer65
clearTimeout(data.timeout);66
67
// if we already have 2 or more clicks, run the callback immediately68
if (++data.i >= 2) {69
cb();70
delete timers[id];71
} else {72
// else reset the counter in 500ms73
data.timeout = setTimeout(() => {74
delete timers[id];75
}, 500);76
}77
}78
});79