From 20c9c092586983e206a37576a23346e9dc012aea Mon Sep 17 00:00:00 2001 From: Enrico Schwendig Date: Wed, 14 Jun 2023 15:03:38 +0200 Subject: [PATCH] Build EC room use method (#1108) * Build custom useRoom --- src/room/InCallView.tsx | 28 +++++---- src/room/useRoom.ts | 135 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+), 12 deletions(-) create mode 100644 src/room/useRoom.ts diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index d9005c0..5b7c000 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -16,7 +16,6 @@ limitations under the License. import { ResizeObserver } from "@juggle/resize-observer"; import { - useLiveKitRoom, useLocalParticipant, useParticipants, useToken, @@ -79,6 +78,7 @@ import { InviteModal } from "./InviteModal"; import { useRageshakeRequestModal } from "../settings/submit-rageshake"; import { RageshakeRequestModal } from "./RageshakeRequestModal"; import { VideoTile } from "../video-grid/VideoTile"; +import { useRoom } from "./useRoom"; const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {}); // There is currently a bug in Safari our our code with cloning and sending MediaStreams @@ -86,6 +86,16 @@ const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {}); // For now we can disable screensharing in Safari. const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); +const onConnectedCallback = (): void => { + console.log("connected to LiveKit room"); +}; +const onDisconnectedCallback = (): void => { + console.log("disconnected from LiveKit room"); +}; +const onErrorCallback = (err: Error): void => { + console.error("error connecting to LiveKit room", err); +}; + interface LocalUserChoices { videoMuted: boolean; audioMuted: boolean; @@ -150,22 +160,16 @@ export function InCallView({ options ); - // Uses a hook to connect to the LiveKit room (on unmount the room will be left) and publish local media tracks (default). - useLiveKitRoom({ + useRoom({ token, serverUrl: Config.get().livekit.server_url, room: livekitRoom, audio: !userChoices.audioMuted, video: !userChoices.videoMuted, - onConnected: () => { - console.log("connected to LiveKit room"); - }, - onDisconnected: () => { - console.log("disconnected from LiveKit room"); - }, - onError: (err) => { - console.error("error connecting to LiveKit room", err); - }, + simulateParticipants: 10, + onConnected: onConnectedCallback, + onDisconnected: onDisconnectedCallback, + onError: onErrorCallback, }); const screenSharingTracks = useTracks( diff --git a/src/room/useRoom.ts b/src/room/useRoom.ts new file mode 100644 index 0000000..63bb153 --- /dev/null +++ b/src/room/useRoom.ts @@ -0,0 +1,135 @@ +import * as React from "react"; +import { + ConnectionState, + MediaDeviceFailure, + Room, + RoomEvent, +} from "livekit-client"; +import { LiveKitRoomProps } from "@livekit/components-react/src/components/LiveKitRoom"; +import { logger } from "matrix-js-sdk/src/logger"; + +const defaultRoomProps: Partial = { + connect: true, + audio: false, + video: false, +}; + +export function useRoom(props: LiveKitRoomProps) { + const { + token, + serverUrl, + options, + room: passedRoom, + connectOptions, + connect, + audio, + video, + screen, + onConnected, + onDisconnected, + onError, + onMediaDeviceFailure, + } = { ...defaultRoomProps, ...props }; + if (options && passedRoom) { + logger.warn( + "when using a manually created room, the options object will be ignored. set the desired options directly when creating the room instead." + ); + } + + const [room, setRoom] = React.useState(); + + React.useEffect(() => { + setRoom(passedRoom ?? new Room(options)); + }, [options, passedRoom]); + + React.useEffect(() => { + if (!room) return; + const onSignalConnected = () => { + const localP = room.localParticipant; + try { + logger.debug("trying to publish local tracks"); + localP.setMicrophoneEnabled( + !!audio, + typeof audio !== "boolean" ? audio : undefined + ); + localP.setCameraEnabled( + !!video, + typeof video !== "boolean" ? video : undefined + ); + localP.setScreenShareEnabled( + !!screen, + typeof screen !== "boolean" ? screen : undefined + ); + } catch (e) { + logger.warn(e); + onError?.(e as Error); + } + }; + + const onMediaDeviceError = (e: Error) => { + const mediaDeviceFailure = MediaDeviceFailure.getFailure(e); + onMediaDeviceFailure?.(mediaDeviceFailure); + }; + room.on(RoomEvent.SignalConnected, onSignalConnected); + room.on(RoomEvent.MediaDevicesError, onMediaDeviceError); + + return () => { + room.off(RoomEvent.SignalConnected, onSignalConnected); + room.off(RoomEvent.MediaDevicesError, onMediaDeviceError); + }; + }, [room, audio, video, screen, onError, onMediaDeviceFailure]); + + React.useEffect(() => { + if (!room) return; + if (!token) { + logger.debug("no token yet"); + return; + } + if (!serverUrl) { + logger.warn("no livekit url provided"); + onError?.(Error("no livekit url provided")); + return; + } + if (connect) { + logger.debug("connecting"); + room.connect(serverUrl, token, connectOptions).catch((e) => { + logger.warn(e); + onError?.(e as Error); + }); + } else { + logger.debug("disconnecting because connect is false"); + room.disconnect(); + } + }, [connect, token, connectOptions, room, onError, serverUrl]); + + React.useEffect(() => { + if (!room) return; + const connectionStateChangeListener = (state: ConnectionState) => { + switch (state) { + case ConnectionState.Disconnected: + if (onDisconnected) onDisconnected(); + break; + case ConnectionState.Connected: + if (onConnected) onConnected(); + break; + + default: + break; + } + }; + room.on(RoomEvent.ConnectionStateChanged, connectionStateChangeListener); + return () => { + room.off(RoomEvent.ConnectionStateChanged, connectionStateChangeListener); + }; + }, [token, onConnected, onDisconnected, room]); + + React.useEffect(() => { + if (!room) return; + return () => { + logger.info("disconnecting on onmount"); + room.disconnect(); + }; + }, [room]); + + return { room }; +}