Fix issue with screen share
This commit is contained in:
parent
1ae4de54f2
commit
3d7ee93fb3
|
|
@ -1,12 +1,17 @@
|
||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { useParticipants } from '@custom/shared/contexts/ParticipantsProvider';
|
import { useParticipants } from '@custom/shared/contexts/ParticipantsProvider';
|
||||||
import { useUIState, VIEW_MODE_SPEAKER } from '@custom/shared/contexts/UIStateProvider';
|
import { useUIState, VIEW_MODE_SPEAKER } from '@custom/shared/contexts/UIStateProvider';
|
||||||
import { GridView } from '../GridView/GridView';
|
import { GridView } from '../GridView/GridView';
|
||||||
import { SpeakerView } from '../SpeakerView';
|
import { SpeakerView } from '../SpeakerView';
|
||||||
|
|
||||||
export const VideoView = () => {
|
export const VideoView = () => {
|
||||||
const { viewMode } = useUIState();
|
const { viewMode, setIsShowingScreenshare } = useUIState();
|
||||||
const { participants } = useParticipants();
|
const { participants, screens } = useParticipants();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const hasScreens = screens.length > 0;
|
||||||
|
setIsShowingScreenshare(hasScreens);
|
||||||
|
}, [screens, setIsShowingScreenshare]);
|
||||||
|
|
||||||
if (!participants.length) return null;
|
if (!participants.length) return null;
|
||||||
return viewMode === VIEW_MODE_SPEAKER ? <SpeakerView />: <GridView />;
|
return viewMode === VIEW_MODE_SPEAKER ? <SpeakerView />: <GridView />;
|
||||||
|
|
|
||||||
|
|
@ -295,25 +295,25 @@ export const GridView = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={gridRef} className="grid">
|
<div ref={gridRef} className="grid">
|
||||||
<Button
|
{(pages > 1 && page > 1) && (
|
||||||
className="page-button prev"
|
<Button
|
||||||
disabled={!(pages > 1 && page > 1)}
|
className="page-button prev"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handlePrevClick}
|
onClick={handlePrevClick}
|
||||||
>
|
>
|
||||||
<IconArrow />
|
<IconArrow />
|
||||||
</Button>
|
</Button>
|
||||||
|
)}
|
||||||
<div className="tiles">{tiles}</div>
|
<div className="tiles">{tiles}</div>
|
||||||
|
{(pages > 1 && page < pages) && (
|
||||||
<Button
|
<Button
|
||||||
className="page-button next"
|
className="page-button next"
|
||||||
disabled={!(pages > 1 && page < pages)}
|
type="button"
|
||||||
type="button"
|
onClick={handleNextClick}
|
||||||
onClick={handleNextClick}
|
>
|
||||||
>
|
<IconArrow />
|
||||||
<IconArrow />
|
</Button>
|
||||||
</Button>
|
)}
|
||||||
|
|
||||||
<style jsx>{`
|
<style jsx>{`
|
||||||
.grid {
|
.grid {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { useState } from 'react';
|
||||||
|
import Tile from '@custom/shared/components/Tile';
|
||||||
|
|
||||||
|
export const ScreenPinTile = ({
|
||||||
|
height,
|
||||||
|
hideName = false,
|
||||||
|
item,
|
||||||
|
maxWidth,
|
||||||
|
ratio: initialRatio,
|
||||||
|
}) => {
|
||||||
|
const [ratio, setRatio] = useState(initialRatio);
|
||||||
|
const handleResize = (aspectRatio) => setRatio(aspectRatio);
|
||||||
|
|
||||||
|
if (item.isScreenshare) {
|
||||||
|
return (
|
||||||
|
<Tile
|
||||||
|
aspectRatio={initialRatio}
|
||||||
|
hideName={hideName}
|
||||||
|
participant={item}
|
||||||
|
mirrored={false}
|
||||||
|
style={{
|
||||||
|
height,
|
||||||
|
maxWidth,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tile
|
||||||
|
aspectRatio={ratio}
|
||||||
|
participant={item}
|
||||||
|
onVideoResize={handleResize}
|
||||||
|
style={{
|
||||||
|
maxHeight: height,
|
||||||
|
maxWidth: height * ratio,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ScreenPinTile;
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
import { useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
import { useCallState } from '@custom/shared/contexts/CallProvider';
|
||||||
|
import { useUIState } from '@custom/shared/contexts/UIStateProvider';
|
||||||
|
import { useResize } from '@custom/shared/hooks/useResize';
|
||||||
|
import { useDeepCompareMemo } from 'use-deep-compare';
|
||||||
|
import { ScreenPinTile } from './ScreenPinTile';
|
||||||
|
|
||||||
|
const MAX_SCREENS_AND_PINS = 3;
|
||||||
|
|
||||||
|
export const ScreensAndPins = ({ items }) => {
|
||||||
|
const { showNames } = useCallState();
|
||||||
|
const { pinnedId, sidebarView } = useUIState();
|
||||||
|
const viewRef = useRef(null);
|
||||||
|
const [dimensions, setDimensions] = useState({
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
useResize(() => {
|
||||||
|
const { width, height } = viewRef.current?.getBoundingClientRect();
|
||||||
|
setDimensions({
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
});
|
||||||
|
}, [viewRef, sidebarView]);
|
||||||
|
|
||||||
|
const visibleItems = useDeepCompareMemo(() => {
|
||||||
|
const isPinnedScreenshare = ({ id, isScreenshare }) =>
|
||||||
|
isScreenshare && id === pinnedId;
|
||||||
|
if (items.some(isPinnedScreenshare)) {
|
||||||
|
return items.filter(isPinnedScreenshare);
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}, [items, pinnedId]);
|
||||||
|
|
||||||
|
const { height, maxWidth, aspectRatio } = useMemo(() => {
|
||||||
|
/**
|
||||||
|
* We're relying on calculating what there is room for
|
||||||
|
* for the total number of s+p tiles instead of using
|
||||||
|
* videoTrack.getSettings because (currently) getSettings
|
||||||
|
* is unreliable in Firefox.
|
||||||
|
*/
|
||||||
|
const containerAR = dimensions.width / dimensions.height;
|
||||||
|
const maxItems = Math.min(visibleItems.length, MAX_SCREENS_AND_PINS);
|
||||||
|
const cols = Math.min(maxItems, Math.ceil(containerAR));
|
||||||
|
const rows = Math.ceil(visibleItems.length / cols);
|
||||||
|
const height = dimensions.height / rows;
|
||||||
|
const maxWidth = dimensions.width / cols;
|
||||||
|
return {
|
||||||
|
height,
|
||||||
|
maxWidth,
|
||||||
|
aspectRatio: maxWidth / height,
|
||||||
|
};
|
||||||
|
}, [dimensions, visibleItems?.length]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={viewRef}>
|
||||||
|
{visibleItems.map((item) => (
|
||||||
|
<div
|
||||||
|
className="tileWrapper"
|
||||||
|
key={item.id}
|
||||||
|
style={{
|
||||||
|
height,
|
||||||
|
maxWidth,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ScreenPinTile
|
||||||
|
height={height}
|
||||||
|
hideName={!showNames}
|
||||||
|
item={item}
|
||||||
|
maxWidth={maxWidth}
|
||||||
|
ratio={aspectRatio}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<style jsx>{`
|
||||||
|
div {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
height: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
div :global(.tileWrapper) {
|
||||||
|
background: var(--background);
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex: none;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
div :global(.tile .content) {
|
||||||
|
margin: auto;
|
||||||
|
max-height: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ScreensAndPins;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export { ScreensAndPins } from './ScreensAndPins';
|
||||||
|
|
@ -18,10 +18,7 @@ export const SpeakerTile = ({ participant, screenRef }) => {
|
||||||
setScreenHeight(rect.height);
|
setScreenHeight(rect.height);
|
||||||
}, [screenRef]);
|
}, [screenRef]);
|
||||||
|
|
||||||
useResize(() => {
|
useResize(() => updateRatio(), [updateRatio]);
|
||||||
updateRatio();
|
|
||||||
}, [updateRatio]);
|
|
||||||
|
|
||||||
useEffect(() => updateRatio(), [updateRatio]);
|
useEffect(() => updateRatio(), [updateRatio]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,11 @@
|
||||||
import React, { useEffect, useMemo, useRef } from 'react';
|
import React, { useEffect, useMemo, useRef } from 'react';
|
||||||
import { Container } from '@custom/basic-call/components/Call/Container';
|
|
||||||
import Header from '@custom/basic-call/components/Call/Header';
|
|
||||||
import ParticipantBar from '@custom/shared/components/ParticipantBar/ParticipantBar';
|
import ParticipantBar from '@custom/shared/components/ParticipantBar/ParticipantBar';
|
||||||
import VideoContainer from '@custom/shared/components/VideoContainer/VideoContainer';
|
|
||||||
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';
|
||||||
import { useTracks } from '@custom/shared/contexts/TracksProvider';
|
import { useTracks } from '@custom/shared/contexts/TracksProvider';
|
||||||
import { useUIState } from '@custom/shared/contexts/UIStateProvider';
|
import { useUIState } from '@custom/shared/contexts/UIStateProvider';
|
||||||
import { isScreenId } from '@custom/shared/contexts/participantsState';
|
import { isScreenId } from '@custom/shared/contexts/participantsState';
|
||||||
|
import { ScreensAndPins } from './ScreensAndPins';
|
||||||
import { SpeakerTile } from './SpeakerTile';
|
import { SpeakerTile } from './SpeakerTile';
|
||||||
|
|
||||||
const SIDEBAR_WIDTH = 186;
|
const SIDEBAR_WIDTH = 186;
|
||||||
|
|
@ -40,10 +38,10 @@ export const SpeakerView = () => {
|
||||||
return participants.length > 1 || hasScreenshares;
|
return participants.length > 1 || hasScreenshares;
|
||||||
}, [participants, pinnedId, screens]);
|
}, [participants, pinnedId, screens]);
|
||||||
|
|
||||||
/* const screenShareTiles = useMemo(
|
const screenShareTiles = useMemo(
|
||||||
() => <ScreensAndPins items={screensAndPinned} />,
|
() => <ScreensAndPins items={screensAndPinned} />,
|
||||||
[screensAndPinned]
|
[screensAndPinned]
|
||||||
); */
|
);
|
||||||
|
|
||||||
const hasScreenshares = useMemo(() => screens.length > 0, [screens]);
|
const hasScreenshares = useMemo(() => screens.length > 0, [screens]);
|
||||||
|
|
||||||
|
|
@ -80,7 +78,11 @@ export const SpeakerView = () => {
|
||||||
return (
|
return (
|
||||||
<div className="speaker-view">
|
<div className="speaker-view">
|
||||||
<div ref={activeRef} className="active">
|
<div ref={activeRef} className="active">
|
||||||
<SpeakerTile participant={currentSpeaker} screenRef={activeRef} />
|
{screensAndPinned.length > 0 ? (
|
||||||
|
screenShareTiles
|
||||||
|
) : (
|
||||||
|
<SpeakerTile screenRef={activeRef} participant={currentSpeaker} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{showSidebar && (
|
{showSidebar && (
|
||||||
<ParticipantBar
|
<ParticipantBar
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,14 @@ export const ViewTray = () => {
|
||||||
const { participants } = useParticipants();
|
const { participants } = useParticipants();
|
||||||
const { viewMode, setPreferredViewMode } = useUIState();
|
const { viewMode, setPreferredViewMode } = useUIState();
|
||||||
|
|
||||||
const onClick = () =>
|
const onViewClick = () =>
|
||||||
setPreferredViewMode(viewMode === VIEW_MODE_SPEAKER ? VIEW_MODE_GRID: VIEW_MODE_SPEAKER);
|
setPreferredViewMode(viewMode === VIEW_MODE_SPEAKER ? VIEW_MODE_GRID : VIEW_MODE_SPEAKER);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TrayButton
|
<TrayButton
|
||||||
label={viewMode === VIEW_MODE_GRID ? 'Speaker': 'Grid'}
|
label={viewMode === VIEW_MODE_GRID ? 'Speaker': 'Grid'}
|
||||||
disabled={participants.length < 2}
|
disabled={participants.length < 2}
|
||||||
onClick={onClick}
|
onClick={onViewClick}
|
||||||
>
|
>
|
||||||
{viewMode === VIEW_MODE_SPEAKER ? <IconGridView />: <IconSpeakerView />}
|
{viewMode === VIEW_MODE_SPEAKER ? <IconGridView />: <IconSpeakerView />}
|
||||||
</TrayButton>
|
</TrayButton>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue