@fishjam-cloud/react-client 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/DeviceManager.d.ts +3 -4
- package/dist/DeviceManager.d.ts.map +1 -1
- package/dist/DeviceManager.js +25 -20
- package/dist/constraints.d.ts +2 -21
- package/dist/constraints.d.ts.map +1 -1
- package/dist/constraints.js +3 -7
- package/dist/fishjamProvider.d.ts +2 -2
- package/dist/fishjamProvider.d.ts.map +1 -1
- package/dist/fishjamProvider.js +32 -23
- package/dist/hooks/deviceManagers/useAudioDeviceManager.d.ts +2 -0
- package/dist/hooks/deviceManagers/useAudioDeviceManager.d.ts.map +1 -0
- package/dist/hooks/deviceManagers/useAudioDeviceManager.js +7 -0
- package/dist/hooks/deviceManagers/useDeviceManager.d.ts +4 -0
- package/dist/hooks/deviceManagers/useDeviceManager.d.ts.map +1 -0
- package/dist/hooks/deviceManagers/useDeviceManager.js +39 -0
- package/dist/hooks/deviceManagers/useVideoDeviceManager.d.ts +2 -0
- package/dist/hooks/deviceManagers/useVideoDeviceManager.d.ts.map +1 -0
- package/dist/hooks/deviceManagers/useVideoDeviceManager.js +7 -0
- package/dist/hooks/internal.d.ts +2 -0
- package/dist/hooks/internal.d.ts.map +1 -0
- package/dist/hooks/internal.js +1 -0
- package/dist/hooks/public.d.ts +8 -6
- package/dist/hooks/public.d.ts.map +1 -1
- package/dist/hooks/public.js +8 -6
- package/dist/hooks/useConnection.d.ts +4 -0
- package/dist/hooks/useConnection.d.ts.map +1 -0
- package/dist/hooks/useConnection.js +12 -0
- package/dist/hooks/useDevices.d.ts +7 -0
- package/dist/hooks/useDevices.d.ts.map +1 -0
- package/dist/hooks/useDevices.js +84 -0
- package/dist/hooks/useFishjamClientState.d.ts +11 -0
- package/dist/hooks/useFishjamClientState.d.ts.map +1 -0
- package/dist/hooks/{clientState.js → useFishjamClientState.js} +53 -34
- package/dist/hooks/useFishjamClient_DO_NOT_USE.d.ts +2 -0
- package/dist/hooks/useFishjamClient_DO_NOT_USE.d.ts.map +1 -0
- package/dist/hooks/useFishjamClient_DO_NOT_USE.js +6 -0
- package/dist/hooks/useFishjamContext.d.ts +20 -0
- package/dist/hooks/useFishjamContext.d.ts.map +1 -0
- package/dist/hooks/useParticipantStatus.d.ts +8 -0
- package/dist/hooks/useParticipantStatus.d.ts.map +1 -0
- package/dist/hooks/useParticipantStatus.js +39 -0
- package/dist/hooks/{participants.d.ts → useParticipants.d.ts} +1 -1
- package/dist/hooks/useParticipants.d.ts.map +1 -0
- package/dist/hooks/useParticipants.js +16 -0
- package/dist/hooks/useProcessedPreviewStream.d.ts +3 -0
- package/dist/hooks/useProcessedPreviewStream.d.ts.map +1 -0
- package/dist/hooks/useProcessedPreviewStream.js +17 -0
- package/dist/hooks/useReconnection.d.ts +3 -0
- package/dist/hooks/useReconnection.d.ts.map +1 -0
- package/dist/hooks/useReconnection.js +27 -0
- package/dist/hooks/{screenShare.d.ts → useScreenShare.d.ts} +1 -1
- package/dist/hooks/useScreenShare.d.ts.map +1 -0
- package/dist/hooks/{screenShare.js → useScreenShare.js} +49 -24
- package/dist/hooks/useStatus.d.ts +2 -0
- package/dist/hooks/useStatus.d.ts.map +1 -0
- package/dist/hooks/useStatus.js +4 -0
- package/dist/hooks/useTrackManager.d.ts +11 -0
- package/dist/hooks/useTrackManager.d.ts.map +1 -0
- package/dist/hooks/useTrackManager.js +176 -0
- package/dist/index.d.ts +2 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/state.types.d.ts +11 -24
- package/dist/state.types.d.ts.map +1 -1
- package/dist/types.d.ts +56 -36
- package/dist/types.d.ts.map +1 -1
- package/package.json +12 -15
- package/dist/Client.d.ts +0 -198
- package/dist/Client.d.ts.map +0 -1
- package/dist/Client.js +0 -461
- package/dist/hooks/clientState.d.ts +0 -4
- package/dist/hooks/clientState.d.ts.map +0 -1
- package/dist/hooks/connection.d.ts +0 -6
- package/dist/hooks/connection.d.ts.map +0 -1
- package/dist/hooks/connection.js +0 -28
- package/dist/hooks/devices.d.ts +0 -4
- package/dist/hooks/devices.d.ts.map +0 -1
- package/dist/hooks/devices.js +0 -9
- package/dist/hooks/fishjamContext.d.ts +0 -4
- package/dist/hooks/fishjamContext.d.ts.map +0 -1
- package/dist/hooks/participants.d.ts.map +0 -1
- package/dist/hooks/participants.js +0 -13
- package/dist/hooks/screenShare.d.ts.map +0 -1
- package/dist/hooks/setupMedia.d.ts +0 -3
- package/dist/hooks/setupMedia.d.ts.map +0 -1
- package/dist/hooks/setupMedia.js +0 -200
- package/dist/hooks/state.d.ts +0 -6
- package/dist/hooks/state.d.ts.map +0 -1
- package/dist/hooks/state.js +0 -15
- package/dist/trackManager.d.ts +0 -9
- package/dist/trackManager.d.ts.map +0 -1
- package/dist/trackManager.js +0 -116
- /package/dist/hooks/{fishjamContext.js → useFishjamContext.js} +0 -0
|
@@ -1,38 +1,29 @@
|
|
|
1
|
-
import { useCallback, useRef, useSyncExternalStore } from "react";
|
|
1
|
+
import { useCallback, useMemo, useRef, useSyncExternalStore } from "react";
|
|
2
2
|
const eventNames = [
|
|
3
|
-
"socketOpen",
|
|
4
|
-
"socketError",
|
|
5
3
|
"socketClose",
|
|
4
|
+
"socketError",
|
|
5
|
+
"socketOpen",
|
|
6
6
|
"authSuccess",
|
|
7
7
|
"authError",
|
|
8
8
|
"disconnected",
|
|
9
|
-
"
|
|
10
|
-
"joinError",
|
|
11
|
-
"peerJoined",
|
|
12
|
-
"peerUpdated",
|
|
13
|
-
"peerLeft",
|
|
9
|
+
"reconnectionStarted",
|
|
14
10
|
"reconnected",
|
|
15
11
|
"reconnectionRetriesLimitReached",
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"componentUpdated",
|
|
19
|
-
"componentRemoved",
|
|
12
|
+
"joined",
|
|
13
|
+
"joinError",
|
|
20
14
|
"trackReady",
|
|
21
15
|
"trackAdded",
|
|
22
16
|
"trackRemoved",
|
|
23
17
|
"trackUpdated",
|
|
18
|
+
"peerJoined",
|
|
19
|
+
"peerLeft",
|
|
20
|
+
"peerUpdated",
|
|
21
|
+
"componentAdded",
|
|
22
|
+
"componentRemoved",
|
|
23
|
+
"componentUpdated",
|
|
24
|
+
"connectionError",
|
|
25
|
+
"tracksPriorityChanged",
|
|
24
26
|
"bandwidthEstimationChanged",
|
|
25
|
-
"encodingChanged",
|
|
26
|
-
"voiceActivityChanged",
|
|
27
|
-
"deviceDisabled",
|
|
28
|
-
"deviceEnabled",
|
|
29
|
-
"managerInitialized",
|
|
30
|
-
"managerStarted",
|
|
31
|
-
"deviceStopped",
|
|
32
|
-
"deviceReady",
|
|
33
|
-
"devicesStarted",
|
|
34
|
-
"devicesReady",
|
|
35
|
-
"error",
|
|
36
27
|
"targetTrackEncodingRequested",
|
|
37
28
|
"localTrackAdded",
|
|
38
29
|
"localTrackRemoved",
|
|
@@ -47,7 +38,35 @@ const eventNames = [
|
|
|
47
38
|
"localTrackMetadataChanged",
|
|
48
39
|
"disconnectRequested",
|
|
49
40
|
];
|
|
50
|
-
|
|
41
|
+
function trackContextToTrack(track) {
|
|
42
|
+
return {
|
|
43
|
+
rawMetadata: track.rawMetadata,
|
|
44
|
+
metadata: track.metadata,
|
|
45
|
+
trackId: track.trackId,
|
|
46
|
+
stream: track.stream,
|
|
47
|
+
simulcastConfig: track.simulcastConfig ?? null,
|
|
48
|
+
encoding: track.encoding ?? null,
|
|
49
|
+
vadStatus: track.vadStatus,
|
|
50
|
+
track: track.track,
|
|
51
|
+
metadataParsingError: track.metadataParsingError,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function endpointToPeerState(peer) {
|
|
55
|
+
const tracks = [...peer.tracks].reduce((acc, [, track]) => ({ ...acc, [track.trackId]: trackContextToTrack(track) }), {});
|
|
56
|
+
return {
|
|
57
|
+
rawMetadata: peer.rawMetadata,
|
|
58
|
+
metadata: peer.metadata,
|
|
59
|
+
metadataParsingError: peer.metadataParsingError,
|
|
60
|
+
id: peer.id,
|
|
61
|
+
tracks,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/*
|
|
65
|
+
This is an internally used hook.
|
|
66
|
+
It is not meant to be used by the end user.
|
|
67
|
+
*/
|
|
68
|
+
export function useFishjamClientState(fishjamClient) {
|
|
69
|
+
const client = useMemo(() => fishjamClient, [fishjamClient]);
|
|
51
70
|
const mutationRef = useRef(false);
|
|
52
71
|
const subscribe = useCallback((subscribeCallback) => {
|
|
53
72
|
const callback = () => {
|
|
@@ -62,20 +81,20 @@ export const useClientState = (client) => {
|
|
|
62
81
|
const lastSnapshotRef = useRef(null);
|
|
63
82
|
const getSnapshot = useCallback(() => {
|
|
64
83
|
if (mutationRef.current || lastSnapshotRef.current === null) {
|
|
84
|
+
const peers = Object.values(client.getRemotePeers()).map(endpointToPeerState);
|
|
85
|
+
const components = Object.values(client.getRemoteComponents()).map(endpointToPeerState);
|
|
86
|
+
const localEndpoint = client.getLocalPeer();
|
|
87
|
+
const localPeer = localEndpoint ? endpointToPeerState(localEndpoint) : null;
|
|
88
|
+
const isReconnecting = client.isReconnecting();
|
|
65
89
|
lastSnapshotRef.current = {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
local: client.local,
|
|
71
|
-
status: client.status,
|
|
72
|
-
devices: client.devices,
|
|
73
|
-
client: client,
|
|
74
|
-
reconnectionStatus: client.reconnectionStatus,
|
|
90
|
+
peers,
|
|
91
|
+
components,
|
|
92
|
+
localPeer,
|
|
93
|
+
isReconnecting,
|
|
75
94
|
};
|
|
76
95
|
mutationRef.current = false;
|
|
77
96
|
}
|
|
78
97
|
return lastSnapshotRef.current;
|
|
79
98
|
}, [client]);
|
|
80
99
|
return useSyncExternalStore(subscribe, getSnapshot);
|
|
81
|
-
}
|
|
100
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFishjamClient_DO_NOT_USE.d.ts","sourceRoot":"","sources":["../../src/hooks/useFishjamClient_DO_NOT_USE.ts"],"names":[],"mappings":"AAGA,wBAAgB,2BAA2B,4GAI1C"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { FishjamClient } from "@fishjam-cloud/ts-client";
|
|
2
|
+
import { type MutableRefObject } from "react";
|
|
3
|
+
import type { PeerMetadata, TrackMetadata, ScreenShareState, TrackManager } from "../types";
|
|
4
|
+
import type { DeviceManager } from "../DeviceManager";
|
|
5
|
+
import type { ParticipantStatus } from "../state.types";
|
|
6
|
+
import type { FishjamClientState } from "./useFishjamClientState";
|
|
7
|
+
export type FishjamContextType = {
|
|
8
|
+
fishjamClientRef: MutableRefObject<FishjamClient<PeerMetadata, TrackMetadata>>;
|
|
9
|
+
videoDeviceManagerRef: MutableRefObject<DeviceManager>;
|
|
10
|
+
audioDeviceManagerRef: MutableRefObject<DeviceManager>;
|
|
11
|
+
hasDevicesBeenInitializedRef: MutableRefObject<boolean>;
|
|
12
|
+
screenShareState: [ScreenShareState, React.Dispatch<React.SetStateAction<ScreenShareState>>];
|
|
13
|
+
peerStatus: ParticipantStatus;
|
|
14
|
+
videoTrackManager: TrackManager;
|
|
15
|
+
audioTrackManager: TrackManager;
|
|
16
|
+
clientState: FishjamClientState;
|
|
17
|
+
};
|
|
18
|
+
export declare const FishjamContext: import("react").Context<FishjamContextType | null>;
|
|
19
|
+
export declare function useFishjamContext(): FishjamContextType;
|
|
20
|
+
//# sourceMappingURL=useFishjamContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFishjamContext.d.ts","sourceRoot":"","sources":["../../src/hooks/useFishjamContext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAiB,KAAK,gBAAgB,EAAc,MAAM,OAAO,CAAC;AACzE,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC5F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAElE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,gBAAgB,EAAE,gBAAgB,CAAC,aAAa,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC;IAC/E,qBAAqB,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAC;IACvD,qBAAqB,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAC;IACvD,4BAA4B,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACxD,gBAAgB,EAAE,CAAC,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAC7F,UAAU,EAAE,iBAAiB,CAAC;IAC9B,iBAAiB,EAAE,YAAY,CAAC;IAChC,iBAAiB,EAAE,YAAY,CAAC;IAChC,WAAW,EAAE,kBAAkB,CAAC;CACjC,CAAC;AAEF,eAAO,MAAM,cAAc,oDAAiD,CAAC;AAE7E,wBAAgB,iBAAiB,IAGb,kBAAkB,CACrC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ParticipantStatus } from "../state.types";
|
|
2
|
+
import type { FishjamClient } from "@fishjam-cloud/ts-client";
|
|
3
|
+
import type { PeerMetadata, TrackMetadata } from "../types";
|
|
4
|
+
export declare const useParticipantStatus: (client: FishjamClient<PeerMetadata, TrackMetadata>) => {
|
|
5
|
+
readonly peerStatus: ParticipantStatus;
|
|
6
|
+
readonly getCurrentParticipantStatus: () => ParticipantStatus;
|
|
7
|
+
};
|
|
8
|
+
//# sourceMappingURL=useParticipantStatus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useParticipantStatus.d.ts","sourceRoot":"","sources":["../../src/hooks/useParticipantStatus.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE5D,eAAO,MAAM,oBAAoB,WAAY,aAAa,CAAC,YAAY,EAAE,aAAa,CAAC;;;CA8CtF,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
export const useParticipantStatus = (client) => {
|
|
3
|
+
const [peerStatus, setParticipantStatusState] = useState("idle");
|
|
4
|
+
const peerStatusRef = useRef("idle");
|
|
5
|
+
const setParticipantStatus = useCallback((status) => {
|
|
6
|
+
peerStatusRef.current = status;
|
|
7
|
+
setParticipantStatusState(status);
|
|
8
|
+
}, [setParticipantStatusState]);
|
|
9
|
+
const getCurrentParticipantStatus = useCallback(() => peerStatusRef.current, []);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
const setConnecting = () => {
|
|
12
|
+
setParticipantStatus("connecting");
|
|
13
|
+
};
|
|
14
|
+
const setError = () => {
|
|
15
|
+
setParticipantStatus("error");
|
|
16
|
+
};
|
|
17
|
+
const setJoined = () => {
|
|
18
|
+
setParticipantStatus("connected");
|
|
19
|
+
};
|
|
20
|
+
const setDisconnected = () => {
|
|
21
|
+
setParticipantStatus("idle");
|
|
22
|
+
};
|
|
23
|
+
client.on("connectionStarted", setConnecting);
|
|
24
|
+
client.on("joined", setJoined);
|
|
25
|
+
client.on("authError", setError);
|
|
26
|
+
client.on("joinError", setError);
|
|
27
|
+
client.on("connectionError", setError);
|
|
28
|
+
client.on("disconnected", setDisconnected);
|
|
29
|
+
return () => {
|
|
30
|
+
client.off("connectionStarted", setConnecting);
|
|
31
|
+
client.off("joined", setJoined);
|
|
32
|
+
client.off("authError", setError);
|
|
33
|
+
client.off("joinError", setError);
|
|
34
|
+
client.off("connectionError", setError);
|
|
35
|
+
client.off("disconnected", setDisconnected);
|
|
36
|
+
};
|
|
37
|
+
}, [client, setParticipantStatus]);
|
|
38
|
+
return { peerStatus, getCurrentParticipantStatus };
|
|
39
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useParticipants.d.ts","sourceRoot":"","sources":["../../src/hooks/useParticipants.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAcpD,wBAAgB,eAAe;;;EAQ9B"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { useFishjamContext } from "./useFishjamContext";
|
|
2
|
+
function getPeerWithDistinguishedTracks(peerState) {
|
|
3
|
+
const peerTracks = Object.values(peerState.tracks ?? {});
|
|
4
|
+
const cameraTrack = peerTracks.find(({ metadata }) => metadata?.type === "camera");
|
|
5
|
+
const microphoneTrack = peerTracks.find(({ metadata }) => metadata?.type === "microphone");
|
|
6
|
+
const screenShareVideoTrack = peerTracks.find(({ metadata }) => metadata?.type === "screenShareVideo");
|
|
7
|
+
const screenShareAudioTrack = peerTracks.find(({ metadata }) => metadata?.type === "screenShareAudio");
|
|
8
|
+
return { ...peerState, cameraTrack, microphoneTrack, screenShareVideoTrack, screenShareAudioTrack };
|
|
9
|
+
}
|
|
10
|
+
export function useParticipants() {
|
|
11
|
+
const { clientState } = useFishjamContext();
|
|
12
|
+
const { localPeer, peers } = clientState;
|
|
13
|
+
const localParticipant = localPeer ? getPeerWithDistinguishedTracks(localPeer) : null;
|
|
14
|
+
const participants = Object.values(peers).map(getPeerWithDistinguishedTracks);
|
|
15
|
+
return { localParticipant, participants };
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useProcessedPreviewStream.d.ts","sourceRoot":"","sources":["../../src/hooks/useProcessedPreviewStream.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,YAAY,EAC1B,WAAW,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,sBAiBjD"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
export function useProcessedPreviewStream(trackManager, deviceTrack) {
|
|
3
|
+
const [previewStream, setPreviewStream] = useState(null);
|
|
4
|
+
const { currentTrack, currentTrackMiddleware } = trackManager;
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
if (currentTrack || !deviceTrack)
|
|
7
|
+
return;
|
|
8
|
+
const result = currentTrackMiddleware?.(deviceTrack);
|
|
9
|
+
if (result?.track)
|
|
10
|
+
setPreviewStream(new MediaStream([result.track]));
|
|
11
|
+
return () => {
|
|
12
|
+
result?.onClear?.();
|
|
13
|
+
setPreviewStream(null);
|
|
14
|
+
};
|
|
15
|
+
}, [currentTrack, deviceTrack, currentTrackMiddleware]);
|
|
16
|
+
return previewStream;
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useReconnection.d.ts","sourceRoot":"","sources":["../../src/hooks/useReconnection.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAEnE,eAAO,MAAM,eAAe,QAAO,kBA6BlC,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import { useFishjamContext } from "./useFishjamContext";
|
|
3
|
+
export const useReconnection = () => {
|
|
4
|
+
const { fishjamClientRef } = useFishjamContext();
|
|
5
|
+
const [reconnectionStatus, setReconnectionStatus] = useState("idle");
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
const client = fishjamClientRef.current;
|
|
8
|
+
const setReconnecting = () => {
|
|
9
|
+
setReconnectionStatus("reconnecting");
|
|
10
|
+
};
|
|
11
|
+
const setIdle = () => {
|
|
12
|
+
setReconnectionStatus("idle");
|
|
13
|
+
};
|
|
14
|
+
const setError = () => {
|
|
15
|
+
setReconnectionStatus("error");
|
|
16
|
+
};
|
|
17
|
+
client.on("reconnectionStarted", setReconnecting);
|
|
18
|
+
client.on("reconnected", setIdle);
|
|
19
|
+
client.on("reconnectionRetriesLimitReached", setError);
|
|
20
|
+
return () => {
|
|
21
|
+
client.off("reconnectionStarted", setReconnecting);
|
|
22
|
+
client.off("reconnected", setIdle);
|
|
23
|
+
client.off("reconnectionRetriesLimitReached", setError);
|
|
24
|
+
};
|
|
25
|
+
}, [fishjamClientRef]);
|
|
26
|
+
return reconnectionStatus;
|
|
27
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useScreenShare.d.ts","sourceRoot":"","sources":["../../src/hooks/useScreenShare.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAoB,MAAM,UAAU,CAAC;AAUjE,eAAO,MAAM,cAAc,QAAO,cA0IjC,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { useCallback, useEffect } from "react";
|
|
1
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
2
2
|
import { getRemoteOrLocalTrack } from "../utils/track";
|
|
3
|
-
import { useFishjamContext } from "./
|
|
3
|
+
import { useFishjamContext } from "./useFishjamContext";
|
|
4
4
|
const getTracks = (stream) => {
|
|
5
5
|
const video = stream.getVideoTracks()[0];
|
|
6
6
|
const audio = stream.getAudioTracks()[0] ?? null;
|
|
@@ -8,17 +8,29 @@ const getTracks = (stream) => {
|
|
|
8
8
|
};
|
|
9
9
|
export const useScreenShare = () => {
|
|
10
10
|
const ctx = useFishjamContext();
|
|
11
|
-
const [state, setState] = ctx.
|
|
12
|
-
const
|
|
11
|
+
const [state, setState] = ctx.screenShareState;
|
|
12
|
+
const cleanMiddlewareFnRef = useRef(null);
|
|
13
|
+
const { fishjamClientRef } = useFishjamContext();
|
|
14
|
+
const tsClient = fishjamClientRef.current;
|
|
15
|
+
const stream = state.stream ?? null;
|
|
16
|
+
const [videoTrack, audioTrack] = stream ? getTracks(stream) : [null, null];
|
|
17
|
+
const getDisplayName = () => tsClient.getLocalPeer()?.metadata?.displayName;
|
|
13
18
|
const startStreaming = async (props) => {
|
|
14
19
|
const stream = await navigator.mediaDevices.getDisplayMedia({
|
|
15
20
|
video: props?.videoConstraints ?? true,
|
|
16
21
|
audio: props?.audioConstraints ?? true,
|
|
17
22
|
});
|
|
18
|
-
const
|
|
19
|
-
|
|
23
|
+
const displayName = getDisplayName();
|
|
24
|
+
let [video, audio] = getTracks(stream);
|
|
25
|
+
if (state.tracksMiddleware) {
|
|
26
|
+
const { videoTrack, audioTrack, onClear } = state.tracksMiddleware(video, audio);
|
|
27
|
+
video = videoTrack;
|
|
28
|
+
audio = audioTrack;
|
|
29
|
+
cleanMiddlewareFnRef.current = onClear;
|
|
30
|
+
}
|
|
31
|
+
const addTrackPromises = [tsClient.addTrack(video, { displayName, type: "screenShareVideo", paused: false })];
|
|
20
32
|
if (audio)
|
|
21
|
-
addTrackPromises.push(tsClient.addTrack(audio));
|
|
33
|
+
addTrackPromises.push(tsClient.addTrack(audio, { displayName, type: "screenShareAudio", paused: false }));
|
|
22
34
|
const [videoId, audioId] = await Promise.all(addTrackPromises);
|
|
23
35
|
setState({ stream, trackIds: { videoId, audioId } });
|
|
24
36
|
};
|
|
@@ -31,15 +43,25 @@ export const useScreenShare = () => {
|
|
|
31
43
|
}
|
|
32
44
|
await Promise.all(addTrackPromises);
|
|
33
45
|
};
|
|
46
|
+
const cleanMiddleware = useCallback(() => {
|
|
47
|
+
cleanMiddlewareFnRef.current?.();
|
|
48
|
+
cleanMiddlewareFnRef.current = null;
|
|
49
|
+
}, []);
|
|
34
50
|
const setTracksMiddleware = async (middleware) => {
|
|
35
51
|
if (!state?.stream)
|
|
36
52
|
return;
|
|
37
|
-
const [
|
|
38
|
-
|
|
39
|
-
|
|
53
|
+
const [video, audio] = getTracks(state.stream);
|
|
54
|
+
cleanMiddleware();
|
|
55
|
+
const { videoTrack, audioTrack, onClear } = middleware?.(video, audio) ?? {
|
|
56
|
+
videoTrack: video,
|
|
57
|
+
audioTrack: audio,
|
|
58
|
+
onClear: null,
|
|
59
|
+
};
|
|
60
|
+
cleanMiddlewareFnRef.current = onClear;
|
|
61
|
+
await replaceTracks(videoTrack, audioTrack);
|
|
40
62
|
};
|
|
41
63
|
const stopStreaming = useCallback(async () => {
|
|
42
|
-
if (!state) {
|
|
64
|
+
if (!state.stream) {
|
|
43
65
|
console.warn("No stream to stop");
|
|
44
66
|
return;
|
|
45
67
|
}
|
|
@@ -47,14 +69,16 @@ export const useScreenShare = () => {
|
|
|
47
69
|
video.stop();
|
|
48
70
|
if (audio)
|
|
49
71
|
audio.stop();
|
|
50
|
-
const
|
|
72
|
+
const client = fishjamClientRef.current;
|
|
73
|
+
const removeTrackPromises = [client.removeTrack(state.trackIds.videoId)];
|
|
51
74
|
if (state.trackIds.audioId)
|
|
52
|
-
removeTrackPromises.push(
|
|
75
|
+
removeTrackPromises.push(client.removeTrack(state.trackIds.audioId));
|
|
53
76
|
await Promise.all(removeTrackPromises);
|
|
54
|
-
|
|
55
|
-
|
|
77
|
+
cleanMiddleware();
|
|
78
|
+
setState(({ tracksMiddleware }) => ({ stream: null, trackIds: null, tracksMiddleware }));
|
|
79
|
+
}, [state, fishjamClientRef, setState, cleanMiddleware]);
|
|
56
80
|
useEffect(() => {
|
|
57
|
-
if (!state)
|
|
81
|
+
if (!state.stream)
|
|
58
82
|
return;
|
|
59
83
|
const [video, audio] = getTracks(state.stream);
|
|
60
84
|
const trackEndedHandler = () => {
|
|
@@ -68,18 +92,19 @@ export const useScreenShare = () => {
|
|
|
68
92
|
};
|
|
69
93
|
}, [state, stopStreaming]);
|
|
70
94
|
useEffect(() => {
|
|
95
|
+
const client = fishjamClientRef.current;
|
|
71
96
|
const onDisconnected = () => {
|
|
72
|
-
|
|
97
|
+
if (stream) {
|
|
98
|
+
stopStreaming();
|
|
99
|
+
}
|
|
73
100
|
};
|
|
74
|
-
|
|
101
|
+
client.on("disconnected", onDisconnected);
|
|
75
102
|
return () => {
|
|
76
|
-
|
|
103
|
+
client.removeListener("disconnected", onDisconnected);
|
|
77
104
|
};
|
|
78
|
-
}, [stopStreaming,
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
const videoBroadcast = state ? getRemoteOrLocalTrack(tsClient, state.trackIds.videoId) : null;
|
|
82
|
-
const audioBroadcast = state?.trackIds.audioId ? getRemoteOrLocalTrack(tsClient, state.trackIds.audioId) : null;
|
|
105
|
+
}, [stopStreaming, fishjamClientRef, stream]);
|
|
106
|
+
const videoBroadcast = state.stream ? getRemoteOrLocalTrack(tsClient, state.trackIds.videoId) : null;
|
|
107
|
+
const audioBroadcast = state.trackIds?.audioId ? getRemoteOrLocalTrack(tsClient, state.trackIds.audioId) : null;
|
|
83
108
|
return {
|
|
84
109
|
startStreaming,
|
|
85
110
|
stopStreaming,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useStatus.d.ts","sourceRoot":"","sources":["../../src/hooks/useStatus.ts"],"names":[],"mappings":"AAEA,wBAAgB,SAAS,mCAExB"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FishjamClient } from "@fishjam-cloud/ts-client";
|
|
2
|
+
import type { MediaManager, PeerMetadata, TrackManager, TrackMetadata } from "../types";
|
|
3
|
+
import type { ParticipantStatus } from "../state.types";
|
|
4
|
+
interface TrackManagerConfig {
|
|
5
|
+
mediaManager: MediaManager;
|
|
6
|
+
tsClient: FishjamClient<PeerMetadata, TrackMetadata>;
|
|
7
|
+
getCurrentParticipantStatus: () => ParticipantStatus;
|
|
8
|
+
}
|
|
9
|
+
export declare const useTrackManager: ({ mediaManager, tsClient, getCurrentParticipantStatus, }: TrackManagerConfig) => TrackManager;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=useTrackManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useTrackManager.d.ts","sourceRoot":"","sources":["../../src/hooks/useTrackManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAwC,MAAM,0BAA0B,CAAC;AACpG,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAc,YAAY,EAAE,aAAa,EAAmB,MAAM,UAAU,CAAC;AAGrH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD,UAAU,kBAAkB;IAC1B,YAAY,EAAE,YAAY,CAAC;IAC3B,QAAQ,EAAE,aAAa,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;IACrD,2BAA2B,EAAE,MAAM,iBAAiB,CAAC;CACtD;AAOD,eAAO,MAAM,eAAe,6DAIzB,kBAAkB,KAAG,YA2MvB,CAAC"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { getRemoteOrLocalTrack } from "../utils/track";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
const TRACK_TYPE_TO_DEVICE = {
|
|
4
|
+
video: "camera",
|
|
5
|
+
audio: "microphone",
|
|
6
|
+
};
|
|
7
|
+
export const useTrackManager = ({ mediaManager, tsClient, getCurrentParticipantStatus, }) => {
|
|
8
|
+
const [currentTrackId, setCurrentTrackId] = useState(null);
|
|
9
|
+
const [paused, setPaused] = useState(false);
|
|
10
|
+
const clearMiddlewareFnRef = useRef(null);
|
|
11
|
+
const [currentTrackMiddleware, setCurrentTrackMiddleware] = useState(null);
|
|
12
|
+
const type = TRACK_TYPE_TO_DEVICE[mediaManager.getDeviceType()];
|
|
13
|
+
const metadata = useMemo(() => ({ type, paused }), [type, paused]);
|
|
14
|
+
const currentTrack = useMemo(() => getRemoteOrLocalTrack(tsClient, currentTrackId), [tsClient, currentTrackId]);
|
|
15
|
+
function clearMiddleware() {
|
|
16
|
+
clearMiddlewareFnRef.current?.();
|
|
17
|
+
clearMiddlewareFnRef.current = null;
|
|
18
|
+
}
|
|
19
|
+
const clearAndGetProcessedTrack = useCallback((inputTrack) => {
|
|
20
|
+
clearMiddleware();
|
|
21
|
+
const { onClear, track } = currentTrackMiddleware?.(inputTrack) ?? {};
|
|
22
|
+
if (onClear)
|
|
23
|
+
clearMiddlewareFnRef.current = onClear;
|
|
24
|
+
return track ?? inputTrack;
|
|
25
|
+
}, [currentTrackMiddleware]);
|
|
26
|
+
async function setTrackMiddleware(middleware) {
|
|
27
|
+
const mediaTrack = mediaManager.getTracks()[0];
|
|
28
|
+
setCurrentTrackMiddleware(() => middleware);
|
|
29
|
+
if (!currentTrack || !mediaTrack)
|
|
30
|
+
return;
|
|
31
|
+
clearMiddleware();
|
|
32
|
+
const { onClear, track } = middleware?.(mediaTrack) ?? { track: mediaTrack };
|
|
33
|
+
if (onClear)
|
|
34
|
+
clearMiddlewareFnRef.current = onClear;
|
|
35
|
+
await tsClient.replaceTrack(currentTrack.trackId, track);
|
|
36
|
+
}
|
|
37
|
+
async function initialize(deviceId) {
|
|
38
|
+
await mediaManager?.start(deviceId);
|
|
39
|
+
if (!currentTrackId)
|
|
40
|
+
return;
|
|
41
|
+
const newTrack = mediaManager.getTracks()[0];
|
|
42
|
+
await tsClient.replaceTrack(currentTrackId, clearAndGetProcessedTrack(newTrack));
|
|
43
|
+
}
|
|
44
|
+
function stop() {
|
|
45
|
+
clearMiddleware();
|
|
46
|
+
return mediaManager?.stop();
|
|
47
|
+
}
|
|
48
|
+
const startStreaming = useCallback(async (simulcastConfig, maxBandwidth) => {
|
|
49
|
+
if (currentTrackId)
|
|
50
|
+
throw Error("Track already added");
|
|
51
|
+
const media = mediaManager.getMedia();
|
|
52
|
+
if (!media || !media.stream || !media.track)
|
|
53
|
+
throw Error("Device is unavailable");
|
|
54
|
+
const track = getRemoteOrLocalTrack(tsClient, currentTrackId);
|
|
55
|
+
if (track)
|
|
56
|
+
return track.trackId;
|
|
57
|
+
// see `getRemoteOrLocalTrackContext()` explanation
|
|
58
|
+
setCurrentTrackId(media.track.id);
|
|
59
|
+
const displayName = tsClient.getLocalPeer()?.metadata?.displayName;
|
|
60
|
+
const trackMetadata = { ...metadata, displayName, paused: false };
|
|
61
|
+
const processedTrack = clearAndGetProcessedTrack(media.track);
|
|
62
|
+
const remoteTrackId = await tsClient.addTrack(processedTrack, trackMetadata, simulcastConfig, maxBandwidth);
|
|
63
|
+
setCurrentTrackId(remoteTrackId);
|
|
64
|
+
setPaused(false);
|
|
65
|
+
return remoteTrackId;
|
|
66
|
+
}, [clearAndGetProcessedTrack, currentTrackId, mediaManager, metadata, tsClient]);
|
|
67
|
+
async function refreshStreamedTrack() {
|
|
68
|
+
if (!currentTrack)
|
|
69
|
+
return;
|
|
70
|
+
const newTrack = mediaManager.getTracks()[0];
|
|
71
|
+
if (!newTrack)
|
|
72
|
+
throw Error("New track is empty");
|
|
73
|
+
return tsClient.replaceTrack(currentTrack.trackId, clearAndGetProcessedTrack(newTrack));
|
|
74
|
+
}
|
|
75
|
+
async function stopStreaming() {
|
|
76
|
+
if (!currentTrack)
|
|
77
|
+
return;
|
|
78
|
+
clearMiddleware();
|
|
79
|
+
setCurrentTrackId(null);
|
|
80
|
+
setPaused(true);
|
|
81
|
+
return tsClient.removeTrack(currentTrack.trackId);
|
|
82
|
+
}
|
|
83
|
+
async function pauseStreaming() {
|
|
84
|
+
if (!currentTrack)
|
|
85
|
+
return;
|
|
86
|
+
setPaused(true);
|
|
87
|
+
await tsClient.replaceTrack(currentTrack.trackId, null);
|
|
88
|
+
const trackMetadata = { ...metadata, paused: true };
|
|
89
|
+
return tsClient.updateTrackMetadata(currentTrack.trackId, trackMetadata);
|
|
90
|
+
}
|
|
91
|
+
async function resumeStreaming() {
|
|
92
|
+
if (!currentTrack)
|
|
93
|
+
return;
|
|
94
|
+
const media = mediaManager.getMedia();
|
|
95
|
+
if (!media)
|
|
96
|
+
throw Error("Device is unavailable");
|
|
97
|
+
const processedTrack = media.track ? clearAndGetProcessedTrack(media.track) : null;
|
|
98
|
+
setPaused(false);
|
|
99
|
+
await tsClient.replaceTrack(currentTrack.trackId, processedTrack);
|
|
100
|
+
const trackMetadata = { ...metadata, paused: false };
|
|
101
|
+
return tsClient.updateTrackMetadata(currentTrack.trackId, trackMetadata);
|
|
102
|
+
}
|
|
103
|
+
function disableTrack() {
|
|
104
|
+
mediaManager.disable();
|
|
105
|
+
}
|
|
106
|
+
function enableTrack() {
|
|
107
|
+
mediaManager.enable();
|
|
108
|
+
}
|
|
109
|
+
const stream = async () => {
|
|
110
|
+
if (getCurrentParticipantStatus() !== "connected")
|
|
111
|
+
return;
|
|
112
|
+
if (currentTrack?.trackId) {
|
|
113
|
+
await resumeStreaming();
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
await startStreaming();
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* @see {@link TrackManager#toggle} for more details.
|
|
121
|
+
*/
|
|
122
|
+
async function toggle(mode = "hard") {
|
|
123
|
+
const mediaStream = mediaManager.getMedia()?.stream;
|
|
124
|
+
const track = mediaManager.getTracks()?.[0];
|
|
125
|
+
const enabled = Boolean(track?.enabled);
|
|
126
|
+
if (mediaStream && enabled) {
|
|
127
|
+
mediaManager.disable();
|
|
128
|
+
if (currentTrack?.trackId) {
|
|
129
|
+
await pauseStreaming();
|
|
130
|
+
}
|
|
131
|
+
if (mode === "hard") {
|
|
132
|
+
await mediaManager.stop();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
else if (mediaStream && !enabled) {
|
|
136
|
+
mediaManager.enable();
|
|
137
|
+
await stream();
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
await mediaManager.start();
|
|
141
|
+
await stream();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
const joinedHandler = () => {
|
|
146
|
+
if (mediaManager.getTracks().length > 0) {
|
|
147
|
+
startStreaming();
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
const disconnectedHandler = () => {
|
|
151
|
+
setCurrentTrackId(null);
|
|
152
|
+
};
|
|
153
|
+
tsClient.on("joined", joinedHandler);
|
|
154
|
+
tsClient.on("disconnected", disconnectedHandler);
|
|
155
|
+
return () => {
|
|
156
|
+
tsClient.off("joined", joinedHandler);
|
|
157
|
+
tsClient.off("disconnected", disconnectedHandler);
|
|
158
|
+
};
|
|
159
|
+
}, [mediaManager, startStreaming, tsClient]);
|
|
160
|
+
return {
|
|
161
|
+
currentTrack,
|
|
162
|
+
setTrackMiddleware,
|
|
163
|
+
initialize,
|
|
164
|
+
stop,
|
|
165
|
+
startStreaming,
|
|
166
|
+
stopStreaming,
|
|
167
|
+
pauseStreaming,
|
|
168
|
+
resumeStreaming,
|
|
169
|
+
disableTrack,
|
|
170
|
+
enableTrack,
|
|
171
|
+
currentTrackMiddleware,
|
|
172
|
+
refreshStreamedTrack,
|
|
173
|
+
paused,
|
|
174
|
+
toggle,
|
|
175
|
+
};
|
|
176
|
+
};
|