Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: equal prominence layout #1603

Merged
merged 2 commits into from
Aug 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/react-sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@ export type { usePDFShareResult } from './hooks/usePDFShare';
// helpers
export { throwErrorHandler } from './utils/commons';

export { getVideoTracksFromPeers as getPeersWithTiles } from './utils/layout';
// reexport everything from store so app can import everything directly from this
export * from '@100mslive/hms-video-store';
1 change: 1 addition & 0 deletions packages/roomkit-react/src/Pagination/StyledPagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const Chevron = styled('button', {
color: '$on_surface_low',
cursor: 'not-allowed',
},
backgroundColor: 'transparent',
display: 'flex',
});

Expand Down
152 changes: 152 additions & 0 deletions packages/roomkit-react/src/Prebuilt/components/EqualProminence.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import React, { useEffect, useState } from 'react';
import { useMeasure, useMedia } from 'react-use';
import {
getPeersWithTiles,
selectLocalPeer,
selectRemotePeers,
selectTracksMap,
useHMSStore,
useHMSVanillaStore,
} from '@100mslive/react-sdk';
import { Box, Flex } from '../../Layout';
import { config as cssConfig } from '../../Theme';
import { InsetTile } from '../layouts/InsetView';
import { Pagination } from './Pagination';
import VideoTile from './VideoTile';
import { useUISettings } from './AppData/useUISettings';
import { UI_SETTINGS } from '../common/constants';

const aspectRatioConfig = { default: [1 / 1, 4 / 3, 16 / 9], mobile: [1 / 1, 3 / 4, 9 / 16] };

