Receive side of remote mute

This commit is contained in:
Miriam Zimmerman 2025-05-01 14:26:35 -04:00 committed by GitHub
commit a444790bf9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 540 additions and 16 deletions

View file

@ -2,7 +2,7 @@
// SPDX-License-Identifier: AGPL-3.0-only
import React, { useEffect, useMemo, useRef } from 'react';
import type { ActiveCallType } from '../types/Calling';
import type { ActiveCallType, ObservedRemoteMuteType } from '../types/Calling';
import { CallMode } from '../types/CallDisposition';
import type { ConversationType } from '../state/ducks/conversations';
import type { LocalizerType } from '../types/Util';
@ -12,6 +12,7 @@ import { difference as setDifference } from '../util/setUtil';
import { isMoreRecentThan } from '../util/timestamp';
import { isGroupOrAdhocActiveCall } from '../util/isGroupOrAdhocCall';
import { SECOND } from '../util/durations';
import type { SetMutedByType } from '../state/ducks/calling';
type PropsType = {
activeCall: ActiveCallType;
@ -83,9 +84,11 @@ export function useScreenSharingStoppedToast({
function useMutedToast({
hasLocalAudio,
mutedBy,
i18n,
}: {
hasLocalAudio: boolean;
mutedBy: number | undefined;
i18n: LocalizerType;
}): void {
const previousHasLocalAudio = usePrevious(hasLocalAudio, hasLocalAudio);
@ -95,7 +98,8 @@ function useMutedToast({
useEffect(() => {
if (
previousHasLocalAudio !== undefined &&
hasLocalAudio !== previousHasLocalAudio
hasLocalAudio !== previousHasLocalAudio &&
mutedBy === undefined // skip this if we were muted by someone
) {
hideToast(MUTED_TOAST_KEY);
showToast({
@ -107,7 +111,14 @@ function useMutedToast({
dismissable: true,
});
}
}, [hasLocalAudio, previousHasLocalAudio, hideToast, showToast, i18n]);
}, [
hasLocalAudio,
previousHasLocalAudio,
hideToast,
showToast,
mutedBy,
i18n,
]);
}
function useOutgoingRingToast({
@ -321,6 +332,143 @@ function useLowerHandSuggestionToast({
}, [isHandRaised, hideToast]);
}
function useMutedByToast({
mutedBy,
setLocalAudioRemoteMuted,
conversationsByDemuxId,
i18n,
}: {
mutedBy: number | undefined;
setLocalAudioRemoteMuted?: SetMutedByType;
conversationsByDemuxId?: Map<number, ConversationType>;
i18n: LocalizerType;
}): void {
const previousMutedBy = usePrevious(mutedBy, mutedBy);
const { showToast, hideToast } = useCallingToasts();
const MUTED_BY_TOAST_KEY = 'MUTED_BY_TOAST_KEY';
useEffect(() => {
if (setLocalAudioRemoteMuted === undefined) {
return;
}
if (
mutedBy === undefined ||
// if it's undefined, likely we just received a remote mute request
// and hadn't had one before.
(previousMutedBy !== undefined && previousMutedBy === mutedBy) ||
conversationsByDemuxId === undefined
) {
return;
}
const otherConversation = conversationsByDemuxId.get(mutedBy);
const title = otherConversation?.title;
if (title === undefined) {
return;
}
setLocalAudioRemoteMuted({ mutedBy });
let content;
if (otherConversation?.isMe) {
content = i18n('icu:CallControls__YouMutedYourselfToast');
} else {
content = i18n('icu:CallControls__MutedBySomeoneToast', {
otherName: title,
});
}
hideToast(MUTED_BY_TOAST_KEY);
showToast({
key: MUTED_BY_TOAST_KEY,
content,
dismissable: true,
autoClose: true,
lifetime: 10 * SECOND,
});
}, [
mutedBy,
previousMutedBy,
conversationsByDemuxId,
i18n,
showToast,
hideToast,
MUTED_BY_TOAST_KEY,
setLocalAudioRemoteMuted,
]);
}
function useObservedRemoteMuteToast({
observedRemoteMute,
conversationsByDemuxId,
i18n,
}: {
observedRemoteMute: ObservedRemoteMuteType | undefined;
conversationsByDemuxId?: Map<number, ConversationType>;
i18n: LocalizerType;
}): void {
const { showToast, hideToast } = useCallingToasts();
const OBSERVED_REMOTE_MUTE_TOAST_KEY = 'OBSERVED_REMOTE_MUTE_TOAST_KEY';
const previousObservedRemoteMute = usePrevious(
observedRemoteMute,
observedRemoteMute
);
useEffect(() => {
if (
observedRemoteMute === undefined ||
(previousObservedRemoteMute !== undefined &&
previousObservedRemoteMute === observedRemoteMute) ||
conversationsByDemuxId === undefined
) {
return;
}
const sourceConversation = conversationsByDemuxId.get(
observedRemoteMute.source
);
const targetConversation = conversationsByDemuxId.get(
observedRemoteMute.target
);
if (sourceConversation?.serviceId === targetConversation?.serviceId) {
// Ignore self-mutes.
return;
}
const targetTitle = targetConversation?.title;
if (targetTitle === undefined) {
return;
}
let content;
if (sourceConversation?.isMe) {
content = i18n('icu:CallControls__YouMutedSomeoneToast', {
otherName: targetTitle,
});
} else {
const sourceTitle = sourceConversation?.title;
if (sourceTitle === undefined) {
return;
}
content = i18n('icu:CallControls__SomeoneMutedSomeoneToast', {
name: sourceTitle,
otherName: targetTitle,
});
}
hideToast(OBSERVED_REMOTE_MUTE_TOAST_KEY);
showToast({
key: OBSERVED_REMOTE_MUTE_TOAST_KEY,
content,
dismissable: true,
autoClose: true,
lifetime: 10 * SECOND,
});
}, [
observedRemoteMute,
previousObservedRemoteMute,
conversationsByDemuxId,
i18n,
showToast,
hideToast,
OBSERVED_REMOTE_MUTE_TOAST_KEY,
]);
}
type CallingButtonToastsType = {
hasLocalAudio: boolean;
outgoingRing: boolean | undefined;
@ -331,7 +479,11 @@ type CallingButtonToastsType = {
suggestLowerHand?: boolean;
isHandRaised?: boolean;
handleLowerHand?: () => void;
mutedBy?: number;
observedRemoteMute?: ObservedRemoteMuteType;
conversationsByDemuxId?: Map<number, ConversationType>;
i18n: LocalizerType;
setLocalAudioRemoteMuted?: SetMutedByType;
};
export function CallingButtonToastsContainer(
@ -361,8 +513,12 @@ function CallingButtonToasts({
handleLowerHand,
isHandRaised,
i18n,
mutedBy,
observedRemoteMute,
conversationsByDemuxId,
setLocalAudioRemoteMuted,
}: CallingButtonToastsType) {
useMutedToast({ hasLocalAudio, i18n });
useMutedToast({ hasLocalAudio, mutedBy, i18n });
useOutgoingRingToast({ outgoingRing, i18n });
useRaisedHandsToast({ raisedHands, renderRaisedHandsToast });
useLowerHandSuggestionToast({
@ -371,6 +527,17 @@ function CallingButtonToasts({
handleLowerHand,
isHandRaised,
});
useMutedByToast({
mutedBy,
setLocalAudioRemoteMuted,
conversationsByDemuxId,
i18n,
});
useObservedRemoteMuteToast({
observedRemoteMute,
conversationsByDemuxId,
i18n,
});
return null;
}