@100mslive/roomkit-react 0.1.7 → 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/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();
|