@100mslive/roomkit-react 0.3.3-alpha.1 → 0.3.3-alpha.3

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.
Files changed (43) hide show
  1. package/dist/{HLSView-GDOF4655.js → HLSView-BCIIVR2T.js} +331 -166
  2. package/dist/HLSView-BCIIVR2T.js.map +7 -0
  3. package/dist/IconButton/IconButton.d.ts +1 -1
  4. package/dist/Modal/Dialog.d.ts +1 -1
  5. package/dist/Prebuilt/IconButton.d.ts +2 -3
  6. package/dist/Prebuilt/components/Footer/ChatToggle.d.ts +3 -1
  7. package/dist/Prebuilt/components/HMSVideo/PlayPauseButton.d.ts +2 -2
  8. package/dist/Prebuilt/components/HMSVideo/PlayPauseSeekControls.d.ts +14 -0
  9. package/dist/Prebuilt/components/HMSVideo/{SeekControls.d.ts → SeekControl.d.ts} +2 -2
  10. package/dist/Prebuilt/components/HMSVideo/index.d.ts +15 -11
  11. package/dist/Prebuilt/components/Leave/LeaveAtoms.d.ts +2 -2
  12. package/dist/Prebuilt/components/VideoLayouts/WhiteboardLayout.d.ts +3 -0
  13. package/dist/Sheet/Sheet.d.ts +1 -1
  14. package/dist/{chunk-ZDW56PPL.js → chunk-W47ZJGSD.js} +212 -165
  15. package/dist/chunk-W47ZJGSD.js.map +7 -0
  16. package/dist/index.cjs.js +923 -696
  17. package/dist/index.cjs.js.map +4 -4
  18. package/dist/index.js +1 -1
  19. package/dist/meta.cjs.json +196 -112
  20. package/dist/meta.esbuild.json +210 -125
  21. package/package.json +6 -6
  22. package/src/IconButton/IconButton.tsx +2 -7
  23. package/src/Prebuilt/IconButton.tsx +6 -10
  24. package/src/Prebuilt/components/Footer/ChatToggle.tsx +2 -2
  25. package/src/Prebuilt/components/Footer/ParticipantList.tsx +4 -1
  26. package/src/Prebuilt/components/Footer/RoleAccordion.tsx +5 -6
  27. package/src/Prebuilt/components/Footer/WhiteboardToggle.tsx +17 -3
  28. package/src/Prebuilt/components/HMSVideo/HMSVideo.jsx +1 -0
  29. package/src/Prebuilt/components/HMSVideo/PlayPauseButton.tsx +2 -2
  30. package/src/Prebuilt/components/HMSVideo/PlayPauseSeekControls.tsx +158 -0
  31. package/src/Prebuilt/components/HMSVideo/{SeekControls.tsx → SeekControl.tsx} +2 -2
  32. package/src/Prebuilt/components/HMSVideo/VideoProgress.tsx +7 -3
  33. package/src/Prebuilt/components/HMSVideo/index.ts +5 -4
  34. package/src/Prebuilt/components/IconButtonWithOptions/IconButtonWithOptions.tsx +7 -7
  35. package/src/Prebuilt/components/VideoLayouts/GridLayout.tsx +27 -4
  36. package/src/Prebuilt/components/VideoLayouts/WhiteboardLayout.tsx +95 -0
  37. package/src/Prebuilt/components/hooks/useCloseScreenshareWhiteboard.tsx +13 -5
  38. package/src/Prebuilt/layouts/HLSView.jsx +115 -78
  39. package/src/Prebuilt/layouts/VideoStreamingSection.tsx +1 -17
  40. package/dist/HLSView-GDOF4655.js.map +0 -7
  41. package/dist/Prebuilt/layouts/WhiteboardView.d.ts +0 -2
  42. package/dist/chunk-ZDW56PPL.js.map +0 -7
  43. package/src/Prebuilt/layouts/WhiteboardView.tsx +0 -69
package/package.json CHANGED
@@ -10,7 +10,7 @@
10
10
  "prebuilt",
11
11
  "roomkit"
12
12
  ],
13
- "version": "0.3.3-alpha.1",
13
+ "version": "0.3.3-alpha.3",
14
14
  "author": "100ms",
15
15
  "license": "MIT",
