Skip to content

Commit

Permalink
feat(spa): add layout animation for participant tiles
Browse files Browse the repository at this point in the history
  • Loading branch information
Anapher committed Jun 12, 2021
1 parent fe45e60 commit fc76ba6
Show file tree
Hide file tree
Showing 12 changed files with 105 additions and 102 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,10 @@ export default function ClassConference() {
const [chatWidth, setChatWidth] = useState(CHAT_DEFAULT_WIDTH);

useEffect(() => {
let fixedDimensions: Size | undefined;
if (dimensions && dimensions.width !== undefined && dimensions.height !== undefined)
fixedDimensions = {
width: dimensions.width,
height: dimensions.height - 40 /** for chips and the arrow back */,
};
const fixedDimensions = dimensions && {
width: dimensions.width,
height: dimensions.height - 40 /** for chips and the arrow back */,
};

if (fixedDimensions) {
const computedSize = expandToBox(defaultContentRatio, fixedDimensions);
Expand All @@ -74,7 +72,7 @@ export default function ClassConference() {

setChatWidth(newChatWidth);
}
}, [dimensions.width, dimensions.height]);
}, [dimensions?.width, dimensions?.height]);

const chatContainer = useRef<HTMLDivElement>(null);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import React from 'react';
import ActiveParticipantsChips from './ActiveParticipantsChips';
import clsx from 'classnames';

export const ACTIVE_CHIPS_LAYOUT_HEIGHT = 40;

