mirror of
https://github.com/vector-im/element-call.git
synced 2024-11-15 00:04:59 +08:00
Add DeviceMute widget action io.element.device_mute
. (#2482)
* Add DeviceMute widget action `io.element.device_mute`. This allows to send mute requests ("toWidget") and get the current mute state as a response. And it will update the client about each change of mute states. * review + better explanation * review * add comments * use `useCallback`
This commit is contained in:
parent
2b67a9cfbe
commit
f53ea75c94
@ -14,10 +14,18 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Dispatch, SetStateAction, useMemo } from "react";
|
import {
|
||||||
|
Dispatch,
|
||||||
|
SetStateAction,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
} from "react";
|
||||||
|
import { IWidgetApiRequest } from "matrix-widget-api";
|
||||||
|
|
||||||
import { MediaDevice, useMediaDevices } from "../livekit/MediaDevicesContext";
|
import { MediaDevice, useMediaDevices } from "../livekit/MediaDevicesContext";
|
||||||
import { useReactiveState } from "../useReactiveState";
|
import { useReactiveState } from "../useReactiveState";
|
||||||
|
import { ElementWidgetActions, widget } from "../widget";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If there already are this many participants in the call, we automatically mute
|
* If there already are this many participants in the call, we automatically mute
|
||||||
@ -74,5 +82,62 @@ export function useMuteStates(): MuteStates {
|
|||||||
const audio = useMuteState(devices.audioInput, () => true);
|
const audio = useMuteState(devices.audioInput, () => true);
|
||||||
const video = useMuteState(devices.videoInput, () => true);
|
const video = useMuteState(devices.videoInput, () => true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
widget?.api.transport.send(ElementWidgetActions.DeviceMute, {
|
||||||
|
audio_enabled: audio.enabled,
|
||||||
|
video_enabled: video.enabled,
|
||||||
|
});
|
||||||
|
}, [audio, video]);
|
||||||
|
|
||||||
|
const onMuteStateChangeRequest = useCallback(
|
||||||
|
(ev: CustomEvent<IWidgetApiRequest>) => {
|
||||||
|
// First copy the current state into our new state.
|
||||||
|
const newState = {
|
||||||
|
audio_enabled: audio.enabled,
|
||||||
|
video_enabled: video.enabled,
|
||||||
|
};
|
||||||
|
// Update new state if there are any requested changes from the widget action
|
||||||
|
// in `ev.detail.data`.
|
||||||
|
if (
|
||||||
|
ev.detail.data.audio_enabled != null &&
|
||||||
|
typeof ev.detail.data.audio_enabled === "boolean"
|
||||||
|
) {
|
||||||
|
audio.setEnabled?.(ev.detail.data.audio_enabled);
|
||||||
|
newState.audio_enabled = ev.detail.data.audio_enabled;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
ev.detail.data.video_enabled != null &&
|
||||||
|
typeof ev.detail.data.video_enabled === "boolean"
|
||||||
|
) {
|
||||||
|
video.setEnabled?.(ev.detail.data.video_enabled);
|
||||||
|
newState.video_enabled = ev.detail.data.video_enabled;
|
||||||
|
}
|
||||||
|
// Always reply with the new (now "current") state.
|
||||||
|
// This allows to also use this action to just get the unaltered current state
|
||||||
|
// by using a fromWidget request with: `ev.detail.data = {}`
|
||||||
|
widget!.api.transport.reply(ev.detail, newState);
|
||||||
|
},
|
||||||
|
[audio, video],
|
||||||
|
);
|
||||||
|
useEffect(() => {
|
||||||
|
// We setup a event listener for the widget action ElementWidgetActions.DeviceMute.
|
||||||
|
if (widget) {
|
||||||
|
// only setup the listener in widget mode
|
||||||
|
|
||||||
|
widget.lazyActions.on(
|
||||||
|
ElementWidgetActions.DeviceMute,
|
||||||
|
onMuteStateChangeRequest,
|
||||||
|
);
|
||||||
|
|
||||||
|
return (): void => {
|
||||||
|
// return a call to `off` so that we always clean up our listener.
|
||||||
|
widget?.lazyActions.off(
|
||||||
|
ElementWidgetActions.DeviceMute,
|
||||||
|
onMuteStateChangeRequest,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [onMuteStateChangeRequest]);
|
||||||
|
|
||||||
return useMemo(() => ({ audio, video }), [audio, video]);
|
return useMemo(() => ({ audio, video }), [audio, video]);
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,21 @@ export enum ElementWidgetActions {
|
|||||||
// host -> Element Call telling EC to stop screen sharing, or that
|
// host -> Element Call telling EC to stop screen sharing, or that
|
||||||
// the user cancelled when selecting a source after a ScreenshareRequest
|
// the user cancelled when selecting a source after a ScreenshareRequest
|
||||||
ScreenshareStop = "io.element.screenshare_stop",
|
ScreenshareStop = "io.element.screenshare_stop",
|
||||||
|
// This can be sent as from or to widget
|
||||||
|
// fromWidget: updates the client about the current device mute state
|
||||||
|
// toWidget: the client requests a specific device mute configuration
|
||||||
|
// The reply will always be the resulting configuration
|
||||||
|
// It is possible to sent an empty configuration to retrieve the current values or
|
||||||
|
// just one of the fields to update that particular value
|
||||||
|
// An undefined field means that EC will keep the mute state as is.
|
||||||
|
// -> this will allow the client to only get the current state
|
||||||
|
//
|
||||||
|
// The data of the widget action request and the response are:
|
||||||
|
// {
|
||||||
|
// audio_enabled?: boolean,
|
||||||
|
// video_enabled?: boolean
|
||||||
|
// }
|
||||||
|
DeviceMute = "io.element.device_mute",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface JoinCallData {
|
export interface JoinCallData {
|
||||||
@ -88,6 +103,7 @@ export const widget = ((): WidgetHelpers | null => {
|
|||||||
ElementWidgetActions.SpotlightLayout,
|
ElementWidgetActions.SpotlightLayout,
|
||||||
ElementWidgetActions.ScreenshareStart,
|
ElementWidgetActions.ScreenshareStart,
|
||||||
ElementWidgetActions.ScreenshareStop,
|
ElementWidgetActions.ScreenshareStop,
|
||||||
|
ElementWidgetActions.DeviceMute,
|
||||||
].forEach((action) => {
|
].forEach((action) => {
|
||||||
api.on(`action:${action}`, (ev: CustomEvent<IWidgetApiRequest>) => {
|
api.on(`action:${action}`, (ev: CustomEvent<IWidgetApiRequest>) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
Loading…
Reference in New Issue
Block a user