@100mslive/roomkit-react 0.1.8-alpha.0 → 0.1.9-alpha.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ };