diff --git a/packages/discord-types/src/stores/MediaEngineStore.d.ts b/packages/discord-types/src/stores/MediaEngineStore.d.ts index 2452a6056..d44f5aa58 100644 --- a/packages/discord-types/src/stores/MediaEngineStore.d.ts +++ b/packages/discord-types/src/stores/MediaEngineStore.d.ts @@ -3,6 +3,8 @@ import { FluxStore } from ".."; export class MediaEngineStore extends FluxStore { isSelfMute(): boolean; isSelfDeaf(): boolean; + isLocalMute(userId): boolean; + isLocalVideoDisabled(userId): boolean; setAv1Enabled(AV1: boolean): void; setH265Enabled(H265: boolean): void; diff --git a/src/equicordplugins/imagePreview/index.ts b/src/equicordplugins/imagePreview/index.ts index 065d8a392..948dd870d 100644 --- a/src/equicordplugins/imagePreview/index.ts +++ b/src/equicordplugins/imagePreview/index.ts @@ -376,6 +376,24 @@ function updatePreviewPosition(mouseEvent: MouseEvent, element: HTMLElement) { } } +let previewCheckInterval; + +function watchSourceElement(el: Element) { + if (previewCheckInterval) { + clearInterval(previewCheckInterval); + previewCheckInterval = null; + } + + previewCheckInterval = setInterval(() => { + const node = el as HTMLElement; + if (!document.body.contains(node) || !node.offsetParent) { + deleteCurrentPreview(); + clearInterval(previewCheckInterval); + previewCheckInterval = null; + } + }, 300); +} + function addHoverListener(element: Element, sticker: boolean = false) { element.setAttribute("data-processed", "true"); @@ -406,6 +424,7 @@ function addHoverListener(element: Element, sticker: boolean = false) { hoverDelayTimeout = setTimeout(() => { loadImagePreview(strippedURL, sticker); + watchSourceElement(element); if (lastMouseEvent) { updatePreviewPosition(lastMouseEvent, element as HTMLElement); } diff --git a/src/equicordplugins/voiceButtons/index.tsx b/src/equicordplugins/voiceButtons/index.tsx new file mode 100644 index 000000000..dfe0bfca7 --- /dev/null +++ b/src/equicordplugins/voiceButtons/index.tsx @@ -0,0 +1,74 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2025 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +import { definePluginSettings } from "@api/Settings"; +import { EquicordDevs } from "@utils/constants"; +import definePlugin, { OptionType } from "@utils/types"; +import { User } from "@vencord/discord-types"; +import { React } from "@webpack/common"; + +import { UserChatButton, UserDeafenButton, UserMuteButton } from "./utils"; + +export const settings = definePluginSettings({ + showChatButton: { + type: OptionType.BOOLEAN, + default: true, + description: "Whether you want to display the chat button", + restartNeeded: true, + }, + showMuteButton: { + type: OptionType.BOOLEAN, + default: true, + description: "Whether you want to display the mute button", + restartNeeded: true, + }, + showDeafenButton: { + type: OptionType.BOOLEAN, + default: true, + description: "Whether you want to display the deafen button", + restartNeeded: true, + }, + muteSoundboard: { + type: OptionType.BOOLEAN, + default: true, + description: "Toggles their soundboard upon clicking deafen button.", + restartNeeded: false, + }, + disableVideo: { + type: OptionType.BOOLEAN, + default: true, + description: "Toggles their video upon clicking deafen button.", + restartNeeded: false, + }, +}); + +export default definePlugin({ + name: "VoiceButtons", + description: "Quickly DM, mute, or deafen any user right from the voice-call panel.", + authors: [EquicordDevs.nicola02nb, EquicordDevs.omaw], + settings, + patches: [ + { + find: "\"avatarContainerClass\",\"userNameClassName\"", + replacement: [ + { + match: /:(\i)\.username.*?flipped\]:\i\}\),children:\[/, + replace: "$&$self.renderButtons($1)," + } + ] + } + ], + renderPing(user?: User) { + if (!user) return null; + return ( +