) => client.connect({ url, token: peerToken, peerMetadata: peerMetadata ?? {} }),
+ const joinRoom: UseConnectionResult["joinRoom"] = useCallback(
+ ({ url, peerToken, peerMetadata }) => client.connect({ url, token: peerToken, peerMetadata: peerMetadata ?? {} }),
[client],
);
@@ -46,17 +69,8 @@ export function useConnection() {
const peerStatus = context.peerStatus;
return {
- /**
- * Join room and start streaming camera and microphone
- *
- * See {@link JoinRoomConfig} for parameter list
- */
joinRoom,
- /**
- * Leave room and stop streaming
- */
leaveRoom,
-
peerStatus,
reconnectionStatus,
};
diff --git a/packages/react-client/src/hooks/usePeers.ts b/packages/react-client/src/hooks/usePeers.ts
index edac0921..ea58db93 100644
--- a/packages/react-client/src/hooks/usePeers.ts
+++ b/packages/react-client/src/hooks/usePeers.ts
@@ -2,16 +2,31 @@ import type {
Component,
Endpoint,
FishjamTrackContext,
+ Metadata,
Peer,
TrackContext,
TrackMetadata,
} from "@fishjam-cloud/ts-client";
-import type { DistinguishedTracks, PeerState } from "../types/internal";
+import type { PeerId } from "../types/internal";
import type { Track } from "../types/public";
import { useFishjamContext } from "./internal/useFishjamContext";
-export type PeerWithTracks = PeerState
& DistinguishedTracks;
+/**
+ *
+ * @category Connection
+ * @typeParam PeerMetadata Type of metadata set by peer while connecting to a room.
+ * @typeParam ServerMetadata Type of metadata set by the server while creating a peer.
+ */
+export type PeerWithTracks = {
+ id: PeerId;
+ metadata?: Metadata;
+ tracks: Track[];
+ cameraTrack?: Track;
+ microphoneTrack?: Track;
+ screenShareVideoTrack?: Track;
+ screenShareAudioTrack?: Track;
+};
function trackContextToTrack(track: FishjamTrackContext | TrackContext): Track {
return {
@@ -20,7 +35,6 @@ function trackContextToTrack(track: FishjamTrackContext | TrackContext): Track {
stream: track.stream,
simulcastConfig: track.simulcastConfig ?? null,
encoding: track.encoding ?? null,
- vadStatus: track.vadStatus,
track: track.track,
};
}
@@ -45,41 +59,51 @@ function getPeerWithDistinguishedTracks(peer: Peer
| Component | End
}
/**
- * Result type for the usePeers hook.
+ *
+ * @category Connection
+ * @typeParam PeerMetadata Type of metadata set by peer while connecting to a room.
+ * @typeParam ServerMetadata Type of metadata set by the server while creating a peer.
*/
-export type UsePeersResult
= {
+export type UsePeersResult = {
/**
* The local peer with distinguished tracks (camera, microphone, screen share).
* Will be null if the local peer is not found.
*/
- localPeer: PeerWithTracks | null;
+ localPeer: PeerWithTracks | null;
/**
* Array of remote peers with distinguished tracks (camera, microphone, screen share).
*/
- remotePeers: PeerWithTracks[];
+ remotePeers: PeerWithTracks[];
/**
* @deprecated Use remotePeers instead
* Legacy array containing remote peers.
* This property will be removed in future versions.
*/
- peers: PeerWithTracks[];
+ peers: PeerWithTracks[];
};
/**
*
* @category Connection
* @group Hooks
- * @typeParam P Type of metadata set by peer while connecting to a room.
- * @typeParam S Type of metadata set by the server while creating a peer.
+ * @typeParam PeerMetadata Type of metadata set by peer while connecting to a room.
+ * @typeParam ServerMetadata Type of metadata set by the server while creating a peer.
*/
-export function usePeers, S = Record>(): UsePeersResult {
+export function usePeers<
+ PeerMetadata = Record,
+ ServerMetadata = Record,
+>(): UsePeersResult {
const { clientState } = useFishjamContext();
- const localPeer = clientState.localPeer ? getPeerWithDistinguishedTracks(clientState.localPeer) : null;
+ const localPeer = clientState.localPeer
+ ? getPeerWithDistinguishedTracks(clientState.localPeer)
+ : null;
- const remotePeers = Object.values(clientState.peers).map((peer) => getPeerWithDistinguishedTracks(peer));
+ const remotePeers = Object.values(clientState.peers).map((peer) =>
+ getPeerWithDistinguishedTracks(peer),
+ );
return { localPeer, remotePeers, peers: remotePeers };
}
diff --git a/packages/react-client/src/hooks/useScreenShare.ts b/packages/react-client/src/hooks/useScreenShare.ts
index 0eb9d212..6aa7036a 100644
--- a/packages/react-client/src/hooks/useScreenShare.ts
+++ b/packages/react-client/src/hooks/useScreenShare.ts
@@ -1,167 +1,29 @@
-import type { FishjamClient } from "@fishjam-cloud/ts-client";
-import { useCallback, useEffect, useRef, useState } from "react";
-
-import type { ScreenShareState } from "../types/internal";
-import type { PeerStatus, ScreenshareApi, TracksMiddleware } from "../types/public";
-import { getRemoteOrLocalTrack } from "../utils/track";
+import type { Track, TracksMiddleware } from "../types/public";
import { useFishjamContext } from "./internal/useFishjamContext";
-interface ScreenShareManagerProps {
- fishjamClient: FishjamClient;
- getCurrentPeerStatus: () => PeerStatus;
-}
-
-export const useScreenShareManager = ({
- fishjamClient,
- getCurrentPeerStatus,
-}: ScreenShareManagerProps): ScreenshareApi => {
- const [state, setState] = useState({ stream: null, trackIds: null });
-
- const cleanMiddlewareFnRef = useRef<(() => void) | null>(null);
-
- const stream = state.stream ?? null;
- const [mediaVideoTrack, mediaAudioTrack] = stream ? getTracksFromStream(stream) : [null, null];
-
- const getDisplayName = () => {
- const name = fishjamClient.getLocalPeer()?.metadata?.peer?.displayName;
- if (typeof name === "string") return name;
- };
-
- const startStreaming: ScreenshareApi["startStreaming"] = async (props) => {
- const displayStream = await navigator.mediaDevices.getDisplayMedia({
- video: props?.videoConstraints ?? true,
- audio: props?.audioConstraints ?? true,
- });
-
- const displayName = getDisplayName();
-
- let [video, audio] = getTracksFromStream(displayStream);
-
- if (state.tracksMiddleware) {
- const { videoTrack, audioTrack, onClear } = state.tracksMiddleware(video, audio);
- video = videoTrack;
- audio = audioTrack;
- cleanMiddlewareFnRef.current = onClear;
- }
-
- const addTrackPromises = [fishjamClient.addTrack(video, { displayName, type: "screenShareVideo", paused: false })];
- if (audio)
- addTrackPromises.push(fishjamClient.addTrack(audio, { displayName, type: "screenShareAudio", paused: false }));
-
- const [videoId, audioId] = await Promise.all(addTrackPromises);
- setState({ stream: displayStream, trackIds: { videoId, audioId } });
- };
-
- const replaceTracks = async (newVideoTrack: MediaStreamTrack, newAudioTrack: MediaStreamTrack | null) => {
- if (!state?.stream) return;
-
- const addTrackPromises = [fishjamClient.replaceTrack(state.trackIds.videoId, newVideoTrack)];
-
- if (newAudioTrack && state.trackIds.audioId) {
- addTrackPromises.push(fishjamClient.replaceTrack(state.trackIds.audioId, newAudioTrack));
- }
-
- await Promise.all(addTrackPromises);
- };
-
- const cleanMiddleware = useCallback(() => {
- cleanMiddlewareFnRef.current?.();
- cleanMiddlewareFnRef.current = null;
- }, []);
-
- const setTracksMiddleware = async (middleware: TracksMiddleware | null): Promise => {
- if (!state?.stream) return;
-
- const [video, audio] = getTracksFromStream(state.stream);
-
- cleanMiddleware();
-
- const { videoTrack, audioTrack, onClear } = middleware?.(video, audio) ?? {
- videoTrack: video,
- audioTrack: audio,
- onClear: null,
- };
- cleanMiddlewareFnRef.current = onClear;
- await replaceTracks(videoTrack, audioTrack);
- };
-
- const stopStreaming: ScreenshareApi["stopStreaming"] = useCallback(async () => {
- if (!state.stream) {
- console.warn("No stream to stop");
- return;
- }
- const [video, audio] = getTracksFromStream(state.stream);
-
- video.stop();
- if (audio) audio.stop();
-
- if (getCurrentPeerStatus() === "connected") {
- const removeTrackPromises = [fishjamClient.removeTrack(state.trackIds.videoId)];
- if (state.trackIds.audioId) removeTrackPromises.push(fishjamClient.removeTrack(state.trackIds.audioId));
-
- await Promise.all(removeTrackPromises);
- }
-
- cleanMiddleware();
- setState(({ tracksMiddleware }) => ({ stream: null, trackIds: null, tracksMiddleware }));
- }, [state, fishjamClient, setState, cleanMiddleware, getCurrentPeerStatus]);
-
- useEffect(() => {
- if (!state.stream) return;
- const [video, audio] = getTracksFromStream(state.stream);
-
- const trackEndedHandler = () => {
- stopStreaming();
- };
-
- video.addEventListener("ended", trackEndedHandler);
- audio?.addEventListener("ended", trackEndedHandler);
-
- return () => {
- video.removeEventListener("ended", trackEndedHandler);
- audio?.removeEventListener("ended", trackEndedHandler);
- };
- }, [state, stopStreaming]);
-
- useEffect(() => {
- const onDisconnected = () => {
- if (stream) {
- stopStreaming();
- }
- };
- fishjamClient.on("disconnected", onDisconnected);
-
- return () => {
- fishjamClient.removeListener("disconnected", onDisconnected);
- };
- }, [stopStreaming, fishjamClient, stream]);
-
- const videoBroadcast = state.stream ? getRemoteOrLocalTrack(fishjamClient, state.trackIds.videoId) : null;
- const audioBroadcast = state.trackIds?.audioId ? getRemoteOrLocalTrack(fishjamClient, state.trackIds.audioId) : null;
-
- return {
- startStreaming,
- stopStreaming,
- stream,
- videoTrack: mediaVideoTrack,
- audioTrack: mediaAudioTrack,
- videoBroadcast,
- audioBroadcast,
- setTracksMiddleware,
- currentTracksMiddleware: state?.tracksMiddleware ?? null,
- };
-};
-
-const getTracksFromStream = (stream: MediaStream): [MediaStreamTrack, MediaStreamTrack | null] => {
- const video = stream.getVideoTracks()[0];
- const audio = stream.getAudioTracks()[0] ?? null;
-
- return [video, audio];
+/**
+ *
+ * @category Devices
+ * @group Types
+ */
+export type UseScreenshareResult = {
+ startStreaming: (props?: {
+ audioConstraints?: boolean | MediaTrackConstraints;
+ videoConstraints?: boolean | MediaTrackConstraints;
+ }) => Promise;
+ stopStreaming: () => Promise;
+ stream: MediaStream | null;
+ videoTrack: MediaStreamTrack | null;
+ audioTrack: MediaStreamTrack | null;
+ videoBroadcast: Track | null;
+ audioBroadcast: Track | null;
+ setTracksMiddleware: (middleware: TracksMiddleware | null) => Promise;
+ currentTracksMiddleware: TracksMiddleware | null;
};
/**
*
- * @category Connection
+ * @category Devices
* @group Hooks
*/
export const useScreenShare = () => {
diff --git a/packages/react-client/src/hooks/useUpdatePeerMetadata.ts b/packages/react-client/src/hooks/useUpdatePeerMetadata.ts
index 6a071216..e84c70be 100644
--- a/packages/react-client/src/hooks/useUpdatePeerMetadata.ts
+++ b/packages/react-client/src/hooks/useUpdatePeerMetadata.ts
@@ -4,7 +4,7 @@ import { useCallback } from "react";
import { useFishjamContext } from "./internal/useFishjamContext";
/**
- *
+ * Hook provides a method to update the metadata of the local peer
* @category Connection
* @group Hooks
* @returns
diff --git a/packages/react-client/src/hooks/useVAD.ts b/packages/react-client/src/hooks/useVAD.ts
index 03eb0438..e5673306 100644
--- a/packages/react-client/src/hooks/useVAD.ts
+++ b/packages/react-client/src/hooks/useVAD.ts
@@ -7,10 +7,10 @@ import { useFishjamContext } from "./internal/useFishjamContext";
/**
*
- * @param peerIds
+ * @param peerIds List of ids of peers to subscribe to for voice activity detection notifications.
* @category Connection
* @group Hooks
- * @returns
+ * @returns Each key is a peerId and the boolean value indicates if voice activity is currently detected for that peer.
*/
export const useVAD = (peerIds: PeerId[]): Record => {
const { fishjamClientRef } = useFishjamContext();
diff --git a/packages/react-client/src/index.ts b/packages/react-client/src/index.ts
index 99582fd6..610a05f5 100644
--- a/packages/react-client/src/index.ts
+++ b/packages/react-client/src/index.ts
@@ -1,39 +1,28 @@
-export { FishjamProvider } from "./FishjamProvider";
-export { useCamera } from "./hooks/devices/useCamera";
-export { useInitializeDevices } from "./hooks/devices/useInitializeDevices";
-export { useMicrophone } from "./hooks/devices/useMicrophone";
-export type { JoinRoomConfig } from "./hooks/useConnection";
-export { useConnection } from "./hooks/useConnection";
-export type { PeerWithTracks } from "./hooks/usePeers";
-export { usePeers } from "./hooks/usePeers";
-export { useScreenShare } from "./hooks/useScreenShare";
+export { FishjamProvider, type FishjamProviderProps } from "./FishjamProvider";
+export { useCamera, type UseCameraResult } from "./hooks/devices/useCamera";
+export { useInitializeDevices, type UseInitializeDevicesResult } from "./hooks/devices/useInitializeDevices";
+export { useMicrophone, type UseMicrophoneResult } from "./hooks/devices/useMicrophone";
+export { type JoinRoomConfig, useConnection, type UseConnectionResult } from "./hooks/useConnection";
+export { type PeerWithTracks, usePeers, type UsePeersResult } from "./hooks/usePeers";
+export { useScreenShare, type UseScreenshareResult } from "./hooks/useScreenShare";
export { useUpdatePeerMetadata } from "./hooks/useUpdatePeerMetadata";
export { useVAD } from "./hooks/useVAD";
export type {
- Device,
DeviceItem,
- DeviceType,
PeerStatus,
PersistLastDeviceHandlers,
- ScreenshareApi,
- StartStreamingProps,
+ StreamConfig,
Track,
TrackMiddleware,
TracksMiddleware,
} from "./types/public";
export type {
AuthErrorReason,
- BandwidthLimit,
- CreateConfig,
- EncodingReason,
- MessageEvents,
- Peer,
+ Metadata,
+ ReconnectConfig,
ReconnectionStatus,
SimulcastBandwidthLimit,
SimulcastConfig,
TrackBandwidthLimit,
- TrackContext,
- TrackContextEvents,
- VadStatus,
} from "@fishjam-cloud/ts-client";
export { Variant } from "@fishjam-cloud/ts-client";
diff --git a/packages/react-client/src/types/internal.ts b/packages/react-client/src/types/internal.ts
index e071eeae..82e7a2b1 100644
--- a/packages/react-client/src/types/internal.ts
+++ b/packages/react-client/src/types/internal.ts
@@ -1,16 +1,8 @@
-import type { Peer } from "@fishjam-cloud/ts-client";
-
import type { DeviceType, Track, TrackMiddleware, TracksMiddleware } from "./public";
export type TrackId = string;
export type PeerId = string;
-export type PeerState = {
- id: PeerId;
- metadata?: Peer
["metadata"];
- tracks: Track[];
-};
-
export type DevicesStatus = "OK" | "Error" | "Not requested" | "Requesting";
export type MediaStatus = "OK" | "Error" | "Not requested" | "Requesting";
@@ -89,10 +81,3 @@ export interface TrackManager {
*/
toggleDevice: () => Promise;
}
-
-export type DistinguishedTracks = {
- cameraTrack?: Track;
- microphoneTrack?: Track;
- screenShareVideoTrack?: Track;
- screenShareAudioTrack?: Track;
-};
diff --git a/packages/react-client/src/types/public.ts b/packages/react-client/src/types/public.ts
index 120fe34c..730f89a5 100644
--- a/packages/react-client/src/types/public.ts
+++ b/packages/react-client/src/types/public.ts
@@ -1,4 +1,4 @@
-import type { SimulcastConfig, TrackMetadata, VadStatus, Variant } from "@fishjam-cloud/ts-client";
+import type { SimulcastConfig, TrackMetadata, Variant } from "@fishjam-cloud/ts-client";
import type { DeviceError, DeviceManagerStatus, TrackId } from "./internal";
@@ -8,7 +8,6 @@ export type Track = {
trackId: TrackId;
metadata?: TrackMetadata;
simulcastConfig: SimulcastConfig | null;
- vadStatus: VadStatus;
track: MediaStreamTrack | null;
};
@@ -47,28 +46,13 @@ export type PersistLastDeviceHandlers = {
saveLastDevice: (info: MediaDeviceInfo) => void;
};
-export type ScreenshareApi = {
- startStreaming: (props?: {
- audioConstraints?: boolean | MediaTrackConstraints;
- videoConstraints?: boolean | MediaTrackConstraints;
- }) => Promise;
- stopStreaming: () => Promise;
- stream: MediaStream | null;
- videoTrack: MediaStreamTrack | null;
- audioTrack: MediaStreamTrack | null;
- videoBroadcast: Track | null;
- audioBroadcast: Track | null;
- setTracksMiddleware: (middleware: TracksMiddleware | null) => Promise;
- currentTracksMiddleware: TracksMiddleware | null;
-};
-
export type SimulcastBandwidthLimits = {
[Variant.VARIANT_LOW]: number;
[Variant.VARIANT_MEDIUM]: number;
[Variant.VARIANT_HIGH]: number;
};
-export type StartStreamingProps = { simulcast?: Variant[] | false };
+export type StreamConfig = { simulcast?: Variant[] | false };
export type BandwidthLimits = { singleStream: number; simulcast: SimulcastBandwidthLimits };
diff --git a/packages/react-client/src/utils/track.ts b/packages/react-client/src/utils/track.ts
index b2082e82..a6c93ae7 100644
--- a/packages/react-client/src/utils/track.ts
+++ b/packages/react-client/src/utils/track.ts
@@ -33,7 +33,6 @@ const getTrackFromContext = (context: TrackContext): Track => ({
stream: context.stream,
simulcastConfig: context.simulcastConfig || null,
encoding: context.encoding || null,
- vadStatus: context.vadStatus,
track: context.track,
});
diff --git a/packages/ts-client/src/types.ts b/packages/ts-client/src/types.ts
index 6c716724..923cbaf5 100644
--- a/packages/ts-client/src/types.ts
+++ b/packages/ts-client/src/types.ts
@@ -21,9 +21,15 @@ export type TrackMetadata = {
export type GenericMetadata = Record | undefined;
-export type Metadata = {
- peer: P;
- server: S;
+/**
+ *
+ * @category Connection
+ * @typeParam PeerMetadata Type of metadata set by peer while connecting to a room.
+ * @typeParam ServerMetadata Type of metadata set by the server while creating a peer.
+ */
+export type Metadata = {
+ peer: PeerMetadata;
+ server: ServerMetadata;
};
type TrackContextEvents = {