export function EqualProminence() {
const peers = useHMSStore(selectRemotePeers);
const localPeer = useHMSStore(selectLocalPeer);
const vanillaStore = useHMSVanillaStore();
const isMobile = useMedia(cssConfig.media.md);
let maxTileCount = useUISettings(UI_SETTINGS.maxTileCount);
maxTileCount = isMobile ? Math.min(maxTileCount, 6) : maxTileCount;
const [pagesWithTiles, setPagesWithTiles] = useState([]);
const [page, setPage] = useState(0);
const [ref, { width, height }] = useMeasure();

useEffect(() => {
// currentPageIndex should not exceed pages length
if (page >= pagesWithTiles.length) {
setPage(0);
}
}, [pagesWithTiles.length, page]);

useEffect(() => {
if (width === 0 || height === 0) {
return;
}
const tracksMap = vanillaStore.getState(selectTracksMap);
const peersWithTiles = getPeersWithTiles(peers.length === 0 ? [localPeer] : peers, tracksMap, () => false);
const noOfPages = Math.ceil(peersWithTiles.length / maxTileCount);
let remaining = peersWithTiles.length;
let sliceStart = 0;
let pagesList = [];
// split into pages
for (let i = 0; i < noOfPages; i++) {
const count = Math.min(remaining, maxTileCount);
pagesList.push(peersWithTiles.slice(sliceStart, sliceStart + count));
remaining = remaining - count;
sliceStart += count;
}
// calculate dimesions for each page
for (const page of pagesList) {
const noOfTilesInPage = page.length;
let maxCols =
noOfTilesInPage > 2 && noOfTilesInPage < 9
? Math.ceil(noOfTilesInPage / 2)
: Math.ceil(Math.sqrt(noOfTilesInPage));
if (isMobile) {
maxCols = noOfTilesInPage < 4 ? 1 : Math.min(maxCols, 2);
}
let maxRows = Math.ceil(noOfTilesInPage / maxCols);
let index = 0;
// convert the current page to a matrix(grid)
const matrix = new Array(maxRows).fill(null).map((_, i) => {
const numCols = Math.min(maxCols, noOfTilesInPage - i * maxCols);
let rowElements = [];
for (let j = 0; j < numCols; j++) {
if (index < page.length) {
rowElements.push(page[index++]);
}
}
return rowElements;
});

const maxHeight = height - (maxRows - 1) * 8;
const maxRowHeight = maxHeight / matrix.length;
const aspectRatios = isMobile && noOfTilesInPage > 3 ? aspectRatioConfig.mobile : aspectRatioConfig.default;
// calculate height and width of each tile in a row
for (const row of matrix) {
let tileWidth = (width - (row.length - 1) * 8) / row.length;
let tileHeight = 0;
const calcHeights = aspectRatios.map(aR => tileWidth / aR);
for (const h of calcHeights) {
if (h < maxRowHeight) {
if (tileHeight < h) {
tileHeight = h;
}
}
}

// tileHeight is not calculated as it could be exceeding the max possible height
// find the max possible width instead
if (tileHeight === 0) {
tileHeight = maxRowHeight;
const calcWidths = aspectRatios.map(aR => tileHeight * aR);
tileWidth = 0;
for (const w of calcWidths) {
if (w < width) {
if (tileWidth < w) {
tileWidth = w;
}
}
}
}
for (let i = 0; i < row.length; i++) {
row[i].width = tileWidth;
row[i].height = tileHeight;
}
}
}
setPagesWithTiles(pagesList);
}, [width, height, maxTileCount, vanillaStore, peers, page, isMobile, localPeer]);

return (
<Flex direction="column" css={{ flex: '1 1 0', h: '100%', position: 'relative', minWidth: 0 }}>
<Box
ref={ref}
css={{
flex: '1 1 0',
gap: '$4',
display: 'flex',
placeContent: 'center',
alignItems: 'center',
justifyContent: 'center',
flexFlow: 'row wrap',
minHeight: 0,
}}
>
{pagesWithTiles[page]?.map(tile => {
return (
<VideoTile
key={tile.track?.id || tile.peer?.id}
width={tile.width}
height={tile.height}
peerId={tile.peer?.id}
trackId={tile.track?.id}
rootCSS={{ padding: 0 }}
objectFit="contain"
/>
);
})}
</Box>
{pagesWithTiles.length > 1 && <Pagination page={page} onPageChange={setPage} numPages={pagesWithTiles.length} />}
{peers.length > 0 && <InsetTile />}
</Flex>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,12 @@ export const IconButtonWithOptions = ({
onClick = () => {
return;
},
key = '',
}) => {
const bgCss = { backgroundColor: active ? '$transparent' : '$secondary_dim' };
const iconCss = { color: active ? '$on_surface_high' : '$on_primary_high' };
return (
<Flex>
<IconSection css={bgCss} onClick={onClick} key={key}>
<IconSection css={bgCss} onClick={onClick}>
<Tooltip disabled={!tooltipMessage} title={tooltipMessage}>
<Flex align="center" justify="center" css={iconCss}>
{icon}
Expand Down
8 changes: 4 additions & 4 deletions packages/roomkit-react/src/Prebuilt/components/Pagination.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import React from 'react';
import { ChevronLeftIcon, ChevronRightIcon } from '@100mslive/react-icons';
import { StyledPagination } from '../../Pagination';

export const Pagination = ({ page, setPage, numPages }) => {
export const Pagination = ({ page, onPageChange, numPages }) => {
const disableLeft = page === 0;
const disableRight = page === numPages - 1;
const nextPage = () => {
setPage(Math.min(page + 1, numPages - 1));
onPageChange(Math.min(page + 1, numPages - 1));
};
const prevPage = () => {
setPage(Math.max(page - 1, 0));
onPageChange(Math.max(page - 1, 0));
};
return (
<StyledPagination.Root>
Expand All @@ -18,7 +18,7 @@ export const Pagination = ({ page, setPage, numPages }) => {
</StyledPagination.Chevron>
<StyledPagination.Dots>
{[...Array(numPages)].map((_, i) => (
<StyledPagination.Dot key={i} active={page === i} onClick={() => setPage(i)} />
<StyledPagination.Dot key={i} active={page === i} onClick={() => onPageChange(i)} />
))}
</StyledPagination.Dots>
<StyledPagination.Chevron disabled={disableRight} onClick={nextPage}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const Tile = ({ peerId, width = '100%', height = '100%' }) => {
const isFullScreenSupported = screenfull.isEnabled;
const audioTrack = useHMSStore(selectScreenShareAudioByPeerID(peer?.id));
return (
<StyledVideoTile.Root css={{ width, height }} data-testid="screenshare_tile">
<StyledVideoTile.Root css={{ width, height, p: 0 }} data-testid="screenshare_tile">
{peer ? (
<StyledVideoTile.Container
transparentBg
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const List = ({ maxTileCount, peers, maxColCount, maxRowCount, includeScreenShar
: null}
</StyledVideoList.Container>
{!isHeadless && pagesWithTiles.length > 1 ? (
<Pagination page={page} setPage={setPage} numPages={pagesWithTiles.length} />
<Pagination page={page} onPageChange={setPage} numPages={pagesWithTiles.length} />
) : null}
</StyledVideoList.Root>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ const Conference = () => {
w: '100%',
flex: '1 1 0',
minHeight: 0,
px: '$10',
paddingBottom: 'env(safe-area-inset-bottom)',
}}
id="conferencing"
Expand Down
1 change: 0 additions & 1 deletion packages/roomkit-react/src/Prebuilt/layouts/EmbedView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,6 @@ const EmbedComponent = () => {
<Box
ref={iframeRef}
css={{
mx: '$8',
flex: '3 1 0',
'@lg': {
flex: '2 1 0',
Expand Down
18 changes: 7 additions & 11 deletions packages/roomkit-react/src/Prebuilt/layouts/InsetView.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import React, { Fragment, useEffect, useRef } from 'react';
import Draggable from 'react-draggable';
import { useMedia } from 'react-use';
import { selectAppData, selectLocalPeer, selectRemotePeers, selectRolesMap, useHMSStore } from '@100mslive/react-sdk';
import { selectLocalPeer, selectRemotePeers, selectRolesMap, useHMSStore } from '@100mslive/react-sdk';
import { FirstPersonDisplay } from '../components/FirstPersonDisplay';
import VideoTile from '../components/VideoTile';
import { Box, Flex } from '../../Layout';
import { config as cssConfig } from '../../Theme';
import { useRolePreference } from '../components/hooks/useFeatures';
import { APP_DATA } from '../common/constants';

const getAspectRatio = ({ roleMap, roleName, isMobile }) => {
const role = roleMap[roleName];
Expand Down Expand Up @@ -152,20 +151,18 @@ export function InsetView() {
);
}

const InsetTile = ({ isMobile, roleMap, isLandscape }) => {
export const InsetTile = () => {
const isMobile = useMedia(cssConfig.media.md);
const isLandscape = useMedia(cssConfig.media.ls);
const localPeer = useHMSStore(selectLocalPeer);
const sidepane = useHMSStore(selectAppData(APP_DATA.sidePane));
const aspectRatio = getAspectRatio({
roleMap,
roleName: localPeer.roleName,
isMobile,
});
const aspectRatio = isMobile ? 9 / 16 : 16 / 9;
let height = 180;
let width = height * aspectRatio;
if (isLandscape && width > 240) {
width = 240;
height = width / aspectRatio;
}

const nodeRef = useRef(null);

useEffect(() => {
Expand Down Expand Up @@ -195,8 +192,7 @@ const InsetTile = ({ isMobile, roleMap, isLandscape }) => {
css={{
position: 'absolute',
bottom: 0,
right: sidepane ? '$100' : '$10',
mr: sidepane ? '$14' : 0,
right: 0,
boxShadow: '0 0 8px 0 rgba(0,0,0,0.3)',
zIndex: 10,
aspectRatio: aspectRatio,
Expand Down
1 change: 0 additions & 1 deletion packages/roomkit-react/src/Prebuilt/layouts/PDFView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ export const PDFEmbedComponent = () => {
<Box
ref={iframeRef}
css={{
mx: '$8',
flex: '3 1 0',
'@lg': {
flex: '2 1 0',
Expand Down
12 changes: 6 additions & 6 deletions packages/roomkit-react/src/Prebuilt/layouts/SidePane.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,23 @@ const SidePane = ({ css = {} }) => {
return (
<Box
css={{
position: 'absolute',
w: '$100',
h: '100%',
p: '$10',
bg: '$surface_default',
r: '$1',
top: 0,
right: '$10',
zIndex: 10,
boxShadow: '$md',
ml: '$8',
position: 'relative',
...css,
'@lg': {
w: '100%',
h: '100%',
ml: 0,
right: 0,
position: 'fixed',
bottom: 0,
height: 'unset',
r: 0,
zIndex: 10,
...(css['@lg'] || {}),
},
}}
Expand Down
Loading