16
16
  "repository": {
@@ -82,11 +82,11 @@
82
82
  "react": ">=17.0.2 <19.0.0"
83
83
  },
84
84
  "dependencies": {
85
- "@100mslive/hls-player": "0.3.3-alpha.1",
85
+ "@100mslive/hls-player": "0.3.3-alpha.3",
86
86
  "@100mslive/hms-noise-cancellation": "0.0.0-alpha.1",
87
- "@100mslive/hms-virtual-background": "1.13.3-alpha.1",
88
- "@100mslive/react-icons": "0.10.3-alpha.1",
89
- "@100mslive/react-sdk": "0.10.3-alpha.1",
87
+ "@100mslive/hms-virtual-background": "1.13.3-alpha.3",
88
+ "@100mslive/react-icons": "0.10.3-alpha.3",
89
+ "@100mslive/react-sdk": "0.10.3-alpha.3",
90
90
  "@100mslive/types-prebuilt": "0.12.8",
91
91
  "@emoji-mart/data": "^1.0.6",
92
92
  "@emoji-mart/react": "^1.0.1",
@@ -122,5 +122,5 @@
122
122
  "uuid": "^8.3.2",
123
123
  "worker-timers": "^7.0.40"
124
124
  },
125
- "gitHead": "8e27b80c474e3487828a60fce4aa1f444aa14577"
125
+ "gitHead": "2f0dfb954601e7028cf0a4d9582a30be0fe726ca"
126
126
  }
