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

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 (46) hide show
  1. package/dist/{HLSView-IQRPLYNH.js → HLSView-DDGPZHA2.js} +3 -3
  2. package/dist/Prebuilt/App.d.ts +1 -0
  3. package/dist/Prebuilt/AppContext.d.ts +1 -0
  4. package/dist/Prebuilt/components/Footer/PaginatedParticipants.d.ts +5 -0
  5. package/dist/Prebuilt/components/Footer/RoleAccordion.d.ts +10 -3
  6. package/dist/Prebuilt/components/Notifications/HeadlessEndRoomListener.d.ts +2 -0
  7. package/dist/Prebuilt/components/PrebuiltTileElements.d.ts +2198 -0
  8. package/dist/{VirtualBackground-GP4ATXD3.js → VirtualBackground-UVZJVOA2.js} +3 -3
  9. package/dist/{chunk-Z3O2WGWV.js → chunk-6SQTFOK6.js} +2 -2
  10. package/dist/{chunk-Z3O2WGWV.js.map → chunk-6SQTFOK6.js.map} +1 -1
  11. package/dist/{chunk-2H5NIZB7.js → chunk-HUMNPIYI.js} +2 -2
  12. package/dist/{chunk-GLYGPYNS.js → chunk-PRM33R4R.js} +286 -251
  13. package/dist/chunk-PRM33R4R.js.map +7 -0
  14. package/dist/{conference-JD35TNH4.js → conference-N7S47TDK.js} +484 -385
  15. package/dist/conference-N7S47TDK.js.map +7 -0
  16. package/dist/index.cjs.js +1895 -1727
  17. package/dist/index.cjs.js.map +4 -4
  18. package/dist/index.js +2 -2
  19. package/dist/meta.cjs.json +234 -42
  20. package/dist/meta.esbuild.json +267 -74
  21. package/package.json +6 -6
  22. package/src/AudioLevel/AudioLevel.tsx +1 -1
  23. package/src/Prebuilt/App.tsx +5 -0
  24. package/src/Prebuilt/AppContext.tsx +2 -0
  25. package/src/Prebuilt/common/constants.js +1 -1
  26. package/src/Prebuilt/components/AppData/AppData.jsx +1 -1
  27. package/src/Prebuilt/components/AppData/useUISettings.js +1 -1
  28. package/src/Prebuilt/components/Chip.tsx +6 -2
  29. package/src/Prebuilt/components/Footer/PaginatedParticipants.tsx +94 -0
  30. package/src/Prebuilt/components/Footer/ParticipantList.jsx +53 -23
  31. package/src/Prebuilt/components/Footer/RoleAccordion.tsx +86 -84
  32. package/src/Prebuilt/components/Footer/RoleOptions.tsx +1 -1
  33. package/src/Prebuilt/components/Notifications/HeadlessEndRoomListener.tsx +23 -0
  34. package/src/Prebuilt/components/Notifications/Notifications.jsx +1 -1
  35. package/src/Prebuilt/components/PrebuiltTileElements.tsx +5 -0
  36. package/src/Prebuilt/components/Preview/PreviewJoin.tsx +9 -6
  37. package/src/Prebuilt/components/SidePaneTabs.tsx +31 -5
  38. package/src/Prebuilt/components/VideoTile.jsx +19 -34
  39. package/src/Prebuilt/components/conference.jsx +4 -3
  40. package/src/Prebuilt/components/hooks/useDropdownSelection.jsx +1 -1
  41. package/src/Prebuilt/layouts/SidePane.tsx +1 -0
  42. package/dist/chunk-GLYGPYNS.js.map +0 -7
  43. package/dist/conference-JD35TNH4.js.map +0 -7
  44. /package/dist/{HLSView-IQRPLYNH.js.map → HLSView-DDGPZHA2.js.map} +0 -0
  45. /package/dist/{VirtualBackground-GP4ATXD3.js.map → VirtualBackground-UVZJVOA2.js.map} +0 -0
  46. /package/dist/{chunk-2H5NIZB7.js.map → chunk-HUMNPIYI.js.map} +0 -0
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "prebuilt",
11
11
  "roomkit"
