@100mslive/roomkit-react 0.1.8-alpha.0 → 0.1.9-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 (142) hide show
  1. package/dist/{HLSView-IQRPLYNH.js → HLSView-U53QN3AC.js} +3 -3
  2. package/dist/Modal/Dialog.d.ts +402 -1706
  3. package/dist/Prebuilt/App.d.ts +6 -0
  4. package/dist/Prebuilt/AppContext.d.ts +2 -0
  5. package/dist/Prebuilt/AppStateContext.d.ts +16 -0
  6. package/dist/Prebuilt/components/ConferenceScreen.d.ts +2 -0
  7. package/dist/Prebuilt/components/Footer/PaginatedParticipants.d.ts +5 -0
  8. package/dist/Prebuilt/components/Footer/PollsToggle.d.ts +2 -0
  9. package/dist/Prebuilt/components/Footer/RoleAccordion.d.ts +10 -3
  10. package/dist/Prebuilt/components/LeaveScreen.d.ts +2 -0
  11. package/dist/Prebuilt/components/MwebLandscapePrompt.d.ts +2 -0
  12. package/dist/Prebuilt/components/Notifications/AutoplayBlockedModal.d.ts +2 -0
  13. package/dist/Prebuilt/components/Notifications/HLSFailureModal.d.ts +2 -0
  14. package/dist/Prebuilt/components/Notifications/InitErrorModal.d.ts +2 -0
  15. package/dist/Prebuilt/components/Notifications/Notifications.d.ts +2 -0
  16. package/dist/Prebuilt/components/Notifications/PeerNotifications.d.ts +1 -0
  17. package/dist/Prebuilt/components/Notifications/PermissionErrorModal.d.ts +2 -0
  18. package/dist/Prebuilt/components/Notifications/ReconnectNotifications.d.ts +2 -0
  19. package/dist/Prebuilt/components/Notifications/TrackBulkUnmuteModal.d.ts +2 -0
  20. package/dist/Prebuilt/components/Notifications/TrackNotifications.d.ts +1 -0
  21. package/dist/Prebuilt/components/Notifications/TrackUnmuteModal.d.ts +2 -0
  22. package/dist/Prebuilt/components/Polls/Polls.d.ts +2 -0
  23. package/dist/Prebuilt/components/Preview/PreviewJoin.d.ts +1 -2
  24. package/dist/Prebuilt/components/Preview/PreviewScreen.d.ts +2 -0
  25. package/dist/Prebuilt/components/hooks/useRedirectToLeave.d.ts +1 -1
  26. package/dist/{VirtualBackground-GP4ATXD3.js → VirtualBackground-PMLQPJB6.js} +3 -5
  27. package/dist/{VirtualBackground-GP4ATXD3.js.map → VirtualBackground-PMLQPJB6.js.map} +1 -1
  28. package/dist/chunk-ANQRGVIX.js +14441 -0
  29. package/dist/chunk-ANQRGVIX.js.map +7 -0
  30. package/dist/{chunk-Z3O2WGWV.js → chunk-XQ2NRKIW.js} +66 -3
  31. package/dist/chunk-XQ2NRKIW.js.map +7 -0
  32. package/dist/context/DialogContext.d.ts +6 -0
  33. package/dist/hooks/useDialogContainerSelector.d.ts +1 -0
  34. package/dist/index.cjs.js +10956 -9818
  35. package/dist/index.cjs.js.map +4 -4
  36. package/dist/index.d.ts +1 -0
  37. package/dist/index.js +6 -2
  38. package/dist/meta.cjs.json +4076 -3201
  39. package/dist/meta.esbuild.json +4391 -3623
  40. package/dist/utils/animations.d.ts +11 -0
  41. package/package.json +6 -7
  42. package/src/AudioLevel/AudioLevel.tsx +1 -1
  43. package/src/Modal/Dialog.tsx +31 -3
  44. package/src/Prebuilt/App.tsx +49 -97
  45. package/src/Prebuilt/AppContext.tsx +6 -0
  46. package/src/Prebuilt/AppStateContext.tsx +71 -0
  47. package/src/Prebuilt/common/constants.js +36 -1
  48. package/src/Prebuilt/common/utils.js +47 -0
  49. package/src/Prebuilt/components/AppData/AppData.jsx +6 -1
  50. package/src/Prebuilt/components/AppData/useSidepane.js +23 -1
  51. package/src/Prebuilt/components/AppData/useUISettings.js +49 -5
  52. package/src/Prebuilt/components/Chip.tsx +6 -2
  53. package/src/Prebuilt/components/{conference.jsx → ConferenceScreen.tsx} +34 -46
  54. package/src/Prebuilt/components/Footer/Footer.tsx +5 -0
  55. package/src/Prebuilt/components/Footer/PaginatedParticipants.tsx +125 -0
  56. package/src/Prebuilt/components/Footer/ParticipantList.jsx +55 -24
  57. package/src/Prebuilt/components/Footer/PollsToggle.tsx +22 -0
  58. package/src/Prebuilt/components/Footer/RoleAccordion.tsx +87 -85
  59. package/src/Prebuilt/components/Footer/RoleOptions.tsx +1 -1
  60. package/src/Prebuilt/components/Header/StreamActions.tsx +5 -3
  61. package/src/Prebuilt/components/Leave/DesktopLeaveRoom.tsx +4 -5
  62. package/src/Prebuilt/components/Leave/LeaveRoom.tsx +0 -4
  63. package/src/Prebuilt/components/{PostLeave.jsx → LeaveScreen.tsx} +6 -13
  64. package/src/Prebuilt/components/MoreSettings/ChangeNameModal.jsx +2 -3
  65. package/src/Prebuilt/components/MoreSettings/EmbedUrl.jsx +2 -3
  66. package/src/Prebuilt/components/MoreSettings/SplitComponents/MwebOptions.tsx +18 -1
  67. package/src/Prebuilt/components/{MwebLandscapePrompt.jsx → MwebLandscapePrompt.tsx} +10 -11
  68. package/src/Prebuilt/components/Notifications/{AutoplayBlockedModal.jsx → AutoplayBlockedModal.tsx} +2 -1
  69. package/src/Prebuilt/components/Notifications/{HLSFailureModal.jsx → HLSFailureModal.tsx} +10 -8
  70. package/src/Prebuilt/components/Notifications/{InitErrorModal.jsx → InitErrorModal.tsx} +5 -2
  71. package/src/Prebuilt/components/Notifications/{Notifications.jsx → Notifications.tsx} +41 -27
  72. package/src/Prebuilt/components/Notifications/{PeerNotifications.jsx → PeerNotifications.tsx} +3 -0
  73. package/src/Prebuilt/components/Notifications/{PermissionErrorModal.jsx → PermissionErrorModal.tsx} +6 -4
  74. package/src/Prebuilt/components/Notifications/{ReconnectNotifications.jsx → ReconnectNotifications.tsx} +11 -6
  75. package/src/Prebuilt/components/Notifications/{TrackBulkUnmuteModal.jsx → TrackBulkUnmuteModal.tsx} +9 -3
  76. package/src/Prebuilt/components/Notifications/{TrackUnmuteModal.jsx → TrackUnmuteModal.tsx} +9 -3
  77. package/src/Prebuilt/components/Notifications/index.tsx +1 -0
  78. package/src/Prebuilt/components/Polls/CreatePollQuiz/PollsQuizMenu.jsx +229 -0
  79. package/src/Prebuilt/components/Polls/CreatePollQuiz/Timer.jsx +71 -0
  80. package/src/Prebuilt/components/Polls/CreateQuestions/CreateQuestions.jsx +132 -0
  81. package/src/Prebuilt/components/Polls/CreateQuestions/DeleteQuestionModal.jsx +66 -0
  82. package/src/Prebuilt/components/Polls/CreateQuestions/QuestionForm.jsx +251 -0
  83. package/src/Prebuilt/components/Polls/CreateQuestions/SavedQuestion.jsx +57 -0
  84. package/src/Prebuilt/components/Polls/Polls.tsx +28 -0
  85. package/src/Prebuilt/components/Polls/Voting/PollResultSummary.jsx +125 -0
  86. package/src/Prebuilt/components/Polls/Voting/QuestionCard.jsx +249 -0
  87. package/src/Prebuilt/components/Polls/Voting/StandardVoting.jsx +40 -0
  88. package/src/Prebuilt/components/Polls/Voting/TimedVoting.jsx +36 -0
  89. package/src/Prebuilt/components/Polls/Voting/Voting.jsx +99 -0
  90. package/src/Prebuilt/components/Polls/common/MultipleChoiceOptions.jsx +101 -0
  91. package/src/Prebuilt/components/Polls/common/OptionInputWithDelete.jsx +25 -0
  92. package/src/Prebuilt/components/Polls/common/SingleChoiceOptions.jsx +125 -0
  93. package/src/Prebuilt/components/Polls/common/StatusIndicator.jsx +47 -0
  94. package/src/Prebuilt/components/Polls/common/VoteCount.jsx +28 -0
  95. package/src/Prebuilt/components/Polls/common/VoteProgress.jsx +17 -0
  96. package/src/Prebuilt/components/Polls/common/VoterList.jsx +22 -0
  97. package/src/Prebuilt/components/Polls/common/Votes.jsx +72 -0
  98. package/src/Prebuilt/components/Preview/PreviewForm.tsx +3 -2
  99. package/src/Prebuilt/components/Preview/PreviewJoin.tsx +29 -21
  100. package/src/Prebuilt/components/Preview/{PreviewContainer.tsx → PreviewScreen.tsx} +2 -19
  101. package/src/Prebuilt/components/RaiseHand.jsx +1 -1
  102. package/src/Prebuilt/components/RoleChangeModal.jsx +2 -3
  103. package/src/Prebuilt/components/RoleChangeRequest/RequestPrompt.tsx +2 -3
  104. package/src/Prebuilt/components/Settings/SettingsModal.jsx +2 -3
  105. package/src/Prebuilt/components/Settings/StartRecording.jsx +15 -4
  106. package/src/Prebuilt/components/SidePaneTabs.tsx +32 -6
  107. package/src/Prebuilt/components/StatsForNerds.jsx +2 -3
  108. package/src/Prebuilt/components/Streaming/Common.jsx +31 -21
  109. package/src/Prebuilt/components/VideoLayouts/ScreenshareLayout.tsx +8 -9
  110. package/src/Prebuilt/components/VideoTile.jsx +28 -39
  111. package/src/Prebuilt/components/hooks/useAutoStartStreaming.tsx +3 -3
  112. package/src/Prebuilt/components/hooks/useDropdownSelection.jsx +1 -1
  113. package/src/Prebuilt/components/hooks/useRedirectToLeave.tsx +9 -17
  114. package/src/Prebuilt/components/pdfAnnotator/pdfFileOptions.jsx +2 -3
  115. package/src/Prebuilt/components/pdfAnnotator/submitPdf.jsx +1 -1
  116. package/src/Prebuilt/components/pdfAnnotator/uploadedFile.jsx +2 -3
  117. package/src/Prebuilt/layouts/EmbedView.jsx +47 -60
  118. package/src/Prebuilt/layouts/PDFView.jsx +49 -99
  119. package/src/Prebuilt/layouts/SidePane.tsx +9 -4
  120. package/src/Prebuilt/layouts/VideoStreamingSection.tsx +2 -2
  121. package/src/Prebuilt/primitives/DialogContent.jsx +4 -5
  122. package/src/context/DialogContext.tsx +13 -0
  123. package/src/hooks/useDialogContainerSelector.tsx +7 -0
  124. package/src/index.ts +1 -0
  125. package/src/utils/animations.ts +6 -0
  126. package/dist/Prebuilt/components/PrebuiltDialogPortal.d.ts +0 -4
  127. package/dist/Prebuilt/components/Preview/PreviewContainer.d.ts +0 -3
  128. package/dist/chunk-2H5NIZB7.js +0 -70
  129. package/dist/chunk-2H5NIZB7.js.map +0 -7
  130. package/dist/chunk-GLYGPYNS.js +0 -7125
  131. package/dist/chunk-GLYGPYNS.js.map +0 -7
  132. package/dist/chunk-Z3O2WGWV.js.map +0 -7
  133. package/dist/conference-JD35TNH4.js +0 -6503
  134. package/dist/conference-JD35TNH4.js.map +0 -7
  135. package/src/Prebuilt/components/GoLiveButton.jsx +0 -42
  136. package/src/Prebuilt/components/PrebuiltDialogPortal.tsx +0 -6
  137. package/src/Prebuilt/components/Streaming/HLSStreaming.jsx +0 -220
  138. package/src/Prebuilt/components/Streaming/RTMPStreaming.jsx +0 -334
  139. package/src/Prebuilt/components/Streaming/StreamingLanding.jsx +0 -76
  140. /package/dist/{HLSView-IQRPLYNH.js.map → HLSView-U53QN3AC.js.map} +0 -0
  141. /package/{src/Prebuilt/components/Notifications/index.jsx → dist/Prebuilt/components/Notifications/index.d.ts} +0 -0
  142. /package/src/Prebuilt/components/Notifications/{TrackNotifications.jsx → TrackNotifications.tsx} +0 -0
@@ -1,8 +1,10 @@
1
- import { useCallback } from 'react';
1
+ import { useCallback, useMemo } from 'react';
2
2
  import {
3
3
  selectAppData,
4
4
  selectAppDataByPath,
5
5
  selectAudioTrackByPeerID,
6
+ selectPermissions,
7
+ selectPolls,
6
8
  selectSessionStore,
7
9
  selectTrackByID,
8
10
  selectVideoTrackByPeerID,
@@ -11,7 +13,7 @@ import {
11
13
  useHMSVanillaStore,
12
14
  } from '@100mslive/react-sdk';
13
15
  import { UserPreferencesKeys, useUserPreferences } from '../hooks/useUserPreferences';
14
- import { APP_DATA, SESSION_STORE_KEY } from '../../common/constants';
16
+ import { APP_DATA, POLL_STATE, SESSION_STORE_KEY } from '../../common/constants';
15
17
 
16
18
  /**
17
19
  * fields saved related to UI settings in store's app data can be
@@ -69,8 +71,18 @@ export const useUrlToEmbed = () => {
69
71
  return useHMSStore(selectAppData(APP_DATA.embedConfig))?.url;
70
72
  };
71
73
 
72
- export const usePDFAnnotator = () => {
73
- return useHMSStore(selectAppData(APP_DATA.pdfConfig))?.state;
74
+ export const usePDFConfig = () => {
75
+ return useHMSStore(selectAppData(APP_DATA.pdfConfig));
76
+ };
77
+
78
+ export const useResetPDFConfig = () => {
79
+ const [, setPDFConfig] = useSetAppDataByKey(APP_DATA.pdfConfig);
80
+ return useCallback(() => setPDFConfig(), [setPDFConfig]);
81
+ };
82
+
83
+ export const useResetEmbedConfig = () => {
84
+ const [, setEmbedConfig] = useSetAppDataByKey(APP_DATA.embedConfig);
85
+ return () => setEmbedConfig();
74
86
  };
75
87
  export const usePinnedTrack = () => {
76
88
  const pinnedTrackId = useHMSStore(selectAppData(APP_DATA.pinnedTrackId));
@@ -86,7 +98,7 @@ export const useSubscribedNotifications = notificationKey => {
86
98
  };
87
99
 
88
100
  export const useIsNotificationDisabled = () => {
89
- const notificationPreference = useHMSStore(selectAppDataByPath(APP_DATA.disableNotificiations));
101
+ const notificationPreference = useHMSStore(selectAppDataByPath(APP_DATA.disableNotifications));
90
102
  return notificationPreference;
91
103
  };
92
104
 
@@ -153,3 +165,35 @@ const useSetAppData = ({ key1, key2 }) => {
153
165
  );
154
166
  return setValue;
155
167
  };
168
+
169
+ export const useShowPolls = () => {
170
+ const permissions = useHMSStore(selectPermissions);
171
+ const polls = useHMSStore(selectPolls)?.filter(poll => poll.state === 'started' || poll.state === 'stopped');
172
+
173
+ const showPolls = useMemo(() => {
174
+ return permissions?.pollWrite || (permissions?.pollRead && polls?.length > 0);
175
+ }, [permissions?.pollRead, permissions?.pollWrite, polls?.length]);
176
+
177
+ return { showPolls };
178
+ };
179
+
180
+ export const usePollViewState = () => {
181
+ const [pollState, setPollState] = useSetAppDataByKey(APP_DATA.pollState);
182
+
183
+ const setPollView = useCallback(
184
+ view => {
185
+ setPollState({
186
+ [POLL_STATE.pollInView]: pollState?.pollInView,
187
+ [POLL_STATE.view]: view,
188
+ });
189
+ },
190
+ [pollState?.pollInView, setPollState],
191
+ );
192
+
193
+ return {
194
+ setPollState,
195
+ setPollView,
196
+ pollInView: pollState?.pollInView,
197
+ view: pollState?.view,
198
+ };
199
+ };
@@ -24,9 +24,13 @@ const Chip = ({
24
24
  return null;
25
25
  }
26
26
  return (
27
- <Flex align="center" css={{ backgroundColor, p: '$4 $6', borderRadius: '$4', ...css }} onClick={() => onClick?.()}>
27
+ <Flex
28
+ align="center"
29
+ css={{ backgroundColor, p: '$4 $6', gap: '$2', borderRadius: '$4', ...css }}
30
+ onClick={() => onClick?.()}
31
+ >
28
32
  {icon}
29
- <Text variant="sm" css={{ fontWeight: '$semiBold', color: textColor, ml: '$2' }}>
33
+ <Text variant="sm" css={{ fontWeight: '$semiBold', color: textColor }}>
30
34
  {content}
31
35
  </Text>
32
36
  </Flex>
@@ -1,6 +1,5 @@
1
1
  import React, { useEffect, useRef, useState } from 'react';
2
- import { useNavigate, useParams } from 'react-router-dom';
3
- import { usePrevious } from 'react-use';
2
+ import { DefaultConferencingScreen_Elements } from '@100mslive/types-prebuilt';
4
3
  import {
5
4
  HMSRoomState,
6
5
  selectAppData,
@@ -11,38 +10,40 @@ import {
11
10
  } from '@100mslive/react-sdk';
12
11
  import { Footer } from './Footer/Footer';
13
12
  import { HLSFailureModal } from './Notifications/HLSFailureModal';
13
+ // @ts-ignore: No implicit Any
14
14
  import { ActivatedPIP } from './PIP/PIPComponent';
15
+ // @ts-ignore: No implicit Any
15
16
  import { PictureInPicture } from './PIP/PIPManager';
16
17
  import { RoleChangeRequestModal } from './RoleChangeRequest/RoleChangeRequestModal';
17
18
  import { Box, Flex } from '../../Layout';
18
19
  import { useHMSPrebuiltContext } from '../AppContext';
19
20
  import { VideoStreamingSection } from '../layouts/VideoStreamingSection';
21
+ // @ts-ignore: No implicit Any
20
22
  import FullPageProgress from './FullPageProgress';
21
23
  import { Header } from './Header';
22
24
  import {
23
25
  useRoomLayoutConferencingScreen,
24
26
  useRoomLayoutPreviewScreen,
25
27
  } from '../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
28
+ // @ts-ignore: No implicit Any
26
29
  import { useAuthToken, useSetAppDataByKey } from './AppData/useUISettings';
30
+ // @ts-ignore: No implicit Any
27
31
  import { APP_DATA, isAndroid, isIOS, isIPadOS } from '../common/constants';
28
32
 
29
- const Conference = () => {
30
- const navigate = useNavigate();
31
- const { roomId, role } = useParams();
32
- const { userName, endpoints } = useHMSPrebuiltContext();
33
+ export const ConferenceScreen = () => {
34
+ const { userName, endpoints, onJoin: onJoinFunc } = useHMSPrebuiltContext();
33
35
  const screenProps = useRoomLayoutConferencingScreen();
34
36
  const { isPreviewScreenEnabled } = useRoomLayoutPreviewScreen();
35
37
  const roomState = useHMSStore(selectRoomState);
36
- const prevState = usePrevious(roomState);
37
38
  const isConnectedToRoom = useHMSStore(selectIsConnectedToRoom);
38
39
  const hmsActions = useHMSActions();
39
40
  const [hideControls, setHideControls] = useState(false);
40
41
  const dropdownList = useHMSStore(selectAppData(APP_DATA.dropdownList));
41
42
  const authTokenInAppData = useAuthToken();
42
- const headerRef = useRef();
43
- const footerRef = useRef();
43
+ const headerRef = useRef<HTMLDivElement | null>(null);
44
+ const footerRef = useRef<HTMLDivElement | null>(null);
44
45
  const isMobileDevice = isAndroid || isIOS || isIPadOS;
45
- const dropdownListRef = useRef();
46
+ const dropdownListRef = useRef<string[]>();
46
47
  const [isHLSStarted] = useSetAppDataByKey(APP_DATA.hlsStarted);
47
48
  const toggleControls = () => {
48
49
  if (dropdownListRef.current?.length === 0 && isMobileDevice) {
@@ -52,12 +53,12 @@ const Conference = () => {
52
53
  const autoRoomJoined = useRef(isPreviewScreenEnabled);
53
54
 
54
55
  useEffect(() => {
55
- let timeout = null;
56
+ let timeout: undefined | ReturnType<typeof setTimeout>;
56
57
  dropdownListRef.current = dropdownList || [];
57
- if (dropdownListRef.current.length === 0) {
58
+ if (dropdownListRef.current && dropdownListRef.current.length === 0) {
58
59
  clearTimeout(timeout);
59
60
  timeout = setTimeout(() => {
60
- if (dropdownListRef.current.length === 0) {
61
+ if (dropdownListRef.current && dropdownListRef.current.length === 0) {
61
62
  setHideControls(isMobileDevice);
62
63
  }
63
64
  }, 5000);
@@ -67,23 +68,6 @@ const Conference = () => {
67
68
  };
68
69
  }, [dropdownList, hideControls, isMobileDevice]);
69
70
 
70
- useEffect(() => {
71
- if (!roomId) {
72
- navigate(`/`);
73
- return;
74
- }
75
- if (!isPreviewScreenEnabled) {
76
- return;
77
- }
78
- if (
79
- !prevState &&
80
- !(roomState === HMSRoomState.Connecting || roomState === HMSRoomState.Reconnecting || isConnectedToRoom)
81
- ) {
82
- if (role) navigate(`/preview/${roomId || ''}/${role}`);
83
- else navigate(`/preview/${roomId || ''}`);
84
- }
85
- }, [isConnectedToRoom, prevState, roomState, navigate, role, roomId, isPreviewScreenEnabled]);
86
-
87
71
  useEffect(() => {
88
72
  if (
89
73
  authTokenInAppData &&
@@ -94,10 +78,10 @@ const Conference = () => {
94
78
  ) {
95
79
  hmsActions
96
80
  .join({
97
- userName,
81
+ userName: userName || '',
98
82
  authToken: authTokenInAppData,
99
83
  initEndpoint: endpoints?.init,
100
- initialSettings: {
84
+ settings: {
101
85
  isAudioMuted: !isPreviewScreenEnabled,
102
86
  isVideoMuted: !isPreviewScreenEnabled,
103
87
  speakerAutoSelectionBlacklist: ['Yeti Stereo Microphone'],
@@ -109,13 +93,14 @@ const Conference = () => {
109
93
  }, [authTokenInAppData, endpoints?.init, hmsActions, isConnectedToRoom, isPreviewScreenEnabled, roomState, userName]);
110
94
 
111
95
  useEffect(() => {
96
+ onJoinFunc?.();
112
97
  return () => {
113
- PictureInPicture.stop().catch(error => console.error('stopping pip', error));
98
+ PictureInPicture.stop().catch((error: unknown) => console.error('stopping pip', (error as Error).message));
114
99
  };
115
- }, []);
100
+ }, [onJoinFunc]);
116
101
 
117
- if (!isConnectedToRoom) {
118
- return <FullPageProgress text="Joining..." />;
102
+ if (!isConnectedToRoom && ![HMSRoomState.Reconnecting, HMSRoomState.Disconnected].includes(roomState)) {
103
+ return <FullPageProgress text={roomState === HMSRoomState.Connecting ? 'Joining...' : ''} />;
119
104
  }
120
105
 
121
106
  return (
@@ -139,7 +124,7 @@ const Conference = () => {
139
124
  }}
140
125
  data-testid="header"
141
126
  >
142
- <Header elements={screenProps.elements} screenType={screenProps.screenType} />
127
+ <Header />
143
128
  </Box>
144
129
  )}
145
130
  <Box
@@ -147,7 +132,10 @@ const Conference = () => {
147
132
  w: '100%',
148
133
  flex: '1 1 0',
149
134
  minHeight: 0,
150
- px: screenProps?.elements?.video_tile_layout?.grid?.edge_to_edge ? 0 : '$10', // TODO: padding to be controlled by section/element
135
+ // @ts-ignore
136
+ px: (screenProps?.elements as DefaultConferencingScreen_Elements)?.video_tile_layout?.grid?.edge_to_edge
137
+ ? 0
138
+ : '$10', // TODO: padding to be controlled by section/element
151
139
  paddingBottom: 'env(safe-area-inset-bottom)',
152
140
  '@lg': {
153
141
  px: 0,
@@ -157,13 +145,15 @@ const Conference = () => {
157
145
  data-testid="conferencing"
158
146
  onClick={toggleControls}
159
147
  >
160
- <VideoStreamingSection
161
- screenType={screenProps.screenType}
162
- elements={screenProps.elements}
163
- hideControls={hideControls}
164
- />
148
+ {screenProps.elements ? (
149
+ <VideoStreamingSection
150
+ screenType={screenProps.screenType}
151
+ elements={screenProps.elements}
152
+ hideControls={hideControls}
153
+ />
154
+ ) : null}
165
155
  </Box>
166
- {!screenProps.hideSections.includes('footer') && (
156
+ {!screenProps.hideSections.includes('footer') && screenProps.elements && (
167
157
  <Box
168
158
  ref={footerRef}
169
159
  css={{
@@ -189,5 +179,3 @@ const Conference = () => {
189
179
  </>
190
180
  );
191
181
  };
192
-
193
- export default Conference;
@@ -24,9 +24,12 @@ import { ScreenshareToggle } from '../ScreenShareToggle';
24
24
  import { ChatToggle } from './ChatToggle';
25
25
  // @ts-ignore: No implicit Any
26
26
  import { ParticipantCount } from './ParticipantList';
27
+ import { PollsToggle } from './PollsToggle';
27
28
  // @ts-ignore: No implicit Any
28
29
  import { useIsSidepaneTypeOpen, useSidepaneToggle } from '../AppData/useSidepane';
29
30
  // @ts-ignore: No implicit Any
31
+ import { useShowPolls } from '../AppData/useUISettings';
32
+ // @ts-ignore: No implicit Any
30
33
  import { SIDE_PANE_OPTIONS } from '../../common/constants';
31
34
  // @ts-ignore: No implicit Any
32
35
  const VirtualBackground = React.lazy(() => import('../../plugins/VirtualBackground/VirtualBackground'));
@@ -46,6 +49,7 @@ export const Footer = ({
46
49
  const noAVPermissions = !(toggleAudio || toggleVideo);
47
50
  const isChatOpen = useIsSidepaneTypeOpen(SIDE_PANE_OPTIONS.CHAT);
48
51
  const toggleChat = useSidepaneToggle(SIDE_PANE_OPTIONS.CHAT);
52
+ const { showPolls } = useShowPolls();
49
53
 
50
54
  useEffect(() => {
51
55
  if (!isChatOpen && openByDefault) {
@@ -108,6 +112,7 @@ export const Footer = ({
108
112
  )}
109
113
  </AppFooter.Center>
110
114
  <AppFooter.Right>
115
+ {showPolls && <PollsToggle />}
111
116
  {!isMobile && elements?.chat && <ChatToggle />}
112
117
  {elements?.participant_list && <ParticipantCount />}
113
118
  <MoreSettings elements={elements} screenType={screenType} />
@@ -0,0 +1,125 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { useInView } from 'react-intersection-observer';
3
+ import { useMeasure } from 'react-use';
4
+ import { VariableSizeList } from 'react-window';
5
+ import { selectIsConnectedToRoom, useHMSStore, usePaginatedParticipants } from '@100mslive/react-sdk';
6
+ import { ChevronLeftIcon, CrossIcon } from '@100mslive/react-icons';
7
+ import { IconButton } from '../../../IconButton';
8
+ import { Box, Flex } from '../../../Layout';
9
+ import { Loading } from '../../../Loading';
10
+ import { Text } from '../../../Text';
11
+ // @ts-ignore: No implicit Any
12
+ import { Participant, ParticipantSearch } from './ParticipantList';
13
+ import { ItemData, itemKey, ROW_HEIGHT } from './RoleAccordion';
14
+ // @ts-ignore: No implicit Any
15
+ import { useSidepaneReset } from '../AppData/useSidepane';
16
+ // @ts-ignore: No implicit Any
17
+ import { getFormattedCount } from '../../common/utils';
18
+
19
+ const LoadMoreParticipants = ({
20
+ hasNext,
21
+ loadMore,
22
+ style,
23
+ }: {
24
+ hasNext: boolean;
25
+ loadMore: () => Promise<void>;
26
+ style: React.CSSProperties;
27
+ }) => {
28
+ const { ref, inView } = useInView();
29
+ const [inProgress, setInProgress] = useState(false);
30
+
31
+ useEffect(() => {
32
+ if (hasNext && inView && !inProgress) {
33
+ setInProgress(true);
34
+ loadMore()
35
+ .catch(console.error)
36
+ .finally(() => setInProgress(false));
37
+ }
38
+ }, [hasNext, loadMore, inView, inProgress]);
39
+ return (
40
+ <Flex ref={ref} style={style} align="center" justify="center">
41
+ {inProgress ? <Loading size={16} /> : null}
42
+ </Flex>
43
+ );
44
+ };
45
+
46
+ const VirtualizedParticipantItem = React.memo(
47
+ ({
48
+ index,
49
+ data,
50
+ style,
51
+ }: {
52
+ index: number;
53
+ data: ItemData & { hasNext: boolean; loadMorePeers: () => Promise<void> };
54
+ style: React.CSSProperties;
55
+ }) => {
56
+ if (!data.peerList[index]) {
57
+ return <LoadMoreParticipants hasNext={data.hasNext} loadMore={data.loadMorePeers} style={style} />;
58
+ }
59
+ return (
60
+ <Participant
61
+ key={data.peerList[index].id}
62
+ peer={data.peerList[index]}
63
+ isConnected={data.isConnected}
64
+ style={style}
65
+ />
66
+ );
67
+ },
68
+ );
69
+
70
+ export const PaginatedParticipants = ({ roleName, onBack }: { roleName: string; onBack: () => void }) => {
71
+ const { peers, total, hasNext, loadPeers, loadMorePeers } = usePaginatedParticipants({ role: roleName, limit: 20 });
72
+ const [search, setSearch] = useState<string>('');
73
+ const filteredPeers = peers.filter(p => p.name?.toLowerCase().includes(search));
74
+ const isConnected = useHMSStore(selectIsConnectedToRoom);
75
+ const [ref, { width }] = useMeasure<HTMLDivElement>();
76
+ const height = ROW_HEIGHT * (filteredPeers.length + 1);
77
+ const resetSidePane = useSidepaneReset();
78
+
79
+ useEffect(() => {
80
+ loadPeers();
81
+ // eslint-disable-next-line react-hooks/exhaustive-deps
82
+ }, []);
83
+
84
+ return (
85
+ <Flex ref={ref} direction="column" css={{ size: '100%', gap: '$4' }}>
86
+ <Flex align="center">
87
+ <Flex align="center" css={{ flex: '1 1 0', cursor: 'pointer' }} onClick={onBack}>
88
+ <ChevronLeftIcon />
89
+ <Text variant="lg" css={{ flex: '1 1 0' }}>
90
+ Participants
91
+ </Text>
92
+ </Flex>
93
+ <IconButton
94
+ onClick={e => {
95
+ e.stopPropagation();
96
+ resetSidePane();
97
+ }}
98
+ data-testid="close_sidepane"
99
+ >
100
+ <CrossIcon />
101
+ </IconButton>
102
+ </Flex>
103
+ <ParticipantSearch onSearch={(search: string) => setSearch(search)} placeholder={`Search for ${roleName}`} />
104
+ <Flex direction="column" css={{ border: '1px solid $border_default', borderRadius: '$1', flex: '1 1 0' }}>
105
+ <Flex align="center" css={{ height: ROW_HEIGHT, borderBottom: '1px solid $border_default', px: '$8' }}>
106
+ <Text css={{ fontSize: '$space$7' }}>
107
+ {roleName}({getFormattedCount(peers.length)}/{getFormattedCount(total)})
108
+ </Text>
109
+ </Flex>
110
+ <Box css={{ flex: '1 1 0', overflowY: 'auto', overflowX: 'hidden', mr: '-$10' }}>
111
+ <VariableSizeList
112
+ itemSize={index => (index === filteredPeers.length + 1 ? 16 : ROW_HEIGHT)}
113
+ itemData={{ peerList: filteredPeers, hasNext: hasNext(), loadMorePeers, isConnected: isConnected === true }}
114
+ itemKey={itemKey}
115
+ itemCount={filteredPeers.length + 1}
116
+ width={width}
117
+ height={height}
118
+ >
119
+ {VirtualizedParticipantItem}
120
+ </VariableSizeList>
121
+ </Box>
122
+ </Flex>
123
+ </Flex>
124
+ );
125
+ };
@@ -3,6 +3,7 @@ import { useDebounce, useMedia } from 'react-use';
3
3
  import {
4
4
  selectHandRaisedPeers,
5
5
  selectHasPeerHandRaised,
6
+ selectIsLargeRoom,
6
7
  selectIsPeerAudioEnabled,
7
8
  selectLocalPeerID,
8
9
  selectPeerCount,
@@ -20,7 +21,7 @@ import {
20
21
  SearchIcon,
21
22
  VerticalMenuIcon,
22
23
  } from '@100mslive/react-icons';
23
- import { Box, config as cssConfig, Dropdown, Flex, Input, Text, textEllipsis } from '../../..';
24
+ import { Accordion, Box, config as cssConfig, Dropdown, Flex, Input, Text, textEllipsis } from '../../..';
24
25
  import IconButton from '../../IconButton';
25
26
  import { ConnectionIndicator } from '../Connection/ConnectionIndicator';
26
27
  import { ToastManager } from '../Toast/ToastManager';
@@ -31,9 +32,10 @@ import { useParticipants } from '../../common/hooks';
31
32
  import { getFormattedCount } from '../../common/utils';
32
33
  import { SIDE_PANE_OPTIONS } from '../../common/constants';
33
34
 
34
- export const ParticipantList = () => {
35
+ export const ParticipantList = ({ offStageRoles = [], onActive }) => {
35
36
  const [filter, setFilter] = useState();
36
37
  const { participants, isConnected, peerCount } = useParticipants(filter);
38
+ const isLargeRoom = useHMSStore(selectIsLargeRoom);
37
39
  const peersOrderedByRoles = {};
38
40
 
39
41
  const handRaisedPeers = useHMSStore(selectHandRaisedPeers);
@@ -45,6 +47,15 @@ export const ParticipantList = () => {
45
47
  peersOrderedByRoles[participant.roleName].push(participant);
46
48
  });
47
49
 
50
+ // prefill off_stage roles of large rooms to load more peers
51
+ if (isLargeRoom) {
52
+ offStageRoles.forEach(role => {
53
+ if (!peersOrderedByRoles[role]) {
54
+ peersOrderedByRoles[role] = [];
55
+ }
56
+ });
57
+ }
58
+
48
59
  const onSearch = useCallback(value => {
49
60
  setFilter(filterValue => {
50
61
  if (!filterValue) {
@@ -72,6 +83,9 @@ export const ParticipantList = () => {
72
83
  handRaisedList={handRaisedPeers}
73
84
  isConnected={isConnected}
74
85
  filter={filter}
86
+ offStageRoles={offStageRoles}
87
+ isLargeRoom={isLargeRoom}
88
+ onActive={onActive}
75
89
  />
76
90
  </Flex>
77
91
  </Fragment>
@@ -114,7 +128,15 @@ export const ParticipantCount = () => {
114
128
  );
115
129
  };
116
130
 
117
- const VirtualizedParticipants = ({ peersOrderedByRoles = {}, isConnected, filter, handRaisedList = [] }) => {
131
+ const VirtualizedParticipants = ({
132
+ peersOrderedByRoles = {},
133
+ isConnected,
134
+ filter,
135
+ handRaisedList = [],
136
+ offStageRoles,
137
+ isLargeRoom,
138
+ onActive,
139
+ }) => {
118
140
  return (
119
141
  <Flex
120
142
  direction="column"
@@ -127,29 +149,34 @@ const VirtualizedParticipants = ({ peersOrderedByRoles = {}, isConnected, filter
127
149
  flex: '1 1 0',
128
150
  }}
129
151
  >
130
- {handRaisedList.length > 0 ? (
131
- <RoleAccordion
132
- peerList={handRaisedList}
133
- roleName="Hand Raised"
134
- filter={filter}
135
- isConnected={isConnected}
136
- isHandRaisedAccordion
137
- />
138
- ) : null}
139
- {Object.keys(peersOrderedByRoles).map(role => (
140
- <RoleAccordion
141
- key={role}
142
- peerList={peersOrderedByRoles[role]}
143
- roleName={role}
144
- isConnected={isConnected}
145
- filter={filter}
146
- />
147
- ))}
152
+ <Accordion.Root type={isLargeRoom ? 'single' : 'multiple'} collapsible>
153
+ {handRaisedList.length > 0 ? (
154
+ <RoleAccordion
155
+ peerList={handRaisedList}
156
+ roleName="Hand Raised"
157
+ filter={filter}
158
+ isConnected={isConnected}
159
+ isHandRaisedAccordion
160
+ offStageRoles={offStageRoles}
161
+ />
162
+ ) : null}
163
+ {Object.keys(peersOrderedByRoles).map(role => (
164
+ <RoleAccordion
165
+ key={role}
166
+ peerList={peersOrderedByRoles[role]}
167
+ roleName={role}
168
+ isConnected={isConnected}
169
+ filter={filter}
170
+ offStageRoles={offStageRoles}
171
+ onActive={onActive}
172
+ />
173
+ ))}
174
+ </Accordion.Root>
148
175
  </Flex>
149
176
  );
150
177
  };
151
178
 
152
- export const Participant = ({ peer, isConnected }) => {
179
+ export const Participant = ({ peer, isConnected, style }) => {
153
180
  const localPeerId = useHMSStore(selectLocalPeerID);
154
181
  return (
155
182
  <Flex
@@ -164,8 +191,12 @@ export const Participant = ({ peer, isConnected }) => {
164
191
  align="center"
165
192
  justify="between"
166
193
  data-testid={'participant_' + peer.name}
194
+ style={style}
167
195
  >
168
- <Text variant="sm" css={{ ...textEllipsis(150), fontWeight: '$semiBold', color: '$on_surface_high' }}>
196
+ <Text
197
+ variant="sm"
198
+ css={{ ...textEllipsis('100%'), flex: '1 1 0', mr: '$8', fontWeight: '$semiBold', color: '$on_surface_high' }}
199
+ >
169
200
  {peer.name} {localPeerId === peer.id ? '(You)' : ''}
170
201
  </Text>
171
202
  {isConnected ? (
@@ -338,7 +369,7 @@ export const ParticipantSearch = ({ onSearch, placeholder, inSidePane = false })
338
369
  <Input
339
370
  type="text"
340
371
  placeholder={placeholder || 'Search for participants'}
341
- css={{ w: '100%', p: '$6', pl: '$16', mr: '$4', bg: inSidePane ? '$surface_default' : '$surface_dim' }}
372
+ css={{ w: '100%', p: '$6', pl: '$14', bg: inSidePane ? '$surface_default' : '$surface_dim' }}
342
373
  value={value}
343
374
  onKeyDown={event => {
344
375
  event.stopPropagation();
@@ -0,0 +1,22 @@
1
+ import React from 'react';
2
+ import { QuizIcon } from '@100mslive/react-icons';
3
+ import { Tooltip } from '../../..';
4
+ // @ts-ignore: No implicit Any
5
+ import IconButton from '../../IconButton';
6
+ // @ts-ignore: No implicit Any
7
+ import { useIsSidepaneTypeOpen, usePollViewToggle } from '../AppData/useSidepane';
8
+ // @ts-ignore: No implicit Any
9
+ import { SIDE_PANE_OPTIONS } from '../../common/constants';
10
+
11
+ export const PollsToggle = () => {
12
+ const isPollsOpen = useIsSidepaneTypeOpen(SIDE_PANE_OPTIONS.POLLS);
13
+ const togglePollView = usePollViewToggle();
14
+
15
+ return (
16
+ <Tooltip key="polls" title={`${isPollsOpen ? 'Close' : 'Open'} polls and quizzes`}>
17
+ <IconButton onClick={togglePollView} active={!isPollsOpen} data-testid="polls_btn">
18
+ <QuizIcon />
19
+ </IconButton>
20
+ </Tooltip>
21
+ );
22
+ };