@100mslive/roomkit-react 0.1.15 → 0.1.16
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-MXBOUQBG.js → HLSView-EMUOLCTM.js} +2 -2
- package/dist/Prebuilt/common/PeersSorter.d.ts +1 -0
- package/dist/Prebuilt/common/constants.d.ts +7 -4
- 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/Notifications/HandRaisedNotifications.d.ts +1 -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-HEOH5H43.js → chunk-ZYR4B4KQ.js} +1886 -7116
- package/dist/chunk-ZYR4B4KQ.js.map +7 -0
- package/dist/index.cjs.js +2477 -7662
- package/dist/index.cjs.js.map +4 -4
- package/dist/index.js +1 -1
- package/dist/meta.cjs.json +438 -161
- package/dist/meta.esbuild.json +443 -166
- package/package.json +7 -7
- package/src/Prebuilt/AppStateContext.tsx +1 -1
- package/src/Prebuilt/common/PeersSorter.ts +12 -5
- package/src/Prebuilt/common/constants.ts +5 -6
- package/src/Prebuilt/common/hooks.ts +16 -0
- package/src/Prebuilt/common/utils.js +5 -6
- package/src/Prebuilt/components/AppData/AppData.tsx +1 -16
- package/src/Prebuilt/components/Chat/Chat.jsx +7 -30
- 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/Notifications/HandRaisedNotifications.tsx +35 -0
- package/src/Prebuilt/components/Notifications/Notifications.tsx +14 -12
- package/src/Prebuilt/components/Notifications/PeerNotifications.tsx +7 -2
- package/src/Prebuilt/components/Polls/Voting/QuestionCard.jsx +10 -2
- 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/GridLayout.tsx +5 -2
- package/src/Prebuilt/components/hooks/useCloseScreenshareWhiteboard.tsx +24 -0
- package/src/Prebuilt/layouts/VideoStreamingSection.tsx +20 -3
- package/src/Prebuilt/layouts/WhiteboardView.tsx +66 -0
- package/dist/chunk-HEOH5H43.js.map +0 -7
- package/src/Prebuilt/components/AppData/useAppLayout.js +0 -6
- package/src/Prebuilt/components/init/initUtils.js +0 -67
- /package/dist/{HLSView-MXBOUQBG.js.map → HLSView-EMUOLCTM.js.map} +0 -0
@@ -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,35 @@
|
|
1
|
+
import { useEffect } from 'react';
|
2
|
+
import {
|
3
|
+
HMSNotificationTypes,
|
4
|
+
HMSRoomState,
|
5
|
+
selectHasPeerHandRaised,
|
6
|
+
selectRoomState,
|
7
|
+
useHMSNotifications,
|
8
|
+
useHMSStore,
|
9
|
+
useHMSVanillaStore,
|
10
|
+
} from '@100mslive/react-sdk';
|
11
|
+
// @ts-ignore: No implicit Any
|
12
|
+
import { ToastBatcher } from '../Toast/ToastBatcher';
|
13
|
+
import { useRoomLayoutConferencingScreen } from '../../provider/roomLayoutProvider/hooks/useRoomLayoutScreen';
|
14
|
+
|
15
|
+
export const HandRaisedNotifications = () => {
|
16
|
+
const notification = useHMSNotifications(HMSNotificationTypes.HAND_RAISE_CHANGED);
|
17
|
+
const roomState = useHMSStore(selectRoomState);
|
18
|
+
const vanillaStore = useHMSVanillaStore();
|
19
|
+
const { on_stage_exp } = useRoomLayoutConferencingScreen().elements || {};
|
20
|
+
|
21
|
+
useEffect(() => {
|
22
|
+
if (!notification?.data) {
|
23
|
+
return;
|
24
|
+
}
|
25
|
+
if (roomState !== HMSRoomState.Connected || notification.data.isLocal || !on_stage_exp) {
|
26
|
+
return;
|
27
|
+
}
|
28
|
+
const hasPeerHandRaised = vanillaStore.getState(selectHasPeerHandRaised(notification.data.id));
|
29
|
+
if (hasPeerHandRaised) {
|
30
|
+
ToastBatcher.showToast({ notification, type: 'RAISE_HAND' });
|
31
|
+
}
|
32
|
+
}, [notification, on_stage_exp, roomState, vanillaStore]);
|
33
|
+
|
34
|
+
return null;
|
35
|
+
};
|
@@ -4,7 +4,6 @@ import {
|
|
4
4
|
HMSNotificationTypes,
|
5
5
|
HMSRoleChangeRequest,
|
6
6
|
HMSRoomState,
|
7
|
-
selectHasPeerHandRaised,
|
8
7
|
selectLocalPeerID,
|
9
8
|
selectPeerNameByID,
|
10
9
|
selectRoomState,
|
@@ -13,7 +12,8 @@ import {
|
|
13
12
|
useHMSStore,
|
14
13
|
useHMSVanillaStore,
|
15
14
|
} from '@100mslive/react-sdk';
|
16
|
-
import {
|
15
|
+
import { GroupIcon } from '@100mslive/react-icons';
|
16
|
+
import { Box, Button } from '../../..';
|
17
17
|
import { useUpdateRoomLayout } from '../../provider/roomLayoutProvider';
|
18
18
|
// @ts-ignore: No implicit Any
|
19
19
|
import { ToastBatcher } from '../Toast/ToastBatcher';
|
@@ -21,6 +21,7 @@ import { ToastBatcher } from '../Toast/ToastBatcher';
|
|
21
21
|
import { ToastManager } from '../Toast/ToastManager';
|
22
22
|
import { AutoplayBlockedModal } from './AutoplayBlockedModal';
|
23
23
|
import { ChatNotifications } from './ChatNotifications';
|
24
|
+
import { HandRaisedNotifications } from './HandRaisedNotifications';
|
24
25
|
import { InitErrorModal } from './InitErrorModal';
|
25
26
|
import { PeerNotifications } from './PeerNotifications';
|
26
27
|
import { PermissionErrorModal } from './PermissionErrorModal';
|
@@ -91,16 +92,6 @@ export function Notifications() {
|
|
91
92
|
return;
|
92
93
|
}
|
93
94
|
switch (notification.type) {
|
94
|
-
case HMSNotificationTypes.HAND_RAISE_CHANGED: {
|
95
|
-
if (roomState !== HMSRoomState.Connected || notification.data.isLocal) {
|
96
|
-
return;
|
97
|
-
}
|
98
|
-
const hasPeerHandRaised = vanillaStore.getState(selectHasPeerHandRaised(notification.data.id));
|
99
|
-
if (hasPeerHandRaised) {
|
100
|
-
ToastBatcher.showToast({ notification, type: 'RAISE_HAND' });
|
101
|
-
}
|
102
|
-
break;
|
103
|
-
}
|
104
95
|
case HMSNotificationTypes.METADATA_UPDATED:
|
105
96
|
if (roomState !== HMSRoomState.Connected) {
|
106
97
|
return;
|
@@ -123,6 +114,16 @@ export function Notifications() {
|
|
123
114
|
ToastManager.addToast({
|
124
115
|
title: `Error: ${notification.data?.message}`,
|
125
116
|
});
|
117
|
+
} else if (notification.data?.message === 'role limit reached') {
|
118
|
+
ToastManager.addToast({
|
119
|
+
title: 'The room is currently full, try joining later',
|
120
|
+
close: true,
|
121
|
+
icon: (
|
122
|
+
<Box css={{ color: '$alert_error_default' }}>
|
123
|
+
<GroupIcon />
|
124
|
+
</Box>
|
125
|
+
),
|
126
|
+
});
|
126
127
|
} else {
|
127
128
|
ToastManager.addToast({
|
128
129
|
title:
|
@@ -219,6 +220,7 @@ export function Notifications() {
|
|
219
220
|
<PermissionErrorModal />
|
220
221
|
<InitErrorModal />
|
221
222
|
<ChatNotifications />
|
223
|
+
<HandRaisedNotifications />
|
222
224
|
</>
|
223
225
|
);
|
224
226
|
}
|
@@ -3,9 +3,9 @@ import { HMSNotificationTypes, useHMSNotifications } from '@100mslive/react-sdk'
|
|
3
3
|
// @ts-ignore: No implicit Any
|
4
4
|
import { ToastBatcher } from '../Toast/ToastBatcher';
|
5
5
|
// @ts-ignore: No implicit Any
|
6
|
-
import { useSubscribedNotifications } from '../AppData/useUISettings';
|
6
|
+
import { useSetSubscribedChatSelector, useSubscribedNotifications } from '../AppData/useUISettings';
|
7
7
|
// @ts-ignore: No implicit Any
|
8
|
-
import { SUBSCRIBED_NOTIFICATIONS } from '../../common/constants';
|
8
|
+
import { CHAT_SELECTOR, SUBSCRIBED_NOTIFICATIONS } from '../../common/constants';
|
9
9
|
|
10
10
|
const notificationTypes = [
|
11
11
|
HMSNotificationTypes.PEER_LIST,
|
@@ -17,6 +17,8 @@ export const PeerNotifications = () => {
|
|
17
17
|
const notification = useHMSNotifications(notificationTypes);
|
18
18
|
const isPeerJoinSubscribed = useSubscribedNotifications(SUBSCRIBED_NOTIFICATIONS.PEER_JOINED);
|
19
19
|
const isPeerLeftSubscribed = useSubscribedNotifications(SUBSCRIBED_NOTIFICATIONS.PEER_LEFT);
|
20
|
+
const [selectedPeer, setPeerSelector] = useSetSubscribedChatSelector(CHAT_SELECTOR.PEER);
|
21
|
+
|
20
22
|
useEffect(() => {
|
21
23
|
if (!notification?.data) {
|
22
24
|
return;
|
@@ -35,6 +37,9 @@ export const PeerNotifications = () => {
|
|
35
37
|
}
|
36
38
|
break;
|
37
39
|
case HMSNotificationTypes.PEER_LEFT:
|
40
|
+
if (selectedPeer.id === notification.data.id) {
|
41
|
+
setPeerSelector({});
|
42
|
+
}
|
38
43
|
if (!isPeerLeftSubscribed) {
|
39
44
|
return;
|
40
45
|
}
|