Skip to content

Commit

Permalink
Add spotlights for screen shares and extra videos
Browse files Browse the repository at this point in the history
  • Loading branch information
havfo committed Mar 13, 2024
1 parent 301ea03 commit 3cfb275
Show file tree
Hide file tree
Showing 5 changed files with 300 additions and 114 deletions.
55 changes: 26 additions & 29 deletions src/components/democratic/Democratic.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
import { styled, useTheme } from '@mui/material/styles';
import { useEffect, useRef, useState } from 'react';
import { useAppSelector } from '../../store/hooks';
import { speakerVideoConsumersSelector, videoBoxesSelector } from '../../store/selectors';
import { spotlightWebcamConsumerSelector, videoBoxesSelector } from '../../store/selectors';
import Me from '../me/Me';
import VideoConsumer from '../videoconsumer/VideoConsumer';
import Peers from '../peers/Peers';
import ControlButtonsBar from '../controlbuttonsbar/ControlButtonsBar';
import { Box } from '@mui/material';

type DemocraticDivProps = {
headless: number;
horizontal: number;
spotlights: number;
};

const DemocraticDiv = styled('div')<DemocraticDivProps>(({
const DemocraticDiv = styled(Box)<DemocraticDivProps>(({
theme,
headless,
horizontal,
spotlights,
}) => ({
width: '100%',
height: headless ? 'calc(100% - 8px)' : 'calc(100% - 64px)',
width: horizontal ? '25%' : '100%',
height: headless ? `calc(${horizontal || !spotlights ? '100' : '25'}% - 8px)` : `calc(${horizontal || !spotlights ? '100' : '25'}% - 64px)`,
marginBottom: headless ? 0 : 64,
display: 'flex',
flexDirection: 'row',
Expand All @@ -28,7 +32,17 @@ const DemocraticDiv = styled('div')<DemocraticDivProps>(({
alignContent: 'center',
}));

const Democratic = (): JSX.Element => {
type DemocraticProps = {
windowSize: number;
horizontal: boolean;
spotlights: boolean;
};

const Democratic = ({
windowSize,
horizontal,
spotlights,
}: DemocraticProps): JSX.Element => {
const theme = useTheme();
const peersRef = useRef<HTMLDivElement>(null);
const aspectRatio = useAppSelector((state) => state.settings.aspectRatio);
Expand All @@ -37,10 +51,9 @@ const Democratic = (): JSX.Element => {
const chatOpen = useAppSelector((state) => state.ui.chatOpen);
const participantListOpen = useAppSelector((state) => state.ui.participantListOpen);
const boxes = useAppSelector(videoBoxesSelector);
const speakerVideoConsumers = useAppSelector(speakerVideoConsumersSelector);
const webcamConsumers = useAppSelector(spotlightWebcamConsumerSelector);
const currentSession = useAppSelector((state) => state.me.sessionId);
const headless = useAppSelector((state) => state.room.headless);
const [ windowSize, setWindowSize ] = useState(0);
const [ dimensions, setDimensions ] = useState<Record<'peerWidth' | 'peerHeight', number>>({ peerWidth: 320, peerHeight: 240 });

const updateDimensions = (): void => {
Expand Down Expand Up @@ -87,23 +100,6 @@ const Democratic = (): JSX.Element => {
}
};

useEffect(() => {
let timeoutId: ReturnType<typeof setTimeout> | undefined;

const resizeListener = () => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => setWindowSize(window.innerWidth + window.innerHeight), 50);
};

window.addEventListener('resize', resizeListener);
updateDimensions();

return () => {
window.removeEventListener('resize', resizeListener);
clearTimeout(timeoutId);
};
}, []);

useEffect(() => updateDimensions(), [
boxes,
windowSize,
Expand All @@ -112,7 +108,9 @@ const Democratic = (): JSX.Element => {
participantListOpen,
verticalDivide,
currentSession,
speakerVideoConsumers
webcamConsumers,
horizontal,
spotlights
]);

const style = {
Expand All @@ -121,10 +119,9 @@ const Democratic = (): JSX.Element => {
};

return (
<DemocraticDiv ref={peersRef} headless={headless ? 1 : 0}>
<ControlButtonsBar />
<DemocraticDiv ref={peersRef} headless={headless ? 1 : 0} horizontal={horizontal ? 1 : 0} spotlights={spotlights ? 1 : 0}>
<Me style={style} />
{ speakerVideoConsumers.map((consumer) => (
{ webcamConsumers.map((consumer) => (
<VideoConsumer
key={consumer.id}
consumer={consumer}
Expand Down
64 changes: 59 additions & 5 deletions src/components/maincontent/MainContent.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { memo } from 'react';
import { memo, useEffect, useRef, useState } from 'react';
import { styled } from '@mui/material/styles';
import { Box, Paper } from '@mui/material';
import ParticipantList from '../participantlist/ParticipantList';
import Democratic from '../democratic/Democratic';
import Chat from '../chat/Chat';
import { useAppSelector } from '../../store/hooks';
import { isMobileSelector } from '../../store/selectors';
import { isMobileSelector, selectedVideoBoxesSelector, videoBoxesSelector } from '../../store/selectors';
import Spotlights from '../spotlights/Spotlights';
import ControlButtonsBar from '../controlbuttonsbar/ControlButtonsBar';

type WrapperContainerProps = {
headless: number;
Expand All @@ -15,13 +17,28 @@ const WrapperContainer = styled(Box)<WrapperContainerProps>(({ theme, headless }
width: 'calc(100% - 8px)',
height: headless ? 'calc(100% - 8px)' : 'calc(100% - 52px)',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
marginLeft: theme.spacing(0.5),
marginRight: theme.spacing(0.5),
marginBottom: theme.spacing(0.5),
marginTop: headless ? theme.spacing(0.5) : 48,
gap: theme.spacing(0.5),
}));

type MainContainerProps = {
horizontal: number;
};

const MainContainer = styled(Box)<MainContainerProps>(({ theme, horizontal }) => ({
width: '100%',
height: '100%',
display: 'flex',
flexDirection: horizontal ? 'row' : 'column',
gap: theme.spacing(0.5),
overflow: 'auto',
}));

interface SideContentProps {
verticaldivide?: number;
dynamicwidth?: number;
Expand Down Expand Up @@ -94,20 +111,57 @@ const SideContainer = styled(Paper)<SideContainerProps>(({ theme, height, width
}));

const MainContent = (): JSX.Element => {
const [ windowSize, setWindowSize ] = useState(0);
const [ horizontal, setHorizontal ] = useState(true);

const mainContainer = useRef<HTMLDivElement>(null);

const isMobile = useAppSelector(isMobileSelector);
const aspectRatio = useAppSelector((state) => state.settings.aspectRatio);
const chatOpen = useAppSelector((state) => state.ui.chatOpen);
const participantListOpen = useAppSelector((state) => state.ui.participantListOpen);
const eitherOpen = chatOpen || participantListOpen;
const bothOpen = chatOpen && participantListOpen;
const verticalDivide = useAppSelector((state) => state.settings.verticalDivide);
const dynamicWidth = useAppSelector((state) => state.settings.dynamicWidth);
const headless = useAppSelector((state) => state.room.headless);
const spotlightsVisible = useAppSelector(selectedVideoBoxesSelector) > 0;
const videosVisible = useAppSelector(videoBoxesSelector) > 0;

const height = (chatOpen && participantListOpen) && verticalDivide ? '50%' : '100%';

useEffect(() => {
if (!mainContainer.current) return;

let timeoutId: ReturnType<typeof setTimeout> | undefined;

const resizeListener = (entries: ResizeObserverEntry[]) => {
clearTimeout(timeoutId);

const { contentRect: { width: mainWidth, height: mainHeight } } = entries[0];

timeoutId = setTimeout(() => {
setHorizontal((mainWidth / mainHeight) > aspectRatio);
setWindowSize(mainWidth + mainHeight);
}, 100);
};

const resizeObserver = new ResizeObserver(resizeListener);

resizeObserver.observe(mainContainer.current);

return () => {
resizeObserver.disconnect();
};
}, []);

return (
<WrapperContainer headless={headless ? 1 : 0}>
<Democratic />
<WrapperContainer headless={headless ? 1 : 0} ref={mainContainer}>
<ControlButtonsBar />
<MainContainer horizontal={horizontal ? 1 : 0} >
{ spotlightsVisible && <Spotlights windowSize={windowSize} horizontal={horizontal} videos={videosVisible} /> }
{ videosVisible && <Democratic windowSize={windowSize} horizontal={spotlightsVisible && horizontal} spotlights={spotlightsVisible} /> }
</MainContainer>
{ !isMobile && eitherOpen &&
<SideContent
verticaldivide={verticalDivide ? 1 : 0}
Expand All @@ -122,4 +176,4 @@ const MainContent = (): JSX.Element => {
);
};

export default memo(MainContent);
export default memo(MainContent);
49 changes: 0 additions & 49 deletions src/components/me/Me.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import React from 'react';
import { useAppSelector, useIsActiveSpeaker } from '../../store/hooks';
import { isMobileSelector } from '../../store/selectors';
import ScreenshareButton from '../controlbuttons/ScreenshareButton';
import DisplayName from '../displayname/DisplayName';
import MediaControls from '../mediacontrols/MediaControls';
import UnmuteAlert from '../unmutealert/UnmuteAlert';
import VideoBox from '../videobox/VideoBox';
import VideoView from '../videoview/VideoView';
import Volume from '../volume/Volume';
import ExtraVideoButton from '../controlbuttons/ExtraVideoButton';

interface MeProps {
style: Record<'width' | 'height', number>
Expand All @@ -23,8 +20,6 @@ const Me = ({ style }: MeProps): React.JSX.Element => {
const isMobile = useAppSelector(isMobileSelector);
const micEnabled = useAppSelector((state) => state.me.micEnabled);
const webcamEnabled = useAppSelector((state) => state.me.webcamEnabled);
const screenEnabled = useAppSelector((state) => state.me.screenEnabled);
const extraVideoEnabled = useAppSelector((state) => state.me.extraVideoEnabled);

return (
<>
Expand All @@ -41,50 +36,6 @@ const Me = ({ style }: MeProps): React.JSX.Element => {
<DisplayName disabled={false} displayName={displayName} isMe />
</VideoBox>
)}
{ screenEnabled && (
<VideoBox
activeSpeaker={isActiveSpeaker}
order={2}
width={style.width}
height={style.height}
>
<VideoView source='screen' contain />
<DisplayName disabled={false} displayName={displayName} isMe />
<MediaControls
orientation='vertical'
horizontalPlacement='right'
verticalPlacement='center'
>
<ScreenshareButton
onColor='default'
offColor='error'
disabledColor='default'
/>
</MediaControls>
</VideoBox>
)}
{ extraVideoEnabled &&
<VideoBox
activeSpeaker={isActiveSpeaker}
order={3}
width={style.width}
height={style.height}
>
<VideoView source='extravideo' />
<DisplayName disabled={false} displayName={displayName} isMe />
<MediaControls
orientation='vertical'
horizontalPlacement='right'
verticalPlacement='center'
>
<ExtraVideoButton
onColor='default'
offColor='error'
disabledColor='default'
/>
</MediaControls>
</VideoBox>
}
</>
);
};
Expand Down
Loading

0 comments on commit 3cfb275

Please sign in to comment.