@100mslive/react-sdk 0.0.16 → 0.0.18-alpha.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.
Files changed (69) hide show
  1. package/dist/hooks/useAVToggle.js +1 -0
  2. package/dist/hooks/useAVToggle.js.map +1 -0
  3. package/dist/hooks/useAudioLevelStyles.js +1 -0
  4. package/dist/hooks/useAudioLevelStyles.js.map +1 -0
  5. package/dist/hooks/useAutoplayError.js +1 -0
  6. package/dist/hooks/useAutoplayError.js.map +1 -0
  7. package/dist/hooks/useCustomEvent.js +1 -0
  8. package/dist/hooks/useCustomEvent.js.map +1 -0
  9. package/dist/hooks/useDevices.js +1 -0
  10. package/dist/hooks/useDevices.js.map +1 -0
  11. package/dist/hooks/useParticipantList.js +1 -0
  12. package/dist/hooks/useParticipantList.js.map +1 -0
  13. package/dist/hooks/usePreviewJoin.js +1 -0
  14. package/dist/hooks/usePreviewJoin.js.map +1 -0
  15. package/dist/hooks/useRecordingStreaming.js +1 -0
  16. package/dist/hooks/useRecordingStreaming.js.map +1 -0
  17. package/dist/hooks/useRemoteAVToggle.js +1 -0
  18. package/dist/hooks/useRemoteAVToggle.js.map +1 -0
  19. package/dist/hooks/useScreenShare.js +1 -0
  20. package/dist/hooks/useScreenShare.js.map +1 -0
  21. package/dist/hooks/useVideo.js +1 -0
  22. package/dist/hooks/useVideo.js.map +1 -0
  23. package/dist/hooks/useVideoList.js +1 -0
  24. package/dist/hooks/useVideoList.js.map +1 -0
  25. package/dist/index.cjs.js +1 -0
  26. package/dist/index.cjs.js.map +1 -0
  27. package/dist/index.js +1 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/node_modules/tslib/tslib.es6.js +1 -0
  30. package/dist/node_modules/tslib/tslib.es6.js.map +1 -0
  31. package/dist/node_modules/zustand/esm/shallow.js +1 -0
  32. package/dist/node_modules/zustand/esm/shallow.js.map +1 -0
  33. package/dist/primitives/HmsRoomProvider.js +1 -0
  34. package/dist/primitives/HmsRoomProvider.js.map +1 -0
  35. package/dist/primitives/store.js +1 -0
  36. package/dist/primitives/store.js.map +1 -0
  37. package/dist/utils/commons.js +1 -0
  38. package/dist/utils/commons.js.map +1 -0
  39. package/dist/utils/groupBy.js +1 -0
  40. package/dist/utils/groupBy.js.map +1 -0
  41. package/dist/utils/isBrowser.js +1 -0
  42. package/dist/utils/isBrowser.js.map +1 -0
  43. package/dist/utils/layout.js +1 -0
  44. package/dist/utils/layout.js.map +1 -0
  45. package/dist/utils/logger.js +1 -0
  46. package/dist/utils/logger.js.map +1 -0
  47. package/package.json +6 -5
  48. package/src/hooks/types.ts +6 -0
  49. package/src/hooks/useAVToggle.ts +62 -0
  50. package/src/hooks/useAudioLevelStyles.ts +37 -0
  51. package/src/hooks/useAutoplayError.ts +41 -0
  52. package/src/hooks/useCustomEvent.ts +85 -0
  53. package/src/hooks/useDevices.ts +93 -0
  54. package/src/hooks/useParticipantList.ts +27 -0
  55. package/src/hooks/usePreviewJoin.ts +132 -0
  56. package/src/hooks/useRecordingStreaming.ts +34 -0
  57. package/src/hooks/useRemoteAVToggle.ts +101 -0
  58. package/src/hooks/useScreenShare.ts +76 -0
  59. package/src/hooks/useVideo.ts +77 -0
  60. package/src/hooks/useVideoList.ts +162 -0
  61. package/src/index.ts +40 -0
  62. package/src/primitives/HmsRoomProvider.ts +203 -0
  63. package/src/primitives/store.ts +59 -0
  64. package/src/primitives/types.ts +6 -0
  65. package/src/utils/commons.ts +14 -0
  66. package/src/utils/groupBy.ts +29 -0
  67. package/src/utils/isBrowser.ts +1 -0
  68. package/src/utils/layout.ts +466 -0
  69. package/src/utils/logger.ts +62 -0