12
12
  ],
13
- "version": "0.1.8-alpha.0",
13
+ "version": "0.1.8",
14
14
  "author": "100ms",
15
15
  "license": "MIT",
16
16
  "files": [
@@ -76,10 +76,10 @@
76
76
  "react": ">=17.0.2 <19.0.0"
77
77
  },
78
78
  "dependencies": {
79
- "@100mslive/hls-player": "0.1.17-alpha.0",
80
- "@100mslive/hms-virtual-background": "1.11.17-alpha.0",
81
- "@100mslive/react-icons": "0.8.17-alpha.0",
82
- "@100mslive/react-sdk": "0.8.17-alpha.0",
79
+ "@100mslive/hls-player": "0.1.17",
80
+ "@100mslive/hms-virtual-background": "1.11.17",
81
+ "@100mslive/react-icons": "0.8.17",
82
+ "@100mslive/react-sdk": "0.8.17",
83
83
  "@100mslive/types-prebuilt": "0.12.0",
84
84
  "@emoji-mart/data": "^1.0.6",
85
85
  "@emoji-mart/react": "^1.0.1",
@@ -115,5 +115,5 @@
115
115
  "uuid": "^8.3.2",
116
116
  "worker-timers": "^7.0.40"
117
117
  },
118
- "gitHead": "d6e072de081b508a297ebc1684a33a8e9db048ef"
118
+ "gitHead": "966ed9ffac3d55586196ad0b738eb1977dfc2ff7"
119
119
  }
@@ -27,7 +27,7 @@ const barAnimation = keyframes({
27
27
  to: {
28
28
  maskSize: '4em .8em',
29
29
  '-webkit-mask-position-y': '.1em',
30
- maskPosition: 'initial 0',
30
+ maskPosition: 'initial 0.1em',
31
31
  },
32
32
  });
33
33
 
@@ -24,6 +24,7 @@ import { Init } from './components/init/Init';
24
24
  import { KeyboardHandler } from './components/Input/KeyboardInputManager';
25
25
  // @ts-ignore: No implicit Any
26
26
  import { Notifications } from './components/Notifications';
27
+ import { HeadlessEndRoomListener } from './components/Notifications/HeadlessEndRoomListener';
27
28
  // @ts-ignore: No implicit Any
28
29
  import PostLeave from './components/PostLeave';
29
30
  // @ts-ignore: No implicit Any
@@ -68,6 +69,7 @@ export type HMSPrebuiltProps = {
68
69
  roomId?: string;
69
70
  role?: string;
70
71
  onLeave?: () => void;
72
+ onJoin?: () => void;
71
73
  };
72
74
 
73
75
  export type HMSPrebuiltRefType = {
@@ -90,6 +92,7 @@ export const HMSPrebuilt = React.forwardRef<HMSPrebuiltRefType, HMSPrebuiltProps
90
92
  options: { userName = '', userId = '', endpoints } = {},
91
93
  screens,
92
94
  onLeave,
95
+ onJoin,
93
96
  },
94
97
  ref,
