@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.
- package/dist/{HLSView-IQRPLYNH.js → HLSView-DDGPZHA2.js} +3 -3
- package/dist/Prebuilt/App.d.ts +1 -0
- package/dist/Prebuilt/AppContext.d.ts +1 -0
- package/dist/Prebuilt/components/Footer/PaginatedParticipants.d.ts +5 -0
- package/dist/Prebuilt/components/Footer/RoleAccordion.d.ts +10 -3
- package/dist/Prebuilt/components/Notifications/HeadlessEndRoomListener.d.ts +2 -0
- package/dist/Prebuilt/components/PrebuiltTileElements.d.ts +2198 -0
- package/dist/{VirtualBackground-GP4ATXD3.js → VirtualBackground-UVZJVOA2.js} +3 -3
- package/dist/{chunk-Z3O2WGWV.js → chunk-6SQTFOK6.js} +2 -2
- package/dist/{chunk-Z3O2WGWV.js.map → chunk-6SQTFOK6.js.map} +1 -1
- package/dist/{chunk-2H5NIZB7.js → chunk-HUMNPIYI.js} +2 -2
- package/dist/{chunk-GLYGPYNS.js → chunk-PRM33R4R.js} +286 -251
- package/dist/chunk-PRM33R4R.js.map +7 -0
- package/dist/{conference-JD35TNH4.js → conference-N7S47TDK.js} +484 -385
- package/dist/conference-N7S47TDK.js.map +7 -0
- package/dist/index.cjs.js +1895 -1727
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.js +2 -2
- package/dist/meta.cjs.json +234 -42
- package/dist/meta.esbuild.json +267 -74
- package/package.json +6 -6
- package/src/AudioLevel/AudioLevel.tsx +1 -1
- package/src/Prebuilt/App.tsx +5 -0
- package/src/Prebuilt/AppContext.tsx +2 -0
- package/src/Prebuilt/common/constants.js +1 -1
- package/src/Prebuilt/components/AppData/AppData.jsx +1 -1
- package/src/Prebuilt/components/AppData/useUISettings.js +1 -1
- package/src/Prebuilt/components/Chip.tsx +6 -2
- package/src/Prebuilt/components/Footer/PaginatedParticipants.tsx +94 -0
- package/src/Prebuilt/components/Footer/ParticipantList.jsx +53 -23
- package/src/Prebuilt/components/Footer/RoleAccordion.tsx +86 -84
- package/src/Prebuilt/components/Footer/RoleOptions.tsx +1 -1
- package/src/Prebuilt/components/Notifications/HeadlessEndRoomListener.tsx +23 -0
- package/src/Prebuilt/components/Notifications/Notifications.jsx +1 -1
- package/src/Prebuilt/components/PrebuiltTileElements.tsx +5 -0
- package/src/Prebuilt/components/Preview/PreviewJoin.tsx +9 -6
- package/src/Prebuilt/components/SidePaneTabs.tsx +31 -5
- package/src/Prebuilt/components/VideoTile.jsx +19 -34
- package/src/Prebuilt/components/conference.jsx +4 -3
- package/src/Prebuilt/components/hooks/useDropdownSelection.jsx +1 -1
- package/src/Prebuilt/layouts/SidePane.tsx +1 -0
- package/dist/chunk-GLYGPYNS.js.map +0 -7
- package/dist/conference-JD35TNH4.js.map +0 -7
- /package/dist/{HLSView-IQRPLYNH.js.map → HLSView-DDGPZHA2.js.map} +0 -0
- /package/dist/{VirtualBackground-GP4ATXD3.js.map → VirtualBackground-UVZJVOA2.js.map} +0 -0
- /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
|
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
|
80
|
-
"@100mslive/hms-virtual-background": "1.11.17
|
81
|
-
"@100mslive/react-icons": "0.8.17
|
82
|
-
"@100mslive/react-sdk": "0.8.17
|
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": "
|
118
|
+
"gitHead": "966ed9ffac3d55586196ad0b738eb1977dfc2ff7"
|
119
119
|
}
|
package/src/Prebuilt/App.tsx
CHANGED
@@ -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
|
-
|
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.
|
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.
|
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
|
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
|
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 = ({
|
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
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
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
|
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: '$
|
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, {
|
1
|
+
import React, { useEffect } from 'react';
|
2
2
|
import { useMeasure } from 'react-use';
|
3
3
|
import { FixedSizeList } from 'react-window';
|
4
|
-
import { HMSPeer,
|
5
|
-
import {
|
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 {
|
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
|
46
|
-
const
|
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
|
-
|
49
|
-
if (!
|
52
|
+
useEffect(() => {
|
53
|
+
if (!isOffStageRole || !isLargeRoom) {
|
50
54
|
return;
|
51
55
|
}
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
-
<
|
75
|
-
<Accordion.
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
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
|
-
<
|
82
|
-
<
|
83
|
-
|
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
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
-
<
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
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' });
|