From c70816d76322c62e21d96d2e914193a076fd9b3c Mon Sep 17 00:00:00 2001 From: Robin Date: Thu, 21 Apr 2022 11:00:32 -0400 Subject: [PATCH] Persist audio and video mute state in video rooms (#8376) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …so that video lobbies remember whether you've disabled your camera. --- src/components/views/voip/VideoLobby.tsx | 16 ++++++--- src/stores/VideoChannelStore.ts | 44 +++++++++++++++++++++++- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/src/components/views/voip/VideoLobby.tsx b/src/components/views/voip/VideoLobby.tsx index f9e95089270..862e58fe630 100644 --- a/src/components/views/voip/VideoLobby.tsx +++ b/src/components/views/voip/VideoLobby.tsx @@ -21,7 +21,6 @@ import { Room } from "matrix-js-sdk/src/models/room"; import { _t } from "../../../languageHandler"; import { useAsyncMemo } from "../../../hooks/useAsyncMemo"; -import { useStateToggle } from "../../../hooks/useStateToggle"; import { useConnectedMembers } from "../../../utils/VideoChannelUtils"; import VideoChannelStore from "../../../stores/VideoChannelStore"; import IconizedContextMenu, { @@ -108,6 +107,7 @@ const DeviceButton: FC = ({ const MAX_FACES = 8; const VideoLobby: FC<{ room: Room }> = ({ room }) => { + const store = VideoChannelStore.instance; const [connecting, setConnecting] = useState(false); const me = useMemo(() => room.getMember(room.myUserId), [room]); const connectedMembers = useConnectedMembers(room, false); @@ -130,8 +130,16 @@ const VideoLobby: FC<{ room: Room }> = ({ room }) => { const audioDevice = selectedAudioDevice ?? audioDevices[0]; const videoDevice = selectedVideoDevice ?? videoDevices[0]; - const [audioActive, toggleAudio] = useStateToggle(true); - const [videoActive, toggleVideo] = useStateToggle(true); + const [audioActive, setAudioActive] = useState(!store.audioMuted); + const [videoActive, setVideoActive] = useState(!store.videoMuted); + const toggleAudio = () => { + store.audioMuted = audioActive; + setAudioActive(!audioActive); + }; + const toggleVideo = () => { + store.videoMuted = videoActive; + setVideoActive(!videoActive); + }; const videoStream = useAsyncMemo(async () => { if (videoDevice && videoActive) { @@ -162,7 +170,7 @@ const VideoLobby: FC<{ room: Room }> = ({ room }) => { const connect = async () => { setConnecting(true); try { - await VideoChannelStore.instance.connect( + await store.connect( room.roomId, audioActive ? audioDevice : null, videoActive ? videoDevice : null, ); } catch (e) { diff --git a/src/stores/VideoChannelStore.ts b/src/stores/VideoChannelStore.ts index 14016800487..d32f748fb7f 100644 --- a/src/stores/VideoChannelStore.ts +++ b/src/stores/VideoChannelStore.ts @@ -94,6 +94,20 @@ export default class VideoChannelStore extends AsyncStoreWithClient { public get participants(): IJitsiParticipant[] { return this._participants; } private set participants(value: IJitsiParticipant[]) { this._participants = value; } + private _audioMuted = localStorage.getItem("mx_audioMuted") === "true"; + public get audioMuted(): boolean { return this._audioMuted; } + public set audioMuted(value: boolean) { + this._audioMuted = value; + localStorage.setItem("mx_audioMuted", value.toString()); + } + + private _videoMuted = localStorage.getItem("mx_videoMuted") === "true"; + public get videoMuted(): boolean { return this._videoMuted; } + public set videoMuted(value: boolean) { + this._videoMuted = value; + localStorage.setItem("mx_videoMuted", value.toString()); + } + public connect = async (roomId: string, audioDevice: MediaDeviceInfo, videoDevice: MediaDeviceInfo) => { if (this.activeChannel) await this.disconnect(); @@ -136,10 +150,14 @@ export default class VideoChannelStore extends AsyncStoreWithClient { } } + // Participant data and mute state will come down the event pipeline quickly, so prepare in advance this.activeChannel = messaging; this.roomId = roomId; - // Participant data will come down the event pipeline quickly, so prepare in advance messaging.on(`action:${ElementWidgetActions.CallParticipants}`, this.onParticipants); + messaging.on(`action:${ElementWidgetActions.MuteAudio}`, this.onMuteAudio); + messaging.on(`action:${ElementWidgetActions.UnmuteAudio}`, this.onUnmuteAudio); + messaging.on(`action:${ElementWidgetActions.MuteVideo}`, this.onMuteVideo); + messaging.on(`action:${ElementWidgetActions.UnmuteVideo}`, this.onUnmuteVideo); this.emit(VideoChannelEvent.StartConnect, roomId); @@ -163,6 +181,10 @@ export default class VideoChannelStore extends AsyncStoreWithClient { this.activeChannel = null; this.roomId = null; messaging.off(`action:${ElementWidgetActions.CallParticipants}`, this.onParticipants); + messaging.off(`action:${ElementWidgetActions.MuteAudio}`, this.onMuteAudio); + messaging.off(`action:${ElementWidgetActions.UnmuteAudio}`, this.onUnmuteAudio); + messaging.off(`action:${ElementWidgetActions.MuteVideo}`, this.onMuteVideo); + messaging.off(`action:${ElementWidgetActions.UnmuteVideo}`, this.onUnmuteVideo); this.emit(VideoChannelEvent.Disconnect, roomId); @@ -238,4 +260,24 @@ export default class VideoChannelStore extends AsyncStoreWithClient { this.emit(VideoChannelEvent.Participants, this.roomId, ev.detail.data.participants); this.ack(ev); }; + + private onMuteAudio = (ev: CustomEvent) => { + this.audioMuted = true; + this.ack(ev); + }; + + private onUnmuteAudio = (ev: CustomEvent) => { + this.audioMuted = false; + this.ack(ev); + }; + + private onMuteVideo = (ev: CustomEvent) => { + this.videoMuted = true; + this.ack(ev); + }; + + private onUnmuteVideo = (ev: CustomEvent) => { + this.videoMuted = false; + this.ack(ev); + }; }