From 56d25b03f918b952172986676ecd4ec6f368359b Mon Sep 17 00:00:00 2001 From: Gabriel <38334104+gabrielmar@users.noreply.github.com> Date: Tue, 16 Sep 2025 22:35:01 -0300 Subject: [PATCH] UserVoiceShow: Improve tooltip & add icons for muted/deafened (#3630) Co-authored-by: V Co-authored-by: Nuckyz <61953774+Nuckyz@users.noreply.github.com> --- src/plugins/userVoiceShow/components.tsx | 166 ++++++++++++----------- src/plugins/userVoiceShow/style.css | 4 +- 2 files changed, 87 insertions(+), 83 deletions(-) diff --git a/src/plugins/userVoiceShow/components.tsx b/src/plugins/userVoiceShow/components.tsx index b7907978..13dfed60 100644 --- a/src/plugins/userVoiceShow/components.tsx +++ b/src/plugins/userVoiceShow/components.tsx @@ -8,8 +8,9 @@ import { classNameFactory } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { classes } from "@utils/misc"; import { Channel } from "@vencord/discord-types"; -import { filters, findByCodeLazy, findByPropsLazy, findComponentByCodeLazy, mapMangledModuleLazy } from "@webpack"; -import { ChannelRouter, ChannelStore, GuildStore, IconUtils, match, P, PermissionsBits, PermissionStore, React, showToast, Text, Toasts, Tooltip, useMemo, UserStore, UserSummaryItem, useStateFromStores, VoiceStateStore } from "@webpack/common"; +import { filters, findByPropsLazy, mapMangledModuleLazy } from "@webpack"; +import { ChannelRouter, ChannelStore, Parser, PermissionsBits, PermissionStore, React, showToast, Text, Toasts, Tooltip, useMemo, UserStore, UserSummaryItem, useStateFromStores, VoiceStateStore } from "@webpack/common"; +import { PropsWithChildren } from "react"; const cl = classNameFactory("vc-uvs-"); @@ -17,60 +18,71 @@ const { selectVoiceChannel } = findByPropsLazy("selectVoiceChannel", "selectChan const { useChannelName } = mapMangledModuleLazy("#{intl::GROUP_DM_ALONE}", { useChannelName: filters.byCode("()=>null==") }); -const getDMChannelIcon = findByCodeLazy(".getChannelIconURL({"); - -const Avatar = findComponentByCodeLazy(".status)/2):0"); -const GroupDMAvatars = findComponentByCodeLazy("frontSrc:", "getAvatarURL"); const ActionButtonClasses = findByPropsLazy("actionButton", "highlight"); -interface IconProps extends React.ComponentPropsWithoutRef<"div"> { +type IconProps = Omit, "children"> & { size?: number; iconClassName?: string; -} +}; -function SpeakerIcon(props: IconProps) { - props.size ??= 16; +function Icon(props: PropsWithChildren) { + const { + size = 16, + className, + iconClassName, + ...restProps + } = props; return (
- - + {props.children}
); } -function LockedSpeakerIcon(props: IconProps) { - props.size ??= 16; - +function SpeakerIcon(props: IconProps) { return ( -
- - - - -
+ + + + + ); +} + +function LockedSpeakerIcon(props: IconProps) { + return ( + + + + + ); +} + +function MutedIcon(props: IconProps) { + return ( + + + + ); +} + +function DeafIcon(props: IconProps) { + return ( + + + ); } @@ -87,36 +99,13 @@ function VoiceChannelTooltip({ channel, isLocked }: VoiceChannelTooltipProps) { [voiceStates] ); - const guild = channel.getGuildId() == null ? undefined : GuildStore.getGuild(channel.getGuildId()); - const guildIcon = guild?.icon == null ? undefined : IconUtils.getGuildIconURL({ - id: guild.id, - icon: guild.icon, - size: 30 - }); - - const channelIcon = match(channel.type) - .with(P.union(1, 3), () => { - return channel.recipients.length >= 2 && channel.icon == null - ? - : ; - }) - .otherwise(() => null); - const channelName = useChannelName(channel); - + const Icon = isLocked ? LockedSpeakerIcon : SpeakerIcon; return ( <> - {guild != null && ( -
- {guildIcon != null && } - {guild.name} -
- )} -
- {channelIcon} - {channelName} -
+ In Voice Chat + {Parser.parse(`<#${channel.id}>`)}
- {isLocked ? : } + ; +const clickTimers = new Map(); export const VoiceChannelIndicator = ErrorBoundary.wrap(({ userId, isProfile, isActionButton, shouldHighlight }: VoiceChannelIndicatorProps) => { const channelId = useStateFromStores([VoiceStateStore], () => VoiceStateStore.getVoiceStateForUser(userId)?.channelId); + const { isMuted, isDeaf } = useStateFromStores([VoiceStateStore], () => { + const voiceState = VoiceStateStore.getVoiceStateForUser(userId); + return { + isMuted: voiceState?.mute || voiceState?.selfMute || false, + isDeaf: voiceState?.deaf || voiceState?.selfDeaf || false + }; + }); + const channel = channelId == null ? undefined : ChannelStore.getChannel(channelId); if (channel == null) return null; @@ -154,8 +151,8 @@ export const VoiceChannelIndicator = ErrorBoundary.wrap(({ userId, isProfile, is if (channel == null || channelId == null) return; - clearTimeout(clickTimers[channelId]); - delete clickTimers[channelId]; + clearTimeout(clickTimers.get(channelId)); + clickTimers.delete(channelId); if (e.detail > 1) { if (!isDM && !PermissionStore.can(PermissionsBits.CONNECT, channel)) { @@ -165,32 +162,39 @@ export const VoiceChannelIndicator = ErrorBoundary.wrap(({ userId, isProfile, is selectVoiceChannel(channelId); } else { - clickTimers[channelId] = setTimeout(() => { + const timeoutId = setTimeout(() => { ChannelRouter.transitionToChannel(channelId); - delete clickTimers[channelId]; + clickTimers.delete(channelId); }, 250); + clickTimers.set(channelId, timeoutId); } } + const IconComponent = + isLocked + ? LockedSpeakerIcon + : isDeaf + ? DeafIcon + : isMuted + ? MutedIcon + : SpeakerIcon; + return ( } tooltipClassName={cl("tooltip-container")} tooltipContentClassName={cl("tooltip-content")} > - {props => { - const iconProps: IconProps = { - ...props, - className: classes(isActionButton && ActionButtonClasses.actionButton, isActionButton && shouldHighlight && ActionButtonClasses.highlight), - iconClassName: classes(isProfile && cl("profile-speaker")), - size: isActionButton ? 20 : 16, - onClick - }; - - return isLocked ? - - : ; - }} + {props => ( + + )} ); }, { noop: true }); diff --git a/src/plugins/userVoiceShow/style.css b/src/plugins/userVoiceShow/style.css index 32b42208..322b8ed3 100644 --- a/src/plugins/userVoiceShow/style.css +++ b/src/plugins/userVoiceShow/style.css @@ -19,7 +19,7 @@ } .vc-uvs-tooltip-container { - max-width: 300px; + max-width: 50vw; } .vc-uvs-tooltip-content { @@ -42,4 +42,4 @@ .vc-uvs-vc-members { display: flex; gap: 6px; -} +} \ No newline at end of file