fix(voice): surface stream audio volume for screen-share viewers (#2095)

This commit is contained in:
Hampus authored and GitHub committed 2026-08-29 22:05:06 +02:00
1 parent ce91ff95ab
commit dbc63e9ef7
4 files changed
+12 -7

No files matched your search

@@ -199,10 +199,6 @@ const RELATIONSHIP_ACTIONS_DESCRIPTOR = msg({
message: 'Relationship', message: 'Relationship',
comment: 'Voice participant context menu submenu label for friend and block actions.', comment: 'Voice participant context menu submenu label for friend and block actions.',
}); });
const STREAM_CONTROLS_DESCRIPTOR = msg({
message: 'Stream controls',
comment: 'Voice participant context menu submenu label for incoming screen share audio controls.',
});
const USER_ACTIONS_DESCRIPTOR = msg({ const USER_ACTIONS_DESCRIPTOR = msg({
message: 'User actions', message: 'User actions',
comment: 'Voice participant context menu submenu label for lower-frequency profile and communication actions.', comment: 'Voice participant context menu submenu label for lower-frequency profile and communication actions.',
@@ -492,6 +488,9 @@ export function useVoiceParticipantMenuData(options: VoiceParticipantMenuDataOpt
if (streamRootActions.length > 0) { if (streamRootActions.length > 0) {
menuGroups.push({items: streamRootActions}); menuGroups.push({items: streamRootActions});
} }
if (streamControlActions.length > 0) {
menuGroups.push({items: streamControlActions});
}
const primaryActions: Array<MenuItemType> = []; const primaryActions: Array<MenuItemType> = [];
primaryActions.push({ primaryActions.push({
icon: ( icon: (
@@ -1384,7 +1383,6 @@ export function useVoiceParticipantMenuData(options: VoiceParticipantMenuDataOpt
TextCopyCommands.copy(i18n, user.id, true); TextCopyCommands.copy(i18n, user.id, true);
}, },
}); });
addSecondarySubmenu(i18n._(STREAM_CONTROLS_DESCRIPTOR), streamControlActions);
addSecondarySubmenu(i18n._(MEDIA_CONTROLS_DESCRIPTOR), mediaActions); addSecondarySubmenu(i18n._(MEDIA_CONTROLS_DESCRIPTOR), mediaActions);
addSecondarySubmenu(i18n._(DEVICE_CONTROLS_DESCRIPTOR), deviceActions); addSecondarySubmenu(i18n._(DEVICE_CONTROLS_DESCRIPTOR), deviceActions);
addSecondarySubmenu(i18n._(DISPLAY_OPTIONS_DESCRIPTOR), displayActions); addSecondarySubmenu(i18n._(DISPLAY_OPTIONS_DESCRIPTOR), displayActions);
@@ -9,7 +9,9 @@ import {
} from '@app/features/voice/components/CallVolumeState'; } from '@app/features/voice/components/CallVolumeState';
import {MediaVerticalVolumeControl} from '@app/features/voice/components/media_player/components/MediaVerticalVolumeControl'; import {MediaVerticalVolumeControl} from '@app/features/voice/components/media_player/components/MediaVerticalVolumeControl';
import VoiceSettings from '@app/features/voice/state/VoiceSettings'; import VoiceSettings from '@app/features/voice/state/VoiceSettings';
import {VOICE_OUTPUT_VOLUME_DESCRIPTOR} from '@app/features/voice/utils/VoiceMessageDescriptors';
import {VOICE_VOLUME_MAX_SLIDER_VOLUME} from '@app/features/voice/utils/VoiceVolumeUtils'; import {VOICE_VOLUME_MAX_SLIDER_VOLUME} from '@app/features/voice/utils/VoiceVolumeUtils';
import {useLingui} from '@lingui/react/macro';
import {observer} from 'mobx-react-lite'; import {observer} from 'mobx-react-lite';
import type React from 'react'; import type React from 'react';
import {useCallback, useEffect, useRef} from 'react'; import {useCallback, useEffect, useRef} from 'react';
@@ -25,6 +27,7 @@ export const CallVolumeControl: React.FC<CallVolumeControlProps> = observer(func
position = 'above', position = 'above',
iconSize = 18, iconSize = 18,
}) { }) {
const {i18n} = useLingui();
const outputVolume = VoiceSettings.outputVolume; const outputVolume = VoiceSettings.outputVolume;
const lastNonZeroVolumeRef = useRef(resolveLastNonZeroCallVolume(outputVolume, 0)); const lastNonZeroVolumeRef = useRef(resolveLastNonZeroCallVolume(outputVolume, 0));
useEffect(() => { useEffect(() => {
@@ -50,6 +53,7 @@ export const CallVolumeControl: React.FC<CallVolumeControlProps> = observer(func
iconSize={iconSize} iconSize={iconSize}
className={className} className={className}
position={position} position={position}
ariaLabel={i18n._(VOICE_OUTPUT_VOLUME_DESCRIPTOR)}
data-flx="voice.call-volume-control.media-vertical-volume-control" data-flx="voice.call-volume-control.media-vertical-volume-control"
/> />
); );
@@ -615,7 +615,7 @@ const VoiceParticipantTileInner = observer(function VoiceParticipantTileInner({
const participantDisplayName = const participantDisplayName =
(participantUser ? NicknameUtils.getNickname(participantUser, guildId, channelId) : participant.name) || (participantUser ? NicknameUtils.getNickname(participantUser, guildId, channelId) : participant.name) ||
i18n._(UNKNOWN_USER_DESCRIPTOR); i18n._(UNKNOWN_USER_DESCRIPTOR);
const showStreamAudioControls = isScreenShare && !isOwnScreenShare && isWatching; const showStreamAudioControls = isScreenShare && !isOwnScreenShare && isWatching && hasScreenShareAudio;
const viewerStreamCount = graphViewerStreamKeys.length; const viewerStreamCount = graphViewerStreamKeys.length;
const addStreamTooltipText = plural( const addStreamTooltipText = plural(
{count: viewerStreamCount}, {count: viewerStreamCount},
@@ -49,6 +49,7 @@ interface MediaVerticalVolumeControlProps {
className?: string; className?: string;
position?: 'above' | 'below'; position?: 'above' | 'below';
maxVolume?: number; maxVolume?: number;
ariaLabel?: string;
} }
function getVolumeIcon(volume: number, isMuted: boolean) { function getVolumeIcon(volume: number, isMuted: boolean) {
@@ -75,6 +76,7 @@ export function MediaVerticalVolumeControl({
className, className,
position = 'above', position = 'above',
maxVolume = 1, maxVolume = 1,
ariaLabel,
}: MediaVerticalVolumeControlProps) { }: MediaVerticalVolumeControlProps) {
const {i18n} = useLingui(); const {i18n} = useLingui();
const buttonRef = useRef<HTMLButtonElement>(null); const buttonRef = useRef<HTMLButtonElement>(null);
@@ -286,7 +288,8 @@ export function MediaVerticalVolumeControl({
<div <div
className={clsx(styles.container, className)} className={clsx(styles.container, className)}
role="group" role="group"
aria-label={i18n._(VOLUME_CONTROL_DESCRIPTOR)} aria-label={ariaLabel ?? i18n._(VOLUME_CONTROL_DESCRIPTOR)}
title={ariaLabel}
data-flx="voice.media-player.media-vertical-volume-control.container" data-flx="voice.media-player.media-vertical-volume-control.container"
> >
{popoutElement} {popoutElement}