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

Sign up to get free protection for your applications and to get access to all the features.
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
  };