@fishjam-cloud/react-client 0.26.1-rc.1 → 0.26.2
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/hooks/internal/useCurrentCallback.d.ts +8 -0
- package/dist/hooks/internal/useCurrentCallback.d.ts.map +1 -0
- package/dist/hooks/internal/useCurrentCallback.js +14 -0
- package/dist/hooks/internal/useScreenshareManager.d.ts.map +1 -1
- package/dist/hooks/internal/useScreenshareManager.js +15 -16
- package/dist/hooks/internal/useTrackManager.d.ts.map +1 -1
- package/dist/hooks/internal/useTrackManager.js +44 -37
- package/dist/hooks/useDataChannel.d.ts.map +1 -1
- package/dist/hooks/useDataChannel.js +6 -2
- package/dist/hooks/usePeers.d.ts +7 -1
- package/dist/hooks/usePeers.d.ts.map +1 -1
- package/dist/hooks/usePeers.js +8 -1
- package/dist/hooks/useScreenShare.d.ts.map +1 -1
- package/dist/tests/useCurrentCallback.spec.d.ts +2 -0
- package/dist/tests/useCurrentCallback.spec.d.ts.map +1 -0
- package/dist/tests/useCurrentCallback.spec.js +49 -0
- package/package.json +2 -2
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns a stable function reference whose body always reads the latest
|
|
3
|
+
* closure values. Use when a method needs to be called from effects or
|
|
4
|
+
* captured by consumers without being memoized against changing state
|
|
5
|
+
* (peerStatus, deviceTrack, etc.) in its dep array.
|
|
6
|
+
*/
|
|
7
|
+
export declare const useCurrentCallback: <Args extends unknown[], R>(handler: (...args: Args) => R) => (...args: Args) => R;
|
|
8
|
+
//# sourceMappingURL=useCurrentCallback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCurrentCallback.d.ts","sourceRoot":"","sources":["../../../src/hooks/internal/useCurrentCallback.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,IAAI,SAAS,OAAO,EAAE,EAAE,CAAC,EAAE,SAAS,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,CAAC,eAK5D,IAAI,MAClC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useCallback, useLayoutEffect, useRef } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Returns a stable function reference whose body always reads the latest
|
|
4
|
+
* closure values. Use when a method needs to be called from effects or
|
|
5
|
+
* captured by consumers without being memoized against changing state
|
|
6
|
+
* (peerStatus, deviceTrack, etc.) in its dep array.
|
|
7
|
+
*/
|
|
8
|
+
export const useCurrentCallback = (handler) => {
|
|
9
|
+
const ref = useRef(handler);
|
|
10
|
+
useLayoutEffect(() => {
|
|
11
|
+
ref.current = handler;
|
|
12
|
+
});
|
|
13
|
+
return useCallback((...args) => ref.current(...args), []);
|
|
14
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useScreenshareManager.d.ts","sourceRoot":"","sources":["../../../src/hooks/internal/useScreenshareManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,MAAM,EAAsC,MAAM,0BAA0B,CAAC;AAI/G,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"useScreenshareManager.d.ts","sourceRoot":"","sources":["../../../src/hooks/internal/useScreenshareManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,MAAM,EAAsC,MAAM,0BAA0B,CAAC;AAI/G,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGvE,MAAM,MAAM,oBAAoB,GAAG;IACjC;;OAEG;IACH,cAAc,EAAE,CAAC,KAAK,CAAC,EAAE;QACvB,gBAAgB,CAAC,EAAE,OAAO,GAAG,qBAAqB,CAAC;QACnD,gBAAgB,CAAC,EAAE,OAAO,GAAG,qBAAqB,CAAC;KACpD,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpB;;OAEG;IACH,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC;;OAEG;IACH,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;IAC3B;;OAEG;IACH,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACpC;;OAEG;IACH,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACpC;;;;OAIG;IACH,uBAAuB,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjD;;;;OAIG;IACH,mBAAmB,EAAE,CAAC,UAAU,EAAE,gBAAgB,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7E,CAAC;AAEF,UAAU,uBAAuB;IAC/B,aAAa,EAAE,aAAa,CAAC;IAC7B,UAAU,EAAE,UAAU,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,qBAAqB,GAAI,wCAInC,uBAAuB,KAAG,oBA8K5B,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { TrackTypeError } from "@fishjam-cloud/ts-client";
|
|
2
2
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
+
import { useCurrentCallback } from "./useCurrentCallback";
|
|
3
4
|
export const useScreenShareManager = ({ fishjamClient, peerStatus, logger, }) => {
|
|
4
5
|
const [state, setState] = useState({ stream: null, trackIds: null });
|
|
5
6
|
const cleanMiddlewareFnRef = useRef(null);
|
|
@@ -79,7 +80,9 @@ export const useScreenShareManager = ({ fishjamClient, peerStatus, logger, }) =>
|
|
|
79
80
|
cleanMiddlewareFnRef.current = onClear;
|
|
80
81
|
await replaceTracks(videoTrack, audioTrack);
|
|
81
82
|
}, [state.stream, cleanMiddleware, replaceTracks]);
|
|
82
|
-
|
|
83
|
+
// Stable identity with a live closure: peerStatus must be observed at call
|
|
84
|
+
// time so a captured reference doesn't skip the SFU removeTrack calls.
|
|
85
|
+
const stopStreaming = useCurrentCallback(async () => {
|
|
83
86
|
if (!state.stream) {
|
|
84
87
|
logger.warn("No stream to stop");
|
|
85
88
|
return;
|
|
@@ -98,21 +101,15 @@ export const useScreenShareManager = ({ fishjamClient, peerStatus, logger, }) =>
|
|
|
98
101
|
}
|
|
99
102
|
cleanMiddleware();
|
|
100
103
|
setState((prev) => ({ stream: null, trackIds: null, tracksMiddleware: prev.tracksMiddleware }));
|
|
101
|
-
}
|
|
102
|
-
state.stream,
|
|
103
|
-
state.trackIds?.videoId,
|
|
104
|
-
state.trackIds?.audioId,
|
|
105
|
-
peerStatus,
|
|
106
|
-
cleanMiddleware,
|
|
107
|
-
logger,
|
|
108
|
-
fishjamClient,
|
|
109
|
-
]);
|
|
104
|
+
});
|
|
110
105
|
useEffect(() => {
|
|
111
106
|
if (!state.stream)
|
|
112
107
|
return;
|
|
113
108
|
const [video, audio] = getTracksFromStream(state.stream);
|
|
114
109
|
const trackEndedHandler = () => {
|
|
115
|
-
stopStreaming()
|
|
110
|
+
void stopStreaming().catch((err) => {
|
|
111
|
+
logger.error(err);
|
|
112
|
+
});
|
|
116
113
|
};
|
|
117
114
|
video.addEventListener("ended", trackEndedHandler);
|
|
118
115
|
audio?.addEventListener("ended", trackEndedHandler);
|
|
@@ -120,18 +117,20 @@ export const useScreenShareManager = ({ fishjamClient, peerStatus, logger, }) =>
|
|
|
120
117
|
video.removeEventListener("ended", trackEndedHandler);
|
|
121
118
|
audio?.removeEventListener("ended", trackEndedHandler);
|
|
122
119
|
};
|
|
123
|
-
}, [state, stopStreaming]);
|
|
120
|
+
}, [state, stopStreaming, logger]);
|
|
124
121
|
useEffect(() => {
|
|
125
122
|
const onDisconnected = () => {
|
|
126
|
-
if (stream)
|
|
127
|
-
|
|
128
|
-
|
|
123
|
+
if (!stream)
|
|
124
|
+
return;
|
|
125
|
+
void stopStreaming().catch((err) => {
|
|
126
|
+
logger.error(err);
|
|
127
|
+
});
|
|
129
128
|
};
|
|
130
129
|
fishjamClient.on("disconnected", onDisconnected);
|
|
131
130
|
return () => {
|
|
132
131
|
fishjamClient.removeListener("disconnected", onDisconnected);
|
|
133
132
|
};
|
|
134
|
-
}, [stopStreaming, fishjamClient, stream]);
|
|
133
|
+
}, [stopStreaming, fishjamClient, stream, logger]);
|
|
135
134
|
return {
|
|
136
135
|
startStreaming,
|
|
137
136
|
stopStreaming,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTrackManager.d.ts","sourceRoot":"","sources":["../../../src/hooks/internal/useTrackManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,MAAM,EAA+C,MAAM,0BAA0B,CAAC;AAGxH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAmB,MAAM,oBAAoB,CAAC;AAErG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"useTrackManager.d.ts","sourceRoot":"","sources":["../../../src/hooks/internal/useTrackManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,MAAM,EAA+C,MAAM,0BAA0B,CAAC;AAGxH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAmB,MAAM,oBAAoB,CAAC;AAErG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAGhE,UAAU,kBAAkB;IAC1B,aAAa,EAAE,aAAa,CAAC;IAC7B,QAAQ,EAAE,aAAa,CAAC;IACxB,UAAU,EAAE,UAAU,CAAC;IACvB,eAAe,EAAE,eAAe,CAAC;IACjC,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,IAAI,EAAE,QAAQ,GAAG,YAAY,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,eAAe,GAAI,uFAQ7B,kBAAkB,KAAG,YA2KvB,CAAC"}
|
|
@@ -1,36 +1,44 @@
|
|
|
1
1
|
import { TrackTypeError, Variant } from "@fishjam-cloud/ts-client";
|
|
2
|
-
import {
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
3
|
import { getConfigAndBandwidthFromProps, getRemoteOrLocalTrack } from "../../utils/track";
|
|
4
|
+
import { useCurrentCallback } from "./useCurrentCallback";
|
|
4
5
|
export const useTrackManager = ({ deviceManager, tsClient, peerStatus, bandwidthLimits, streamConfig, type, logger, }) => {
|
|
5
6
|
const currentTrackIdRef = useRef(null);
|
|
7
|
+
const connectionPromiseRef = useRef(null);
|
|
6
8
|
const { startDevice, stopDevice, enableDevice, disableDevice, deviceTrack, applyMiddleware, currentMiddleware, selectDevice: _selectDevice, } = deviceManager;
|
|
7
|
-
|
|
9
|
+
// Read live deviceTrack from the `joined` listener without re-subscribing
|
|
10
|
+
// every time it changes.
|
|
11
|
+
const getDeviceTrack = useCurrentCallback(() => deviceTrack);
|
|
12
|
+
const getCurrentTrackId = async () => {
|
|
13
|
+
if (connectionPromiseRef.current) {
|
|
14
|
+
await connectionPromiseRef.current;
|
|
15
|
+
}
|
|
8
16
|
const refTrackId = currentTrackIdRef.current;
|
|
9
17
|
if (!refTrackId)
|
|
10
18
|
return null;
|
|
11
19
|
const currentTrack = getRemoteOrLocalTrack(tsClient, refTrackId);
|
|
12
20
|
return currentTrack?.trackId ?? null;
|
|
13
|
-
}
|
|
14
|
-
const selectDevice =
|
|
21
|
+
};
|
|
22
|
+
const selectDevice = useCurrentCallback(async (deviceId) => {
|
|
15
23
|
const result = await _selectDevice(deviceId);
|
|
16
24
|
if (!result)
|
|
17
25
|
return;
|
|
18
26
|
const [newTrack, error] = result;
|
|
19
27
|
if (error)
|
|
20
28
|
return error;
|
|
21
|
-
const currentTrackId = getCurrentTrackId();
|
|
29
|
+
const currentTrackId = await getCurrentTrackId();
|
|
22
30
|
if (!currentTrackId)
|
|
23
31
|
return;
|
|
24
32
|
await tsClient.replaceTrack(currentTrackId, newTrack);
|
|
25
|
-
}
|
|
26
|
-
const setTrackMiddleware =
|
|
33
|
+
});
|
|
34
|
+
const setTrackMiddleware = useCurrentCallback(async (middleware) => {
|
|
27
35
|
const processedTrack = await applyMiddleware(middleware);
|
|
28
|
-
const currentTrackId = getCurrentTrackId();
|
|
36
|
+
const currentTrackId = await getCurrentTrackId();
|
|
29
37
|
if (!currentTrackId)
|
|
30
38
|
return;
|
|
31
39
|
await tsClient.replaceTrack(currentTrackId, processedTrack);
|
|
32
|
-
}
|
|
33
|
-
const startStreaming =
|
|
40
|
+
});
|
|
41
|
+
const startStreaming = useCurrentCallback(async (track, props = { sentQualities: [Variant.VARIANT_LOW, Variant.VARIANT_MEDIUM, Variant.VARIANT_HIGH] }) => {
|
|
34
42
|
// temporarily setting the local trackId until we have the remoteTrackId
|
|
35
43
|
currentTrackIdRef.current = track.id;
|
|
36
44
|
const trackMetadata = { type, paused: false };
|
|
@@ -40,7 +48,9 @@ export const useTrackManager = ({ deviceManager, tsClient, peerStatus, bandwidth
|
|
|
40
48
|
}
|
|
41
49
|
const [maxBandwidth, simulcastConfig] = getConfigAndBandwidthFromProps(props.sentQualities, bandwidthLimits);
|
|
42
50
|
try {
|
|
43
|
-
const
|
|
51
|
+
const addTrackJob = tsClient.addTrack(track, trackMetadata, simulcastConfig, maxBandwidth);
|
|
52
|
+
connectionPromiseRef.current = addTrackJob;
|
|
53
|
+
const remoteTrackId = await addTrackJob;
|
|
44
54
|
currentTrackIdRef.current = remoteTrackId;
|
|
45
55
|
}
|
|
46
56
|
catch (err) {
|
|
@@ -50,24 +60,24 @@ export const useTrackManager = ({ deviceManager, tsClient, peerStatus, bandwidth
|
|
|
50
60
|
}
|
|
51
61
|
throw err;
|
|
52
62
|
}
|
|
53
|
-
}
|
|
54
|
-
const pauseStreaming =
|
|
63
|
+
});
|
|
64
|
+
const pauseStreaming = useCurrentCallback(async (trackId) => {
|
|
55
65
|
if (peerStatus !== "connected")
|
|
56
66
|
return;
|
|
57
67
|
await tsClient.replaceTrack(trackId, null);
|
|
58
68
|
return tsClient.updateTrackMetadata(trackId, { type, paused: true });
|
|
59
|
-
}
|
|
60
|
-
const resumeStreaming =
|
|
69
|
+
});
|
|
70
|
+
const resumeStreaming = useCurrentCallback(async (trackId, track) => {
|
|
61
71
|
if (peerStatus !== "connected")
|
|
62
72
|
return;
|
|
63
73
|
await tsClient.replaceTrack(trackId, track);
|
|
64
74
|
return tsClient.updateTrackMetadata(trackId, { type, paused: false });
|
|
65
|
-
}
|
|
75
|
+
});
|
|
66
76
|
/**
|
|
67
77
|
* @see {@link TrackManager#toggleMute} for more details.
|
|
68
78
|
*/
|
|
69
|
-
const toggleMute =
|
|
70
|
-
const currentTrackId = getCurrentTrackId();
|
|
79
|
+
const toggleMute = useCurrentCallback(async () => {
|
|
80
|
+
const currentTrackId = await getCurrentTrackId();
|
|
71
81
|
const isTrackCurrentlyEnabled = Boolean(deviceTrack?.enabled);
|
|
72
82
|
if (!currentTrackId) {
|
|
73
83
|
logger.warn("Toggling mute is only possible while connected to a room.");
|
|
@@ -81,16 +91,16 @@ export const useTrackManager = ({ deviceManager, tsClient, peerStatus, bandwidth
|
|
|
81
91
|
enableDevice();
|
|
82
92
|
await resumeStreaming(currentTrackId, deviceTrack);
|
|
83
93
|
}
|
|
84
|
-
}
|
|
94
|
+
});
|
|
85
95
|
/**
|
|
86
96
|
* @see {@link TrackManager#toggleDevice} for more details.
|
|
87
97
|
*/
|
|
88
|
-
const toggleDevice =
|
|
89
|
-
const currentTrackId = getCurrentTrackId();
|
|
98
|
+
const toggleDevice = useCurrentCallback(async () => {
|
|
99
|
+
const currentTrackId = await getCurrentTrackId();
|
|
90
100
|
if (deviceTrack) {
|
|
91
101
|
stopDevice();
|
|
92
102
|
if (currentTrackId) {
|
|
93
|
-
pauseStreaming(currentTrackId);
|
|
103
|
+
await pauseStreaming(currentTrackId);
|
|
94
104
|
}
|
|
95
105
|
}
|
|
96
106
|
else {
|
|
@@ -104,22 +114,19 @@ export const useTrackManager = ({ deviceManager, tsClient, peerStatus, bandwidth
|
|
|
104
114
|
await startStreaming(newTrack, streamConfig);
|
|
105
115
|
}
|
|
106
116
|
}
|
|
107
|
-
}
|
|
108
|
-
getCurrentTrackId,
|
|
109
|
-
deviceTrack,
|
|
110
|
-
stopDevice,
|
|
111
|
-
pauseStreaming,
|
|
112
|
-
startDevice,
|
|
113
|
-
peerStatus,
|
|
114
|
-
resumeStreaming,
|
|
115
|
-
startStreaming,
|
|
116
|
-
streamConfig,
|
|
117
|
-
]);
|
|
117
|
+
});
|
|
118
118
|
useEffect(() => {
|
|
119
119
|
const onJoinedRoom = () => {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
const currentDeviceTrack = getDeviceTrack();
|
|
121
|
+
if (!currentDeviceTrack)
|
|
122
|
+
return;
|
|
123
|
+
// The handler is sync; observe rejections so non-TrackTypeError failures
|
|
124
|
+
// from addTrack don't surface as unhandledrejection.
|
|
125
|
+
void startStreaming(currentDeviceTrack, streamConfig).catch((err) => {
|
|
126
|
+
if (err instanceof TrackTypeError)
|
|
127
|
+
return;
|
|
128
|
+
logger.error(err);
|
|
129
|
+
});
|
|
123
130
|
};
|
|
124
131
|
const onLeftRoom = () => {
|
|
125
132
|
currentTrackIdRef.current = null;
|
|
@@ -130,7 +137,7 @@ export const useTrackManager = ({ deviceManager, tsClient, peerStatus, bandwidth
|
|
|
130
137
|
tsClient.off("joined", onJoinedRoom);
|
|
131
138
|
tsClient.off("disconnected", onLeftRoom);
|
|
132
139
|
};
|
|
133
|
-
}, [
|
|
140
|
+
}, [startStreaming, tsClient, streamConfig, getDeviceTrack, logger]);
|
|
134
141
|
return {
|
|
135
142
|
deviceTrack,
|
|
136
143
|
currentMiddleware,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDataChannel.d.ts","sourceRoot":"","sources":["../../src/hooks/useDataChannel.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"useDataChannel.d.ts","sourceRoot":"","sources":["../../src/hooks/useDataChannel.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAG5D;;;;;GAKG;AACH,wBAAgB,cAAc,IAAI,oBAAoB,CAyFrD"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useCallback, useContext, useEffect, useState } from "react";
|
|
2
2
|
import { FishjamClientContext } from "../contexts/fishjamClient";
|
|
3
3
|
import { PeerStatusContext } from "../contexts/peerStatus";
|
|
4
|
+
import { useCurrentCallback } from "./internal/useCurrentCallback";
|
|
4
5
|
/**
|
|
5
6
|
* Hook for data channel operations - publish and subscribe to data.
|
|
6
7
|
*
|
|
@@ -39,7 +40,10 @@ export function useDataChannel() {
|
|
|
39
40
|
client.removeListener("disconnected", handleDisconnect);
|
|
40
41
|
};
|
|
41
42
|
}, [client]);
|
|
42
|
-
|
|
43
|
+
// Stable identity with a live closure: peerStatus / loading / ready must be
|
|
44
|
+
// observed at call time so a captured reference doesn't reject with a stale
|
|
45
|
+
// "Peer is not connected" right after the connect promise settles.
|
|
46
|
+
const initialize = useCurrentCallback(async () => {
|
|
43
47
|
if (loading || ready)
|
|
44
48
|
return;
|
|
45
49
|
if (peerStatus !== "connected") {
|
|
@@ -58,7 +62,7 @@ export function useDataChannel() {
|
|
|
58
62
|
finally {
|
|
59
63
|
setLoading(false);
|
|
60
64
|
}
|
|
61
|
-
}
|
|
65
|
+
});
|
|
62
66
|
const publishData = useCallback((data, options) => {
|
|
63
67
|
try {
|
|
64
68
|
client.publishData(data, options);
|
package/dist/hooks/usePeers.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Metadata } from "@fishjam-cloud/ts-client";
|
|
1
|
+
import type { Metadata, Variant } from "@fishjam-cloud/ts-client";
|
|
2
2
|
import type { PeerId, RemoteTrack, Track } from "../types/public";
|
|
3
3
|
/**
|
|
4
4
|
*
|
|
@@ -38,5 +38,11 @@ export declare function usePeers<PeerMetadata = Record<string, unknown>, ServerM
|
|
|
38
38
|
* This property will be removed in future versions.
|
|
39
39
|
*/
|
|
40
40
|
peers: PeerWithTracks<PeerMetadata, ServerMetadata, RemoteTrack>[];
|
|
41
|
+
/**
|
|
42
|
+
* This function allows to set the quality of tracks received from remote peers.
|
|
43
|
+
* @param trackIds The array of the track ids to set the quality for.
|
|
44
|
+
* @param quality The quality to set for the track.
|
|
45
|
+
*/
|
|
46
|
+
setReceivedTracksQuality: (trackIds: string[], quality: Variant) => void;
|
|
41
47
|
};
|
|
42
48
|
//# sourceMappingURL=usePeers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePeers.d.ts","sourceRoot":"","sources":["../../src/hooks/usePeers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,QAAQ,
|
|
1
|
+
{"version":3,"file":"usePeers.d.ts","sourceRoot":"","sources":["../../src/hooks/usePeers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,QAAQ,EAAwC,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAMvH,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAW,MAAM,iBAAiB,CAAC;AAE3E;;;;GAIG;AACH,MAAM,MAAM,cAAc,CAAC,YAAY,EAAE,cAAc,EAAE,CAAC,SAAS,KAAK,GAAG,KAAK,IAAI;IAClF,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,QAAQ,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC;IAClD,MAAM,EAAE,CAAC,EAAE,CAAC;IACZ,WAAW,CAAC,EAAE,CAAC,CAAC;IAChB,eAAe,CAAC,EAAE,CAAC,CAAC;IACpB,qBAAqB,CAAC,EAAE,CAAC,CAAC;IAC1B,qBAAqB,CAAC,EAAE,CAAC,CAAC;IAC1B,iBAAiB,EAAE,CAAC,EAAE,CAAC;IACvB,iBAAiB,EAAE,CAAC,EAAE,CAAC;CACxB,CAAC;AAuEF;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IA0BrG;;;OAGG;IACH;;OAEG;IACH;;;;OAIG;;IAEH;;;;OAIG;yCAvBQ,MAAM,EAAE,WAAW,OAAO;EA0BxC"}
|
package/dist/hooks/usePeers.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useContext } from "react";
|
|
1
|
+
import { useCallback, useContext } from "react";
|
|
2
2
|
import { FishjamClientContext } from "../contexts/fishjamClient";
|
|
3
3
|
import { FishjamClientStateContext } from "../contexts/fishjamState";
|
|
4
4
|
function trackContextToTrack(track) {
|
|
@@ -63,6 +63,7 @@ export function usePeers() {
|
|
|
63
63
|
? getLocalPeerWithTracks(clientState.localPeer)
|
|
64
64
|
: null;
|
|
65
65
|
const remotePeers = Object.values(clientState.peers).map((peer) => getRemotePeerWithTracks(peer, fishjamClient.current));
|
|
66
|
+
const setReceivedTracksQuality = useCallback((trackIds, quality) => trackIds.forEach((trackId) => fishjamClient.current.setTargetTrackEncoding(trackId, quality)), [fishjamClient]);
|
|
66
67
|
return {
|
|
67
68
|
/**
|
|
68
69
|
* The local peer with distinguished tracks (camera, microphone, screen share).
|
|
@@ -77,5 +78,11 @@ export function usePeers() {
|
|
|
77
78
|
* This property will be removed in future versions.
|
|
78
79
|
*/
|
|
79
80
|
peers: remotePeers,
|
|
81
|
+
/**
|
|
82
|
+
* This function allows to set the quality of tracks received from remote peers.
|
|
83
|
+
* @param trackIds The array of the track ids to set the quality for.
|
|
84
|
+
* @param quality The quality to set for the track.
|
|
85
|
+
*/
|
|
86
|
+
setReceivedTracksQuality,
|
|
80
87
|
};
|
|
81
88
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useScreenShare.d.ts","sourceRoot":"","sources":["../../src/hooks/useScreenShare.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,eAAO,MAAM,cAAc;IAKvB;;OAEG;;
|
|
1
|
+
{"version":3,"file":"useScreenShare.d.ts","sourceRoot":"","sources":["../../src/hooks/useScreenShare.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,eAAO,MAAM,cAAc;IAKvB;;OAEG;;wBACiB,CAAC;wBAER,CAAC;;IADd;;OAEG;;IAEH;;OAEG;;IAEH;;OAEG;;IAEH;;OAEG;;IAEH;;;;OAIG;;IAEH;;;;OAIG;;CAGN,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCurrentCallback.spec.d.ts","sourceRoot":"","sources":["../../src/tests/useCurrentCallback.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { renderHook } from "@testing-library/react";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { useCurrentCallback } from "../hooks/internal/useCurrentCallback";
|
|
4
|
+
describe("useCurrentCallback", () => {
|
|
5
|
+
it("returns a stable function reference across re-renders", () => {
|
|
6
|
+
const { result, rerender } = renderHook((handler) => useCurrentCallback(handler), {
|
|
7
|
+
initialProps: () => undefined,
|
|
8
|
+
});
|
|
9
|
+
const firstRef = result.current;
|
|
10
|
+
rerender(() => undefined);
|
|
11
|
+
rerender(() => undefined);
|
|
12
|
+
expect(result.current).toBe(firstRef);
|
|
13
|
+
});
|
|
14
|
+
it("invokes the latest handler when called after a re-render", () => {
|
|
15
|
+
const first = vi.fn();
|
|
16
|
+
const second = vi.fn();
|
|
17
|
+
const { result, rerender } = renderHook((handler) => useCurrentCallback(handler), {
|
|
18
|
+
initialProps: first,
|
|
19
|
+
});
|
|
20
|
+
result.current();
|
|
21
|
+
expect(first).toHaveBeenCalledTimes(1);
|
|
22
|
+
expect(second).not.toHaveBeenCalled();
|
|
23
|
+
rerender(second);
|
|
24
|
+
result.current();
|
|
25
|
+
expect(first).toHaveBeenCalledTimes(1);
|
|
26
|
+
expect(second).toHaveBeenCalledTimes(1);
|
|
27
|
+
});
|
|
28
|
+
it("forwards arguments and returns the handler's value", () => {
|
|
29
|
+
const handler = vi.fn((a, b) => a + b);
|
|
30
|
+
const { result } = renderHook(() => useCurrentCallback(handler));
|
|
31
|
+
const sum = result.current(2, 3);
|
|
32
|
+
expect(handler).toHaveBeenCalledWith(2, 3);
|
|
33
|
+
expect(sum).toBe(5);
|
|
34
|
+
});
|
|
35
|
+
it("the wrapper captured before a re-render still calls the latest handler", () => {
|
|
36
|
+
const first = vi.fn(() => "first");
|
|
37
|
+
const second = vi.fn(() => "second");
|
|
38
|
+
const { result, rerender } = renderHook((handler) => useCurrentCallback(handler), {
|
|
39
|
+
initialProps: first,
|
|
40
|
+
});
|
|
41
|
+
// Simulates a consumer capturing the reference (e.g. inside a useEffect
|
|
42
|
+
// closure) before the handler closure has changed.
|
|
43
|
+
const captured = result.current;
|
|
44
|
+
rerender(second);
|
|
45
|
+
expect(captured()).toBe("second");
|
|
46
|
+
expect(second).toHaveBeenCalledTimes(1);
|
|
47
|
+
expect(first).not.toHaveBeenCalled();
|
|
48
|
+
});
|
|
49
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fishjam-cloud/react-client",
|
|
3
|
-
"version": "0.26.
|
|
3
|
+
"version": "0.26.2",
|
|
4
4
|
"description": "React client library for Fishjam",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Fishjam Team",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"vitest": "^3.1.1"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@fishjam-cloud/ts-client": "0.26.
|
|
65
|
+
"@fishjam-cloud/ts-client": "0.26.2",
|
|
66
66
|
"events": "3.3.0",
|
|
67
67
|
"lodash.isequal": "4.5.0"
|
|
68
68
|
},
|