@@ -0,0 +1,41 @@
1
+ import { useCallback, useEffect, useState } from 'react';
2
+ import { HMSNotificationTypes } from '@100mslive/hms-video-store';
3
+ import { useHMSActions, useHMSNotifications } from '../primitives/HmsRoomProvider';
4
+
5
+ export interface useAutoplayErrorResult {
6
+ /*
7
+ * Autoplay error message
8
+ */
9
+ error: string;
10
+ /**
11
+ * call this method on a UI element click to unblock the blocked audio autoplay.
12
+ */
13
+ unblockAudio: () => Promise<void>;
14
+ /**
15
+ * Call this method to reset(hide) the UI that is rendered when there was an error
16
+ */
17
+ resetError: () => void;
18
+ }
19
+
20
+ /**
21
+ * Use this hook to show a UI(modal or a toast) when autoplay is blocked by browser and allow the user to
22
+ * unblock the browser autoplay block
23
+ * @returns {useAutoplayErrorResult}
24
+ */
25
+ export const useAutoplayError = (): useAutoplayErrorResult => {
26
+ const notification = useHMSNotifications(HMSNotificationTypes.ERROR);
27
+ const [error, setError] = useState('');
28
+ const actions = useHMSActions();
29
+
30
+ const unblockAudio = useCallback(async () => {
31
+ await actions.unblockAudio();
32
+ }, [actions]);
33
+
34
+ useEffect(() => {
35
+ if (notification?.data.code === 3008) {
36
+ setError(notification?.data.message);
37
+ }
38
+ }, [notification]);
39
+
40
+ return { error, unblockAudio, resetError: () => setError('') };
41
+ };
@@ -0,0 +1,85 @@
1
+ import { useHMSActions, useHMSVanillaNotifications } from '../primitives/HmsRoomProvider';
2
+ import { useCallback, useEffect } from 'react';
3
+ import { HMSNotificationTypes } from '@100mslive/hms-video-store';
4
+ import { hooksErrHandler } from './types';
5
+ import { logErrorHandler } from '../utils/commons';
6
+
7
+ export interface useCustomEventInput<T> {
8
+ /**
9
+ * type of the event, e.g. MODERATOR_EVENT, EMOJI_REACTIONS etc.
10
+ */
11
+ type: string;
12
+ /**
13
+ * the handler function for when the custom event comes. It's recommended
14
+ * to use `useCallback` for the function passed in here for performance
15
+ * reasons.
16
+ * The callback is optional in case you want to decouple sending event and
17
+ * handling event in the UI.
18
+ */
19
+ onEvent?: (data: T) => void;
20
+ /**
21
+ * function to handle errors happening during sending the event
22
+ */
23
+ handleError?: hooksErrHandler;
24
+ }
25
+
26
+ export interface useCustomEventResult<T> {
27
+ /**
28
+ * sends the event data to others in the room who will receive it in onEvent
29
+ */
30
+ sendEvent: (data: T) => void;
31
+ }
32
+
33
+ /**
34
+ * A generic function to implement [custom events](https://www.100ms.live/docs/javascript/v2/features/chat#custom-events) in your UI.
35
+ * The data to be sent to remote is expected to be a serializable JSON. The serialization
36
+ * and deserialization is taken care of by the hook.
37
+ */
38
+ export const useCustomEvent = <T>({
39
+ type,
40
+ onEvent,
41
+ handleError = logErrorHandler,
42
+ }: useCustomEventInput<T>): useCustomEventResult<T> => {
43
+ const actions = useHMSActions();
44
+ const notifications = useHMSVanillaNotifications();
45
+
46
+ useEffect(() => {
47
+ // we don't want these messages to be stored in store reserving that for chat messages
48
+ actions.ignoreMessageTypes([type]);
49
+ }, [actions, type]);
50
+
51
+ // this is to handle messages from remote peers
52
+ useEffect(() => {
53
+ if (!notifications) {
54
+ return;
55
+ }
56
+ const unsubscribe = notifications.onNotification(notification => {
57
+ const msg = notification.data;
58
+ if (msg && msg.type === type) {
59
+ try {
60
+ const data = JSON.parse(msg.message);
61
+ onEvent?.(data as T);
62
+ } catch (err) {
63
+ handleError(err as Error, 'handleCustomEvent');
64
+ }
65
+ }
66
+ }, HMSNotificationTypes.NEW_MESSAGE);
67
+ return unsubscribe;
68
+ }, [notifications, type, onEvent, handleError]);
69
+
70
+ // this is to send message to remote peers and call onEvent
71
+ const sendEvent = useCallback(
72
+ async (data: T) => {
73
+ try {
74
+ const dataStr = JSON.stringify(data || '');
75
+ await actions.sendBroadcastMessage(dataStr, type);
76
+ onEvent?.(data);
77
+ } catch (err) {
78
+ handleError(err as Error, 'sendCustomEvent');
79
+ }
80
+ },
81
+ [actions, handleError, onEvent, type],
82
+ );
83
+
84
+ return { sendEvent };
85
+ };
@@ -0,0 +1,93 @@
1
+ import { selectDevices, selectIsAllowedToPublish, selectLocalMediaSettings } from '@100mslive/hms-video-store';
2
+ import { useCallback } from 'react';
3
+ import { useHMSActions, useHMSStore } from '../primitives/HmsRoomProvider';
4
+ import { logErrorHandler } from '../utils/commons';
5
+ import { hooksErrHandler } from '../hooks/types';
6
+
7
+ export enum DeviceType {
8
+ videoInput = 'videoInput',
9
+ audioInput = 'audioInput',
10
+ audioOutput = 'audioOutput',
11
+ }
12
+
13
+ export type DeviceTypeAndInfo<T> = {
14
+ [key in DeviceType]?: T;
15
+ };
16
+
17
+ export interface useDevicesResult {
18
+ /**
19
+ * list of all devices by type
20
+ */
21
+ allDevices: DeviceTypeAndInfo<MediaDeviceInfo[]>;
22
+ /**
23
+ * selected device ids for all types
24
+ */
25
+ selectedDeviceIDs: DeviceTypeAndInfo<string>;
26
+ /**
27
+ * function to call to update device
28
+ */
29
+ updateDevice: ({ deviceType, deviceId }: { deviceType: DeviceType; deviceId: string }) => Promise<void>;
30
+ }
31
+
32
+ /**
33
+ * This hook can be used to implement a UI component which allows the user to manually change their
34
+ * audio/video device. It returns the list of all devices as well as the currently selected one. The input
35
+ * devices will be returned based on what the user is allowed to publish, so a audio only user won't get
36
+ * the audioInput field. This can be used to show the UI dropdowns properly.
37
+ *
38
+ * Note:
39
+ * - Browsers give access to the list of devices only if the user has given permission to access them
40
+ * - Changing devices manually work best in combination with remembering the user's selection for the next time, do
41
+ * pass the rememberDeviceSelection flag at time of join for this to happen.
42
+ *
43
+ * @param handleError error handler for any errors during device change
44
+ */
45
+ export const useDevices = (handleError: hooksErrHandler = logErrorHandler): useDevicesResult => {
46
+ const actions = useHMSActions();
47
+ const sdkAllDevices: DeviceTypeAndInfo<MediaDeviceInfo[]> = useHMSStore(selectDevices);
48
+ const sdkSelectedDevices = useHMSStore(selectLocalMediaSettings);
49
+ const isAllowedToPublish = useHMSStore(selectIsAllowedToPublish);
50
+
51
+ const selectedDeviceIDs: DeviceTypeAndInfo<string> = {
52
+ [DeviceType.audioOutput]: sdkSelectedDevices.audioOutputDeviceId,
53
+ };
54
+ const allDevices: DeviceTypeAndInfo<MediaDeviceInfo[]> = {
55
+ [DeviceType.audioOutput]: sdkAllDevices.audioOutput,
56
+ };
57
+
58
+ if (isAllowedToPublish.video) {
59
+ allDevices[DeviceType.videoInput] = sdkAllDevices.videoInput;
60
+ selectedDeviceIDs[DeviceType.videoInput] = sdkSelectedDevices.videoInputDeviceId;
61
+ }
62
+ if (isAllowedToPublish.audio) {
63
+ allDevices[DeviceType.audioInput] = sdkAllDevices.audioInput;
64
+ selectedDeviceIDs[DeviceType.audioInput] = sdkSelectedDevices.audioInputDeviceId;
65
+ }
66
+
67
+ const updateDevice = useCallback(
68
+ async ({ deviceType, deviceId }: { deviceType: DeviceType; deviceId: string }) => {
69
+ try {
70
+ switch (deviceType) {
71
+ case DeviceType.audioInput:
72
+ await actions.setAudioSettings({ deviceId });
73
+ break;
74
+ case DeviceType.videoInput:
75
+ await actions.setVideoSettings({ deviceId });
76
+ break;
77
+ case DeviceType.audioOutput:
78
+ actions.setAudioOutputDevice(deviceId);
79
+ break;
80
+ }
81
+ } catch (err) {
82
+ handleError(err as Error, 'updateDevices');
83
+ }
84
+ },
85
+ [handleError, actions],
86
+ );
87
+
88
+ return {
89
+ allDevices,
90
+ selectedDeviceIDs,
91
+ updateDevice,
92
+ };
93
+ };
@@ -0,0 +1,27 @@
1
+ import { useMemo } from 'react';
2
+ import {
3
+ HMSPeer,
4
+ HMSRoleName,
5
+ selectIsConnectedToRoom,
6
+ selectPeerCount,
7
+ selectPeers,
8
+ selectRemotePeers,
9
+ } from '@100mslive/hms-video-store';
10
+ import { useHMSStore } from '../primitives/HmsRoomProvider';
11
+ import { groupByRoles } from '../utils/groupBy';
12
+
13
+ export interface useParticipantListResult {
14
+ roles: HMSRoleName[];
15
+ participantsByRoles: Record<string, HMSPeer[]>;
16
+ peerCount: number;
17
+ isConnected: boolean;
18
+ }
19
+
20
+ export const useParticipantList = () => {
21
+ const isConnected = useHMSStore(selectIsConnectedToRoom);
22
+ const participantList = useHMSStore(isConnected ? selectPeers : selectRemotePeers);
23
+ const peerCount = useHMSStore(selectPeerCount);
24
+ const participantsByRoles = useMemo(() => groupByRoles(participantList), [participantList]);
25
+ const roles = Object.keys(participantsByRoles);
26
+ return { roles, participantsByRoles, peerCount, isConnected };
27
+ };
@@ -0,0 +1,132 @@
1
+ import {
2
+ HMSRoomState,
3
+ selectIsConnectedToRoom,
4
+ selectRoomState,
5
+ HMSConfigInitialSettings,
6
+ } from '@100mslive/hms-video-store';
7
+ import { useCallback, useMemo } from 'react';
8
+ import { useHMSActions, useHMSStore } from '../primitives/HmsRoomProvider';
9
+ import { hooksErrHandler } from './types';
10
+ import { logErrorHandler } from '../utils/commons';
11
+ import { HMSConfig } from '@100mslive/hms-video';
12
+
13
+ export interface usePreviewInput {
14
+ /**
15
+ * name of user who is joining, this is only required if join is called
16
+ */
17
+ name?: string;
18
+ /**
19
+ * app side authentication token
20
+ */
21
+ token: string;
22
+ /**
23
+ * any extra metadata info for the peer
24
+ */
25
+ metadata?: string;
26
+ /**
27
+ * function to handle errors happening during preview
28
+ */
29
+ handleError?: hooksErrHandler;
30
+ initEndpoint?: string;
31
+ /**
32
+ * initial settings for audio/video and device to be used.
33
+ */
34
+ initialSettings?: HMSConfigInitialSettings;
35
+ /**
36
+ * Enable to get a network quality score while in preview. The score ranges from -1 to 5.
37
+ * -1 when we are not able to connect to 100ms servers within an expected time limit
38
+ * 0 when there is a timeout/failure when measuring the quality
39
+ * 1-5 ranges from poor to good quality.
40
+ */
41
+ captureNetworkQualityInPreview?: boolean;
42
+ }
43
+
44
+ export interface usePreviewResult {
45
+ /**
46
+ * enable the join button for the user only when this is true
47
+ */
48
+ enableJoin: boolean;
49
+ /**
50
+ * call this function to join the room
51
+ */
52
+ join: () => void;
53
+ /**
54
+ * once the user has joined the room, till leave happens this flag will be true. It can be used
55
+ * to decide to show between preview form and conferencing component/video tiles.
56
+ */
57
+ isConnected: boolean;
58
+ /**
59
+ * call this function to join the room
60
+ */
61
+ preview: () => void;
62
+ }
63
+
64
+ /**
65
+ * This hook can be used to build a preview UI component, this lets you call preview everytime the passed in
66
+ * token changes. This hook is best used in combination with useDevices for changing devices, useAVToggle for
67
+ * muting/unmuting and useAudioLevelStyles for showing mic audio level to the user.
68
+ * Any device change or mute/unmute will be carried across to join.
69
+ */
70
+ export const usePreviewJoin = ({
71
+ name = '',
72
+ token,
73
+ metadata,
74
+ handleError = logErrorHandler,
75
+ initEndpoint,
76
+ initialSettings,
77
+ captureNetworkQualityInPreview,
78
+ }: usePreviewInput): usePreviewResult => {
79
+ const actions = useHMSActions();
80
+ const roomState = useHMSStore(selectRoomState);
81
+ const isConnected = useHMSStore(selectIsConnectedToRoom) || false;
82
+ const enableJoin = roomState === HMSRoomState.Preview;
83
+
84
+ const config: HMSConfig = useMemo(() => {
85
+ return {
86
+ userName: name,
87
+ authToken: token,
88
+ metaData: metadata,
89
+ rememberDeviceSelection: true,
90
+ settings: initialSettings,
91
+ initEndpoint: initEndpoint,
92
+ captureNetworkQualityInPreview,
93
+ };
94
+ }, [name, token, metadata, initEndpoint, initialSettings, captureNetworkQualityInPreview]);
95
+
96
+ const preview = useCallback(() => {
97
+ (async () => {
98
+ if (!token) {
99
+ return;
100
+ }
101
+ if (roomState !== HMSRoomState.Disconnected) {
102
+ return;
103
+ }
104
+ if (isConnected) {
105
+ await actions.leave();
106
+ }
107
+ try {
108
+ await actions.preview(config);
109
+ } catch (err) {
110
+ handleError(err as Error, 'preview');
111
+ }
112
+ })();
113
+ }, [actions, handleError, token, roomState, config, isConnected]);
114
+
115
+ const join = useCallback(() => {
116
+ if (!token) {
117
+ return;
118
+ }
119
+ try {
120
+ actions.join(config);
121
+ } catch (err) {
122
+ handleError(err as Error, 'join');
123
+ }
124
+ }, [actions, config, handleError, token]);
125
+
126
+ return {
127
+ enableJoin,
128
+ join,
129
+ isConnected,
130
+ preview,
131
+ };
132
+ };
@@ -0,0 +1,34 @@
1
+ import { selectHLSState, selectRecordingState, selectRTMPState } from '@100mslive/hms-video-store';
2
+ import { useHMSStore } from '../primitives/HmsRoomProvider';
3
+
4
+ export interface useRecordingStreamingResult {
5
+ isServerRecordingOn: boolean;
6
+ isBrowserRecordingOn: boolean;
7
+ isHLSRecordingOn: boolean;
8
+ isStreamingOn: boolean;
9
+ isHLSRunning: boolean;
10
+ isRTMPRunning: boolean;
11
+ isRecordingOn: boolean;
12
+ }
13
+
14
+ export const useRecordingStreaming = (): useRecordingStreamingResult => {
15
+ const recording = useHMSStore(selectRecordingState);
16
+ const rtmp = useHMSStore(selectRTMPState);
17
+ const hls = useHMSStore(selectHLSState);
18
+
19
+ const isServerRecordingOn = recording.server.running;
20
+ const isBrowserRecordingOn = recording.browser.running;
21
+ const isHLSRecordingOn = recording.hls.running;
22
+ const isStreamingOn = hls.running || rtmp.running;
23
+ const isRecordingOn = isServerRecordingOn || isBrowserRecordingOn || isHLSRecordingOn;
24
+
25
+ return {
26
+ isServerRecordingOn,
27
+ isBrowserRecordingOn,
28
+ isHLSRecordingOn,
29
+ isStreamingOn,
30
+ isHLSRunning: hls.running,
31
+ isRTMPRunning: rtmp.running,
32
+ isRecordingOn,
33
+ };
34
+ };
@@ -0,0 +1,101 @@
1
+ /* eslint-disable complexity */
2
+ import {
3
+ HMSActions,
4
+ HMSTrack,
5
+ HMSTrackID,
6
+ selectAudioTrackVolume,
7
+ selectPermissions,
8
+ selectTrackByID,
9
+ } from '@100mslive/hms-video-store';
10
+ import { useHMSActions, useHMSStore } from '../primitives/HmsRoomProvider';
11
+ import { useCallback } from 'react';
12
+ import { hooksErrHandler } from './types';
13
+ import { logErrorHandler } from '../utils/commons';
14
+
15
+ export interface useRemoteAVToggleResult {
16
+ /**
17
+ * true if unmuted and vice versa
18
+ */
19
+ isAudioEnabled: boolean;
20
+ isVideoEnabled: boolean;
21
+ /**
22
+ * current volume of the audio track
23
+ */
24
+ volume?: number;
25
+ /**
26
+ * use this function to toggle audio state, the function will only be present if the user
27
+ * has permission to mute/unmute remote audio
28
+ */
29
+ toggleAudio?: () => void;
30
+ /**
31
+ * use this function to toggle video state, the function will only be present if the user
32
+ * has permission to mute/unmute remote video
33
+ */
34
+ toggleVideo?: () => void;
35
+ /**
36
+ * use this function to set the volume of peer's audio track for the local user, the function will
37
+ * only be present if the remote peer has an audio track to change volume for
38
+ */
39
+ setVolume?: (volume: number) => void;
40
+ }
41
+
42
+ const toggleTrackEnabled = async (
43
+ actions: HMSActions,
44
+ track: HMSTrack | undefined | null,
45
+ handleError: hooksErrHandler,
46
+ ) => {
47
+ if (track) {
48
+ try {
49
+ await actions.setRemoteTrackEnabled(track.id, !track.enabled);
50
+ } catch (err) {
51
+ handleError(err as Error, 'remoteToggle');
52
+ }
53
+ }
54
+ };
55
+
56
+ /**
57
+ * This hook can be used to implement remote mute/unmute + audio volume changer on tile level.
58
+ * @param peerId of the peer whose tracks need to be managed
59
+ * @param audioTrackId of the peer whose tracks need to be managed
60
+ * @param videoTrackId of the peer whose tracks need to be managed
61
+ * @param handleError to handle any error during toggle of audio/video
62
+ */
63
+ export const useRemoteAVToggle = (
64
+ audioTrackId: HMSTrackID,
65
+ videoTrackId: HMSTrackID,
66
+ handleError: hooksErrHandler = logErrorHandler,
67
+ ): useRemoteAVToggleResult => {
68
+ const actions = useHMSActions();
69
+ const audioTrack = useHMSStore(selectTrackByID(audioTrackId));
70
+ const videoTrack = useHMSStore(selectTrackByID(videoTrackId));
71
+ const volume = useHMSStore(selectAudioTrackVolume(audioTrack?.id));
72
+ const permissions = useHMSStore(selectPermissions);
73
+ const canToggleVideo = videoTrack?.enabled ? permissions?.mute : permissions?.unmute;
74
+ const canToggleAudio = audioTrack?.enabled ? permissions?.mute : permissions?.unmute;
75
+
76
+ const toggleAudio = useCallback(async () => {
77
+ await toggleTrackEnabled(actions, audioTrack, handleError);
78
+ }, [actions, audioTrack, handleError]);
79
+
80
+ const toggleVideo = useCallback(async () => {
81
+ await toggleTrackEnabled(actions, videoTrack, handleError);
82
+ }, [actions, handleError, videoTrack]);
83
+
84
+ const setVolume = useCallback(
85
+ (volume: number) => {
86
+ if (audioTrack) {
87
+ actions.setVolume(volume, audioTrack.id);
88
+ }
89
+ },
90
+ [actions, audioTrack],
91
+ );
92
+
93
+ return {
94
+ isAudioEnabled: !!audioTrack?.enabled,
95
+ isVideoEnabled: !!videoTrack?.enabled,
96
+ volume,
97
+ toggleAudio: audioTrack && canToggleAudio ? toggleAudio : undefined,
98
+ toggleVideo: videoTrack?.source === 'regular' && canToggleVideo ? toggleVideo : undefined,
99
+ setVolume: audioTrack ? setVolume : undefined,
100
+ };
101
+ };
@@ -0,0 +1,76 @@
1
+ import { hooksErrHandler } from '../hooks/types';
2
+ import {
3
+ HMSPeerID,
4
+ HMSTrackID,
5
+ selectIsLocalScreenShared,
6
+ selectPeerScreenSharing,
7
+ selectScreenSharesByPeerId,
8
+ } from '@100mslive/hms-video-store';
9
+ import { useHMSActions, useHMSStore } from '../primitives/HmsRoomProvider';
10
+ import { useCallback } from 'react';
11
+ import { logErrorHandler } from '../utils/commons';
12
+
13
+ export interface useScreenShareResult {
14
+ /**
15
+ * true if the local user is screensharing, false otherwise
16
+ */
17
+ amIScreenSharing: boolean;
18
+ /**
19
+ * toggle screenshare for the local user, will only be present if the user has the permission to toggle
20
+ */
21
+ toggleScreenShare?: () => void;
22
+ /**
23
+ * the id of the peer who is currently screensharing, will only be present if there is a screenshare in the room.
24
+ * In case of multiple screenshares, the behaviour of which one is picked is not defined.
25
+ */
26
+ screenSharingPeerId?: HMSPeerID;
27
+ /**
28
+ * the name of the peer who is currently screensharing. Will be undefined if no one is sharing the screen.
29
+ * In case of multiple screenshares, the behavior of which one is picked is not defined.
30
+ */
31
+ screenSharingPeerName?: string;
32
+ /**
33
+ * screenShare audio track id, will only be present if there is a screenshare with video track
34
+ */
35
+ screenShareVideoTrackId?: HMSTrackID;
36
+ /**
37
+ * screenShare audio track id, will only be present if there is a screenshare with audio track
38
+ */
39
+ screenShareAudioTrackId?: HMSTrackID;
40
+ }
41
+
42
+ /**
43
+ * This hook can be used to implement a screenshare toggle button as well as know about the screenshare in the room.
44
+ * This works best if your application only needs to show one screenshare in large view at a time with other screenshares
45
+ * similar to normal user tiles.
46
+ * For any complicated requirement with multiple screenshares it's best to use the store directly.
47
+ * For viewing the screenshare this hook is best used in combination with useVideo.
48
+ * For implementing control bar for local peer, this is used based with useAVToggle.
49
+ * @param handleError to handle any errors during screenshare toggle
50
+ */
51
+ export const useScreenShare = (handleError: hooksErrHandler = logErrorHandler): useScreenShareResult => {
52
+ const actions = useHMSActions();
53
+ const amIScreenSharing = useHMSStore(selectIsLocalScreenShared);
54
+ const screenSharePeer = useHMSStore(selectPeerScreenSharing);
55
+ const screenShare = useHMSStore(selectScreenSharesByPeerId(screenSharePeer?.id));
56
+
57
+ const toggleScreenShare = useCallback(
58
+ async (audioOnly = false) => {
59
+ try {
60
+ await actions.setScreenShareEnabled(!amIScreenSharing, audioOnly);
61
+ } catch (err) {
62
+ handleError(err as Error, 'toggleScreenShare');
63
+ }
64
+ },
65
+ [actions, amIScreenSharing, handleError],
66
+ );
67
+
68
+ return {
69
+ amIScreenSharing,
70
+ screenSharingPeerId: screenSharePeer?.id,
71
+ screenSharingPeerName: screenSharePeer?.name,
72
+ screenShareVideoTrackId: screenShare?.video?.id,
73
+ screenShareAudioTrackId: screenShare?.audio?.id,
74
+ toggleScreenShare,
75
+ };
76
+ };
@@ -0,0 +1,77 @@
1
+ import { selectTrackByID, HMSTrackID } from '@100mslive/hms-video-store';
2
+ import React, { useCallback, useEffect, useRef } from 'react';
3
+ import { useInView } from 'react-intersection-observer';
4
+ import { useHMSActions, useHMSStore } from '../primitives/HmsRoomProvider';
5
+ import HMSLogger from '../utils/logger';
6
+
7
+ export interface useVideoInput {
8
+ /**
9
+ * TrackId that is to be rendered
10
+ */
11
+ trackId?: HMSTrackID;
12
+ /**
13
+ * Boolean stating whether to override the internal behaviour.
14
+ * when attach is false, even if tile is inView or enabled, it won't be rendered
15
+ */
16
+ attach?: boolean;
17
+ }
18
+
19
+ export interface useVideoOutput {
20
+ videoRef: React.RefCallback<HTMLVideoElement>;
21
+ }
22
+ /**
23
+ * This hooks can be used to implement a video tile component. Given a track id it will return a ref.
24
+ * The returned ref can be used to set on a video element meant to display the video.
25
+ * The hook will take care of attaching and detaching video, and will automatically detach when the video
26
+ * goes out of view to save on bandwidth.
27
+ */
28
+ export const useVideo = ({ trackId, attach }: useVideoInput): useVideoOutput => {
29
+ const actions = useHMSActions();
30
+ const videoRef = useRef<HTMLVideoElement | null>(null);
31
+ const track = useHMSStore(selectTrackByID(trackId));
32
+
33
+ const { ref: inViewRef, inView } = useInView({ threshold: 0.5 });
34
+
35
+ const setRefs = useCallback(
36
+ (node: HTMLVideoElement) => {
37
+ if (node) {
38
+ videoRef.current = node;
39
+ inViewRef(node);
40
+ }
41
+ },
42
+ [inViewRef],
43
+ );
44
+
45
+ useEffect(() => {
46
+ (async () => {
47
+ if (videoRef.current && track?.id) {
48
+ if (inView && track.enabled && attach !== false) {
49
+ // attach when in view and enabled
50
+ await actions.attachVideo(track.id, videoRef.current);
51
+ } else {
52
+ // detach when not in view
53
+ await actions.detachVideo(track.id, videoRef.current);
54
+ }
55
+ }
56
+ })();
57
+ }, [actions, inView, videoRef, track?.id, track?.enabled, track?.deviceID, track?.plugins, attach]);
58
+
59
+ // detach on unmount
60
+ useEffect(() => {
61
+ return () => {
62
+ (async () => {
63
+ if (videoRef.current && track) {
64
+ try {
65
+ // detach on unmount
66
+ await actions.detachVideo(track.id, videoRef.current);
67
+ } catch (err) {
68
+ HMSLogger.w('detach video error for track', track.id, err);
69
+ }
70
+ }
71
+ })();
72
+ };
73
+ // eslint-disable-next-line react-hooks/exhaustive-deps
74
+ }, []);
75
+
76
+ return { videoRef: setRefs };
77
+ };