diff --git a/src/plugins/userVoiceShow/components/VoiceChannelIndicator.css b/src/plugins/userVoiceShow/components/VoiceChannelIndicator.css new file mode 100644 index 000000000..b082560e7 --- /dev/null +++ b/src/plugins/userVoiceShow/components/VoiceChannelIndicator.css @@ -0,0 +1,15 @@ +.vc-uvs-indicator { + display: inline-flex; + justify-content: center; + align-items: center; + margin-left: 4px; + vertical-align: top; + position: relative; + top: 0; + padding: 0; + gap: 2px; +} + +.vc-uvs-indicator > svg { + fill: dodgerblue; +} diff --git a/src/plugins/userVoiceShow/components/VoiceChannelIndicator.tsx b/src/plugins/userVoiceShow/components/VoiceChannelIndicator.tsx new file mode 100644 index 000000000..79ae0683d --- /dev/null +++ b/src/plugins/userVoiceShow/components/VoiceChannelIndicator.tsx @@ -0,0 +1,54 @@ +/* + * Vencord, a modification for Discord's desktop app + * Copyright (c) 2023 Vendicated and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +import "./VoiceChannelIndicator.css"; + +import { NavigationRouter, PermissionsBits, PermissionStore, Toasts, Tooltip } from "@webpack/common"; +import { Channel } from "discord-types/general"; + +import speakerSvg from "./speaker.svg"; + +interface VoiceChannelIndicatorProps { + tooltipText?: string; + channel: Channel; +} + +export const VoiceChannelIndicator = ({ tooltipText, channel }: VoiceChannelIndicatorProps) => ( + { + if (PermissionStore.can(PermissionsBits.VIEW_CHANNEL, channel)) { + NavigationRouter.transitionTo(getChannelPath(channel)); + e.preventDefault(); + e.stopPropagation(); + } + else + Toasts.show({ + message: "Insufficient permissions to view the channel.", + id: "user-voice-show-insufficient-permissions", + type: Toasts.Type.FAILURE, + options: { + position: Toasts.Position.BOTTOM, + } + }); + }}> + + {(tooltipProps: any) => speakerSvg(tooltipProps)} + + +); + +const getChannelPath = (c: Channel) => `/channels/${c.guild_id ?? "@me"}/${c.id}`; diff --git a/src/plugins/userVoiceShow/components/VoiceChannelSection.css b/src/plugins/userVoiceShow/components/VoiceChannelSection.css index 00ecf5d41..c644ca563 100644 --- a/src/plugins/userVoiceShow/components/VoiceChannelSection.css +++ b/src/plugins/userVoiceShow/components/VoiceChannelSection.css @@ -8,20 +8,15 @@ height: unset; } +.vc-uvs-button-view { + width: 32px; + min-width: 32px; + height: unset; + margin: unset; + padding: unset; +} + .vc-uvs-header { color: var(--header-primary); margin-bottom: 6px; } - -.vc-uvs-modal-margin { - margin: 0 12px; -} - -.vc-uvs-modal-margin div { - margin-bottom: 0 !important; -} - -.vc-uvs-popout-margin-self>[class^="section"] { - padding-top: 0; - padding-bottom: 12px; -} diff --git a/src/plugins/userVoiceShow/components/VoiceChannelSection.tsx b/src/plugins/userVoiceShow/components/VoiceChannelSection.tsx index 8ca335bb6..1a07f6215 100644 --- a/src/plugins/userVoiceShow/components/VoiceChannelSection.tsx +++ b/src/plugins/userVoiceShow/components/VoiceChannelSection.tsx @@ -18,44 +18,77 @@ import "./VoiceChannelSection.css"; +import { Flex } from "@components/Flex"; import { findByPropsLazy } from "@webpack"; -import { Button, Forms, PermissionStore, Toasts } from "@webpack/common"; +import { Button, Forms, NavigationRouter, PermissionsBits, PermissionStore, React, Toasts } from "@webpack/common"; import { Channel } from "discord-types/general"; +import eyeSvg from "./eye.svg"; + const ChannelActions = findByPropsLazy("selectChannel", "selectVoiceChannel"); -const CONNECT = 1n << 20n; interface VoiceChannelFieldProps { channel: Channel; label: string; - showHeader: boolean; + showHeader?: boolean; } -export const VoiceChannelSection = ({ channel, label, showHeader }: VoiceChannelFieldProps) => ( - // @TODO The div is supposed to be a UserPopoutSection -
+export const VoiceChannelSection = ({ channel, label, showHeader = false }: VoiceChannelFieldProps) => ( + {showHeader && In a voice channel} - -
+ + + + ); + +const getChannelPath = (c: Channel) => `/channels/${c.guild_id ?? "@me"}/${c.id}`; diff --git a/src/plugins/userVoiceShow/components/eye.svg.tsx b/src/plugins/userVoiceShow/components/eye.svg.tsx new file mode 100644 index 000000000..60ac581f3 --- /dev/null +++ b/src/plugins/userVoiceShow/components/eye.svg.tsx @@ -0,0 +1,20 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +export default () => ( + + + +); diff --git a/src/plugins/userVoiceShow/components/speaker.svg.tsx b/src/plugins/userVoiceShow/components/speaker.svg.tsx new file mode 100644 index 000000000..9ff6c4cc4 --- /dev/null +++ b/src/plugins/userVoiceShow/components/speaker.svg.tsx @@ -0,0 +1,21 @@ +/* + * Vencord, a Discord client mod + * Copyright (c) 2023 Vendicated and contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +export default (props: any) => ( + + + +); diff --git a/src/plugins/userVoiceShow/index.tsx b/src/plugins/userVoiceShow/index.tsx index cad539b46..323058e65 100644 --- a/src/plugins/userVoiceShow/index.tsx +++ b/src/plugins/userVoiceShow/index.tsx @@ -16,17 +16,20 @@ * along with this program. If not, see . */ +import { addDecorator, removeDecorator } from "@api/MemberListDecorators"; import { definePluginSettings } from "@api/Settings"; import ErrorBoundary from "@components/ErrorBoundary"; import { Devs } from "@utils/constants"; import definePlugin, { OptionType } from "@utils/types"; -import { findStoreLazy } from "@webpack"; -import { ChannelStore, GuildStore, UserStore } from "@webpack/common"; +import { findByCodeLazy, findStoreLazy } from "@webpack"; +import { ChannelStore, GuildStore } from "@webpack/common"; import { User } from "discord-types/general"; +import { VoiceChannelIndicator } from "./components/VoiceChannelIndicator"; import { VoiceChannelSection } from "./components/VoiceChannelSection"; const VoiceStateStore = findStoreLazy("VoiceStateStore"); +const UserModalSection = findByCodeLazy("heading:", "subheading:", "scrollIntoView:"); const settings = definePluginSettings({ showInUserProfileModal: { @@ -34,9 +37,9 @@ const settings = definePluginSettings({ description: "Show a user's voice channel in their profile modal", default: true, }, - showVoiceChannelSectionHeader: { + showVoiceChannelIndicator: { type: OptionType.BOOLEAN, - description: 'Whether to show "IN A VOICE CHANNEL" above the join button', + description: "Indicator in the member list wether a user is in a voice channel", default: true, } }); @@ -44,8 +47,12 @@ const settings = definePluginSettings({ interface UserProps { user: User; } +interface VoiceChannelFieldProps { + user: User; + isPopout?: boolean; +} -const VoiceChannelField = ErrorBoundary.wrap(({ user }: UserProps) => { +const VoiceChannelField = ErrorBoundary.wrap(({ user, isPopout = false }: VoiceChannelFieldProps) => { const { channelId } = VoiceStateStore.getVoiceStateForUser(user.id) ?? {}; if (!channelId) return null; @@ -58,11 +65,19 @@ const VoiceChannelField = ErrorBoundary.wrap(({ user }: UserProps) => { const result = `${guild.name} | ${channel.name}`; - return ( + // when popout do padding and show header, when in modal no pad and no header + return isPopout ? ( +
+ +
+ ) : ( ); }); @@ -73,28 +88,67 @@ export default definePlugin({ authors: [Devs.LordElias], settings, + start() { + addDecorator("uvs-indicator", props => { + if (!props.user) return null; + + const { channelId } = VoiceStateStore.getVoiceStateForUser(props.user.id) ?? {}; + if (!channelId) return null; + + const channel = ChannelStore.getChannel(channelId); + if (!channel) return null; + + const guild = GuildStore.getGuild(channel.guild_id); + + if (!guild) return null; // When in DM call + + const result = `${guild.name} | ${channel.name}`; + + return ( + + + + ); + }); + }, + + stop() { + removeDecorator("uvs-indicator"); + }, + patchModal({ user }: UserProps) { - if (!settings.store.showInUserProfileModal) - return null; + if (!settings.store.showInUserProfileModal) return null; return ( -
+ -
+ ); }, - patchProfilePopout: ({ user }: UserProps) => { - const isSelfUser = user.id === UserStore.getCurrentUser().id; + patchPopout: ({ user }: UserProps) => { return ( -
- -
+ ); }, patches: [ - // @TODO Maybe patch UserVoiceShow in simplified profile popout - // @TODO Patch new profile modal + // in profile popout above message box + { + find: ":\"BITE_SIZE_POPOUT_RESTRICTED_BLOCKER_PROFILE", + replacement: { + match: /\(0,\i\.jsx\)\(\i\.\i,{user:\i,guildId:\i,channelId:\i,onClose:\i}\)\]}\),/, + replace: "$self.patchPopout(arguments[0]),$&", + } + }, + // in profile modal + { + find: "action:\"PRESS_APP_CONNECTION\"", + replacement: { + // match: /scroller,children:\[/, // at very top + match: /setLineClamp:!\d}\),/, // after bio + replace: "$&$self.patchModal(arguments[0])," + } + } ], });