@100mslive/roomkit-react 0.1.14 → 0.1.16
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/{HLSView-662T7R7H.js → HLSView-EMUOLCTM.js} +128 -39
- package/dist/HLSView-EMUOLCTM.js.map +7 -0
- package/dist/Prebuilt/common/PeersSorter.d.ts +1 -0
- package/dist/Prebuilt/common/constants.d.ts +9 -5
- package/dist/Prebuilt/common/hooks.d.ts +1 -0
- package/dist/Prebuilt/components/Footer/ParticipantList.d.ts +17 -0
- package/dist/Prebuilt/components/Footer/RoleAccordion.d.ts +3 -2
- package/dist/Prebuilt/components/Footer/WhiteboardToggle.d.ts +2 -0
- package/dist/Prebuilt/components/HMSVideo/HLSCaptionSelector.d.ts +5 -0
- package/dist/Prebuilt/components/Notifications/HandRaisedNotifications.d.ts +1 -0
- package/dist/Prebuilt/components/Polls/Voting/Leaderboard.d.ts +4 -0
- package/dist/Prebuilt/components/Polls/Voting/LeaderboardEntry.d.ts +9 -0
- package/dist/Prebuilt/components/Polls/Voting/PeerParticipationSummary.d.ts +5 -0
- package/dist/Prebuilt/components/PreviousRoleInMetadata.d.ts +1 -0
- package/dist/Prebuilt/components/RemoveParticipant.d.ts +5 -0
- package/dist/Prebuilt/components/hooks/useCloseScreenshareWhiteboard.d.ts +4 -0
- package/dist/Prebuilt/layouts/WhiteboardView.d.ts +2 -0
- package/dist/{chunk-2B7YYNHQ.js → chunk-ZYR4B4KQ.js} +2240 -1767
- package/dist/chunk-ZYR4B4KQ.js.map +7 -0
- package/dist/index.cjs.js +2805 -2172
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.js +1 -1
- package/dist/meta.cjs.json +739 -177
- package/dist/meta.esbuild.json +749 -186
- package/package.json +7 -7
- package/src/Prebuilt/AppStateContext.tsx +1 -1
- package/src/Prebuilt/common/PeersSorter.ts +24 -8
- package/src/Prebuilt/common/constants.ts +6 -6
- package/src/Prebuilt/common/hooks.ts +16 -0
- package/src/Prebuilt/common/utils.js +33 -0
- package/src/Prebuilt/components/AppData/AppData.tsx +1 -16
- package/src/Prebuilt/components/Chat/Chat.jsx +10 -34
- package/src/Prebuilt/components/Chat/ChatBody.jsx +107 -66
- package/src/Prebuilt/components/Chat/ChatFooter.tsx +21 -12
- package/src/Prebuilt/components/Chat/ChatSelector.tsx +25 -25
- package/src/Prebuilt/components/Chat/ChatSelectorContainer.tsx +15 -16
- package/src/Prebuilt/components/Chat/PinnedMessage.tsx +7 -2
- package/src/Prebuilt/components/ConferenceScreen.tsx +2 -0
- package/src/Prebuilt/components/Footer/ChatToggle.tsx +30 -7
- package/src/Prebuilt/components/Footer/Footer.tsx +2 -1
- package/src/Prebuilt/components/Footer/PaginatedParticipants.tsx +0 -1
- package/src/Prebuilt/components/Footer/{ParticipantList.jsx → ParticipantList.tsx} +169 -127
- package/src/Prebuilt/components/Footer/RoleAccordion.tsx +23 -13
- package/src/Prebuilt/components/Footer/WhiteboardToggle.tsx +34 -0
- package/src/Prebuilt/components/HMSVideo/HLSCaptionSelector.tsx +13 -0
- package/src/Prebuilt/components/HMSVideo/HMSVideo.jsx +34 -2
- package/src/Prebuilt/components/Notifications/HandRaisedNotifications.tsx +35 -0
- package/src/Prebuilt/components/Notifications/Notifications.tsx +47 -14
- package/src/Prebuilt/components/Notifications/PeerNotifications.tsx +7 -2
- package/src/Prebuilt/components/Polls/CreatePollQuiz/PollsQuizMenu.jsx +3 -9
- package/src/Prebuilt/components/Polls/CreateQuestions/CreateQuestions.jsx +21 -1
- package/src/Prebuilt/components/Polls/CreateQuestions/QuestionForm.jsx +34 -7
- package/src/Prebuilt/components/Polls/CreateQuestions/SavedQuestion.jsx +2 -2
- package/src/Prebuilt/components/Polls/Polls.tsx +3 -0
- package/src/Prebuilt/components/Polls/Voting/Leaderboard.tsx +115 -0
- package/src/Prebuilt/components/Polls/Voting/LeaderboardEntry.tsx +63 -0
- package/src/Prebuilt/components/Polls/Voting/PeerParticipationSummary.tsx +38 -0
- package/src/Prebuilt/components/Polls/Voting/QuestionCard.jsx +33 -8
- package/src/Prebuilt/components/Polls/Voting/StandardVoting.jsx +7 -1
- package/src/Prebuilt/components/Polls/Voting/Voting.jsx +31 -13
- package/src/Prebuilt/components/Polls/common/MultipleChoiceOptions.jsx +33 -21
- package/src/Prebuilt/components/Polls/common/SingleChoiceOptions.jsx +47 -35
- package/src/Prebuilt/components/Polls/common/StatusIndicator.jsx +2 -22
- package/src/Prebuilt/components/Polls/common/VoteCount.jsx +1 -15
- package/src/Prebuilt/components/PreviousRoleInMetadata.tsx +21 -0
- package/src/Prebuilt/components/RemoveParticipant.tsx +35 -0
- package/src/Prebuilt/components/RoleChangeModal.jsx +1 -1
- package/src/Prebuilt/components/SidePaneTabs.tsx +0 -1
- package/src/Prebuilt/components/TileMenu/TileMenuContent.tsx +1 -1
- package/src/Prebuilt/components/Toast/ToastConfig.jsx +15 -3
- package/src/Prebuilt/components/VideoLayouts/EqualProminence.tsx +6 -5
- package/src/Prebuilt/components/VideoLayouts/GridLayout.tsx +27 -5
- package/src/Prebuilt/components/VideoLayouts/ScreenshareLayout.tsx +0 -1
- package/src/Prebuilt/components/hooks/useCloseScreenshareWhiteboard.tsx +24 -0
- package/src/Prebuilt/layouts/HLSView.jsx +51 -3
- package/src/Prebuilt/layouts/VideoStreamingSection.tsx +20 -3
- package/src/Prebuilt/layouts/WhiteboardView.tsx +66 -0
- package/dist/HLSView-662T7R7H.js.map +0 -7
- package/dist/chunk-2B7YYNHQ.js.map +0 -7
- package/src/Prebuilt/components/AppData/useAppLayout.js +0 -6
- package/src/Prebuilt/components/init/initUtils.js +0 -67
@@ -67,7 +67,6 @@ export const PinnedMessage = ({ clearPinnedMessage }: { clearPinnedMessage: (ind
|
|
67
67
|
/>
|
68
68
|
) : null}
|
69
69
|
<Flex
|
70
|
-
title={pinnedMessages[pinnedMessageIndex]?.text}
|
71
70
|
css={{
|
72
71
|
p: '$4',
|
73
72
|
color: '$on_surface_medium',
|
@@ -95,7 +94,12 @@ export const PinnedMessage = ({ clearPinnedMessage }: { clearPinnedMessage: (ind
|
|
95
94
|
},
|
96
95
|
}}
|
97
96
|
>
|
98
|
-
<Text
|
97
|
+
<Text
|
98
|
+
variant="sm"
|
99
|
+
css={{ color: '$on_surface_medium' }}
|
100
|
+
{...swipeHandlers}
|
101
|
+
title={pinnedMessages[pinnedMessageIndex]?.text}
|
102
|
+
>
|
99
103
|
<AnnotisedMessage
|
100
104
|
message={`${currentPinnedMessage.slice(
|
101
105
|
0,
|
@@ -123,6 +127,7 @@ export const PinnedMessage = ({ clearPinnedMessage }: { clearPinnedMessage: (ind
|
|
123
127
|
'&:hover .hide-on-hover': { display: 'none !important' },
|
124
128
|
'&:hover .show-on-hover': { display: 'block !important' },
|
125
129
|
}}
|
130
|
+
title="Unpin Message"
|
126
131
|
>
|
127
132
|
<UnpinIcon className="show-on-hover" style={{ display: 'none' }} height={20} width={20} />
|
128
133
|
<PinIcon className="hide-on-hover" style={{ display: 'block' }} height={20} width={20} />
|
@@ -21,6 +21,7 @@ import { VideoStreamingSection } from '../layouts/VideoStreamingSection';
|
|
21
21
|
// @ts-ignore: No implicit Any
|
22
22
|
import FullPageProgress from './FullPageProgress';
|
23
23
|
import { Header } from './Header';
|
24
|
+
import { PreviousRoleInMetadata } from './PreviousRoleInMetadata';
|
24
25
|
import {
|
25
26
|
useRoomLayoutConferencingScreen,
|
26
27
|
useRoomLayoutPreviewScreen,
|
@@ -175,6 +176,7 @@ export const ConferenceScreen = () => {
|
|
175
176
|
<RoleChangeRequestModal />
|
176
177
|
<HLSFailureModal />
|
177
178
|
<ActivatedPIP />
|
179
|
+
<PreviousRoleInMetadata />
|
178
180
|
</Flex>
|
179
181
|
</>
|
180
182
|
);
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import React from 'react';
|
2
2
|
import { selectUnreadHMSMessagesCount, useHMSStore } from '@100mslive/react-sdk';
|
3
|
-
import { ChatIcon
|
4
|
-
import { Tooltip } from '../../..';
|
3
|
+
import { ChatIcon } from '@100mslive/react-icons';
|
4
|
+
import { Box, Flex, Text, Tooltip } from '../../..';
|
5
5
|
// @ts-ignore: No implicit Any
|
6
6
|
import IconButton from '../../IconButton';
|
7
7
|
// @ts-ignore: No implicit Any
|
@@ -15,10 +15,33 @@ export const ChatToggle = () => {
|
|
15
15
|
const toggleChat = useSidepaneToggle(SIDE_PANE_OPTIONS.CHAT);
|
16
16
|
|
17
17
|
return (
|
18
|
-
<
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
<Box
|
19
|
+
css={{
|
20
|
+
position: 'relative',
|
21
|
+
}}
|
22
|
+
>
|
23
|
+
<Tooltip key="chat" title={`${isChatOpen ? 'Close' : 'Open'} chat`}>
|
24
|
+
<IconButton onClick={toggleChat} active={!isChatOpen} data-testid="chat_btn">
|
25
|
+
<ChatIcon />
|
26
|
+
</IconButton>
|
27
|
+
</Tooltip>
|
28
|
+
{countUnreadMessages > 0 && (
|
29
|
+
<Flex
|
30
|
+
css={{
|
31
|
+
height: '$8',
|
32
|
+
p: '$4 4.5px',
|
33
|
+
justifyContent: 'center',
|
34
|
+
alignItems: 'center',
|
35
|
+
position: 'absolute',
|
36
|
+
top: '-$4',
|
37
|
+
right: '-$4',
|
38
|
+
borderRadius: '$space$14',
|
39
|
+
background: '$primary_default',
|
40
|
+
}}
|
41
|
+
>
|
42
|
+
<Text variant="overline">{countUnreadMessages > 99 ? '99+' : countUnreadMessages}</Text>
|
43
|
+
</Flex>
|
44
|
+
)}
|
45
|
+
</Box>
|
23
46
|
);
|
24
47
|
};
|
@@ -20,9 +20,9 @@ import { ScreenshareToggle } from '../ScreenShareToggle';
|
|
20
20
|
import { VBToggle } from '../VirtualBackground/VBToggle';
|
21
21
|
// @ts-ignore: No implicit Any
|
22
22
|
import { ChatToggle } from './ChatToggle';
|
23
|
-
// @ts-ignore: No implicit Any
|
24
23
|
import { ParticipantCount } from './ParticipantList';
|
25
24
|
import { PollsToggle } from './PollsToggle';
|
25
|
+
import { WhiteboardToggle } from './WhiteboardToggle';
|
26
26
|
import { ConferencingScreenElements } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
|
27
27
|
// @ts-ignore: No implicit Any
|
28
28
|
import { useIsSidepaneTypeOpen, useSidepaneToggle } from '../AppData/useSidepane';
|
@@ -105,6 +105,7 @@ export const Footer = ({
|
|
105
105
|
)}
|
106
106
|
</AppFooter.Center>
|
107
107
|
<AppFooter.Right>
|
108
|
+
<WhiteboardToggle />
|
108
109
|
{showPolls && <PollsToggle />}
|
109
110
|
{!isMobile && elements?.chat && <ChatToggle />}
|
110
111
|
{elements?.participant_list && <ParticipantCount />}
|
@@ -8,7 +8,6 @@ import { IconButton } from '../../../IconButton';
|
|
8
8
|
import { Box, Flex } from '../../../Layout';
|
9
9
|
import { Loading } from '../../../Loading';
|
10
10
|
import { Text } from '../../../Text';
|
11
|
-
// @ts-ignore: No implicit Any
|
12
11
|
import { Participant, ParticipantSearch } from './ParticipantList';
|
13
12
|
import { ItemData, itemKey, ROW_HEIGHT } from './RoleAccordion';
|
14
13
|
// @ts-ignore: No implicit Any
|
@@ -1,6 +1,8 @@
|
|
1
1
|
import React, { Fragment, useCallback, useState } from 'react';
|
2
2
|
import { useDebounce, useMedia } from 'react-use';
|
3
3
|
import {
|
4
|
+
HMSPeer,
|
5
|
+
HMSRoleName,
|
4
6
|
selectHandRaisedPeers,
|
5
7
|
selectHasPeerHandRaised,
|
6
8
|
selectIsLargeRoom,
|
@@ -12,39 +14,45 @@ import {
|
|
12
14
|
useHMSActions,
|
13
15
|
useHMSStore,
|
14
16
|
} from '@100mslive/react-sdk';
|
15
|
-
import {
|
16
|
-
ChangeRoleIcon,
|
17
|
-
HandIcon,
|
18
|
-
MicOffIcon,
|
19
|
-
PeopleIcon,
|
20
|
-
PeopleRemoveIcon,
|
21
|
-
SearchIcon,
|
22
|
-
VerticalMenuIcon,
|
23
|
-
} from '@100mslive/react-icons';
|
17
|
+
import { ChangeRoleIcon, HandIcon, MicOffIcon, PeopleIcon, SearchIcon, VerticalMenuIcon } from '@100mslive/react-icons';
|
24
18
|
import { Accordion, Box, config as cssConfig, Dropdown, Flex, Input, Text, textEllipsis } from '../../..';
|
19
|
+
// @ts-ignore: No implicit Any
|
25
20
|
import IconButton from '../../IconButton';
|
26
21
|
import { ConnectionIndicator } from '../Connection/ConnectionIndicator';
|
27
|
-
import {
|
22
|
+
import { RemoveParticipant } from '../RemoveParticipant';
|
28
23
|
import { RoleAccordion } from './RoleAccordion';
|
29
|
-
import {
|
24
|
+
import {
|
25
|
+
ConferencingScreenElements,
|
26
|
+
useRoomLayoutConferencingScreen,
|
27
|
+
} from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
|
28
|
+
// @ts-ignore: No implicit Any
|
30
29
|
import { useIsSidepaneTypeOpen, useSidepaneToggle } from '../AppData/useSidepane';
|
31
30
|
import { useParticipants } from '../../common/hooks';
|
31
|
+
// @ts-ignore: No implicit Any
|
32
32
|
import { getFormattedCount } from '../../common/utils';
|
33
33
|
import { SIDE_PANE_OPTIONS } from '../../common/constants';
|
34
34
|
|
35
|
-
export const ParticipantList = ({
|
36
|
-
|
35
|
+
export const ParticipantList = ({
|
36
|
+
offStageRoles = [],
|
37
|
+
onActive,
|
38
|
+
}: {
|
39
|
+
offStageRoles: HMSRoleName[];
|
40
|
+
onActive: (role: string) => void;
|
41
|
+
}) => {
|
42
|
+
const [filter, setFilter] = useState<{ search?: string } | undefined>();
|
37
43
|
const { participants, isConnected, peerCount } = useParticipants(filter);
|
38
44
|
const isLargeRoom = useHMSStore(selectIsLargeRoom);
|
39
|
-
const peersOrderedByRoles = {};
|
45
|
+
const peersOrderedByRoles: Record<string, HMSPeer[]> = {};
|
40
46
|
|
41
47
|
const handRaisedPeers = useHMSStore(selectHandRaisedPeers);
|
42
48
|
|
43
49
|
participants.forEach(participant => {
|
44
|
-
if (
|
45
|
-
peersOrderedByRoles[participant.roleName]
|
50
|
+
if (participant.roleName) {
|
51
|
+
if (peersOrderedByRoles[participant.roleName] === undefined) {
|
52
|
+
peersOrderedByRoles[participant.roleName] = [];
|
53
|
+
}
|
54
|
+
peersOrderedByRoles[participant.roleName].push(participant);
|
46
55
|
}
|
47
|
-
peersOrderedByRoles[participant.roleName].push(participant);
|
48
56
|
});
|
49
57
|
|
50
58
|
// prefill off_stage roles of large rooms to load more peers
|
@@ -56,7 +64,7 @@ export const ParticipantList = ({ offStageRoles = [], onActive }) => {
|
|
56
64
|
});
|
57
65
|
}
|
58
66
|
|
59
|
-
const onSearch = useCallback(value => {
|
67
|
+
const onSearch = useCallback((value: string) => {
|
60
68
|
setFilter(filterValue => {
|
61
69
|
if (!filterValue) {
|
62
70
|
filterValue = {};
|
@@ -71,22 +79,34 @@ export const ParticipantList = ({ offStageRoles = [], onActive }) => {
|
|
71
79
|
|
72
80
|
return (
|
73
81
|
<Fragment>
|
74
|
-
<Flex
|
82
|
+
<Flex
|
83
|
+
direction="column"
|
84
|
+
css={{
|
85
|
+
size: '100%',
|
86
|
+
gap: '$4',
|
87
|
+
}}
|
88
|
+
>
|
75
89
|
{!filter?.search && participants.length === 0 ? null : <ParticipantSearch onSearch={onSearch} inSidePane />}
|
76
|
-
{participants.length === 0 ? (
|
77
|
-
<Flex align="center" justify="center" css={{ w: '100%', p: '$8 0' }}>
|
78
|
-
<Text variant="sm">{!filter ? 'No participants' : 'No matching participants'}</Text>
|
79
|
-
</Flex>
|
80
|
-
) : null}
|
81
90
|
<VirtualizedParticipants
|
82
91
|
peersOrderedByRoles={peersOrderedByRoles}
|
83
92
|
handRaisedList={handRaisedPeers}
|
84
|
-
isConnected={isConnected}
|
93
|
+
isConnected={!!isConnected}
|
85
94
|
filter={filter}
|
86
95
|
offStageRoles={offStageRoles}
|
87
96
|
isLargeRoom={isLargeRoom}
|
88
97
|
onActive={onActive}
|
89
|
-
|
98
|
+
>
|
99
|
+
{participants.length === 0 ? (
|
100
|
+
<Flex
|
101
|
+
align="center"
|
102
|
+
justify="center"
|
103
|
+
className="emptyParticipants"
|
104
|
+
css={{ w: '100%', p: '$8 0', display: 'none' }}
|
105
|
+
>
|
106
|
+
<Text variant="sm">{!filter ? 'No participants' : 'No matching participants'}</Text>
|
107
|
+
</Flex>
|
108
|
+
) : null}
|
109
|
+
</VirtualizedParticipants>
|
90
110
|
</Flex>
|
91
111
|
</Fragment>
|
92
112
|
);
|
@@ -123,6 +143,44 @@ export const ParticipantCount = () => {
|
|
123
143
|
);
|
124
144
|
};
|
125
145
|
|
146
|
+
export const Participant = ({
|
147
|
+
peer,
|
148
|
+
isConnected,
|
149
|
+
style,
|
150
|
+
}: {
|
151
|
+
peer: HMSPeer;
|
152
|
+
isConnected: boolean;
|
153
|
+
style: React.CSSProperties;
|
154
|
+
}) => {
|
155
|
+
const localPeerId = useHMSStore(selectLocalPeerID);
|
156
|
+
return (
|
157
|
+
<Flex
|
158
|
+
key={peer.id}
|
159
|
+
css={{
|
160
|
+
w: '100%',
|
161
|
+
p: '$4 $8',
|
162
|
+
pr: '$6',
|
163
|
+
h: '$16',
|
164
|
+
'&:hover .participant_item': { display: 'flex' },
|
165
|
+
}}
|
166
|
+
align="center"
|
167
|
+
justify="between"
|
168
|
+
data-testid={'participant_' + peer.name}
|
169
|
+
style={style}
|
170
|
+
>
|
171
|
+
<Text
|
172
|
+
variant="sm"
|
173
|
+
css={{ ...textEllipsis('100%'), flex: '1 1 0', mr: '$8', fontWeight: '$semiBold', color: '$on_surface_high' }}
|
174
|
+
>
|
175
|
+
{peer.name} {localPeerId === peer.id ? '(You)' : ''}
|
176
|
+
</Text>
|
177
|
+
{isConnected && peer.roleName ? (
|
178
|
+
<ParticipantActions peerId={peer.id} isLocal={peer.id === localPeerId} role={peer.roleName} />
|
179
|
+
) : null}
|
180
|
+
</Flex>
|
181
|
+
);
|
182
|
+
};
|
183
|
+
|
126
184
|
const VirtualizedParticipants = ({
|
127
185
|
peersOrderedByRoles = {},
|
128
186
|
isConnected,
|
@@ -131,6 +189,16 @@ const VirtualizedParticipants = ({
|
|
131
189
|
offStageRoles,
|
132
190
|
isLargeRoom,
|
133
191
|
onActive,
|
192
|
+
children,
|
193
|
+
}: {
|
194
|
+
peersOrderedByRoles: Record<string, HMSPeer[]>;
|
195
|
+
isConnected: boolean;
|
196
|
+
filter: undefined | { search?: string };
|
197
|
+
handRaisedList: HMSPeer[];
|
198
|
+
offStageRoles: HMSRoleName[];
|
199
|
+
isLargeRoom: boolean;
|
200
|
+
onActive: (role: string) => void;
|
201
|
+
children: React.ReactNode;
|
134
202
|
}) => {
|
135
203
|
return (
|
136
204
|
<Flex
|
@@ -142,6 +210,9 @@ const VirtualizedParticipants = ({
|
|
142
210
|
pr: '$10',
|
143
211
|
mr: '-$10',
|
144
212
|
flex: '1 1 0',
|
213
|
+
'& > div:empty ~ .emptyParticipants': {
|
214
|
+
display: 'flex',
|
215
|
+
},
|
145
216
|
}}
|
146
217
|
>
|
147
218
|
<Accordion.Root type={isLargeRoom ? 'single' : 'multiple'} collapsible>
|
@@ -167,36 +238,7 @@ const VirtualizedParticipants = ({
|
|
167
238
|
/>
|
168
239
|
))}
|
169
240
|
</Accordion.Root>
|
170
|
-
|
171
|
-
);
|
172
|
-
};
|
173
|
-
|
174
|
-
export const Participant = ({ peer, isConnected, style }) => {
|
175
|
-
const localPeerId = useHMSStore(selectLocalPeerID);
|
176
|
-
return (
|
177
|
-
<Flex
|
178
|
-
key={peer.id}
|
179
|
-
css={{
|
180
|
-
w: '100%',
|
181
|
-
p: '$4 $8',
|
182
|
-
pr: '$6',
|
183
|
-
h: '$16',
|
184
|
-
'&:hover .participant_item': { display: 'flex' },
|
185
|
-
}}
|
186
|
-
align="center"
|
187
|
-
justify="between"
|
188
|
-
data-testid={'participant_' + peer.name}
|
189
|
-
style={style}
|
190
|
-
>
|
191
|
-
<Text
|
192
|
-
variant="sm"
|
193
|
-
css={{ ...textEllipsis('100%'), flex: '1 1 0', mr: '$8', fontWeight: '$semiBold', color: '$on_surface_high' }}
|
194
|
-
>
|
195
|
-
{peer.name} {localPeerId === peer.id ? '(You)' : ''}
|
196
|
-
</Text>
|
197
|
-
{isConnected ? (
|
198
|
-
<ParticipantActions peerId={peer.id} isLocal={peer.id === localPeerId} role={peer.roleName} />
|
199
|
-
) : null}
|
241
|
+
{children}
|
200
242
|
</Flex>
|
201
243
|
);
|
202
244
|
};
|
@@ -204,78 +246,86 @@ export const Participant = ({ peer, isConnected, style }) => {
|
|
204
246
|
/**
|
205
247
|
* shows settings to change for a participant like changing their role
|
206
248
|
*/
|
207
|
-
const ParticipantActions = React.memo(
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
249
|
+
const ParticipantActions = React.memo(
|
250
|
+
({ peerId, role, isLocal }: { peerId: string; role: string; isLocal: boolean }) => {
|
251
|
+
const isHandRaised = useHMSStore(selectHasPeerHandRaised(peerId));
|
252
|
+
const canChangeRole = useHMSStore(selectPermissions)?.changeRole;
|
253
|
+
const canRemoveOthers = useHMSStore(selectPermissions)?.removeOthers;
|
254
|
+
const { elements } = useRoomLayoutConferencingScreen();
|
255
|
+
const { on_stage_exp } = elements || {};
|
256
|
+
const shouldShowMoreActions = (on_stage_exp && canChangeRole) || canRemoveOthers;
|
257
|
+
const isAudioMuted = !useHMSStore(selectIsPeerAudioEnabled(peerId));
|
215
258
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
259
|
+
return (
|
260
|
+
<Flex
|
261
|
+
align="center"
|
262
|
+
css={{
|
263
|
+
flexShrink: 0,
|
264
|
+
gap: '$8',
|
265
|
+
}}
|
266
|
+
>
|
267
|
+
<ConnectionIndicator peerId={peerId} />
|
268
|
+
{isHandRaised && (
|
269
|
+
<Flex
|
270
|
+
align="center"
|
271
|
+
justify="center"
|
272
|
+
css={{ p: '$1', c: '$on_surface_high', bg: '$surface_bright', borderRadius: '$round' }}
|
273
|
+
>
|
274
|
+
<HandIcon height={19} width={19} />
|
275
|
+
</Flex>
|
276
|
+
)}
|
277
|
+
{isAudioMuted ? (
|
278
|
+
<Flex
|
279
|
+
align="center"
|
280
|
+
justify="center"
|
281
|
+
css={{ p: '$2', c: '$on_surface_high', bg: '$surface_bright', borderRadius: '$round' }}
|
282
|
+
>
|
283
|
+
<MicOffIcon height={19} width={19} />
|
284
|
+
</Flex>
|
285
|
+
) : null}
|
243
286
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
/>
|
252
|
-
) : null}
|
253
|
-
</Flex>
|
254
|
-
);
|
255
|
-
});
|
287
|
+
{shouldShowMoreActions && !isLocal ? (
|
288
|
+
<ParticipantMoreActions peerId={peerId} role={role} elements={elements} canChangeRole={!!canChangeRole} />
|
289
|
+
) : null}
|
290
|
+
</Flex>
|
291
|
+
);
|
292
|
+
},
|
293
|
+
);
|
256
294
|
|
257
|
-
const ParticipantMoreActions = ({
|
295
|
+
const ParticipantMoreActions = ({
|
296
|
+
peerId,
|
297
|
+
role,
|
298
|
+
elements,
|
299
|
+
canChangeRole,
|
300
|
+
}: {
|
301
|
+
peerId: string;
|
302
|
+
role: string;
|
303
|
+
canChangeRole: boolean;
|
304
|
+
elements: ConferencingScreenElements;
|
305
|
+
}) => {
|
258
306
|
const hmsActions = useHMSActions();
|
259
307
|
const {
|
260
308
|
bring_to_stage_label,
|
261
309
|
remove_from_stage_label,
|
262
310
|
on_stage_role,
|
263
311
|
off_stage_roles = [],
|
312
|
+
skip_preview_for_role_change = false,
|
264
313
|
} = elements.on_stage_exp || {};
|
265
314
|
const isInStage = role === on_stage_role;
|
266
315
|
const shouldShowStageRoleChange =
|
267
316
|
canChangeRole &&
|
268
317
|
((isInStage && remove_from_stage_label) || (off_stage_roles?.includes(role) && bring_to_stage_label));
|
269
318
|
const prevRole = useHMSStore(selectPeerMetadata(peerId))?.prevRole;
|
270
|
-
const localPeerId = useHMSStore(selectLocalPeerID);
|
271
|
-
const isLocal = localPeerId === peerId;
|
272
319
|
const [open, setOpen] = useState(false);
|
273
320
|
|
274
321
|
const handleStageAction = async () => {
|
275
322
|
if (isInStage) {
|
276
323
|
prevRole && hmsActions.changeRoleOfPeer(peerId, prevRole, true);
|
277
|
-
} else {
|
278
|
-
await hmsActions.changeRoleOfPeer(peerId, on_stage_role);
|
324
|
+
} else if (on_stage_role) {
|
325
|
+
await hmsActions.changeRoleOfPeer(peerId, on_stage_role, skip_preview_for_role_change);
|
326
|
+
if (skip_preview_for_role_change) {
|
327
|
+
await hmsActions.lowerRemotePeerHand(peerId);
|
328
|
+
}
|
279
329
|
}
|
280
330
|
setOpen(false);
|
281
331
|
};
|
@@ -315,30 +365,22 @@ const ParticipantMoreActions = ({ peerId, role, elements, canChangeRole, canRemo
|
|
315
365
|
</Dropdown.Item>
|
316
366
|
) : null}
|
317
367
|
|
318
|
-
{
|
319
|
-
<Dropdown.Item
|
320
|
-
css={{ color: '$alert_error_default', bg: '$surface_default' }}
|
321
|
-
onClick={async () => {
|
322
|
-
try {
|
323
|
-
await hmsActions.removePeer(peerId, '');
|
324
|
-
} catch (error) {
|
325
|
-
ToastManager.addToast({ title: error.message, variant: 'error' });
|
326
|
-
}
|
327
|
-
}}
|
328
|
-
>
|
329
|
-
<PeopleRemoveIcon />
|
330
|
-
<Text variant="sm" css={{ ml: '$4', color: 'inherit', fontWeight: '$semiBold' }}>
|
331
|
-
Remove Participant
|
332
|
-
</Text>
|
333
|
-
</Dropdown.Item>
|
334
|
-
)}
|
368
|
+
<RemoveParticipant peerId={peerId} />
|
335
369
|
</Dropdown.Content>
|
336
370
|
</Dropdown.Portal>
|
337
371
|
</Dropdown.Root>
|
338
372
|
);
|
339
373
|
};
|
340
374
|
|
341
|
-
export const ParticipantSearch = ({
|
375
|
+
export const ParticipantSearch = ({
|
376
|
+
onSearch,
|
377
|
+
placeholder = 'Search for participants',
|
378
|
+
inSidePane = false,
|
379
|
+
}: {
|
380
|
+
inSidePane?: boolean;
|
381
|
+
placeholder?: string;
|
382
|
+
onSearch: (val: string) => void;
|
383
|
+
}) => {
|
342
384
|
const [value, setValue] = React.useState('');
|
343
385
|
const isMobile = useMedia(cssConfig.media.md);
|
344
386
|
|
@@ -364,7 +406,7 @@ export const ParticipantSearch = ({ onSearch, placeholder, inSidePane = false })
|
|
364
406
|
<SearchIcon style={{ position: 'absolute', left: '0.5rem' }} />
|
365
407
|
<Input
|
366
408
|
type="text"
|
367
|
-
placeholder={placeholder
|
409
|
+
placeholder={placeholder}
|
368
410
|
css={{ w: '100%', p: '$6', pl: '$14', bg: inSidePane ? '$surface_default' : '$surface_dim' }}
|
369
411
|
value={value}
|
370
412
|
onKeyDown={event => {
|
@@ -6,7 +6,6 @@ import { ChevronRightIcon } from '@100mslive/react-icons';
|
|
6
6
|
import { Accordion } from '../../../Accordion';
|
7
7
|
import { Flex } from '../../../Layout';
|
8
8
|
import { Text } from '../../../Text';
|
9
|
-
// @ts-ignore: No implicit Any
|
10
9
|
import { Participant } from './ParticipantList';
|
11
10
|
import { RoleOptions } from './RoleOptions';
|
12
11
|
// @ts-ignore: No implicit Any
|
@@ -24,9 +23,18 @@ export function itemKey(index: number, data: ItemData) {
|
|
24
23
|
return data.peerList[index]?.id;
|
25
24
|
}
|
26
25
|
|
27
|
-
export const VirtualizedParticipantItem = React.memo(
|
28
|
-
|
29
|
-
|
26
|
+
export const VirtualizedParticipantItem = React.memo(
|
27
|
+
({ index, data, style }: { index: number; data: ItemData; style: React.CSSProperties }) => {
|
28
|
+
return (
|
29
|
+
<Participant
|
30
|
+
key={data.peerList[index].id}
|
31
|
+
peer={data.peerList[index]}
|
32
|
+
isConnected={data.isConnected}
|
33
|
+
style={style}
|
34
|
+
/>
|
35
|
+
);
|
36
|
+
},
|
37
|
+
);
|
30
38
|
|
31
39
|
export const RoleAccordion = ({
|
32
40
|
peerList = [],
|
@@ -39,15 +47,22 @@ export const RoleAccordion = ({
|
|
39
47
|
}: ItemData & {
|
40
48
|
roleName: string;
|
41
49
|
isHandRaisedAccordion?: boolean;
|
42
|
-
filter?: { search
|
50
|
+
filter?: { search?: string };
|
43
51
|
offStageRoles: string[];
|
44
52
|
onActive?: (role: string) => void;
|
45
53
|
}) => {
|
46
54
|
const [ref, { width }] = useMeasure<HTMLDivElement>();
|
47
|
-
const showAcordion = filter?.search ? peerList.some(peer => peer.name.toLowerCase().includes(filter.search)) : true;
|
48
55
|
const isLargeRoom = useHMSStore(selectIsLargeRoom);
|
49
56
|
const { peers, total, loadPeers } = usePaginatedParticipants({ role: roleName, limit: 10 });
|
50
57
|
const isOffStageRole = roleName && offStageRoles.includes(roleName);
|
58
|
+
let peersInAccordion = peerList;
|
59
|
+
// for large rooms, peer list would be empty
|
60
|
+
if (isOffStageRole && isLargeRoom) {
|
61
|
+
peersInAccordion = peers;
|
62
|
+
if (filter?.search) {
|
63
|
+
peersInAccordion = peersInAccordion.filter(peer => peer.name.toLowerCase().includes(filter.search || ''));
|
64
|
+
}
|
65
|
+
}
|
51
66
|
|
52
67
|
useEffect(() => {
|
53
68
|
if (!isOffStageRole || !isLargeRoom) {
|
@@ -60,17 +75,12 @@ export const RoleAccordion = ({
|
|
60
75
|
return () => clearInterval(interval);
|
61
76
|
}, [isOffStageRole, isLargeRoom]); //eslint-disable-line
|
62
77
|
|
63
|
-
if (
|
78
|
+
if (peersInAccordion.length === 0 || (isHandRaisedAccordion && filter?.search)) {
|
64
79
|
return null;
|
65
80
|
}
|
66
81
|
|
67
|
-
const peersInAccordion = isOffStageRole && isLargeRoom ? peers : peerList;
|
68
82
|
const height = ROW_HEIGHT * peersInAccordion.length;
|
69
|
-
const hasNext = total > peersInAccordion.length;
|
70
|
-
|
71
|
-
if (peersInAccordion.length === 0) {
|
72
|
-
return null;
|
73
|
-
}
|
83
|
+
const hasNext = total > peersInAccordion.length && !filter?.search;
|
74
84
|
|
75
85
|
return (
|
76
86
|
<Accordion.Item value={roleName} css={{ '&:hover .role_actions': { visibility: 'visible' }, mb: '$8' }} ref={ref}>
|
@@ -0,0 +1,34 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { useWhiteboard } from '@100mslive/react-sdk';
|
3
|
+
import { PencilDrawIcon } from '@100mslive/react-icons';
|
4
|
+
import { Tooltip } from '../../..';
|
5
|
+
// @ts-ignore: No implicit Any
|
6
|
+
import IconButton from '../../IconButton';
|
7
|
+
// @ts-ignore: No implicit Any
|
8
|
+
import { ToastManager } from '../Toast/ToastManager';
|
9
|
+
|
10
|
+
export const WhiteboardToggle = () => {
|
11
|
+
const { toggle, open, isOwner } = useWhiteboard();
|
12
|
+
if (!toggle) {
|
13
|
+
return null;
|
14
|
+
}
|
15
|
+
|
16
|
+
return (
|
17
|
+
<Tooltip key="whiteboard" title={`${open ? 'Close' : 'Open'} Whiteboard`}>
|
18
|
+
<IconButton
|
19
|
+
onClick={async () => {
|
20
|
+
try {
|
21
|
+
await toggle();
|
22
|
+
} catch (error) {
|
23
|
+
ToastManager.addToast({ title: (error as Error).message, variant: 'error' });
|
24
|
+
}
|
25
|
+
}}
|
26
|
+
active={!open}
|
27
|
+
disabled={open && !isOwner}
|
28
|
+
data-testid="whiteboard_btn"
|
29
|
+
>
|
30
|
+
<PencilDrawIcon />
|
31
|
+
</IconButton>
|
32
|
+
</Tooltip>
|
33
|
+
);
|
34
|
+
};
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import { ClosedCaptionIcon, OpenCaptionIcon } from '@100mslive/react-icons';
|
3
|
+
import { IconButton, Tooltip } from '../../../';
|
4
|
+
|
5
|
+
export function HLSCaptionSelector({ isEnabled, onClick }: { isEnabled: boolean; onClick: () => void }) {
|
6
|
+
return (
|
7
|
+
<Tooltip title="Subtitles/closed captions" side="top">
|
8
|
+
<IconButton css={{ p: '$2' }} onClick={() => onClick()}>
|
9
|
+
{isEnabled ? <ClosedCaptionIcon width="20" height="20px" /> : <OpenCaptionIcon width="20" height="20px" />}
|
10
|
+
</IconButton>
|
11
|
+
</Tooltip>
|
12
|
+
);
|
13
|
+
}
|