const useStyles = makeStyles((theme) => ({
root: {
display: 'flex',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ const useStyles = makeStyles({

type Props = {
width: number;
fixedParticipants?: Participant[];
participant?: Participant;
};

export default function ActiveParticipantsGrid({ width, fixedParticipants }: Props) {
export default function ActiveParticipantsGrid({ width, participant }: Props) {
const classes = useStyles();
const participants = useSomeParticipants(3, {
includedParticipants: fixedParticipants,
const participants = useSomeParticipants(1, {
includedParticipants: participant ? [participant] : undefined,
activeOnly: true,
webcamOnly: true,
});
Expand All @@ -39,10 +39,12 @@ export default function ActiveParticipantsGrid({ width, fixedParticipants }: Pro
});
}, [width]);

console.log(participants, 'participants');

return (
<div className={classes.root}>
{participants.length > 0 && (
<div style={{ ...mainTileSize, marginBottom: 8 }}>
<div style={{ ...mainTileSize, marginBottom: 8 }} key={participants[0].id}>
<ParticipantTile {...mainTileSize} participant={participants[0]} />
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export default function ParticipantTile({ className, participant, width, height

return (
<>
<div className={clsx(classes.root, className)}>
<motion.div layout layoutId={participant.id} className={clsx(classes.root, className)}>
<RenderConsumerVideo consumer={consumer} height={height} width={width} className={classes.video} />
<motion.div style={{ borderWidth: audioBorder }} className={classes.volumeBorder} />

Expand All @@ -103,7 +103,7 @@ export default function ParticipantTile({ className, participant, width, height
</IconButton>
</div>
)}
</div>
</motion.div>
<ParticipantContextMenuPopper
open={contextMenuOpen}
onClose={handleCloseContextMenu}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { makeStyles } from '@material-ui/core';
import React, { useEffect } from 'react';
import { makeStyles, Portal } from '@material-ui/core';
import clsx from 'classnames';
import React, { useContext } from 'react';
import ConferenceLayoutContext from 'src/features/conference/conference-layout-context';
import { Participant } from 'src/features/conference/types';
import { Size } from 'src/types';
import { expandToBox, maxWidth } from '../calculations';
import ActiveChipsLayout from './ActiveChipsLayout';
import ActiveChipsLayout, { ACTIVE_CHIPS_LAYOUT_HEIGHT } from './ActiveChipsLayout';
import ActiveParticipantsGrid from './ActiveParticipantsGrid';
import PresentationSceneParticipants from './PresentationSceneParticipants';
import clsx from 'classnames';

const useStyles = makeStyles((theme) => ({
container: {
Expand All @@ -25,27 +27,37 @@ const useStyles = makeStyles((theme) => ({
},
}));

/**
* max size: the most important thing is that the content has a maximum size.
* If there is no space for participant tiles in the scene, use the place over the chat
*
* default: place the participant tiles to the left or bottom of the scene and scale the content
* if required
*/
type PresentationSceneVariant = 'max-size' | 'default';

export type PresentationSceneProps = {
variant?: PresentationSceneVariant;

className?: string;
dimensions: Size;

contentRatio: Size;
maxContentWidth?: number;

showParticipants?: boolean;
fixedParticipants?: Participant[];
fixedParticipants?: Participant[] | Participant;

participantTileWidth?: number;
participantTileHeight?: number;

maxOverlayFactor?: number;

render: (size: Size, style: React.CSSProperties) => React.ReactNode;

canShowParticipantsWithoutResize?: (canShow: boolean) => void;
};

export default function PresentationScene({
variant = 'default',
className,
contentRatio,
maxContentWidth,
Expand All @@ -56,21 +68,17 @@ export default function PresentationScene({
participantTileWidth = 16 * 18,
participantTileHeight = 9 * 18,
maxOverlayFactor = 0.3,
canShowParticipantsWithoutResize,
}: PresentationSceneProps) {
const classes = useStyles();

dimensions = { ...dimensions, height: dimensions.height - 40 };
dimensions = { ...dimensions, height: dimensions.height - ACTIVE_CHIPS_LAYOUT_HEIGHT };

// measure
let computedSize = expandToBox(contentRatio, dimensions);
if (maxContentWidth) computedSize = maxWidth(computedSize, maxContentWidth);

let participantsPlace: 'bottom' | 'right' | undefined;

console.log(dimensions.width);
console.log(computedSize.width);

// compute the vertical/horizontal margin if we would use the full size content
const marginBottom = dimensions.height - computedSize.height;
const marginRight = dimensions.width - computedSize.width;
Expand All @@ -95,9 +103,24 @@ export default function PresentationScene({
}
}

useEffect(() => {
if (canShowParticipantsWithoutResize) canShowParticipantsWithoutResize(newDimensions === undefined);
}, [newDimensions === undefined]);
if (fixedParticipants !== undefined && !Array.isArray(fixedParticipants)) {
fixedParticipants = [fixedParticipants];
}

console.log('newDimensions', newDimensions);

if (variant === 'max-size' && newDimensions) {
return (
<ActiveChipsLayout className={clsx(className, classes.container)}>
{showParticipants && (
<PortalWithParticipant
participant={fixedParticipants && fixedParticipants.length > 0 ? fixedParticipants[0] : undefined}
/>
)}
{render(computedSize, {})}
</ActiveChipsLayout>
);
}

// arrange
if (newDimensions && showParticipants) {
Expand All @@ -119,3 +142,17 @@ export default function PresentationScene({
</ActiveChipsLayout>
);
}

type PortalWithParticipantProps = {
participant?: Participant;
};

function PortalWithParticipant({ participant }: PortalWithParticipantProps) {
const context = useContext(ConferenceLayoutContext);

return (
<Portal container={context.chatContainer}>
<ActiveParticipantsGrid width={context.chatWidth} participant={participant} />
</Portal>
);
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { makeStyles } from '@material-ui/core';
import clsx from 'classnames';
import { motion } from 'framer-motion';
import React from 'react';
import { Participant } from 'src/features/conference/types';
import useSomeParticipants from '../useSomeParticipants';
Expand Down Expand Up @@ -44,14 +43,9 @@ export default function PresentationSceneParticipants({ location, tileWidth, til
return (
<div className={clsx({ [classes.rootBottom]: location === 'bottom', [classes.rootRight]: location === 'right' })}>
{participants.map((x, i) => (
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
key={x.id}
style={{ width: tileWidth, height: tileHeight, marginLeft: i === 0 ? 8 : 0 }}
>
<div key={x.id} style={{ width: tileWidth, height: tileHeight, marginLeft: i === 0 ? 8 : 0 }}>
<ParticipantTile participant={x} width={tileWidth} height={tileHeight} />
</motion.div>
</div>
))}
</div>
);
Expand Down
13 changes: 2 additions & 11 deletions src/Web/WebSPA/Client/src/features/scenes/components/SceneView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import MediaControls from 'src/features/media/components/MediaControls';
import MediaControlsContext, { MediaControlsContextType } from 'src/features/media/media-controls-context';
import useMyParticipantId from 'src/hooks/useMyParticipantId';
import useThrottledResizeObserver from 'src/hooks/useThrottledResizeObserver';
import { Size } from 'src/types';
import presenters from '../scene-presenter-registry';
import { selectSceneStack } from '../selectors';
import { Scene } from '../types';
Expand Down Expand Up @@ -52,10 +51,6 @@ export default function SceneView() {
const classes = useStyles();
const [contentRef, dimensions] = useThrottledResizeObserver(100);

let fixedDimensions: Size | undefined;
if (dimensions && dimensions.width !== undefined && dimensions.height !== undefined)
fixedDimensions = { width: dimensions.width, height: dimensions.height };

const sceneStack = useSelector(selectSceneStack);

const [showControls, setShowControls] = useState(true);
Expand Down Expand Up @@ -120,12 +115,8 @@ export default function SceneView() {
<MediaControlsContext.Provider value={mediaControlsContextValue}>
<div className={classes.root} ref={contentRef} onMouseMove={handleMouseMove} id="scene-view">
<AnimateSharedLayout>
{fixedDimensions?.width !== undefined && fixedDimensions?.height !== undefined && sceneStack ? (
<SceneSelector
className={classes.currentScene}
dimensions={fixedDimensions}
sceneStack={sceneStack}
/>
{dimensions && sceneStack ? (
<SceneSelector className={classes.currentScene} dimensions={dimensions} sceneStack={sceneStack} />
) : null}
</AnimateSharedLayout>
<MediaControls className={classes.mediaControls} show={showControls} leftActionsRef={mediaLeftActionsRef} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export default function RenderActiveSpeaker({ className, dimensions }: RenderSce
return (
<div className={clsx(className, classes.root, activeParticipants.length === 1 && classes.rootCenter)}>
<div style={{ margin: 8, ...size }}>
<ParticipantTile {...size} participant={activeParticipants[0]} />
<ParticipantTile key={activeParticipants[0].id} {...size} participant={activeParticipants[0]} />
</div>
<div style={{ display: 'flex', marginTop: 8 }}>
{activeParticipants.slice(1).map((participant, i) => (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { makeStyles } from '@material-ui/core';
import { motion } from 'framer-motion';
import clsx from 'classnames';
import _ from 'lodash';
import { Participant } from 'src/features/conference/types';
import { generateGrid } from '../../calculations';
import ParticipantTile from '../ParticipantTile';
import clsx from 'classnames';

const useStyles = makeStyles({
root: {
Expand Down Expand Up @@ -55,14 +54,9 @@ export default function RenderGrid({ participants, className, spacing = 8, itemM
.take(grid.itemsPerRow)
.value()
.map((x, pi) => (
<motion.div
layout
layoutId={x.id}
key={x.id}
style={{ ...grid.itemSize, marginLeft: pi === 0 ? 0 : spacing }}
>
<div key={x.id} style={{ ...grid.itemSize, marginLeft: pi === 0 ? 0 : spacing }}>
<ParticipantTile participant={x} {...grid.itemSize} />
</motion.div>
</div>
))}
</div>
))}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Consumer } from 'mediasoup-client/lib/Consumer';
import React from 'react';
import { useSelector } from 'react-redux';
import RenderConsumerVideo from 'src/components/RenderConsumerVideo';
Expand All @@ -7,19 +8,30 @@ import useConsumer from 'src/store/webrtc/hooks/useConsumer';
import { Size } from 'src/types';
import { selectSceneOptions } from '../../selectors';
import { RenderSceneProps, ScreenShareScene } from '../../types';
import PresentationSceneMaxSize from '../PresentationSceneMaxSize';
import PresentationScene from '../PresentationScene';

const defaultVideoSize: Size = { width: 1920, height: 1080 };

function getVideoSize(consumer: Consumer | null): Size {
const settings = consumer?.track.getSettings();
if (typeof settings?.width === 'number' && typeof settings?.height === 'number') {
return { width: settings.width, height: settings.height };
}

return defaultVideoSize;
}

export default function ScreenShare({ className, dimensions, scene }: RenderSceneProps<ScreenShareScene>) {
const { participantId } = scene;

const videoSize: Size = { width: 1920, height: 1080 };
const consumer = useConsumer(participantId, 'screen');
const videoSize = getVideoSize(consumer);

const participant = useSelector((state: RootState) => selectParticipant(state, participantId));
const sceneOptions = useSelector(selectSceneOptions);

return (
<PresentationSceneMaxSize
<PresentationScene
variant="max-size"
className={className}
maxOverlayFactor={sceneOptions?.overlayScene ? 0.75 : 0}
fixedParticipants={participant && [participant]}
Expand Down
Loading

0 comments on commit fc76ba6

Please sign in to comment.