@100mslive/roomkit-react 0.1.7 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/AudioLevel/AudioLevel.d.ts +5 -8
- package/dist/AudioLevel/index.d.ts +2 -1
- package/dist/AudioLevel/useBorderAudioLevel.d.ts +8 -0
- package/dist/{HLSView-3S74KF3A.js → HLSView-DDGPZHA2.js} +5 -4
- package/dist/{HLSView-3S74KF3A.js.map → HLSView-DDGPZHA2.js.map} +2 -2
- package/dist/Prebuilt/App.d.ts +1 -0
- package/dist/Prebuilt/AppContext.d.ts +1 -0
- package/dist/Prebuilt/components/Chip.d.ts +12 -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/PrebuiltDialogPortal.d.ts +4 -0
- package/dist/Prebuilt/components/PrebuiltTileElements.d.ts +2198 -0
- package/dist/{VirtualBackground-3TI5NA4V.js → VirtualBackground-UVZJVOA2.js} +3 -3
- package/dist/{chunk-5DQ3WTED.js → chunk-6SQTFOK6.js} +2 -2
- package/dist/{chunk-5DQ3WTED.js.map → chunk-6SQTFOK6.js.map} +1 -1
- package/dist/{chunk-36X4ZCLC.js → chunk-HUMNPIYI.js} +2 -2
- package/dist/{chunk-Z7P5WITU.js → chunk-PRM33R4R.js} +591 -1186
- package/dist/chunk-PRM33R4R.js.map +7 -0
- package/dist/{conference-JNABIZBG.js → conference-N7S47TDK.js} +1505 -717
- package/dist/conference-N7S47TDK.js.map +7 -0
- package/dist/index.cjs.js +3083 -2825
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.js +4 -2
- package/dist/meta.cjs.json +1107 -740
- package/dist/meta.esbuild.json +1160 -791
- package/package.json +6 -6
- package/src/AudioLevel/AudioLevel.tsx +79 -30
- package/src/AudioLevel/audio-level.png +0 -0
- package/src/AudioLevel/index.ts +2 -1
- package/src/AudioLevel/useBorderAudioLevel.tsx +34 -0
- package/src/Prebuilt/App.tsx +6 -0
- package/src/Prebuilt/AppContext.tsx +2 -0
- package/src/Prebuilt/common/constants.js +1 -1
- package/src/Prebuilt/common/utils.js +0 -7
- package/src/Prebuilt/components/AppData/AppData.jsx +1 -1
- package/src/Prebuilt/components/AppData/useUISettings.js +1 -1
- package/src/Prebuilt/components/{Chip.jsx → Chip.tsx} +18 -3
- package/src/Prebuilt/components/Footer/PaginatedParticipants.tsx +94 -0
- package/src/Prebuilt/components/Footer/ParticipantList.jsx +67 -26
- package/src/Prebuilt/components/Footer/RoleAccordion.tsx +91 -49
- package/src/Prebuilt/components/Footer/RoleOptions.tsx +1 -1
- package/src/Prebuilt/components/Leave/DesktopLeaveRoom.tsx +7 -4
- package/src/Prebuilt/components/MoreSettings/ChangeNameModal.jsx +3 -2
- package/src/Prebuilt/components/MoreSettings/EmbedUrl.jsx +3 -2
- package/src/Prebuilt/components/MwebLandscapePrompt.jsx +58 -0
- package/src/Prebuilt/components/Notifications/HLSFailureModal.jsx +3 -2
- package/src/Prebuilt/components/Notifications/HeadlessEndRoomListener.tsx +23 -0
- package/src/Prebuilt/components/Notifications/Notifications.jsx +1 -1
- package/src/Prebuilt/components/Notifications/PermissionErrorModal.jsx +3 -2
- package/src/Prebuilt/components/PrebuiltDialogPortal.tsx +6 -0
- package/src/Prebuilt/components/PrebuiltTileElements.tsx +5 -0
- package/src/Prebuilt/components/Preview/PreviewJoin.tsx +11 -7
- package/src/Prebuilt/components/RoleChangeModal.jsx +3 -2
- package/src/Prebuilt/components/RoleChangeRequest/RequestPrompt.tsx +3 -2
- package/src/Prebuilt/components/Settings/SettingsModal.jsx +3 -2
- package/src/Prebuilt/components/Settings/StartRecording.jsx +3 -2
- package/src/Prebuilt/components/SidePaneTabs.tsx +31 -5
- package/src/Prebuilt/components/StatsForNerds.jsx +3 -2
- package/src/Prebuilt/components/VideoTile.jsx +21 -83
- package/src/Prebuilt/components/conference.jsx +4 -3
- package/src/Prebuilt/components/hooks/useDropdownSelection.jsx +1 -1
- package/src/Prebuilt/components/pdfAnnotator/pdfFileOptions.jsx +3 -2
- package/src/Prebuilt/components/pdfAnnotator/shareScreenOptions.jsx +4 -29
- package/src/Prebuilt/components/pdfAnnotator/uploadedFile.jsx +3 -2
- package/src/Prebuilt/layouts/HLSView.jsx +1 -0
- package/src/Prebuilt/layouts/SidePane.tsx +1 -0
- package/src/Prebuilt/primitives/DialogContent.jsx +5 -4
- package/dist/chunk-Z7P5WITU.js.map +0 -7
- package/dist/conference-JNABIZBG.js.map +0 -7
- /package/dist/{VirtualBackground-3TI5NA4V.js.map → VirtualBackground-UVZJVOA2.js.map} +0 -0
- /package/dist/{chunk-36X4ZCLC.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.
|
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.
|
80
|
-
"@100mslive/hms-virtual-background": "1.11.
|
81
|
-
"@100mslive/react-icons": "0.8.
|
82
|
-
"@100mslive/react-sdk": "0.8.
|
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
|
}
|
@@ -1,34 +1,83 @@
|
|
1
|
-
import {
|
2
|
-
import {
|
3
|
-
import {
|
4
|
-
import {
|
1
|
+
import React, { useEffect, useRef } from 'react';
|
2
|
+
import { selectTrackAudioByID, useHMSVanillaStore } from '@100mslive/react-sdk';
|
3
|
+
import { Box, Flex } from '../Layout';
|
4
|
+
import { keyframes } from '../Theme';
|
5
|
+
//@ts-ignore
|
6
|
+
import bg from './audio-level.png';
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
*
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
8
|
+
// keep the calculated values before hand to avoid recalcuation everytime
|
9
|
+
const positionValues = new Array(101).fill(0).reduce((acc, _, index) => {
|
10
|
+
acc[index] = Math.round((index / 100) * 4) / 4; // convert to 0.25 multiples
|
11
|
+
return acc;
|
12
|
+
}, {});
|
13
|
+
|
14
|
+
const barAnimation = keyframes({
|
15
|
+
from: {
|
16
|
+
maskSize: '4em .8em',
|
17
|
+
'-webkit-mask-position-y': '.1em',
|
18
|
+
maskPosition: 'initial .1em',
|
19
|
+
},
|
20
|
+
|
21
|
+
'50%': {
|
22
|
+
maskSize: '4em 1em',
|
23
|
+
'-webkit-mask-position-y': 0,
|
24
|
+
maskPosition: 'initial 0',
|
25
|
+
},
|
26
|
+
|
27
|
+
to: {
|
28
|
+
maskSize: '4em .8em',
|
29
|
+
'-webkit-mask-position-y': '.1em',
|
30
|
+
maskPosition: 'initial 0.1em',
|
31
|
+
},
|
32
|
+
});
|
33
|
+
|
34
|
+
const AudioBar = () => {
|
35
|
+
return (
|
36
|
+
<Box
|
37
|
+
css={{
|
38
|
+
width: '.25em',
|
39
|
+
height: '1em',
|
40
|
+
maskImage: `url(${bg})`,
|
41
|
+
'-webkit-mask-repeat': 'no-repeat',
|
42
|
+
backgroundColor: '$on_primary_high',
|
43
|
+
maskSize: '4em 1em',
|
44
|
+
}}
|
45
|
+
/>
|
22
46
|
);
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
});
|
29
|
-
return ref;
|
30
|
-
}
|
47
|
+
};
|
48
|
+
|
49
|
+
export const AudioLevel = ({ trackId, size }: { trackId?: string; size?: 'small' | 'medium' }) => {
|
50
|
+
const ref = useRef<HTMLDivElement | null>(null);
|
51
|
+
const vanillaStore = useHMSVanillaStore();
|
31
52
|
|
32
|
-
|
33
|
-
|
53
|
+
useEffect(() => {
|
54
|
+
const unsubscribe = vanillaStore.subscribe(audioLevel => {
|
55
|
+
if (ref.current) {
|
56
|
+
let index = 0;
|
57
|
+
//@ts-ignore
|
58
|
+
for (const child of ref.current.children) {
|
59
|
+
const positionX = `-${positionValues[audioLevel] * (index === 1 ? 2.5 : 1.25)}em`;
|
60
|
+
child.style['-webkit-mask-position-x'] = positionX;
|
61
|
+
child.style['mask-position'] = `${positionX} 0`;
|
62
|
+
child.style['animation'] =
|
63
|
+
positionValues[audioLevel] > 0 ? `${barAnimation} 0.6s steps(3,jump-none) 0s infinite` : 'none';
|
64
|
+
index++;
|
65
|
+
}
|
66
|
+
}
|
67
|
+
}, selectTrackAudioByID(trackId));
|
68
|
+
return unsubscribe;
|
69
|
+
}, [vanillaStore, trackId]);
|
70
|
+
return (
|
71
|
+
<Flex
|
72
|
+
ref={ref}
|
73
|
+
css={{
|
74
|
+
fontSize: size === 'small' ? '0.75rem' : '1rem',
|
75
|
+
gap: size === 'small' ? '$1' : '$2',
|
76
|
+
}}
|
77
|
+
>
|
78
|
+
<AudioBar />
|
79
|
+
<AudioBar />
|
80
|
+
<AudioBar />
|
81
|
+
</Flex>
|
82
|
+
);
|
34
83
|
};
|
Binary file
|
package/src/AudioLevel/index.ts
CHANGED
@@ -1 +1,2 @@
|
|
1
|
-
export { useBorderAudioLevel } from './
|
1
|
+
export { useBorderAudioLevel } from './useBorderAudioLevel';
|
2
|
+
export { AudioLevel } from './AudioLevel';
|
@@ -0,0 +1,34 @@
|
|
1
|
+
import { useCallback, useRef } from 'react';
|
2
|
+
import { HMSTrackID } from '@100mslive/hms-video-store';
|
3
|
+
import { useAudioLevelStyles } from '@100mslive/react-sdk';
|
4
|
+
import { useTheme } from '../Theme';
|
5
|
+
|
6
|
+
/**
|
7
|
+
* pass in a track id and get a ref. That ref can be attached to an element which will have border
|
8
|
+
* as per audio level post that.
|
9
|
+
*/
|
10
|
+
export function useBorderAudioLevel(audioTrackId?: HMSTrackID) {
|
11
|
+
const { theme } = useTheme();
|
12
|
+
const color = theme.colors.primary_default.value;
|
13
|
+
const getStyle = useCallback(
|
14
|
+
(level: number) => {
|
15
|
+
const style: Record<string, string> = {
|
16
|
+
transition: 'outline 0.4s ease-in-out',
|
17
|
+
};
|
18
|
+
style['outline'] = level ? `${sigmoid(level) * 4}px solid ${color}` : '0px solid transparent';
|
19
|
+
return style;
|
20
|
+
},
|
21
|
+
[color],
|
22
|
+
);
|
23
|
+
const ref = useRef(null);
|
24
|
+
useAudioLevelStyles({
|
25
|
+
trackId: audioTrackId,
|
26
|
+
getStyle,
|
27
|
+
ref,
|
28
|
+
});
|
29
|
+
return ref;
|
30
|
+
}
|
31
|
+
|
32
|
+
export const sigmoid = (z: number) => {
|
33
|
+
return 1 / (1 + Math.exp(-z));
|
34
|
+
};
|
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: {
|
@@ -218,6 +222,7 @@ export const HMSPrebuilt = React.forwardRef<HMSPrebuiltRefType, HMSPrebuiltProps
|
|
218
222
|
<AppData appDetails={metadata} tokenEndpoint={tokenByRoomIdRoleEndpoint} />
|
219
223
|
<Init />
|
220
224
|
<Box
|
225
|
+
id="prebuilt-container"
|
221
226
|
css={{
|
222
227
|
bg: '$background_dim',
|
223
228
|
size: '100%',
|
@@ -348,6 +353,7 @@ function AppRoutes({
|
|
348
353
|
<BackSwipe />
|
349
354
|
{!isNotificationsDisabled && <FlyingEmoji />}
|
350
355
|
<RemoteStopScreenshare />
|
356
|
+
<HeadlessEndRoomListener />
|
351
357
|
<KeyboardHandler />
|
352
358
|
<AuthToken authTokenByRoomCodeEndpoint={authTokenByRoomCodeEndpoint} defaultAuthToken={defaultAuthToken} />
|
353
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',
|
@@ -88,10 +88,3 @@ export const formatTime = timeInSeconds => {
|
|
88
88
|
const hour = hours !== 0 ? `${hours < 10 ? '0' : ''}${hours}:` : '';
|
89
89
|
return `${hour}${minutes < 10 ? '0' : ''}${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
|
90
90
|
};
|
91
|
-
|
92
|
-
export const getAttributeBoxSize = (width, height) => {
|
93
|
-
if (!width || !height) {
|
94
|
-
return '';
|
95
|
-
}
|
96
|
-
return width < 180 || height < 180 ? 'small' : 'medium';
|
97
|
-
};
|
@@ -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
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
import { Flex } from '../../Layout';
|
3
3
|
import { Text } from '../../Text';
|
4
|
+
import { CSS } from '../../Theme';
|
4
5
|
|
5
6
|
const Chip = ({
|
6
7
|
icon = <></>,
|
@@ -8,14 +9,28 @@ const Chip = ({
|
|
8
9
|
backgroundColor = '$surface_default',
|
9
10
|
textColor = '$on_surface_high',
|
10
11
|
hideIfNoContent = false,
|
12
|
+
onClick,
|
13
|
+
css = {},
|
14
|
+
}: {
|
15
|
+
icon?: React.JSX.Element;
|
16
|
+
content: string;
|
17
|
+
backgroundColor?: string;
|
18
|
+
textColor?: string;
|
19
|
+
hideIfNoContent?: boolean;
|
20
|
+
onClick?: () => void | Promise<void>;
|
21
|
+
css?: CSS;
|
11
22
|
}) => {
|
12
23
|
if (hideIfNoContent && !content) {
|
13
|
-
return;
|
24
|
+
return null;
|
14
25
|
}
|
15
26
|
return (
|
16
|
-
<Flex
|
27
|
+
<Flex
|
28
|
+
align="center"
|
29
|
+
css={{ backgroundColor, p: '$4 $6', gap: '$2', borderRadius: '$4', ...css }}
|
30
|
+
onClick={() => onClick?.()}
|
31
|
+
>
|
17
32
|
{icon}
|
18
|
-
<Text variant="sm" css={{ fontWeight: '$semiBold', color: textColor
|
33
|
+
<Text variant="sm" css={{ fontWeight: '$semiBold', color: textColor }}>
|
19
34
|
{content}
|
20
35
|
</Text>
|
21
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,22 +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
|
-
|
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>
|
146
175
|
</Flex>
|
147
176
|
);
|
148
177
|
};
|
@@ -163,7 +192,10 @@ export const Participant = ({ peer, isConnected }) => {
|
|
163
192
|
justify="between"
|
164
193
|
data-testid={'participant_' + peer.name}
|
165
194
|
>
|
166
|
-
<Text
|
195
|
+
<Text
|
196
|
+
variant="sm"
|
197
|
+
css={{ ...textEllipsis('100%'), flex: '1 1 0', mr: '$8', fontWeight: '$semiBold', color: '$on_surface_high' }}
|
198
|
+
>
|
167
199
|
{peer.name} {localPeerId === peer.id ? '(You)' : ''}
|
168
200
|
</Text>
|
169
201
|
{isConnected ? (
|
@@ -179,7 +211,10 @@ export const Participant = ({ peer, isConnected }) => {
|
|
179
211
|
const ParticipantActions = React.memo(({ peerId, role, isLocal }) => {
|
180
212
|
const isHandRaised = useHMSStore(selectHasPeerHandRaised(peerId));
|
181
213
|
const canChangeRole = useHMSStore(selectPermissions)?.changeRole;
|
182
|
-
const
|
214
|
+
const canRemoveOthers = useHMSStore(selectPermissions)?.removeOthers;
|
215
|
+
const { elements } = useRoomLayoutConferencingScreen();
|
216
|
+
const { on_stage_exp } = elements || {};
|
217
|
+
const shouldShowMoreActions = (on_stage_exp && canChangeRole) || canRemoveOthers;
|
183
218
|
const isAudioMuted = !useHMSStore(selectIsPeerAudioEnabled(peerId));
|
184
219
|
|
185
220
|
return (
|
@@ -210,15 +245,21 @@ const ParticipantActions = React.memo(({ peerId, role, isLocal }) => {
|
|
210
245
|
</Flex>
|
211
246
|
) : null}
|
212
247
|
|
213
|
-
{shouldShowMoreActions && !isLocal ?
|
248
|
+
{shouldShowMoreActions && !isLocal ? (
|
249
|
+
<ParticipantMoreActions
|
250
|
+
peerId={peerId}
|
251
|
+
role={role}
|
252
|
+
elements={elements}
|
253
|
+
canChangeRole={canChangeRole}
|
254
|
+
canRemoveOthers={canRemoveOthers}
|
255
|
+
/>
|
256
|
+
) : null}
|
214
257
|
</Flex>
|
215
258
|
);
|
216
259
|
});
|
217
260
|
|
218
|
-
const ParticipantMoreActions = ({ peerId, role }) => {
|
261
|
+
const ParticipantMoreActions = ({ peerId, role, elements, canChangeRole, canRemoveOthers }) => {
|
219
262
|
const hmsActions = useHMSActions();
|
220
|
-
const { changeRole: canChangeRole, removeOthers: canRemoveOthers } = useHMSStore(selectPermissions);
|
221
|
-
const { elements } = useRoomLayoutConferencingScreen();
|
222
263
|
const {
|
223
264
|
bring_to_stage_label,
|
224
265
|
remove_from_stage_label,
|
@@ -327,7 +368,7 @@ export const ParticipantSearch = ({ onSearch, placeholder, inSidePane = false })
|
|
327
368
|
<Input
|
328
369
|
type="text"
|
329
370
|
placeholder={placeholder || 'Search for participants'}
|
330
|
-
css={{ w: '100%', p: '$6', pl: '$
|
371
|
+
css={{ w: '100%', p: '$6', pl: '$14', bg: inSidePane ? '$surface_default' : '$surface_dim' }}
|
331
372
|
value={value}
|
332
373
|
onKeyDown={event => {
|
333
374
|
event.stopPropagation();
|