95
98
  ) => {
@@ -172,6 +175,7 @@ export const HMSPrebuilt = React.forwardRef<HMSPrebuiltRefType, HMSPrebuiltProps
172
175
  roomId,
173
176
  role,
174
177
  onLeave,
178
+ onJoin,
175
179
  userName,
176
180
  userId,
177
181
  endpoints: {
@@ -349,6 +353,7 @@ function AppRoutes({
349
353
  <BackSwipe />
350
354
  {!isNotificationsDisabled && <FlyingEmoji />}
351
355
  <RemoteStopScreenshare />
356
+ <HeadlessEndRoomListener />
352
357
  <KeyboardHandler />
353
358
  <AuthToken authTokenByRoomCodeEndpoint={authTokenByRoomCodeEndpoint} defaultAuthToken={defaultAuthToken} />
354
359
  {roomLayout && (
@@ -8,6 +8,7 @@ type HMSPrebuiltContextType = {
8
8
  userId?: string;
9
9
  endpoints?: Record<string, string>;
10
10
  onLeave?: () => void;
11
+ onJoin?: () => void;
11
12
  };
12
13
 
13
14
  export const HMSPrebuiltContext = React.createContext<HMSPrebuiltContextType>({
@@ -16,6 +17,7 @@ export const HMSPrebuiltContext = React.createContext<HMSPrebuiltContextType>({
16
17
  userId: '',
17
18
  endpoints: {},
18
19
  onLeave: undefined,
20
+ onJoin: undefined,
19
21
  });
20
22
 
21
23
  HMSPrebuiltContext.displayName = 'HMSPrebuiltContext';
@@ -44,7 +44,7 @@ export const APP_DATA = {
44
44
  pdfConfig: 'pdfConfig',
45
45
  minimiseInset: 'minimiseInset',
46
46
  activeScreensharePeerId: 'activeScreensharePeerId',
47
- disableNotificiations: 'disableNotificiations',
47
+ disableNotifications: 'disableNotifications',
48
48
  };
49
49
  export const UI_SETTINGS = {
50
50
  isAudioOnly: 'isAudioOnly',
@@ -65,7 +65,7 @@ const initialAppData = {
65
65
  [APP_DATA.authToken]: '',
66
66
  [APP_DATA.minimiseInset]: false,
67
67
  [APP_DATA.activeScreensharePeerId]: '',
68
- [APP_DATA.disableNotificiations]: false,
68
+ [APP_DATA.disableNotifications]: false,
69
69
  };
70
70
 
71
71
  export const AppData = React.memo(({ appDetails, tokenEndpoint }) => {
@@ -86,7 +86,7 @@ export const useSubscribedNotifications = notificationKey => {
86
86
  };
87
87
 
88
88
  export const useIsNotificationDisabled = () => {
89
- const notificationPreference = useHMSStore(selectAppDataByPath(APP_DATA.disableNotificiations));
89
+ const notificationPreference = useHMSStore(selectAppDataByPath(APP_DATA.disableNotifications));
90
90
  return notificationPreference;
91
91
  };
92
92
 
@@ -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>
@@ -0,0 +1,94 @@
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import { useMeasure } from 'react-use';
3
+ import { FixedSizeList } from 'react-window';
4
+ import { selectIsConnectedToRoom, useHMSStore, usePaginatedParticipants } from '@100mslive/react-sdk';
5
+ import { ChevronLeftIcon, CrossIcon } from '@100mslive/react-icons';
6
+ import { Button } from '../../../Button';
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 { ParticipantSearch } from './ParticipantList';
13
+ import { itemKey, ROW_HEIGHT, VirtualizedParticipantItem } 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
+ export const PaginatedParticipants = ({ roleName, onBack }: { roleName: string; onBack: () => void }) => {
20
+ const { peers, total, loadPeers, loadMorePeers } = usePaginatedParticipants({ role: roleName, limit: 20 });
21
+ const [search, setSearch] = useState<string>('');
22
+ const [isLoading, setIsLoading] = useState(false);
23
+ const filteredPeers = peers.filter(p => p.name?.toLowerCase().includes(search));
24
+ const isConnected = useHMSStore(selectIsConnectedToRoom);
25
+ const [ref, { width }] = useMeasure<HTMLDivElement>();
26
+ const containerRef = useRef<HTMLDivElement | null>(null);
27
+ const height = ROW_HEIGHT * peers.length;
28
+ const resetSidePane = useSidepaneReset();
29
+ const hasNext = total > peers.length;
30
+
31
+ useEffect(() => {
32
+ loadPeers();
33
+ // eslint-disable-next-line react-hooks/exhaustive-deps
34
+ }, []);
35
+
36
+ return (
37
+ <Flex ref={ref} direction="column" css={{ size: '100%', gap: '$4' }}>
38
+ <Flex align="center">
39
+ <Flex align="center" css={{ flex: '1 1 0', cursor: 'pointer' }} onClick={onBack}>
40
+ <ChevronLeftIcon />
41
+ <Text variant="lg" css={{ flex: '1 1 0' }}>
42
+ Participants
43
+ </Text>
44
+ </Flex>
45
+ <IconButton
46
+ onClick={e => {
47
+ e.stopPropagation();
48
+ resetSidePane();
49
+ }}
50
+ data-testid="close_sidepane"
51
+ >
52
+ <CrossIcon />
53
+ </IconButton>
54
+ </Flex>
55
+ <ParticipantSearch onSearch={(search: string) => setSearch(search)} placeholder={`Search for ${roleName}`} />
56
+ <Flex direction="column" css={{ border: '1px solid $border_default', borderRadius: '$1', flex: '1 1 0' }}>
57
+ <Flex align="center" css={{ height: ROW_HEIGHT, borderBottom: '1px solid $border_default', px: '$8' }}>
58
+ <Text css={{ fontSize: '$space$7' }}>
59
+ {roleName}({getFormattedCount(peers.length)}/{getFormattedCount(total)})
60
+ </Text>
61
+ </Flex>
62
+ <Box css={{ flex: '1 1 0', overflowY: 'auto', overflowX: 'hidden', mr: '-$10' }}>
63
+ <FixedSizeList
64
+ itemSize={ROW_HEIGHT}
65
+ itemData={{ peerList: filteredPeers, isConnected: isConnected === true }}
66
+ itemKey={itemKey}
67
+ itemCount={filteredPeers.length}
68
+ width={width}
69
+ height={height}
70
+ outerRef={containerRef}
71
+ >
72
+ {VirtualizedParticipantItem}
73
+ </FixedSizeList>
74
+ {hasNext ? (
75
+ <Flex justify="center" css={{ w: '100%' }}>
76
+ <Button
77
+ css={{ w: 'max-content', p: '$4' }}
78
+ onClick={() => {
79
+ setIsLoading(true);
80
+ loadMorePeers()
81
+ .catch(console.error)
82
+ .finally(() => setIsLoading(false));
83
+ }}
84
+ disabled={isLoading}
85
+ >
86
+ {isLoading ? <Loading size={16} /> : 'Load More'}
87
+ </Button>
88
+ </Flex>
89
+ ) : null}
90
+ </Box>
91
+ </Flex>
92
+ </Flex>
93
+ );
94
+ };
@@ -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,24 +149,29 @@ 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
  };
@@ -165,7 +192,10 @@ export const Participant = ({ peer, isConnected }) => {
165
192
  justify="between"
166
193
  data-testid={'participant_' + peer.name}
167
194
  >
168
- <Text variant="sm" css={{ ...textEllipsis(150), fontWeight: '$semiBold', color: '$on_surface_high' }}>
195
+ <Text
196
+ variant="sm"
197
+ css={{ ...textEllipsis('100%'), flex: '1 1 0', mr: '$8', fontWeight: '$semiBold', color: '$on_surface_high' }}
198
+ >
169
199
  {peer.name} {localPeerId === peer.id ? '(You)' : ''}
170
200
  </Text>
171
201
  {isConnected ? (
@@ -338,7 +368,7 @@ export const ParticipantSearch = ({ onSearch, placeholder, inSidePane = false })
338
368
  <Input
339
369
  type="text"
340
370
  placeholder={placeholder || 'Search for participants'}
341
- css={{ w: '100%', p: '$6', pl: '$16', mr: '$4', bg: inSidePane ? '$surface_default' : '$surface_dim' }}
371
+ css={{ w: '100%', p: '$6', pl: '$14', bg: inSidePane ? '$surface_default' : '$surface_dim' }}
342
372
  value={value}
343
373
  onKeyDown={event => {
344
374
  event.stopPropagation();
@@ -1,30 +1,30 @@
1
- import React, { useCallback, useEffect, useRef, useState } from 'react';
1
+ import React, { useEffect } from 'react';
2
2
  import { useMeasure } from 'react-use';
3
3
  import { FixedSizeList } from 'react-window';
4
- import { HMSPeer, HMSPeerListIterator, useHMSActions } from '@100mslive/react-sdk';
5
- import { AddCircleIcon } from '@100mslive/react-icons';
4
+ import { HMSPeer, selectIsLargeRoom, useHMSStore, usePaginatedParticipants } from '@100mslive/react-sdk';
5
+ import { ChevronRightIcon } from '@100mslive/react-icons';
6
6
  import { Accordion } from '../../../Accordion';
7
- import { Box, Flex } from '../../../Layout';
7
+ import { Flex } from '../../../Layout';
8
8
  import { Text } from '../../../Text';
9
- import Chip from '../Chip';
10
9
  // @ts-ignore: No implicit Any
11
10
  import { Participant } from './ParticipantList';
12
11
  import { RoleOptions } from './RoleOptions';
13
12
  // @ts-ignore: No implicit Any
14
13
  import { getFormattedCount } from '../../common/utils';
15
14
 
16
- const ROW_HEIGHT = 50;
15
+ export const ROW_HEIGHT = 50;
16
+ const ITER_TIMER = 5000;
17
17
 
18
- interface ItemData {
18
+ export interface ItemData {
19
19
  peerList: HMSPeer[];
20
20
  isConnected: boolean;
21
21
  }
22
22
 
23
- function itemKey(index: number, data: ItemData) {
23
+ export function itemKey(index: number, data: ItemData) {
24
24
  return data.peerList[index].id;
25
25
  }
26
26
 
27
- const VirtualizedParticipantItem = React.memo(({ index, data }: { index: number; data: ItemData }) => {
27
+ export const VirtualizedParticipantItem = React.memo(({ index, data }: { index: number; data: ItemData }) => {
28
28
  return <Participant key={data.peerList[index].id} peer={data.peerList[index]} isConnected={data.isConnected} />;
29
29
  });
30
30
 
@@ -34,101 +34,103 @@ export const RoleAccordion = ({
34
34
  isConnected,
35
35
  filter,
36
36
  isHandRaisedAccordion = false,
37
+ offStageRoles,
38
+ onActive,
37
39
  }: ItemData & {
38
40
  roleName: string;
39
41
  isHandRaisedAccordion?: boolean;
40
42
  filter?: { search: string };
43
+ offStageRoles: string[];
44
+ onActive?: (role: string) => void;
41
45
  }) => {
42
46
  const [ref, { width }] = useMeasure<HTMLDivElement>();
43
- const actions = useHMSActions();
44
47
  const showAcordion = filter?.search ? peerList.some(peer => peer.name.toLowerCase().includes(filter.search)) : true;
45
- const [hasNext, setHasNext] = useState(false);
46
- const iteratorRef = useRef<HMSPeerListIterator | null>(null);
48
+ const isLargeRoom = useHMSStore(selectIsLargeRoom);
49
+ const { peers, total, loadPeers } = usePaginatedParticipants({ role: roleName, limit: 10 });
50
+ const isOffStageRole = roleName && offStageRoles.includes(roleName);
47
51
 
48
- const loadNext = useCallback(() => {
49
- if (!roleName || roleName === 'Hand Raised') {
52
+ useEffect(() => {
53
+ if (!isOffStageRole || !isLargeRoom) {
50
54
  return;
51
55
  }
52
- if (!iteratorRef.current) {
53
- iteratorRef.current = actions.getPeerListIterator({ role: roleName });
54
- }
55
- iteratorRef.current
56
- .next()
57
- .catch(console.error)
58
- .finally(() => {
59
- setHasNext(iteratorRef.current ? iteratorRef.current.hasNext() : false);
60
- });
61
- }, [actions, roleName]);
62
-
63
- useEffect(() => {
64
- loadNext();
65
- }, [loadNext]);
56
+ loadPeers();
57
+ const interval = setInterval(() => {
58
+ loadPeers();
59
+ }, ITER_TIMER);
60
+ return () => clearInterval(interval);
61
+ }, [isOffStageRole, isLargeRoom]); //eslint-disable-line
66
62
 
67
63
  if (!showAcordion || (isHandRaisedAccordion && filter?.search) || (peerList.length === 0 && filter?.search)) {
68
64
  return null;
69
65
  }
70
66
 
71
- const height = ROW_HEIGHT * peerList.length;
67
+ const height = ROW_HEIGHT * (peers.length || peerList.length);
68
+ const peersInAccordion = isOffStageRole && isLargeRoom ? peers : peerList;
69
+ const hasNext = total > peersInAccordion.length;
70
+
71
+ if (peersInAccordion.length === 0) {
72
+ return null;
73
+ }
72
74
 
73
75
  return (
74
- <Flex direction="column" css={{ '&:hover .role_actions': { visibility: 'visible' } }} ref={ref}>
75
- <Accordion.Root
76
- type="single"
77
- collapsible
78
- defaultValue={roleName}
79
- css={{ borderRadius: '$1', border: '1px solid $border_bright' }}
76
+ <Accordion.Item value={roleName} css={{ '&:hover .role_actions': { visibility: 'visible' }, mb: '$8' }} ref={ref}>
77
+ <Accordion.Header
78
+ iconStyles={{ c: '$on_surface_high' }}
79
+ css={{
80
+ textTransform: 'capitalize',
81
+ p: '$6 $8',
82
+ fontSize: '$sm',
83
+ fontWeight: '$semiBold',
84
+ c: '$on_surface_medium',
85
+ borderRadius: '$1',
86
+ border: '1px solid $border_default',
87
+ '&[data-state="open"]': {
88
+ borderBottomLeftRadius: 0,
89
+ borderBottomRightRadius: 0,
90
+ },
91
+ }}
80
92
  >
81
- <Accordion.Item value={roleName}>
82
- <Accordion.Header
83
- iconStyles={{ c: '$on_surface_high' }}
93
+ <Flex justify="between" css={{ flexGrow: 1, pr: '$6' }}>
94
+ <Text
95
+ variant="sm"
96
+ css={{ fontWeight: '$semiBold', textTransform: 'capitalize', color: '$on_surface_medium' }}
97
+ >
98
+ {roleName} {`(${getFormattedCount(isLargeRoom && isOffStageRole ? total : peerList.length)})`}
99
+ </Text>
100
+ <RoleOptions roleName={roleName} peerList={peersInAccordion} />
101
+ </Flex>
102
+ </Accordion.Header>
103
+ <Accordion.Content contentStyles={{ border: '1px solid $border_default', borderTop: 'none' }}>
104
+ <FixedSizeList
105
+ itemSize={ROW_HEIGHT}
106
+ itemData={{ peerList: peersInAccordion, isConnected }}
107
+ itemKey={itemKey}
108
+ itemCount={peersInAccordion.length}
109
+ width={width}
110
+ height={height}
111
+ >
112
+ {VirtualizedParticipantItem}
113
+ </FixedSizeList>
114
+ {offStageRoles?.includes(roleName) && hasNext ? (
115
+ <Flex
116
+ align="center"
117
+ justify="end"
84
118
  css={{
85
- textTransform: 'capitalize',
86
- p: '$6 $8',
87
- fontSize: '$sm',
88
- fontWeight: '$semiBold',
89
- c: '$on_surface_medium',
119
+ gap: '$1',
120
+ cursor: 'pointer',
121
+ color: '$on_surface_high',
122
+ p: '$6',
123
+ borderTop: '1px solid $border_default',
90
124
  }}
125
+ onClick={() => onActive?.(roleName)}
91
126
  >
92
- <Flex justify="between" css={{ flexGrow: 1, pr: '$6' }}>
93
- <Text
94
- variant="sm"
95
- css={{ fontWeight: '$semiBold', textTransform: 'capitalize', color: '$on_surface_medium' }}
96
- >
97
- {roleName} {`(${getFormattedCount(peerList.length)})`}
98
- </Text>
99
- <RoleOptions roleName={roleName} peerList={peerList} />
100
- </Flex>
101
- </Accordion.Header>
102
- <Accordion.Content>
103
- <Box css={{ borderTop: '1px solid $border_default' }} />
104
- <FixedSizeList
105
- itemSize={ROW_HEIGHT}
106
- itemData={{ peerList, isConnected }}
107
- itemKey={itemKey}
108
- itemCount={peerList.length}
109
- width={width}
110
- height={height}
111
- >
112
- {VirtualizedParticipantItem}
113
- </FixedSizeList>
114
- {hasNext ? (
115
- <Chip
116
- icon={<AddCircleIcon />}
117
- content="Load More"
118
- onClick={loadNext}
119
- backgroundColor="$secondary_default"
120
- css={{
121
- w: 'max-content',
122
- borderRadius: '$size$9',
123
- m: '$2 auto',
124
- p: '$4',
125
- cursor: 'pointer',
126
- }}
127
- />
128
- ) : null}
129
- </Accordion.Content>
130
- </Accordion.Item>
131
- </Accordion.Root>
132
- </Flex>
127
+ <Text variant="sm" css={{ color: 'inherit' }}>
128
+ View All
129
+ </Text>
130
+ <ChevronRightIcon />
131
+ </Flex>
132
+ ) : null}
133
+ </Accordion.Content>
134
+ </Accordion.Item>
133
135
  );
134
136
  };
@@ -46,7 +46,7 @@ export const RoleOptions = ({ roleName, peerList }: { roleName: string; peerList
46
46
  const canRemoveRoleFromRoom =
47
47
  permissions?.removeOthers && (on_stage_role === roleName || off_stage_roles?.includes(roleName));
48
48
 
49
- if (!(canMuteRole || canRemoveRoleFromStage || canRemoveRoleFromRoom)) {
49
+ if (!(canMuteRole || canRemoveRoleFromStage || canRemoveRoleFromRoom) || peerList.length === 0) {
50
50
  return null;
51
51
  }
52
52
 
@@ -0,0 +1,23 @@
1
+ import React, { useEffect } from 'react';
2
+ import { HMSNotificationTypes, useHMSNotifications } from '@100mslive/react-sdk';
3
+ // @ts-ignore
4
+ import { useIsNotificationDisabled } from '../AppData/useUISettings';
5
+ import { useRedirectToLeave } from '../hooks/useRedirectToLeave';
6
+
7
+ export function HeadlessEndRoomListener() {
8
+ const notification = useHMSNotifications();
9
+ const isNotificationDisabled = useIsNotificationDisabled();
10
+ const { redirectToLeave } = useRedirectToLeave();
11
+
12
+ useEffect(() => {
13
+ if (!notification || !isNotificationDisabled) {
14
+ return;
15
+ }
16
+ if ([HMSNotificationTypes.ROOM_ENDED, HMSNotificationTypes.REMOVED_FROM_ROOM].includes(notification.type)) {
17
+ redirectToLeave(1000);
18
+ }
19
+ // eslint-disable-next-line react-hooks/exhaustive-deps
20
+ }, [notification]);
21
+
22
+ return <></>;
23
+ }
@@ -104,7 +104,7 @@ export function Notifications() {
104
104
  close: false,
105
105
  });
106
106
  }
107
- // goto leave for terminal if any action is not performed within 2secs
107
+ // goto leave for terminal if any action is not performed within 1s
108
108
  // if network is still unavailable going to preview will throw an error
109
109
  redirectToLeave(1000);
110
110
  return;
@@ -0,0 +1,5 @@
1
+ import { styled } from '../../Theme';
2
+ import { StyledVideoTile } from '../../VideoTile';
3
+
4
+ export const PrebuiltAudioIndicator = styled(StyledVideoTile.AudioIndicator, { height: '$12', width: '$12' });
5
+ export const PrebuiltAttributeBox = styled(StyledVideoTile.AttributeBox, { height: '$12', width: '$12' });