@@ -25,19 +25,14 @@ export const IconButton = styled('button', {
25
25
  '&[disabled]': {
26
26
  opacity: 0.5,
27
27
  cursor: 'not-allowed',
28
+ backgroundColor: '$secondary_dim',
29
+ color: '$on_primary_high',
28
30
  },
29
31
  '&:focus': {
30
32
  outline: 'none',
31
33
  },
32
34
  variants: {
33
35
  active: {
34
- false: {
35
- backgroundColor: '$secondary_dim',
36
- color: '$on_primary_high',
37
- '&:not([disabled]):hover': {
38
- backgroundColor: '$secondary_default',
39
- },
40
- },
41
36
  true: {
42
37
  '&:not([disabled]):hover': {
43
38
  backgroundColor: '$on_surface_low',
@@ -7,22 +7,18 @@ const IconButton = styled(BaseIconButton, {
7
7
  border: '1px solid $border_bright',
8
8
  bg: '$background_dim',
9
9
  r: '$1',
10
+ '&[disabled]': {
11
+ opacity: 0.5,
12
+ cursor: 'not-allowed',
13
+ backgroundColor: '$secondary_dim',
14
+ color: '$on_primary_high',
15
+ },
10
16
  variants: {
11
17
  active: {
12
18
  true: {
13
19
  color: '$on_surface_high',
14
20
  backgroundColor: 'transparent',
15
21
  },
16
- false: {
17
- border: '1px solid transparent',
18
- color: '$on_primary_high',
19
- },
20
- },
21
- disabled: {
22
- true: {
23
- backgroundColor: '$surface_brighter',
24
- color: '$on_surface_low',
25
- },
26
22
  },
27
23
  },
28
24
  });
@@ -9,7 +9,7 @@ import { useIsSidepaneTypeOpen, useSidepaneToggle } from '../AppData/useSidepane
9
9
  // @ts-ignore: No implicit Any
10
10
  import { SIDE_PANE_OPTIONS } from '../../common/constants';
11
11
 
12
- export const ChatToggle = () => {
12
+ export const ChatToggle = ({ onClick }: { onClick?: () => void }) => {
13
13
  const countUnreadMessages = useHMSStore(selectUnreadHMSMessagesCount);
14
14
  const isChatOpen = useIsSidepaneTypeOpen(SIDE_PANE_OPTIONS.CHAT);
15
15
  const toggleChat = useSidepaneToggle(SIDE_PANE_OPTIONS.CHAT);
@@ -21,7 +21,7 @@ export const ChatToggle = () => {
21
21
  }}
22
22
  >
23
23
  <Tooltip key="chat" title={`${isChatOpen ? 'Close' : 'Open'} chat`}>
24
- <IconButton onClick={toggleChat} active={!isChatOpen} data-testid="chat_btn">
24
+ <IconButton onClick={() => (onClick ? onClick() : toggleChat())} active={!isChatOpen} data-testid="chat_btn">
25
25
  <ChatIcon />
26
26
  </IconButton>
27
27
  </Tooltip>
@@ -324,12 +324,15 @@ const HandRaisedAccordionParticipantActions = ({ peerId, role }: { peerId: strin
324
324
  peerId,
325
325
  role,
326
326
  });
327
+ if (!shouldShowStageRoleChange) {
328
+ return null;
329
+ }
327
330
  return (
328
331
  <>
329
332
  <Button variant="standard" css={quickActionStyle} onClick={lowerPeerHand}>
330
333
  <CrossIcon height={18} width={18} />
331
334
  </Button>
332
- {shouldShowStageRoleChange && !isInStage && (
335
+ {!isInStage && (
333
336
  <Button variant="primary" onClick={handleStageAction} css={quickActionStyle}>
334
337
  <AddIcon height={18} width={18} />
335
338
  </Button>
@@ -149,18 +149,17 @@ export const RoleAccordion = ({
149
149
  <ChevronRightIcon />
150
150
  </Flex>
151
151
  ) : null}
152
- {isHandRaisedAccordion && (
152
+ {isHandRaisedAccordion && canBringToStage && (
153
153
  <>
154
154
  <HorizontalDivider />
155
155
  <Flex css={{ w: '100%', p: '$6', gap: '$4' }} justify="center">
156
156
  <Button variant="standard" onClick={lowerAllHands} icon css={{ pl: '$2' }}>
157
157
  <CrossIcon /> Lower all hands
158
158
  </Button>
159
- {canBringToStage && (
160
- <Button onClick={bringAllToStage} icon css={{ pl: '$2' }}>
161
- <AddIcon /> {bring_to_stage_label}
162
- </Button>
163
- )}
159
+
160
+ <Button onClick={bringAllToStage} icon css={{ pl: '$2' }}>
161
+ <AddIcon /> {bring_to_stage_label}
162
+ </Button>
164
163
  </Flex>
165
164
  </>
166
165
  )}
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { useWhiteboard } from '@100mslive/react-sdk';
2
+ import { useScreenShare, useWhiteboard } from '@100mslive/react-sdk';
3
3
  import { PencilDrawIcon } from '@100mslive/react-icons';
4
4
  import { Tooltip } from '../../..';
5
5
  // @ts-ignore: No implicit Any
@@ -9,14 +9,28 @@ import { ToastManager } from '../Toast/ToastManager';
9
9
 
10
10
  export const WhiteboardToggle = () => {
11
11
  const { toggle, open, isOwner } = useWhiteboard();
12
+ const { screenSharingPeerId, amIScreenSharing } = useScreenShare();
13
+ const remoteScreenShare = screenSharingPeerId && !amIScreenSharing;
14
+ const disabled = remoteScreenShare || (open && !isOwner);
15
+
12
16
  if (!toggle) {
13
17
  return null;
14
18
  }
15
19
 
16
20
  return (
17
- <Tooltip key="whiteboard" title={`${open ? 'Close' : 'Open'} Whiteboard`}>
21
+ <Tooltip
22
+ key="whiteboard"
23
+ title={
24
+ remoteScreenShare
25
+ ? 'Cannot open whiteboard when viewing a shared screen'
26
+ : `${open ? 'Close' : 'Open'} Whiteboard`
27
+ }
28
+ >
18
29
  <IconButton
19
30
  onClick={async () => {
31
+ if (disabled) {
32
+ return;
33
+ }
20
34
  try {
21
35
  await toggle();
22
36
  } catch (error) {
@@ -24,7 +38,7 @@ export const WhiteboardToggle = () => {
24
38
  }
25
39
  }}
26
40
  active={!open}
27
- disabled={open && !isOwner}
41
+ disabled={disabled}
28
42
  data-testid="whiteboard_btn"
29
43
  >
30
44
  <PencilDrawIcon />
@@ -9,6 +9,7 @@ export const HMSVideo = forwardRef(({ children, ...props }, videoRef) => {
9
9
  size: '100%',
10
10
  position: 'relative',
11
11
  justifyContent: 'center',
12
+ transition: 'all 0.3s ease-in-out',
12
13
  '@md': {
13
14
  height: 'auto',
14
15
  '& video': {
@@ -9,8 +9,8 @@ export const PlayPauseButton = ({
9
9
  height = 20,
10
10
  }: {
11
11
  isPaused: boolean;
12
- width: number;
13
- height: number;
12
+ width?: number;
13
+ height?: number;
14
14
  }) => {
15
15
  const { hlsPlayer } = useHMSPlayerContext();
16
16
  const onClick = async (event: MouseEvent) => {
@@ -0,0 +1,158 @@
1
+ import React from 'react';
2
+ import { useMedia } from 'react-use';
3
+ import { BackwardArrowIcon, ForwardArrowIcon } from '@100mslive/react-icons';
4
+ import { Box, Flex } from '../../../Layout';
5
+ import { Text } from '../../../Text';
6
+ import { config } from '../../../Theme';
7
+ import { PlayPauseButton } from './PlayPauseButton';
8
+ import { SeekControl } from './SeekControl';
9
+ import { useIsLandscape } from '../../common/hooks';
10
+
11
+ // desktop buttons
12
+ export const PlayPauseSeekControls = ({
13
+ isPaused,
14
+ onSeekTo,
15
+ }: {
16
+ isPaused: boolean;
17
+ onSeekTo: (value: number) => void;
18
+ }) => {
19
+ return (
20
+ <>
21
+ <SeekControl
22
+ onClick={e => {
23
+ e.stopPropagation();
24
+ onSeekTo(-10);
25
+ }}
26
+ title="backward"
27
+ >
28
+ <BackwardArrowIcon width={20} height={20} />
29
+ </SeekControl>
30
+ <PlayPauseButton isPaused={isPaused} />
31
+ <SeekControl
32
+ onClick={e => {
33
+ e.stopPropagation();
34
+ onSeekTo(10);
35
+ }}
36
+ title="forward"
37
+ >
38
+ <ForwardArrowIcon width={20} height={20} />
39
+ </SeekControl>
40
+ </>
41
+ );
42
+ };
43
+
44
+ // overlay handlers
45
+ export const PlayPauseSeekOverlayControls = ({
46
+ isPaused,
47
+ showControls,
48
+ hoverControlsVisible,
49
+ }: {
50
+ isPaused: boolean;
51
+ showControls: boolean;
52
+ hoverControlsVisible: {
53
+ seekBackward: boolean;
54
+ seekForward: boolean;
55
+ pausePlay: boolean;
56
+ };
57
+ }) => {
58
+ const isMobile = useMedia(config.media.md);
59
+ const isLandscape = useIsLandscape();
60
+
61
+ if (!isMobile && !isLandscape) {
62
+ // show desktopOverflow icons
63
+ return (
64
+ <>
65
+ <Flex
66
+ css={{
67
+ bg: 'rgba(0, 0, 0, 0.6)',
68
+ r: '$round',
69
+ size: '$24',
70
+ visibility: hoverControlsVisible.seekBackward ? `` : `hidden`,
71
+ opacity: hoverControlsVisible.seekBackward ? `1` : '0',
72
+ }}
73
+ direction="column"
74
+ align="center"
75
+ >
76
+ <SeekControl title="backward">
77
+ <BackwardArrowIcon width={52} height={52} />
78
+ </SeekControl>
79
+ <Text variant="body2" css={{ fontWeight: '$regular' }}>
80
+ 10 secs
81
+ </Text>
82
+ </Flex>
83
+ <Box
84
+ css={{
85
+ bg: 'rgba(0, 0, 0, 0.6)',
86
+ r: '$round',
87
+ visibility: hoverControlsVisible.pausePlay ? `` : `hidden`,
88
+ opacity: hoverControlsVisible.pausePlay ? `1` : '0',
89
+ }}
90
+ >
91
+ <PlayPauseButton isPaused={isPaused} width={48} height={48} />
92
+ </Box>
93
+ <Flex
94
+ css={{
95
+ bg: 'rgba(0, 0, 0, 0.6)',
96
+ r: '$round',
97
+ size: '$24',
98
+ visibility: hoverControlsVisible.seekForward ? `` : `hidden`,
99
+ opacity: hoverControlsVisible.seekForward ? `1` : '0',
100
+ }}
101
+ direction="column"
102
+ align="center"
103
+ >
104
+ <SeekControl title="forward">
105
+ <ForwardArrowIcon width={52} height={52} />
106
+ </SeekControl>
107
+ <Text variant="body2" css={{ fontWeight: '$regular' }}>
108
+ 10 secs
109
+ </Text>
110
+ </Flex>
111
+ </>
112
+ );
113
+ }
114
+
115
+ return (
116
+ <Flex
117
+ align="center"
118
+ justify="center"
119
+ css={{
120
+ position: 'absolute',
121
+ bg: '#00000066',
122
+ display: 'inline-flex',
123
+ gap: '$2',
124
+ zIndex: 1,
125
+ size: '100%',
126
+ visibility: showControls ? `` : `hidden`,
127
+ opacity: showControls ? `1` : '0',
128
+ }}
129
+ >
130
+ <SeekControl
131
+ title="backward"
132
+ css={{
133
+ visibility: hoverControlsVisible.seekBackward ? `` : `hidden`,
134
+ opacity: hoverControlsVisible.seekBackward ? `1` : '0',
135
+ }}
136
+ >
137
+ <BackwardArrowIcon width={32} height={32} />
138
+ </SeekControl>
139
+ <Box
140
+ css={{
141
+ bg: 'rgba(0, 0, 0, 0.6)',
142
+ r: '$round',
143
+ }}
144
+ >
145
+ <PlayPauseButton isPaused={isPaused} width={48} height={48} />
146
+ </Box>
147
+ <SeekControl
148
+ title="forward"
149
+ css={{
150
+ visibility: hoverControlsVisible.seekForward ? `` : `hidden`,
151
+ opacity: hoverControlsVisible.seekForward ? `1` : '0',
152
+ }}
153
+ >
154
+ <ForwardArrowIcon width={32} height={32} />
155
+ </SeekControl>
156
+ </Flex>
157
+ );
158
+ };
@@ -1,7 +1,7 @@
1
1
  import React, { MouseEventHandler } from 'react';
2
2
  import { IconButton, Tooltip } from '../../..';
3
3
 
4
- export const SeekControls = ({
4
+ export const SeekControl = ({
5
5
  title,
6
6
  onClick,
7
7
  children,
@@ -9,7 +9,7 @@ export const SeekControls = ({
9
9
  }: {
10
10
  title: string;
11
11
  onClick?: MouseEventHandler<HTMLButtonElement>;
12
- css: any;
12
+ css?: any;
13
13
  children: React.ReactNode;
14
14
  }) => {
15
15
  return (
@@ -25,8 +25,12 @@ export const VideoProgress = ({
25
25
  if (videoEl.buffered.length > 0) {
26
26
  bufferProgress = Math.floor(getPercentage(videoEl.buffered?.end(0), duration));
27
27
  }
28
- setVideoProgress(isNaN(videoProgress) ? 0 : videoProgress);
29
- setBufferProgress(isNaN(bufferProgress) ? 0 : bufferProgress);
28
+ if (!isNaN(videoProgress)) {
29
+ setVideoProgress(videoProgress);
30
+ }
31
+ if (!isNaN(bufferProgress)) {
32
+ setBufferProgress(bufferProgress);
33
+ }
30
34
  }, [videoEl]);
31
35
  const timeupdateHandler = useCallback(() => {
32
36
  if (!videoEl || seekProgress) {
@@ -42,7 +46,7 @@ export const VideoProgress = ({
42
46
  return function cleanup() {
43
47
  videoEl?.removeEventListener('timeupdate', timeupdateHandler);
44
48
  };
45
- }, [timeupdateHandler, videoEl]);
49
+ }, [setProgress, timeupdateHandler, videoEl]);
46
50
 
47
51
  const onProgress = (progress: number[]) => {
48
52
  const progress1 = Math.floor(getPercentage(progress[0], 100));
@@ -2,15 +2,13 @@
2
2
  import { LeftControls, RightControls, VideoControls } from './Controls';
3
3
  // @ts-ignore
4
4
  import { HMSVideo } from './HMSVideo';
5
- import { PlayPauseButton } from './PlayPauseButton';
6
- import { SeekControls } from './SeekControls';
5
+ import { PlayPauseSeekControls, PlayPauseSeekOverlayControls } from './PlayPauseSeekControls';
7
6
  import { VideoProgress } from './VideoProgress';
8
7
  import { VideoTime } from './VideoTime';
9
8
  import { VolumeControl } from './VolumeControl';
10
9
 
11
10
  export const HMSVideoPlayer = {
12
11
  Root: HMSVideo,
13
- PlayPauseButton: PlayPauseButton,
14
12
  Progress: VideoProgress,
15
13
  Duration: VideoTime,
16
14
  Volume: VolumeControl,
@@ -19,5 +17,8 @@ export const HMSVideoPlayer = {
19
17
  Left: LeftControls,
20
18
  Right: RightControls,
21
19
  },
22
- Seeker: SeekControls,
20
+ PlayPauseSeekControls: {
21
+ Overlay: PlayPauseSeekOverlayControls,
22
+ Button: PlayPauseSeekControls,
23
+ },
23
24
  };
@@ -25,9 +25,9 @@ const IconSection = styled(IconButton, {
25
25
  p: '$4',
26
26
  r: '$1',
27
27
  bg: 'transparent',
28
- borderTopRightRadius: 0,
28
+ borderTopRightRadius: '0 !important',
29
29
  borderColor: '$border_bright',
30
- borderBottomRightRadius: 0,
30
+ borderBottomRightRadius: '0 !important',
31
31
  position: 'relative',
32
32
  '&:not([disabled]):focus-visible': {
33
33
  zIndex: 1,
@@ -41,8 +41,8 @@ const IconSection = styled(IconButton, {
41
41
  ...variants,
42
42
  hideOptions: {
43
43
  true: {
44
- borderTopRightRadius: '$1',
45
- borderBottomRightRadius: '$1',
44
+ borderTopRightRadius: '$1 !important',
45
+ borderBottomRightRadius: '$1 !important',
46
46
  },
47
47
  },
48
48
  },
@@ -53,10 +53,10 @@ const OptionsSection = styled(IconButton, {
53
53
  h: '$14',
54
54
  p: '$4 $2',
55
55
  r: '$1',
56
- borderTopLeftRadius: 0,
56
+ borderTopLeftRadius: '0 !important',
57
57
  borderColor: '$border_bright',
58
- borderBottomLeftRadius: 0,
59
- borderLeftWidth: 0,
58
+ borderBottomLeftRadius: '0 !important',
59
+ borderLeftWidth: '0 !important',
60
60
  position: 'relative',
61
61
  '&:not([disabled]):focus-visible': {
62
62
  zIndex: 1,
@@ -5,12 +5,14 @@ import {
5
5
  selectLocalPeerRoleName,
6
6
  selectPeers,
7
7
  selectPeerScreenSharing,
8
+ selectWhiteboard,
8
9
  useHMSStore,
9
10
  useHMSVanillaStore,
10
11
  } from '@100mslive/react-sdk';
11
12
  import { EqualProminence } from './EqualProminence';
12
13
  import { RoleProminence } from './RoleProminence';
13
14
  import { ScreenshareLayout } from './ScreenshareLayout';
15
+ import { WhiteboardLayout } from './WhiteboardLayout';
14
16
  // @ts-ignore: No implicit Any
15
17
  import { usePinnedTrack, useSetAppDataByKey } from '../AppData/useUISettings';
16
18
  import { VideoTileContext } from '../hooks/useVideoTileLayout';
@@ -40,6 +42,7 @@ export const GridLayout = ({
40
42
  hide_metadata_on_tile = false,
41
43
  }: GridLayoutProps) => {
42
44
  const peerSharing = useHMSStore(selectPeerScreenSharing);
45
+ const whiteboard = useHMSStore(selectWhiteboard);
43
46
  const pinnedTrack = usePinnedTrack();
44
47
  const peers = useHMSStore(selectPeers);
45
48
  const localPeerRole = useHMSStore(selectLocalPeerRoleName);
@@ -53,9 +56,9 @@ export const GridLayout = ({
53
56
  )) ||
54
57
  pinnedTrack;
55
58
  const updatedPeers = useMemo(() => {
56
- // remove screenshare peer from active speaker sorting
57
- if (activeScreensharePeerId) {
58
- return peers.filter(peer => peer.id !== activeScreensharePeerId);
59
+ // remove screenshare/whiteboard peer from active speaker sorting
60
+ if (activeScreensharePeerId || whiteboard?.open) {
61
+ return peers.filter(peer => peer.id !== activeScreensharePeerId || peer.customerUserId !== whiteboard?.owner);
59
62
  }
60
63
  if (isInsetEnabled) {
61
64
  const isLocalPeerPinned = localPeerID === pinnedTrack?.peerId;
@@ -67,7 +70,16 @@ export const GridLayout = ({
67
70
  }
68
71
  }
69
72
  return peers;
70
- }, [isInsetEnabled, activeScreensharePeerId, localPeerRole, localPeerID, prominentRoles, peers, pinnedTrack]);
73
+ }, [
74
+ isInsetEnabled,
75
+ whiteboard,
76
+ activeScreensharePeerId,
77
+ localPeerRole,
78
+ localPeerID,
79
+ prominentRoles,
80
+ peers,
81
+ pinnedTrack,
82
+ ]);
71
83
  const vanillaStore = useHMSVanillaStore();
72
84
  const [sortedPeers, setSortedPeers] = useState(updatedPeers);
73
85
  const peersSorter = useMemo(() => new PeersSorter(vanillaStore), [vanillaStore]);
@@ -104,6 +116,17 @@ export const GridLayout = ({
104
116
  />
105
117
  </VideoTileContext.Provider>
106
118
  );
119
+ } else if (whiteboard?.open) {
120
+ return (
121
+ <VideoTileContext.Provider value={tileLayout}>
122
+ <WhiteboardLayout
123
+ peers={sortedPeers}
124
+ onPageSize={setPageSize}
125
+ onPageChange={setMainPage}
126
+ edgeToEdge={edge_to_edge}
127
+ />
128
+ </VideoTileContext.Provider>
129
+ );
107
130
  } else if (isRoleProminence) {
108
131
  return (
109
132
  <VideoTileContext.Provider value={tileLayout}>
@@ -0,0 +1,95 @@
1
+ import React, { useEffect, useMemo } from 'react';
2
+ import { useMedia } from 'react-use';
3
+ import { selectPeerByCondition, selectWhiteboard, useHMSStore, useWhiteboard } from '@100mslive/react-sdk';
4
+ import { Box } from '../../../Layout';
5
+ import { config as cssConfig } from '../../../Theme';
6
+ import { InsetTile } from '../InsetTile';
7
+ import { SecondaryTiles } from '../SecondaryTiles';
8
+ import { LayoutMode } from '../Settings/LayoutSettings';
9
+ import { LayoutProps } from './interface';
10
+ import { ProminenceLayout } from './ProminenceLayout';
11
+ // @ts-ignore: No implicit Any
12
+ import { useSetUiSettings } from '../AppData/useUISettings';
13
+ import { UI_SETTINGS } from '../../common/constants';
14
+
15
+ const WhiteboardEmbed = () => {
16
+ const isMobile = useMedia(cssConfig.media.md);
17
+ const { iframeRef } = useWhiteboard(isMobile);
18
+
19
+ return (
20
+ <Box
21
+ css={{
22
+ mx: '$8',
23
+ flex: '3 1 0',
24
+ '@lg': {
25
+ flex: '2 1 0',
26
+ display: 'flex',
27
+ alignItems: 'center',
28
+ },
29
+ }}
30
+ >
31
+ <iframe
32
+ title="Whiteboard View"
33
+ ref={iframeRef}
34
+ style={{
35
+ width: '100%',
36
+ height: '100%',
37
+ border: 0,
38
+ borderRadius: '0.75rem',
39
+ }}
40
+ allow="autoplay; clipboard-write;"
41
+ referrerPolicy="no-referrer"
42
+ />
43
+ </Box>
44
+ );
45
+ };
46
+
47
+ export const WhiteboardLayout = ({ peers, onPageChange, onPageSize, edgeToEdge }: LayoutProps) => {
48
+ const whiteboard = useHMSStore(selectWhiteboard);
49
+ const whiteboardOwner = useHMSStore(selectPeerByCondition(peer => peer.customerUserId === whiteboard?.owner));
50
+ const [layoutMode, setLayoutMode] = useSetUiSettings(UI_SETTINGS.layoutMode);
51
+ const isMobile = useMedia(cssConfig.media.md);
52
+ const hasSidebar = !isMobile && layoutMode === LayoutMode.SIDEBAR;
53
+ const secondaryPeers = useMemo(() => {
54
+ if (layoutMode === LayoutMode.SPOTLIGHT) {
55
+ return [];
56
+ }
57
+ if (isMobile || layoutMode === LayoutMode.SIDEBAR) {
58
+ return whiteboardOwner
59
+ ? [whiteboardOwner, ...peers.filter(p => p.id !== whiteboardOwner?.id)] //keep active sharing peer as first tile
60
+ : peers;
61
+ }
62
+ return peers.filter(p => p.id !== whiteboardOwner?.id);
63
+ }, [whiteboardOwner, peers, isMobile, layoutMode]);
64
+
65
+ useEffect(() => {
66
+ if (isMobile) {
67
+ setLayoutMode(LayoutMode.GALLERY);
68
+ return;
69
+ }
70
+ if (layoutMode === LayoutMode.SIDEBAR) {
71
+ return;
72
+ }
73
+ setLayoutMode(LayoutMode.SIDEBAR);
74
+ return () => {
75
+ // reset to gallery once whiteboard is stopped
76
+ setLayoutMode(LayoutMode.GALLERY);
77
+ };
78
+ }, [isMobile]); // eslint-disable-line react-hooks/exhaustive-deps
79
+
80
+ return (
81
+ <ProminenceLayout.Root edgeToEdge={edgeToEdge} hasSidebar={hasSidebar}>
82
+ <ProminenceLayout.ProminentSection>
83
+ <WhiteboardEmbed />
84
+ </ProminenceLayout.ProminentSection>
85
+ <SecondaryTiles
86
+ peers={secondaryPeers}
87
+ onPageChange={onPageChange}
88
+ onPageSize={onPageSize}
89
+ edgeToEdge={edgeToEdge}
90
+ hasSidebar={hasSidebar}
91
+ />
92
+ {layoutMode === LayoutMode.SPOTLIGHT && whiteboardOwner && <InsetTile peerId={whiteboardOwner?.id} />}
93
+ </ProminenceLayout.Root>
94
+ );
95
+ };
@@ -6,19 +6,27 @@ import { useScreenShare, useWhiteboard } from '@100mslive/react-sdk';
6
6
  * close existing screenshare or whiteboard when the other is started
7
7
  */
8
8
  export const useCloseScreenshareWhiteboard = () => {
9
- const { amIScreenSharing, toggleScreenShare } = useScreenShare();
9
+ const { amIScreenSharing, screenSharingPeerId, toggleScreenShare } = useScreenShare();
10
10
  const { isOwner: isWhiteboardOwner, toggle: toggleWhiteboard } = useWhiteboard();
11
- const prevScreenSharer = usePrevious(amIScreenSharing);
11
+ const prevScreenSharer = usePrevious(screenSharingPeerId);
12
12
  const prevWhiteboardOwner = usePrevious(isWhiteboardOwner);
13
13
 
14
14
  // if both screenshare and whiteboard are open, close the one that was open earlier
15
15
  useEffect(() => {
16
- if (isWhiteboardOwner && amIScreenSharing) {
17
- if (prevScreenSharer && !prevWhiteboardOwner) {
16
+ if (isWhiteboardOwner && screenSharingPeerId) {
17
+ if (prevScreenSharer && amIScreenSharing && !prevWhiteboardOwner) {
18
18
  toggleScreenShare?.();
19
19
  } else if (prevWhiteboardOwner && !prevScreenSharer) {
20
20
  toggleWhiteboard?.();
21
21
  }
22
22
  }
23
- }, [isWhiteboardOwner, amIScreenSharing, prevScreenSharer, prevWhiteboardOwner, toggleScreenShare, toggleWhiteboard]);
23
+ }, [
24
+ isWhiteboardOwner,
25
+ screenSharingPeerId,
26
+ amIScreenSharing,
27
+ prevScreenSharer,
28
+ prevWhiteboardOwner,
29
+ toggleScreenShare,
30
+ toggleWhiteboard,
31
+ ]);
24
32
  };