@100mslive/roomkit-react 0.3.11 → 0.3.12-alpha.1

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.
@@ -8,14 +8,17 @@ import { Text } from '../../../Text';
8
8
  // @ts-ignore: No implicit Any
9
9
  import { ToastManager } from '../Toast/ToastManager';
10
10
  // @ts-ignore: No implicit Any
11
- import { useSetIsCaptionEnabled } from '../AppData/useUISettings';
11
+ import { useSetAppDataByKey, useSetIsCaptionEnabled } from '../AppData/useUISettings';
12
+ import { CAPTION_TOAST } from '../../common/constants';
12
13
 
13
14
  export const CaptionContent = ({ isMobile, onExit }: { isMobile: boolean; onExit: () => void }) => {
14
15
  const DURATION = 2000;
15
16
  const actions = useHMSActions();
16
17
  const isTranscriptionEnabled = useHMSStore(selectIsTranscriptionEnabled);
18
+ const [toastId, setToastId] = useSetAppDataByKey(CAPTION_TOAST.captionToast);
17
19
 
18
20
  const [isCaptionEnabled, setIsCaptionEnabled] = useSetIsCaptionEnabled();
21
+
19
22
  return (
20
23
  <>
21
24
  <Text
@@ -90,30 +93,33 @@ export const CaptionContent = ({ isMobile, onExit }: { isMobile: boolean; onExit
90
93
  await actions.stopTranscription({
91
94
  mode: HMSTranscriptionMode.CAPTION,
92
95
  });
93
- ToastManager.addToast({
96
+ const id = ToastManager.replaceToast(toastId, {
94
97
  title: `Disabling Closed Caption for everyone.`,
95
98
  variant: 'standard',
96
99
  duration: DURATION,
97
100
  icon: <Loading color="currentColor" />,
98
101
  });
102
+ setToastId(id);
99
103
  onExit();
100
104
  return;
101
105
  }
102
106
  await actions.startTranscription({
103
107
  mode: HMSTranscriptionMode.CAPTION,
104
108
  });
105
- ToastManager.addToast({
109
+ const id = ToastManager.replaceToast(toastId, {
106
110
  title: `Enabling Closed Caption for everyone.`,
107
111
  variant: 'standard',
108
112
  duration: DURATION,
109
113
  icon: <Loading color="currentColor" />,
110
114
  });
115
+ setToastId(id);
111
116
  } catch (err) {
112
- ToastManager.addToast({
117
+ const id = ToastManager.replaceToast(toastId, {
113
118
  title: `Failed to ${isTranscriptionEnabled ? 'disabled' : 'enabled'} closed caption`,
114
119
  variant: 'error',
115
120
  icon: <AlertTriangleIcon style={{ marginRight: '0.5rem' }} />,
116
121
  });
122
+ setToastId(id);
117
123
  }
118
124
  onExit();
119
125
  }}
@@ -6,9 +6,25 @@ import {
6
6
  HLSLiveStreamingScreen_Elements,
7
7
  } from '@100mslive/types-prebuilt';
8
8
  import { match } from 'ts-pattern';
9
- import { selectAppData, selectLocalPeerID, useHMSActions, useHMSStore } from '@100mslive/react-sdk';
10
- import { BrbIcon, CheckIcon, HamburgerMenuIcon, InfoIcon, PipIcon, SettingsIcon } from '@100mslive/react-icons';
11
- import { Checkbox, Dropdown, Flex, Text, Tooltip } from '../../../..';
9
+ import {
10
+ HMSTranscriptionMode,
11
+ selectAppData,
12
+ selectIsTranscriptionAllowedByMode,
13
+ selectIsTranscriptionEnabled,
14
+ selectLocalPeerID,
15
+ useHMSActions,
16
+ useHMSStore,
17
+ } from '@100mslive/react-sdk';
18
+ import {
19
+ BrbIcon,
20
+ CheckIcon,
21
+ HamburgerMenuIcon,
22
+ InfoIcon,
23
+ OpenCaptionIcon,
24
+ PipIcon,
25
+ SettingsIcon,
26
+ } from '@100mslive/react-icons';
27
+ import { Checkbox, Dropdown, Flex, Switch, Text, Tooltip } from '../../../..';
12
28
  import IconButton from '../../../IconButton';
13
29
  // @ts-ignore: No implicit any
14
30
  import { PIP } from '../../PIP';
@@ -61,6 +77,8 @@ export const DesktopOptions = ({
61
77
  const { isBRBOn, toggleBRB } = useMyMetadata();
62
78
  const isPipOn = PictureInPicture.isOn();
63
79
  const isBRBEnabled = !!elements?.brb;
80
+ const isTranscriptionAllowed = useHMSStore(selectIsTranscriptionAllowedByMode(HMSTranscriptionMode.CAPTION));
81
+ const isTranscriptionEnabled = useHMSStore(selectIsTranscriptionEnabled);
64
82
 
65
83
  useDropdownList({ open: openModals.size > 0, name: 'MoreSettings' });
66
84
 
@@ -116,6 +134,25 @@ export const DesktopOptions = ({
116
134
  </Flex>
117
135
  </Dropdown.Item>
118
136
  ) : null}
137
+ {isTranscriptionAllowed ? (
138
+ <Dropdown.Item
139
+ data-testid="closed_caption_admin"
140
+ onClick={() => {
141
+ updateState(MODALS.CAPTION, true);
142
+ }}
143
+ >
144
+ <OpenCaptionIcon />
145
+ <Flex direction="column" css={{ flexGrow: '1' }}>
146
+ <Text variant="sm" css={{ ml: '$4', color: '$on_surface_high' }}>
147
+ Closed Captions
148
+ </Text>
149
+ <Text variant="caption" css={{ ml: '$4', color: '$on_surface_medium' }}>
150
+ {isTranscriptionEnabled ? 'Enabled' : 'Disabled'}
151
+ </Text>
152
+ </Flex>
153
+ <Switch id="closed_caption_start_stop" checked={isTranscriptionEnabled} disabled={false} />
154
+ </Dropdown.Item>
155
+ ) : null}
119
156
  {screenType !== 'hls_live_streaming' ? (
120
157
  <Dropdown.Item css={{ p: 0, '&:empty': { display: 'none' } }}>
121
158
  <PIP
@@ -3,8 +3,11 @@ import { useClickAway } from 'react-use';
3
3
  import { ConferencingScreen, DefaultConferencingScreen_Elements } from '@100mslive/types-prebuilt';
4
4
  import { match } from 'ts-pattern';
5
5
  import {
6
+ HMSTranscriptionMode,
6
7
  selectIsConnectedToRoom,
7
8
  selectIsLocalVideoEnabled,
9
+ selectIsTranscriptionAllowedByMode,
10
+ selectIsTranscriptionEnabled,
8
11
  selectPeerCount,
9
12
  selectPermissions,
10
13
  useHMSActions,
@@ -13,12 +16,14 @@ import {
13
16
  } from '@100mslive/react-sdk';
14
17
  import {
15
18
  BrbIcon,
19
+ ClosedCaptionIcon,
16
20
  CrossIcon,
17
21
  EmojiIcon,
18
22
  HamburgerMenuIcon,
19
23
  HandIcon,
20
24
  HandRaiseSlashedIcon,
21
25
  InfoIcon,
26
+ OpenCaptionIcon,
22
27
  PeopleIcon,
23
28
  QuizActiveIcon,
24
29
  QuizIcon,
@@ -50,7 +55,7 @@ import { useSheetToggle } from '../../AppData/useSheet';
50
55
  // @ts-ignore: No implicit any
51
56
  import { usePollViewToggle, useSidepaneToggle } from '../../AppData/useSidepane';
52
57
  // @ts-ignore: No implicit Any
53
- import { useShowPolls } from '../../AppData/useUISettings';
58
+ import { useSetIsCaptionEnabled, useShowPolls } from '../../AppData/useUISettings';
54
59
  // @ts-ignore: No implicit any
55
60
  import { useDropdownList } from '../../hooks/useDropdownList';
56
61
  import { useMyMetadata } from '../../hooks/useMetadata';
@@ -104,7 +109,10 @@ export const MwebOptions = ({
104
109
  const toggleVB = useSidepaneToggle(SIDE_PANE_OPTIONS.VB);
105
110
  const isLocalVideoEnabled = useHMSStore(selectIsLocalVideoEnabled);
106
111
  const { startRecording, isRecordingLoading } = useRecordingHandler();
112
+ const isTranscriptionAllowed = useHMSStore(selectIsTranscriptionAllowedByMode(HMSTranscriptionMode.CAPTION));
113
+ const isTranscriptionEnabled = useHMSStore(selectIsTranscriptionEnabled);
107
114
 
115
+ const [isCaptionEnabled] = useSetIsCaptionEnabled();
108
116
  useDropdownList({ open: openModals.size > 0 || openOptionsSheet || openSettingsSheet, name: 'MoreSettings' });
109
117
 
110
118
  const updateState = (modalName: string, value: boolean) => {
@@ -189,6 +197,17 @@ export const MwebOptions = ({
189
197
  <ActionTile.Title>{isHandRaised ? 'Lower' : 'Raise'} Hand</ActionTile.Title>
190
198
  </ActionTile.Root>
191
199
  ) : null}
200
+ {isTranscriptionAllowed ? (
201
+ <ActionTile.Root
202
+ onClick={() => {
203
+ setOpenOptionsSheet(false);
204
+ updateState(MODALS.CAPTION, true);
205
+ }}
206
+ >
207
+ {isTranscriptionEnabled && isCaptionEnabled ? <ClosedCaptionIcon /> : <OpenCaptionIcon />}
208
+ <ActionTile.Title>Closed Caption</ActionTile.Title>
209
+ </ActionTile.Root>
210
+ ) : null}
192
211
  {isLocalVideoEnabled && !!elements?.virtual_background ? (
193
212
  <ActionTile.Root
194
213
  onClick={() => {
@@ -29,6 +29,7 @@ import { ReconnectNotifications } from './ReconnectNotifications';
29
29
  import { TrackBulkUnmuteModal } from './TrackBulkUnmuteModal';
30
30
  import { TrackNotifications } from './TrackNotifications';
31
31
  import { TrackUnmuteModal } from './TrackUnmuteModal';
32
+ import { TranscriptionNotifications } from './TranscriptionNotifications';
32
33
  import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
33
34
  // @ts-ignore: No implicit Any
34
35
  import { usePollViewToggle } from '../AppData/useSidepane';
@@ -200,6 +201,7 @@ export function Notifications() {
200
201
  <InitErrorModal />
201
202
  <ChatNotifications />
202
203
  <HandRaisedNotifications />
204
+ <TranscriptionNotifications />
203
205
  </>
204
206
  );
205
207
  }
@@ -0,0 +1,58 @@
1
+ import React, { useEffect } from 'react';
2
+ import { match } from 'ts-pattern';
3
+ import { HMSNotificationTypes, HMSTranscriptionState, useHMSNotifications } from '@100mslive/react-sdk';
4
+ import { AlertTriangleIcon, ClosedCaptionIcon, OpenCaptionIcon } from '@100mslive/react-icons';
5
+ // @ts-ignore: No implicit Any
6
+ import { ToastManager } from '../Toast/ToastManager';
7
+ // @ts-ignore: No implicit Any
8
+ import { useSetAppDataByKey } from '../AppData/useUISettings';
9
+ import { CAPTION_TOAST } from '../../common/constants';
10
+
11
+ export const TranscriptionNotifications = () => {
12
+ const notification = useHMSNotifications(HMSNotificationTypes.TRANSCRIPTION_STATE_UPDATED);
13
+ const [toastId, setToastId] = useSetAppDataByKey(CAPTION_TOAST.captionToast);
14
+
15
+ useEffect(() => {
16
+ if (!notification?.data) {
17
+ return;
18
+ }
19
+
20
+ console.debug(`[${notification.type}]`, notification);
21
+ const transcriptionStates = notification.data;
22
+ if (transcriptionStates.length > 0) {
23
+ let id = '';
24
+ match({ state: transcriptionStates[0].state, error: transcriptionStates[0].error })
25
+ .when(
26
+ ({ error }) => !!error,
27
+ () => {
28
+ id = ToastManager.replaceToast(toastId, {
29
+ title: `Failed to enable Closed Caption`,
30
+ variant: 'error',
31
+ icon: <AlertTriangleIcon style={{ marginRight: '0.5rem' }} />,
32
+ });
33
+ },
34
+ )
35
+ .with({ state: HMSTranscriptionState.STARTED }, () => {
36
+ id = ToastManager.replaceToast(toastId, {
37
+ title: `Closed Captioning enabled for everyone`,
38
+ variant: 'standard',
39
+ duration: 2000,
40
+ icon: <OpenCaptionIcon style={{ marginRight: '0.5rem' }} />,
41
+ });
42
+ })
43
+ .with({ state: HMSTranscriptionState.STOPPED }, () => {
44
+ id = ToastManager.replaceToast(toastId, {
45
+ title: `Closed Captioning disabled for everyone`,
46
+ variant: 'standard',
47
+ duration: 2000,
48
+ icon: <ClosedCaptionIcon style={{ marginRight: '0.5rem' }} />,
49
+ });
50
+ })
51
+ .otherwise(() => null);
52
+ setToastId(id);
53
+ }
54
+ // eslint-disable-next-line react-hooks/exhaustive-deps
55
+ }, [notification, setToastId]);
56
+
57
+ return null;
58
+ };
@@ -1,19 +1,15 @@
1
1
  import React, { useEffect, useMemo, useState } from 'react';
2
2
  import { GridVideoTileLayout } from '@100mslive/types-prebuilt/elements/video_tile_layout';
3
3
  import {
4
- HMSTranscriptionInfo,
5
4
  selectLocalPeerID,
6
5
  selectLocalPeerRoleName,
7
6
  selectPeers,
8
7
  selectPeerScreenSharing,
9
- selectTranscriptionsState,
10
8
  selectWhiteboard,
11
9
  useHMSStore,
12
10
  useHMSVanillaStore,
13
11
  } from '@100mslive/react-sdk';
14
- import { AlertTriangleIcon } from '@100mslive/react-icons';
15
12
  // @ts-ignore: No implicit Any
16
- import { ToastManager } from '../Toast/ToastManager';
17
13
  import { EqualProminence } from './EqualProminence';
18
14
  import { RoleProminence } from './RoleProminence';
19
15
  import { ScreenshareLayout } from './ScreenshareLayout';
@@ -99,18 +95,6 @@ export const GridLayout = ({
99
95
  objectFit: video_object_fit,
100
96
  };
101
97
 
102
- const transcriptionStates: HMSTranscriptionInfo[] | undefined = useHMSStore(selectTranscriptionsState);
103
-
104
- useEffect(() => {
105
- if (transcriptionStates && transcriptionStates.length > 0 && transcriptionStates[0].error) {
106
- ToastManager.addToast({
107
- title: `Failed to enable Closed Caption`,
108
- variant: 'error',
109
- icon: <AlertTriangleIcon style={{ marginRight: '0.5rem' }} />,
110
- });
111
- }
112
- }, [transcriptionStates]);
113
-
114
98
  useEffect(() => {
115
99
  if (mainPage !== 0) {
116
100
  return;
@@ -1,6 +1,12 @@
1
1
  import React, { useEffect, useState } from 'react';
2
2
  import { useMedia } from 'react-use';
3
- import { HMSTranscript, selectPeerNameByID, useHMSStore, useTranscript } from '@100mslive/react-sdk';
3
+ import {
4
+ HMSTranscript,
5
+ selectIsTranscriptionEnabled,
6
+ selectPeerNameByID,
7
+ useHMSStore,
8
+ useTranscript,
9
+ } from '@100mslive/react-sdk';
4
10
  import { Box, Flex } from '../../Layout';
5
11
  import { Text } from '../../Text';
6
12
  import { config } from '../../Theme';
@@ -170,6 +176,8 @@ export const CaptionsViewer = () => {
170
176
 
171
177
  const isCaptionEnabled = useIsCaptionEnabled();
172
178
 
179
+ const isTranscriptionEnabled = useHMSStore(selectIsTranscriptionEnabled);
180
+
173
181
  useEffect(() => {
174
182
  const timeInterval = setInterval(() => {
175
183
  if (!captionQueue) {
@@ -193,7 +201,7 @@ export const CaptionsViewer = () => {
193
201
  }
194
202
  return false;
195
203
  });
196
- if (dataToShow.length <= 0 || screenType === 'hls_live_streaming' || !isCaptionEnabled) {
204
+ if (dataToShow.length <= 0 || screenType === 'hls_live_streaming' || !isCaptionEnabled || !isTranscriptionEnabled) {
197
205
  return null;
198
206
  }
199
207
  return (