/* * Vencord, a Discord client mod * Copyright (c) 2024 Vendicated and contributors * SPDX-License-Identifier: GPL-3.0-or-later */ import { classNameFactory } from "@api/Styles"; import ErrorBoundary from "@components/ErrorBoundary"; import { classes } from "@utils/misc"; import { findByPropsLazy, findComponentByCodeLazy, findStoreLazy } from "@webpack"; import { ChannelStore, GuildStore, IconUtils, NavigationRouter, PermissionsBits, PermissionStore, React, showToast, Text, Toasts, Tooltip, useMemo, UserStore, useStateFromStores } from "@webpack/common"; import { Channel } from "discord-types/general"; const cl = classNameFactory("vc-uvs-"); const { selectVoiceChannel } = findByPropsLazy("selectChannel", "selectVoiceChannel"); const VoiceStateStore = findStoreLazy("VoiceStateStore"); const UserSummaryItem = findComponentByCodeLazy("defaultRenderUser", "showDefaultAvatarsForNullUsers"); interface IconProps extends React.ComponentPropsWithoutRef<"div"> { size?: number; } function SpeakerIcon(props: IconProps) { props.size ??= 16; return (
); } function LockedSpeakerIcon(props: IconProps) { props.size ??= 16; return (
); } interface VoiceChannelTooltipProps { channel: Channel; } function VoiceChannelTooltip({ channel }: VoiceChannelTooltipProps) { const voiceStates = useStateFromStores([VoiceStateStore], () => VoiceStateStore.getVoiceStatesForChannel(channel.id)); const users = useMemo( () => Object.values(voiceStates).map(voiceState => UserStore.getUser(voiceState.userId)).filter(user => user != null), [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 }); return ( <> {guild != null && (
{guildIcon != null && } {guild.name}
)} {channel.name}
); } interface VoiceChannelIndicatorProps { userId: string; } const clickTimers = {} as Record; export const VoiceChannelIndicator = ErrorBoundary.wrap(({ userId }: VoiceChannelIndicatorProps) => { const channelId = useStateFromStores([VoiceStateStore], () => VoiceStateStore.getVoiceStateForUser(userId)?.channelId as string | undefined); const channel = channelId == null ? undefined : ChannelStore.getChannel(channelId); if (channel == null) return null; function onClick(e: React.MouseEvent) { e.preventDefault(); e.stopPropagation(); if (channel == null || channelId == null) return; if (!PermissionStore.can(PermissionsBits.VIEW_CHANNEL, channel)) { showToast("You cannot view the user's Voice Channel", Toasts.Type.FAILURE); return; } clearTimeout(clickTimers[channelId]); delete clickTimers[channelId]; if (e.detail > 1) { if (!PermissionStore.can(PermissionsBits.CONNECT, channel)) { showToast("You cannot join the user's Voice Channel", Toasts.Type.FAILURE); return; } selectVoiceChannel(channelId); } else { clickTimers[channelId] = setTimeout(() => { NavigationRouter.transitionTo(`/channels/${channel.getGuildId() ?? "@me"}/${channelId}`); delete clickTimers[channelId]; }, 250); } } const isLocked = !PermissionStore.can(PermissionsBits.VIEW_CHANNEL, channel) || !PermissionStore.can(PermissionsBits.CONNECT, channel); return ( } tooltipClassName={cl("tooltip-container")} tooltipContentClassName={cl("tooltip-content")} > {props => isLocked ? : } ); }, { noop: true });