Update the grid layout whenever someone screenshares (uses participant bar)
This commit is contained in:
parent
209b9bd72e
commit
22b4afbb34
|
|
@ -1,13 +1,54 @@
|
||||||
import React from 'react';
|
import React, { useMemo } from 'react';
|
||||||
|
import ExpiryTimer from '@custom/shared/components/ExpiryTimer';
|
||||||
|
import { useCallState } from '@custom/shared/contexts/CallProvider';
|
||||||
|
import { useCallUI } from '@custom/shared/hooks/useCallUI';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import App from '@custom/basic-call/components/App';
|
|
||||||
import { ChatProvider } from '../../contexts/ChatProvider';
|
import { ChatProvider } from '../../contexts/ChatProvider';
|
||||||
|
import Room from '../Call/Room';
|
||||||
|
import { Asides } from './Asides';
|
||||||
|
import { Modals } from './Modals';
|
||||||
|
|
||||||
// Extend our basic call app component with the chat context
|
export const App = ({ customComponentForState }) => {
|
||||||
export const CustomApp = () => (
|
const { roomExp, state } = useCallState();
|
||||||
<ChatProvider>
|
|
||||||
<App />
|
|
||||||
</ChatProvider>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default CustomApp;
|
const componentForState = useCallUI({
|
||||||
|
state,
|
||||||
|
room: <Room />,
|
||||||
|
...customComponentForState,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Memoize children to avoid unnecassary renders from HOC
|
||||||
|
return useMemo(
|
||||||
|
() => (
|
||||||
|
<>
|
||||||
|
<ChatProvider>
|
||||||
|
{roomExp && <ExpiryTimer expiry={roomExp} />}
|
||||||
|
<div className="app">
|
||||||
|
{componentForState()}
|
||||||
|
<Modals />
|
||||||
|
<Asides />
|
||||||
|
<style jsx>{`
|
||||||
|
color: white;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.loader {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
</ChatProvider>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
[componentForState, roomExp]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
App.propTypes = {
|
||||||
|
customComponentForState: PropTypes.any,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
||||||
|
|
@ -1,9 +1,13 @@
|
||||||
import React, { useState, useMemo, useEffect, useRef } from 'react';
|
import React, { useState, useMemo, useEffect, useRef } from 'react';
|
||||||
|
import ParticipantBar from '@custom/shared/components/ParticipantBar';
|
||||||
import Tile from '@custom/shared/components/Tile';
|
import Tile from '@custom/shared/components/Tile';
|
||||||
import { DEFAULT_ASPECT_RATIO } from '@custom/shared/constants';
|
import { DEFAULT_ASPECT_RATIO } from '@custom/shared/constants';
|
||||||
|
import { useCallState } from '@custom/shared/contexts/CallProvider';
|
||||||
import { useParticipants } from '@custom/shared/contexts/ParticipantsProvider';
|
import { useParticipants } from '@custom/shared/contexts/ParticipantsProvider';
|
||||||
import { useDeepCompareMemo } from 'use-deep-compare';
|
import { useDeepCompareMemo } from 'use-deep-compare';
|
||||||
|
|
||||||
|
const SIDEBAR_WIDTH = 186;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic unpaginated video tile grid, scaled by aspect ratio
|
* Basic unpaginated video tile grid, scaled by aspect ratio
|
||||||
*
|
*
|
||||||
|
|
@ -18,7 +22,8 @@ import { useDeepCompareMemo } from 'use-deep-compare';
|
||||||
export const VideoGrid = React.memo(
|
export const VideoGrid = React.memo(
|
||||||
() => {
|
() => {
|
||||||
const containerRef = useRef();
|
const containerRef = useRef();
|
||||||
const { allParticipants } = useParticipants();
|
const { allParticipants, participants, screens, localParticipant } = useParticipants();
|
||||||
|
const { showLocalVideo } = useCallState();
|
||||||
const [dimensions, setDimensions] = useState({
|
const [dimensions, setDimensions] = useState({
|
||||||
width: 1,
|
width: 1,
|
||||||
height: 1,
|
height: 1,
|
||||||
|
|
@ -45,10 +50,12 @@ export const VideoGrid = React.memo(
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const hasScreenshares = useMemo(() => screens.length > 0, [screens]);
|
||||||
|
|
||||||
// Basic brute-force packing algo
|
// Basic brute-force packing algo
|
||||||
const layout = useMemo(() => {
|
const layout = useMemo(() => {
|
||||||
const aspectRatio = DEFAULT_ASPECT_RATIO;
|
const aspectRatio = DEFAULT_ASPECT_RATIO;
|
||||||
const tileCount = allParticipants.length || 0;
|
const tileCount = hasScreenshares ? screens.length : participants.length || 0;
|
||||||
const w = dimensions.width;
|
const w = dimensions.width;
|
||||||
const h = dimensions.height;
|
const h = dimensions.height;
|
||||||
|
|
||||||
|
|
@ -87,29 +94,73 @@ export const VideoGrid = React.memo(
|
||||||
}
|
}
|
||||||
|
|
||||||
return bestLayout;
|
return bestLayout;
|
||||||
}, [dimensions, allParticipants]);
|
}, [hasScreenshares, screens.length, participants.length, dimensions.width, dimensions.height]);
|
||||||
|
|
||||||
|
const otherParticipants = useMemo(
|
||||||
|
() => participants.filter(({ isLocal }) => !isLocal),
|
||||||
|
[participants]
|
||||||
|
);
|
||||||
|
|
||||||
|
const fixedItems = useMemo(() => {
|
||||||
|
const items = [];
|
||||||
|
if (showLocalVideo) {
|
||||||
|
items.push(localParticipant);
|
||||||
|
}
|
||||||
|
if (hasScreenshares && otherParticipants.length > 0) {
|
||||||
|
items.push(otherParticipants[0]);
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}, [hasScreenshares, localParticipant, otherParticipants, showLocalVideo]);
|
||||||
|
|
||||||
|
const otherItems = useMemo(() => {
|
||||||
|
if (otherParticipants.length > 1) {
|
||||||
|
return otherParticipants.slice(hasScreenshares ? 1 : 0);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}, [hasScreenshares, otherParticipants]);
|
||||||
|
|
||||||
// Memoize our tile list to avoid unnecassary re-renders
|
// Memoize our tile list to avoid unnecassary re-renders
|
||||||
const tiles = useDeepCompareMemo(
|
const tiles = useDeepCompareMemo(
|
||||||
() =>
|
() =>
|
||||||
allParticipants.map((p) => (
|
participants.map((p) => (
|
||||||
<Tile
|
<Tile
|
||||||
participant={p}
|
participant={p}
|
||||||
key={p.id}
|
key={p.id}
|
||||||
mirrored={!p.isScreenshare}
|
mirrored
|
||||||
style={{ maxWidth: layout.width, maxHeight: layout.height }}
|
style={{ maxWidth: layout.width, maxHeight: layout.height }}
|
||||||
/>
|
/>
|
||||||
)),
|
)),
|
||||||
[layout, allParticipants]
|
[layout, participants]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!allParticipants.length) {
|
const screenShareTiles = useDeepCompareMemo(
|
||||||
return null;
|
() =>
|
||||||
}
|
screens.map((p) => (
|
||||||
|
<Tile
|
||||||
|
participant={p}
|
||||||
|
key={p.id}
|
||||||
|
mirrored={false}
|
||||||
|
style={{ maxWidth: layout.width, maxHeight: layout.height }}
|
||||||
|
/>
|
||||||
|
)),
|
||||||
|
[layout, screens]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!participants.length) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="video-grid" ref={containerRef}>
|
<div className="video-grid" ref={containerRef}>
|
||||||
<div className="tiles">{tiles}</div>
|
<div className="tiles">
|
||||||
|
{screenShareTiles}
|
||||||
|
{!hasScreenshares && tiles}
|
||||||
|
</div>
|
||||||
|
{hasScreenshares && (
|
||||||
|
<ParticipantBar
|
||||||
|
fixed={fixedItems}
|
||||||
|
others={otherItems}
|
||||||
|
width={SIDEBAR_WIDTH}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<style jsx>{`
|
<style jsx>{`
|
||||||
.video-grid {
|
.video-grid {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import GlobalStyle from '@custom/shared/components/GlobalStyle';
|
import GlobalStyle from '@custom/shared/components/GlobalStyle';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { CustomApp } from '../components/App/App';
|
import { App as CustomApp } from '../components/App/App';
|
||||||
import ChatAside from '../components/Call/ChatAside';
|
import ChatAside from '../components/Call/ChatAside';
|
||||||
import Tray from '../components/Tray';
|
import Tray from '../components/Tray';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import React, {
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { Tile } from '@custom/shared/components/Tile';
|
import Tile from '@custom/shared/components/Tile';
|
||||||
import { DEFAULT_ASPECT_RATIO } from '@custom/shared/constants';
|
import { DEFAULT_ASPECT_RATIO } from '@custom/shared/constants';
|
||||||
import { useCallState } from '@custom/shared/contexts/CallProvider';
|
import { useCallState } from '@custom/shared/contexts/CallProvider';
|
||||||
import { useParticipants } from '@custom/shared/contexts/ParticipantsProvider';
|
import { useParticipants } from '@custom/shared/contexts/ParticipantsProvider';
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue