Webcam draggable refactoring
This commit is contained in:
parent
f19ba4dda0
commit
68ec6411aa
@ -266,6 +266,9 @@ class App extends Component {
|
|||||||
className={styles.media}
|
className={styles.media}
|
||||||
aria-label={intl.formatMessage(intlMessages.mediaLabel)}
|
aria-label={intl.formatMessage(intlMessages.mediaLabel)}
|
||||||
aria-hidden={this.shouldAriaHide()}
|
aria-hidden={this.shouldAriaHide()}
|
||||||
|
style={{
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{media}
|
{media}
|
||||||
{this.renderCaptions()}
|
{this.renderCaptions()}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import cx from 'classnames';
|
import cx from 'classnames';
|
||||||
import WebcamDraggableOverlay from './webcam-draggable-overlay/component';
|
import WebcamDraggable from './webcam-draggable-overlay/component';
|
||||||
|
|
||||||
import { styles } from './styles';
|
import { styles } from './styles';
|
||||||
|
|
||||||
@ -70,6 +70,9 @@ export default class Media extends Component {
|
|||||||
id="container"
|
id="container"
|
||||||
className={cx(styles.container)}
|
className={cx(styles.container)}
|
||||||
ref={this.refContainer}
|
ref={this.refContainer}
|
||||||
|
style={{
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={!swapLayout ? contentClassName : overlayClassName}
|
className={!swapLayout ? contentClassName : overlayClassName}
|
||||||
@ -79,10 +82,11 @@ export default class Media extends Component {
|
|||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
<WebcamDraggableOverlay
|
<WebcamDraggable
|
||||||
refMediaContainer={this.refContainer}
|
refMediaContainer={this.refContainer}
|
||||||
swapLayout={swapLayout}
|
swapLayout={swapLayout}
|
||||||
floatingOverlay={floatingOverlay}
|
singleWebcam={floatingOverlay}
|
||||||
|
usersVideoLenght={usersVideo.length}
|
||||||
hideOverlay={hideOverlay}
|
hideOverlay={hideOverlay}
|
||||||
disableVideo={disableVideo}
|
disableVideo={disableVideo}
|
||||||
audioModalIsOpen={audioModalIsOpen}
|
audioModalIsOpen={audioModalIsOpen}
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
@import "../../stylesheets/variables/_all";
|
@import "../../stylesheets/variables/_all";
|
||||||
@import "../../stylesheets/variables/video";
|
@import "../../stylesheets/variables/video";
|
||||||
|
|
||||||
|
.cursorGrab{
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cursorGrabbing{
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
order: 1;
|
order: 1;
|
||||||
flex: 2;
|
flex: 2;
|
||||||
@ -22,30 +30,21 @@
|
|||||||
order: 2;
|
order: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay {
|
%overlay {
|
||||||
display: flex;
|
display: flex;
|
||||||
border: 0;
|
border: 0;
|
||||||
margin-top: 10px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
max-height: var(--video-height);
|
max-height: var(--video-height);
|
||||||
min-height: var(--video-height);
|
min-height: var(--video-height);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
|
||||||
|
|
||||||
.overlayRelative{
|
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlayAbsoluteSingle{
|
.overlay {
|
||||||
width: fit-content;
|
@extend %overlay;
|
||||||
}
|
|
||||||
|
|
||||||
.overlayAbsoluteMult{
|
|
||||||
width: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlayToTop {
|
.overlayToTop {
|
||||||
@ -61,27 +60,38 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
clip: rect(0 0 0 0);
|
clip: rect(0 0 0 0);
|
||||||
height: 1px; width: 1px;
|
width: 1px;
|
||||||
margin: -1px; padding: 0; border: 0;
|
height: 1px;
|
||||||
|
margin: -1px;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.floatingOverlay {
|
.floatingOverlay {
|
||||||
margin-top: 10px;
|
@extend %overlay;
|
||||||
margin-bottom: 10px;
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
@include mq($medium-up) {
|
@include mq($medium-up) {
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
|
||||||
right: 0;
|
|
||||||
width: fit-content;
|
|
||||||
min-width: calc(var(--video-height) * var(--video-ratio));
|
min-width: calc(var(--video-height) * var(--video-ratio));
|
||||||
max-width: fit-content;
|
|
||||||
height: fit-content;
|
|
||||||
min-height: var(--video-height);
|
min-height: var(--video-height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fit {
|
||||||
|
width: fit-content;
|
||||||
|
height: fit-content;
|
||||||
|
max-width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.full {
|
||||||
|
min-width: 100%;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.hide {
|
.hide {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@ -90,14 +100,6 @@
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top {
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bottom {
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dragging {
|
.dragging {
|
||||||
opacity: .5;
|
opacity: .5;
|
||||||
}
|
}
|
||||||
@ -110,17 +112,24 @@
|
|||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropZoneTop {
|
.dropZoneBgTop,
|
||||||
|
.dropZoneBgBottom {
|
||||||
|
z-index: 99;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropZoneTop,
|
||||||
|
.dropZoneBgTop {
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropZoneBottom {
|
.dropZoneBottom,
|
||||||
|
.dropZoneBgBottom {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropZoneBg {
|
.dropZoneTop:hover .dropZoneBgTop,
|
||||||
position: absolute;
|
.dropZoneBottom:hover .dropZoneBgBottom {
|
||||||
z-index: 99;
|
|
||||||
width: 100%;
|
|
||||||
background-color: rgba(255, 255, 255, .3);
|
background-color: rgba(255, 255, 255, .3);
|
||||||
}
|
}
|
@ -1,488 +1,212 @@
|
|||||||
import React, { Component, Fragment } from 'react';
|
import React, { Component, Fragment } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import cx from 'classnames';
|
|
||||||
import VideoProviderContainer from '/imports/ui/components/video-provider/container';
|
|
||||||
import _ from 'lodash';
|
|
||||||
import browser from 'browser-detect';
|
|
||||||
|
|
||||||
import Draggable from 'react-draggable';
|
import Draggable from 'react-draggable';
|
||||||
|
import cx from 'classnames';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
import browser from 'browser-detect';
|
||||||
|
import { withDraggableContext } from './context';
|
||||||
|
import VideoProviderContainer from '/imports/ui/components/video-provider/container';
|
||||||
import { styles } from '../styles.scss';
|
import { styles } from '../styles.scss';
|
||||||
|
import Storage from '../../../services/storage/session';
|
||||||
|
|
||||||
const propTypes = {
|
const { webcamsDefaultPlacement } = Meteor.settings.public.layout;
|
||||||
floatingOverlay: PropTypes.bool,
|
|
||||||
hideOverlay: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
const defaultProps = {
|
|
||||||
floatingOverlay: false,
|
|
||||||
hideOverlay: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const fullscreenChangedEvents = [
|
|
||||||
'fullscreenchange',
|
|
||||||
'webkitfullscreenchange',
|
|
||||||
'mozfullscreenchange',
|
|
||||||
'MSFullscreenChange',
|
|
||||||
];
|
|
||||||
|
|
||||||
const BROWSER_ISMOBILE = browser().mobile;
|
const BROWSER_ISMOBILE = browser().mobile;
|
||||||
|
|
||||||
export default class WebcamDraggableOverlay extends Component {
|
class WebcamDraggable extends Component {
|
||||||
static getWebcamGridBySelector() {
|
|
||||||
return document.querySelector('div[class*="videoList"]');
|
|
||||||
}
|
|
||||||
|
|
||||||
static getVideoCountBySelector() {
|
|
||||||
return document.querySelectorAll('video[class*="media"]').length;
|
|
||||||
}
|
|
||||||
|
|
||||||
static getOverlayBySelector() {
|
|
||||||
return document.querySelector('div[class*="overlay"]');
|
|
||||||
}
|
|
||||||
|
|
||||||
static waitFor(condition, callback) {
|
|
||||||
const cond = condition();
|
|
||||||
if (!cond) {
|
|
||||||
setTimeout(WebcamDraggableOverlay.waitFor.bind(null, condition, callback), 500);
|
|
||||||
} else {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
|
||||||
dragging: false,
|
|
||||||
showDropZones: false,
|
|
||||||
showBgDropZoneTop: false,
|
|
||||||
showBgDropZoneBottom: false,
|
|
||||||
dropOnTop: true,
|
|
||||||
dropOnBottom: false,
|
|
||||||
initialPosition: {
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
},
|
|
||||||
initialRectPosition: {
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
},
|
|
||||||
lastPosition: {
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
},
|
|
||||||
resetPosition: false,
|
|
||||||
isFullScreen: false,
|
|
||||||
isVideoLoaded: false,
|
|
||||||
isMinWidth: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.shouldUpdatePosition = true;
|
|
||||||
|
|
||||||
this.updateWebcamPositionByResize = this.updateWebcamPositionByResize.bind(this);
|
|
||||||
this.eventVideoFocusChangeListener = this.eventVideoFocusChangeListener.bind(this);
|
|
||||||
|
|
||||||
this.eventResizeListener = _.throttle(
|
|
||||||
this.updateWebcamPositionByResize,
|
|
||||||
500,
|
|
||||||
{
|
|
||||||
leading: true,
|
|
||||||
trailing: true,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
this.videoMounted = this.videoMounted.bind(this);
|
|
||||||
|
|
||||||
this.handleWebcamDragStart = this.handleWebcamDragStart.bind(this);
|
this.handleWebcamDragStart = this.handleWebcamDragStart.bind(this);
|
||||||
this.handleWebcamDragStop = this.handleWebcamDragStop.bind(this);
|
this.handleWebcamDragStop = this.handleWebcamDragStop.bind(this);
|
||||||
this.handleFullscreenChange = this.handleFullscreenChange.bind(this);
|
|
||||||
this.fullscreenButtonChange = this.fullscreenButtonChange.bind(this);
|
|
||||||
|
|
||||||
this.setIsFullScreen = this.setIsFullScreen.bind(this);
|
|
||||||
this.setResetPosition = this.setResetPosition.bind(this);
|
|
||||||
this.setInitialReferencePoint = this.setInitialReferencePoint.bind(this);
|
|
||||||
this.setLastPosition = this.setLastPosition.bind(this);
|
|
||||||
this.setLastWebcamPosition = this.setLastWebcamPosition.bind(this);
|
|
||||||
this.setisMinWidth = this.setisMinWidth.bind(this);
|
|
||||||
this.setDropOnTop = this.setDropOnTop.bind(this);
|
|
||||||
|
|
||||||
this.dropZoneTopEnterHandler = this.dropZoneTopEnterHandler.bind(this);
|
|
||||||
this.dropZoneTopLeaveHandler = this.dropZoneTopLeaveHandler.bind(this);
|
|
||||||
|
|
||||||
this.dropZoneBottomEnterHandler = this.dropZoneBottomEnterHandler.bind(this);
|
|
||||||
this.dropZoneBottomLeaveHandler = this.dropZoneBottomLeaveHandler.bind(this);
|
|
||||||
|
|
||||||
this.dropZoneTopMouseUpHandler = this.dropZoneTopMouseUpHandler.bind(this);
|
|
||||||
this.dropZoneBottomMouseUpHandler = this.dropZoneBottomMouseUpHandler.bind(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { floatingOverlay } = this.props;
|
window.addEventListener('resize', _.debounce(this.onResize.bind(this), 500));
|
||||||
const { resetPosition } = this.state;
|
|
||||||
|
|
||||||
if (!floatingOverlay
|
|
||||||
&& !resetPosition) {
|
|
||||||
this.setResetPosition(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('resize', this.eventResizeListener);
|
|
||||||
window.addEventListener('videoFocusChange', this.eventVideoFocusChangeListener);
|
|
||||||
|
|
||||||
fullscreenChangedEvents.forEach((event) => {
|
|
||||||
document.addEventListener(event, this.handleFullscreenChange);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Ensures that the event will be called before the resize
|
|
||||||
document.addEventListener('webcamFullscreenButtonChange', this.fullscreenButtonChange);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
const { swapLayout, usersVideo, mediaContainer } = this.props;
|
const { swapLayout } = this.props;
|
||||||
const { lastPosition } = this.state;
|
if (prevProps.swapLayout === true && swapLayout === false) {
|
||||||
const { y } = lastPosition;
|
setTimeout(() => this.forceUpdate(), 500);
|
||||||
const userLength = usersVideo.length;
|
}
|
||||||
const prevUserLength = prevProps.usersVideo.length;
|
}
|
||||||
|
|
||||||
if (prevProps.mediaContainer && mediaContainer) {
|
onResize() {
|
||||||
const mediaContainerRect = mediaContainer.getBoundingClientRect();
|
const { webcamDraggableState, webcamDraggableDispatch } = this.props;
|
||||||
const {
|
const { mediaSize } = webcamDraggableState;
|
||||||
left: mediaLeft,
|
const { width: stateWidth, height: stateHeight } = mediaSize;
|
||||||
top: mediaTop,
|
const { width, height } = this.getMediaBounds();
|
||||||
} = mediaContainerRect;
|
|
||||||
const prevMediaContainerRect = prevProps.mediaContainer.getBoundingClientRect();
|
|
||||||
const {
|
|
||||||
left: prevMediaLeft,
|
|
||||||
top: prevMediaTop,
|
|
||||||
} = prevMediaContainerRect;
|
|
||||||
|
|
||||||
if (mediaLeft !== prevMediaLeft || mediaTop !== prevMediaTop) {
|
if (stateWidth !== width || stateHeight !== height) {
|
||||||
this.shouldUpdatePosition = false;
|
webcamDraggableDispatch(
|
||||||
} else if (this.shouldUpdatePosition === false) {
|
{
|
||||||
this.shouldUpdatePosition = true;
|
type: 'setMediaSize',
|
||||||
}
|
value: {
|
||||||
}
|
width,
|
||||||
|
height,
|
||||||
if (prevProps.swapLayout && !swapLayout && userLength === 1) {
|
},
|
||||||
this.shouldUpdatePosition = false;
|
},
|
||||||
}
|
|
||||||
if (prevProps.swapLayout && !swapLayout && userLength > 1) {
|
|
||||||
this.setLastPosition(0, y);
|
|
||||||
}
|
|
||||||
if (prevUserLength === 1 && userLength > 1) {
|
|
||||||
this.setResetPosition(true);
|
|
||||||
this.setDropOnTop(true);
|
|
||||||
}
|
|
||||||
if (prevUserLength !== userLength) {
|
|
||||||
WebcamDraggableOverlay.waitFor(
|
|
||||||
() => WebcamDraggableOverlay.getVideoCountBySelector() === userLength,
|
|
||||||
this.updateWebcamPositionByResize,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
getMediaBounds() {
|
||||||
fullscreenChangedEvents.forEach((event) => {
|
const { refMediaContainer, webcamDraggableState, webcamDraggableDispatch } = this.props;
|
||||||
document.removeEventListener(event, this.handleFullscreenChange);
|
const { mediaSize: mediaState } = webcamDraggableState;
|
||||||
});
|
|
||||||
|
|
||||||
document.removeEventListener('webcamFullscreenButtonChange', this.fullscreenButtonChange);
|
|
||||||
document.removeEventListener('videoFocusChange', this.eventVideoFocusChangeListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsFullScreen(isFullScreen) {
|
|
||||||
this.setState({ isFullScreen });
|
|
||||||
}
|
|
||||||
|
|
||||||
setResetPosition(resetPosition) {
|
|
||||||
this.setState({ resetPosition });
|
|
||||||
}
|
|
||||||
|
|
||||||
setLastPosition(x, y) {
|
|
||||||
this.setState({
|
|
||||||
lastPosition: {
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setDropOnTop(dropOnTop) {
|
|
||||||
this.setState({ dropOnTop });
|
|
||||||
}
|
|
||||||
|
|
||||||
setInitialReferencePoint() {
|
|
||||||
const { refMediaContainer, usersVideo } = this.props;
|
|
||||||
const { current: mediaContainer } = refMediaContainer;
|
const { current: mediaContainer } = refMediaContainer;
|
||||||
const userLength = usersVideo.length;
|
if (mediaContainer) {
|
||||||
|
|
||||||
const webcamBySelector = WebcamDraggableOverlay.getWebcamGridBySelector();
|
|
||||||
|
|
||||||
if (webcamBySelector && mediaContainer && this.shouldUpdatePosition) {
|
|
||||||
const webcamBySelectorRect = webcamBySelector.getBoundingClientRect();
|
|
||||||
const {
|
|
||||||
width: webcamWidth,
|
|
||||||
height: webcamHeight,
|
|
||||||
} = webcamBySelectorRect;
|
|
||||||
|
|
||||||
const mediaContainerRect = mediaContainer.getBoundingClientRect();
|
const mediaContainerRect = mediaContainer.getBoundingClientRect();
|
||||||
const {
|
const {
|
||||||
width: mediaWidth,
|
top, left, width, height,
|
||||||
height: mediaHeight,
|
|
||||||
} = mediaContainerRect;
|
} = mediaContainerRect;
|
||||||
|
|
||||||
const x = mediaWidth - ((webcamWidth + 10) * userLength); // 10 is margin
|
if (mediaState.width === 0 || mediaState.height === 0) {
|
||||||
const y = mediaHeight - ((webcamHeight + 10)); // 10 is margin
|
webcamDraggableDispatch(
|
||||||
|
{
|
||||||
|
type: 'setMediaSize',
|
||||||
|
value: {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (x === 0 && y === 0) return false;
|
return {
|
||||||
|
top,
|
||||||
this.setState({
|
left,
|
||||||
initialRectPosition: {
|
width,
|
||||||
x,
|
height,
|
||||||
y,
|
};
|
||||||
},
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
setLastWebcamPosition() {
|
getWebcamsListBounds() {
|
||||||
const { refMediaContainer, usersVideo, floatingOverlay } = this.props;
|
const { webcamDraggableState, singleWebcam } = this.props;
|
||||||
const { current: mediaContainer } = refMediaContainer;
|
const { videoListRef } = webcamDraggableState;
|
||||||
const {
|
if (videoListRef) {
|
||||||
initialRectPosition,
|
const videoListRefRect = videoListRef.getBoundingClientRect();
|
||||||
dragging,
|
|
||||||
dropOnTop,
|
|
||||||
dropOnBottom,
|
|
||||||
} = this.state;
|
|
||||||
const userLength = usersVideo.length;
|
|
||||||
|
|
||||||
const { x: initX, y: initY } = initialRectPosition;
|
|
||||||
const webcamBySelector = WebcamDraggableOverlay.getWebcamGridBySelector();
|
|
||||||
|
|
||||||
if (webcamBySelector && mediaContainer && this.shouldUpdatePosition) {
|
|
||||||
const webcamBySelectorRect = webcamBySelector.getBoundingClientRect();
|
|
||||||
const {
|
const {
|
||||||
left: webcamLeft,
|
top, left, width, height,
|
||||||
top: webcamTop,
|
} = videoListRefRect;
|
||||||
} = webcamBySelectorRect;
|
return {
|
||||||
|
top: top - 10, // 10 = margin
|
||||||
const mediaContainerRect = mediaContainer.getBoundingClientRect();
|
left: left - (singleWebcam ? 10 : 0), // 10 = margin
|
||||||
const {
|
width: width + (singleWebcam ? 20 : 0), // 20 = margin
|
||||||
left: mediaLeft,
|
height: height + 20, // 20 = margin
|
||||||
top: mediaTop,
|
};
|
||||||
} = mediaContainerRect;
|
|
||||||
|
|
||||||
const webcamXByMedia = webcamLeft - mediaLeft;
|
|
||||||
const webcamYByMedia = webcamTop - mediaTop;
|
|
||||||
|
|
||||||
let x = -(initX - webcamXByMedia);
|
|
||||||
x = floatingOverlay ? -((initX - webcamXByMedia) + 10) : x;
|
|
||||||
x = userLength > 1 ? 0 : x;
|
|
||||||
|
|
||||||
x = !dragging && webcamXByMedia < 0 ? -initX : x;
|
|
||||||
|
|
||||||
let y = -(initY - webcamYByMedia);
|
|
||||||
y = webcamYByMedia < 0 ? -initY : y;
|
|
||||||
|
|
||||||
y = userLength > 1 && dropOnTop ? -initY : y;
|
|
||||||
y = userLength > 1 && dropOnBottom ? 0 : y;
|
|
||||||
|
|
||||||
y = y < -initY ? -initY : y;
|
|
||||||
y = y > 0 ? 0 : y;
|
|
||||||
|
|
||||||
this.setLastPosition(x, y);
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
setisMinWidth(isMinWidth) {
|
calculatePosition() {
|
||||||
this.setState({ isMinWidth });
|
const { top: mediaTop, left: mediaLeft } = this.getMediaBounds();
|
||||||
|
const { top: webcamsListTop, left: webcamsListLeft } = this.getWebcamsListBounds();
|
||||||
|
const x = webcamsListLeft - mediaLeft;
|
||||||
|
const y = webcamsListTop - mediaTop;
|
||||||
|
return {
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
videoMounted() {
|
async handleWebcamDragStart() {
|
||||||
this.setResetPosition(true);
|
const { webcamDraggableDispatch, singleWebcam } = this.props;
|
||||||
WebcamDraggableOverlay.waitFor(this.setInitialReferencePoint, this.setLastWebcamPosition);
|
const { x, y } = await this.calculatePosition();
|
||||||
this.setState({ isVideoLoaded: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
fullscreenButtonChange() {
|
webcamDraggableDispatch({ type: 'dragStart' });
|
||||||
this.setIsFullScreen(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateWebcamPositionByResize() {
|
webcamDraggableDispatch(
|
||||||
const {
|
{
|
||||||
isVideoLoaded,
|
type: 'setTempPosition',
|
||||||
isMinWidth,
|
value: {
|
||||||
} = this.state;
|
x: singleWebcam ? x : 0,
|
||||||
|
y,
|
||||||
if (isVideoLoaded) {
|
},
|
||||||
this.setInitialReferencePoint();
|
},
|
||||||
this.setLastWebcamPosition();
|
);
|
||||||
}
|
|
||||||
|
|
||||||
if (window.innerWidth < 641) {
|
|
||||||
this.setisMinWidth(true);
|
|
||||||
this.setState({ dropOnTop: true });
|
|
||||||
this.setResetPosition(true);
|
|
||||||
} else if (isMinWidth) {
|
|
||||||
this.setisMinWidth(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eventVideoFocusChangeListener() {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.setInitialReferencePoint();
|
|
||||||
this.setLastWebcamPosition();
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleFullscreenChange() {
|
|
||||||
if (document.fullscreenElement
|
|
||||||
|| document.webkitFullscreenElement
|
|
||||||
|| document.mozFullScreenElement
|
|
||||||
|| document.msFullscreenElement) {
|
|
||||||
window.removeEventListener('resize', this.eventResizeListener);
|
|
||||||
this.setIsFullScreen(true);
|
|
||||||
} else {
|
|
||||||
this.setIsFullScreen(false);
|
|
||||||
window.addEventListener('resize', this.eventResizeListener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleWebcamDragStart() {
|
|
||||||
const { floatingOverlay } = this.props;
|
|
||||||
const {
|
|
||||||
dragging,
|
|
||||||
showDropZones,
|
|
||||||
dropOnTop,
|
|
||||||
dropOnBottom,
|
|
||||||
resetPosition,
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
if (!floatingOverlay && dropOnTop) WebcamDraggableOverlay.getOverlayBySelector().style.top = 0;
|
|
||||||
|
|
||||||
if (!dragging) this.setState({ dragging: true });
|
|
||||||
if (dropOnTop) this.setState({ dropOnTop: false });
|
|
||||||
if (dropOnBottom) this.setState({ dropOnBottom: false });
|
|
||||||
if (!showDropZones) this.setState({ showDropZones: true });
|
|
||||||
|
|
||||||
if (resetPosition) this.setState({ resetPosition: false });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleWebcamDragStop(e, position) {
|
handleWebcamDragStop(e, position) {
|
||||||
const {
|
const { webcamDraggableDispatch, singleWebcam } = this.props;
|
||||||
dragging,
|
const targetClassname = e.target.className;
|
||||||
showDropZones,
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
const { x, y } = position;
|
const { x, y } = position;
|
||||||
|
|
||||||
if (dragging) this.setState({ dragging: false });
|
if (targetClassname.includes('Top')) {
|
||||||
if (showDropZones) this.setState({ showDropZones: false });
|
webcamDraggableDispatch({ type: 'setplacementToTop' });
|
||||||
|
} else if (targetClassname.includes('Bottom')) {
|
||||||
this.setLastPosition(x, y);
|
webcamDraggableDispatch({ type: 'setplacementToBottom' });
|
||||||
window.dispatchEvent(new Event('resize'));
|
} else if (singleWebcam) {
|
||||||
}
|
webcamDraggableDispatch(
|
||||||
|
{
|
||||||
dropZoneTopEnterHandler() {
|
type: 'setLastPosition',
|
||||||
const {
|
value: {
|
||||||
showBgDropZoneTop,
|
x,
|
||||||
} = this.state;
|
y,
|
||||||
|
},
|
||||||
if (!showBgDropZoneTop) this.setState({ showBgDropZoneTop: true });
|
},
|
||||||
}
|
);
|
||||||
|
webcamDraggableDispatch({ type: 'setplacementToFloating' });
|
||||||
dropZoneBottomEnterHandler() {
|
|
||||||
const {
|
|
||||||
showBgDropZoneBottom,
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
if (!showBgDropZoneBottom) this.setState({ showBgDropZoneBottom: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
dropZoneTopLeaveHandler() {
|
|
||||||
const {
|
|
||||||
showBgDropZoneTop,
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
if (showBgDropZoneTop) this.setState({ showBgDropZoneTop: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
dropZoneBottomLeaveHandler() {
|
|
||||||
const {
|
|
||||||
showBgDropZoneBottom,
|
|
||||||
} = this.state;
|
|
||||||
|
|
||||||
if (showBgDropZoneBottom) this.setState({ showBgDropZoneBottom: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
dropZoneTopMouseUpHandler() {
|
|
||||||
const { dropOnTop } = this.state;
|
|
||||||
if (!dropOnTop) {
|
|
||||||
this.setState({
|
|
||||||
dropOnTop: true,
|
|
||||||
dropOnBottom: false,
|
|
||||||
resetPosition: true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
webcamDraggableDispatch({ type: 'dragEnd' });
|
||||||
window.dispatchEvent(new Event('resize'));
|
window.dispatchEvent(new Event('resize'));
|
||||||
setTimeout(() => this.setLastWebcamPosition(), 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
dropZoneBottomMouseUpHandler() {
|
|
||||||
const { dropOnBottom } = this.state;
|
|
||||||
if (!dropOnBottom) {
|
|
||||||
this.setState({
|
|
||||||
dropOnTop: false,
|
|
||||||
dropOnBottom: true,
|
|
||||||
resetPosition: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
window.dispatchEvent(new Event('resize'));
|
|
||||||
setTimeout(() => this.setLastWebcamPosition(), 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
|
webcamDraggableState,
|
||||||
|
singleWebcam,
|
||||||
swapLayout,
|
swapLayout,
|
||||||
floatingOverlay,
|
|
||||||
hideOverlay,
|
hideOverlay,
|
||||||
disableVideo,
|
disableVideo,
|
||||||
audioModalIsOpen,
|
audioModalIsOpen,
|
||||||
refMediaContainer,
|
|
||||||
usersVideo,
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const userLength = usersVideo.length;
|
const { dragging, isFullscreen } = webcamDraggableState;
|
||||||
|
let placement = Storage.getItem('webcamPlacement');
|
||||||
const { current: mediaContainer } = refMediaContainer;
|
const lastPosition = Storage.getItem('webcamLastPosition') || { x: 0, y: 0 };
|
||||||
|
let position = lastPosition;
|
||||||
let mediaContainerRect;
|
if (!placement) {
|
||||||
let mediaHeight;
|
placement = webcamsDefaultPlacement;
|
||||||
if (mediaContainer) {
|
|
||||||
mediaContainerRect = mediaContainer.getBoundingClientRect();
|
|
||||||
const {
|
|
||||||
height,
|
|
||||||
} = mediaContainerRect;
|
|
||||||
mediaHeight = height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dragging) {
|
||||||
|
position = webcamDraggableState.tempPosition;
|
||||||
|
} else if (!dragging && placement === 'floating' && singleWebcam) {
|
||||||
|
position = webcamDraggableState.lastPosition;
|
||||||
|
} else {
|
||||||
|
position = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (swapLayout || isFullscreen || BROWSER_ISMOBILE) {
|
||||||
|
position = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
dragging,
|
width: mediaWidth,
|
||||||
showDropZones,
|
height: mediaHeight,
|
||||||
showBgDropZoneTop,
|
} = this.getMediaBounds();
|
||||||
showBgDropZoneBottom,
|
|
||||||
dropOnTop,
|
const {
|
||||||
dropOnBottom,
|
width: webcamsWidth,
|
||||||
initialPosition,
|
height: webcamsHeight,
|
||||||
lastPosition,
|
} = this.getWebcamsListBounds();
|
||||||
resetPosition,
|
|
||||||
isFullScreen,
|
const isOverflowWidth = (lastPosition.x + webcamsWidth) > mediaWidth;
|
||||||
isMinWidth,
|
const isOverflowHeight = (lastPosition.y + webcamsHeight) > mediaHeight;
|
||||||
} = this.state;
|
|
||||||
|
position = {
|
||||||
|
x: isOverflowWidth
|
||||||
|
&& !dragging && !swapLayout && singleWebcam && placement === 'floating' ? mediaWidth - webcamsWidth : position.x,
|
||||||
|
y: isOverflowHeight
|
||||||
|
&& !dragging && !swapLayout && singleWebcam && placement === 'floating' ? mediaHeight - (webcamsHeight + 1) : position.y,
|
||||||
|
};
|
||||||
|
|
||||||
const contentClassName = cx({
|
const contentClassName = cx({
|
||||||
[styles.content]: true,
|
[styles.content]: true,
|
||||||
@ -490,62 +214,50 @@ export default class WebcamDraggableOverlay extends Component {
|
|||||||
|
|
||||||
const overlayClassName = cx({
|
const overlayClassName = cx({
|
||||||
[styles.overlay]: true,
|
[styles.overlay]: true,
|
||||||
[styles.overlayRelative]: (dropOnTop || dropOnBottom),
|
|
||||||
[styles.overlayAbsoluteMult]: (!dropOnTop && !dropOnBottom) && userLength > 1,
|
|
||||||
[styles.hideOverlay]: hideOverlay,
|
[styles.hideOverlay]: hideOverlay,
|
||||||
[styles.floatingOverlay]: floatingOverlay && (!dropOnTop && !dropOnBottom),
|
[styles.floatingOverlay]: (singleWebcam && placement === 'floating') || dragging,
|
||||||
[styles.overlayToTop]: dropOnTop,
|
[styles.fit]: singleWebcam && (placement === 'floating' || dragging),
|
||||||
[styles.overlayToBottom]: dropOnBottom,
|
[styles.full]: (singleWebcam && (placement === 'top' || placement === 'bottom')
|
||||||
|
&& !dragging)
|
||||||
|
|| !singleWebcam,
|
||||||
|
[styles.overlayToTop]: (placement === 'floating' && !singleWebcam)
|
||||||
|
|| (placement === 'top' && !dragging),
|
||||||
|
[styles.overlayToBottom]: placement === 'bottom' && !dragging,
|
||||||
[styles.dragging]: dragging,
|
[styles.dragging]: dragging,
|
||||||
});
|
});
|
||||||
|
|
||||||
const dropZoneTopClassName = cx({
|
const dropZoneTopClassName = cx({
|
||||||
[styles.dropZoneTop]: true,
|
[styles.dropZoneTop]: true,
|
||||||
[styles.show]: showDropZones,
|
[styles.show]: dragging,
|
||||||
[styles.hide]: !showDropZones,
|
[styles.hide]: !dragging,
|
||||||
|
[styles.cursorGrabbing]: dragging,
|
||||||
});
|
});
|
||||||
|
|
||||||
const dropZoneBottomClassName = cx({
|
const dropZoneBottomClassName = cx({
|
||||||
[styles.dropZoneBottom]: true,
|
[styles.dropZoneBottom]: true,
|
||||||
[styles.show]: showDropZones,
|
[styles.show]: dragging,
|
||||||
[styles.hide]: !showDropZones,
|
[styles.hide]: !dragging,
|
||||||
|
[styles.cursorGrabbing]: dragging,
|
||||||
});
|
});
|
||||||
|
|
||||||
const dropZoneBgTopClassName = cx({
|
const dropZoneBgTopClassName = cx({
|
||||||
[styles.dropZoneBg]: true,
|
[styles.dropZoneBgTop]: true,
|
||||||
[styles.top]: true,
|
|
||||||
[styles.show]: showBgDropZoneTop,
|
|
||||||
[styles.hide]: !showBgDropZoneTop,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const dropZoneBgBottomClassName = cx({
|
const dropZoneBgBottomClassName = cx({
|
||||||
[styles.dropZoneBg]: true,
|
[styles.dropZoneBgBottom]: true,
|
||||||
[styles.bottom]: true,
|
|
||||||
[styles.show]: showBgDropZoneBottom,
|
|
||||||
[styles.hide]: !showBgDropZoneBottom,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const cursor = () => {
|
|
||||||
if ((!swapLayout || !isFullScreen || !BROWSER_ISMOBILE || !isMinWidth) && !dragging) return 'grab';
|
|
||||||
if (dragging) return 'grabbing';
|
|
||||||
return 'default';
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<div
|
<div
|
||||||
className={dropZoneTopClassName}
|
className={dropZoneTopClassName}
|
||||||
onMouseEnter={this.dropZoneTopEnterHandler}
|
style={{ height: !singleWebcam ? '50%' : '20%' }}
|
||||||
onMouseLeave={this.dropZoneTopLeaveHandler}
|
>
|
||||||
onMouseUp={this.dropZoneTopMouseUpHandler}
|
<div
|
||||||
data-dropzone="dropZoneTop"
|
className={dropZoneBgTopClassName}
|
||||||
role="presentation"
|
/>
|
||||||
style={{ height: userLength > 1 ? '50%' : '20%' }}
|
</div>
|
||||||
/>
|
|
||||||
<div
|
|
||||||
className={dropZoneBgTopClassName}
|
|
||||||
style={{ height: userLength > 1 ? '50%' : '20%' }}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Draggable
|
<Draggable
|
||||||
handle="video"
|
handle="video"
|
||||||
@ -553,44 +265,41 @@ export default class WebcamDraggableOverlay extends Component {
|
|||||||
onStart={this.handleWebcamDragStart}
|
onStart={this.handleWebcamDragStart}
|
||||||
onStop={this.handleWebcamDragStop}
|
onStop={this.handleWebcamDragStop}
|
||||||
onMouseDown={e => e.preventDefault()}
|
onMouseDown={e => e.preventDefault()}
|
||||||
disabled={swapLayout || isFullScreen || BROWSER_ISMOBILE || isMinWidth}
|
disabled={swapLayout || isFullscreen || BROWSER_ISMOBILE}
|
||||||
position={resetPosition || swapLayout ? initialPosition : lastPosition}
|
position={position}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={!swapLayout ? overlayClassName : contentClassName}
|
className={!swapLayout ? overlayClassName : contentClassName}
|
||||||
style={{
|
style={{
|
||||||
maxHeight: mediaHeight,
|
marginLeft: singleWebcam
|
||||||
|
&& !(placement === 'bottom' || placement === 'top')
|
||||||
|
? 10
|
||||||
|
: 0,
|
||||||
|
marginRight: singleWebcam
|
||||||
|
&& !(placement === 'bottom' || placement === 'top')
|
||||||
|
? 10
|
||||||
|
: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{
|
{!disableVideo && !audioModalIsOpen ? (
|
||||||
!disableVideo && !audioModalIsOpen
|
<VideoProviderContainer
|
||||||
? (
|
swapLayout={swapLayout}
|
||||||
<VideoProviderContainer
|
/>
|
||||||
cursor={cursor()}
|
) : null}
|
||||||
swapLayout={swapLayout}
|
|
||||||
onMount={this.videoMounted}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
</Draggable>
|
</Draggable>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={dropZoneBottomClassName}
|
className={dropZoneBottomClassName}
|
||||||
onMouseEnter={this.dropZoneBottomEnterHandler}
|
style={{ height: !singleWebcam ? '50%' : '20%' }}
|
||||||
onMouseLeave={this.dropZoneBottomLeaveHandler}
|
>
|
||||||
onMouseUp={this.dropZoneBottomMouseUpHandler}
|
<div
|
||||||
data-dropzone="dropZoneBottom"
|
className={dropZoneBgBottomClassName}
|
||||||
role="presentation"
|
/>
|
||||||
style={{ height: userLength > 1 ? '50%' : '20%' }}
|
</div>
|
||||||
/>
|
|
||||||
<div
|
|
||||||
className={dropZoneBgBottomClassName}
|
|
||||||
style={{ height: userLength > 1 ? '50%' : '20%' }}
|
|
||||||
/>
|
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WebcamDraggableOverlay.propTypes = propTypes;
|
export default withDraggableContext(WebcamDraggable);
|
||||||
WebcamDraggableOverlay.defaultProps = defaultProps;
|
|
||||||
|
@ -0,0 +1,178 @@
|
|||||||
|
import React, { createContext, useReducer, useEffect } from 'react';
|
||||||
|
import Storage from '../../../services/storage/session';
|
||||||
|
|
||||||
|
export const WebcamDraggableContext = createContext();
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
placement: 'top',
|
||||||
|
mediaSize: {
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
},
|
||||||
|
initialRef: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
tempPosition: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
lastPosition: {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
},
|
||||||
|
dragging: false,
|
||||||
|
videoRef: null,
|
||||||
|
videoListRef: null,
|
||||||
|
isFullscreen: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const reducer = (state, action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'setplacementToTop': {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
placement: 'top',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'setplacementToBottom': {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
placement: 'bottom',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'setplacementToFloating': {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
placement: 'floating',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'setMediaSize': {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
mediaSize: {
|
||||||
|
width: action.value.width,
|
||||||
|
height: action.value.height,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'setWebcamRef': {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
webcamRef: action.value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'setInitialRef': {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
initialRef: {
|
||||||
|
x: action.value.x,
|
||||||
|
y: action.value.y,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'setTempPosition': {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
tempPosition: {
|
||||||
|
x: action.value.x,
|
||||||
|
y: action.value.y,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'setLastPosition': {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
lastPosition: {
|
||||||
|
x: action.value.x,
|
||||||
|
y: action.value.y,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'setVideoRef': {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
videoRef: action.value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'setVideoListRef': {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
videoListRef: action.value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'dragStart': {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
dragging: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'dragEnd': {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
dragging: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'onFullscreen': {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
isFullscreen: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'offFullscreen': {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
isFullscreen: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
throw new Error('Unexpected action');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const ContextConsumer = Component => props => (
|
||||||
|
<WebcamDraggableContext.Consumer>
|
||||||
|
{contexts => <Component {...props} {...contexts} />}
|
||||||
|
</WebcamDraggableContext.Consumer>
|
||||||
|
);
|
||||||
|
|
||||||
|
const ContextProvider = (props) => {
|
||||||
|
const [webcamDraggableState, webcamDraggableDispatch] = useReducer(reducer, initialState);
|
||||||
|
const { placement, lastPosition } = webcamDraggableState;
|
||||||
|
const { children } = props;
|
||||||
|
useEffect(() => {
|
||||||
|
Storage.setItem('webcamPlacement', placement);
|
||||||
|
Storage.setItem('webcamLastPosition', lastPosition);
|
||||||
|
}, [
|
||||||
|
placement,
|
||||||
|
lastPosition,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WebcamDraggableContext.Provider value={{
|
||||||
|
webcamDraggableState,
|
||||||
|
webcamDraggableDispatch,
|
||||||
|
...props,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</WebcamDraggableContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const withProvider = Component => props => (
|
||||||
|
<ContextProvider {...props}>
|
||||||
|
<Component />
|
||||||
|
</ContextProvider>
|
||||||
|
);
|
||||||
|
|
||||||
|
const withConsumer = Component => ContextConsumer(Component);
|
||||||
|
|
||||||
|
const withDraggableContext = Component => withProvider(withConsumer(Component));
|
||||||
|
|
||||||
|
export {
|
||||||
|
withProvider,
|
||||||
|
withConsumer,
|
||||||
|
withDraggableContext,
|
||||||
|
};
|
@ -161,9 +161,6 @@ class VideoProvider extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { onMount } = this.props;
|
|
||||||
onMount();
|
|
||||||
|
|
||||||
this.checkIceConnectivity();
|
this.checkIceConnectivity();
|
||||||
document.addEventListener('joinVideo', this.shareWebcam); // TODO find a better way to do this
|
document.addEventListener('joinVideo', this.shareWebcam); // TODO find a better way to do this
|
||||||
document.addEventListener('exitVideo', this.unshareWebcam);
|
document.addEventListener('exitVideo', this.unshareWebcam);
|
||||||
@ -1017,14 +1014,10 @@ class VideoProvider extends Component {
|
|||||||
const {
|
const {
|
||||||
users,
|
users,
|
||||||
enableVideoStats,
|
enableVideoStats,
|
||||||
cursor,
|
|
||||||
swapLayout,
|
|
||||||
mediaHeight,
|
mediaHeight,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
return (
|
return (
|
||||||
<VideoList
|
<VideoList
|
||||||
cursor={cursor}
|
|
||||||
swapLayout={swapLayout}
|
|
||||||
mediaHeight={mediaHeight}
|
mediaHeight={mediaHeight}
|
||||||
users={users}
|
users={users}
|
||||||
onMount={this.createVideoTag}
|
onMount={this.createVideoTag}
|
||||||
|
@ -12,6 +12,7 @@ const VideoProviderContainer = ({ children, ...props }) => {
|
|||||||
export default withTracker(props => ({
|
export default withTracker(props => ({
|
||||||
cursor: props.cursor,
|
cursor: props.cursor,
|
||||||
swapLayout: props.swapLayout,
|
swapLayout: props.swapLayout,
|
||||||
|
mediaHeight: props.mediaHeight,
|
||||||
meetingId: VideoService.meetingId(),
|
meetingId: VideoService.meetingId(),
|
||||||
users: VideoService.getAllUsersVideo(),
|
users: VideoService.getAllUsersVideo(),
|
||||||
userId: VideoService.userId(),
|
userId: VideoService.userId(),
|
||||||
@ -19,5 +20,4 @@ export default withTracker(props => ({
|
|||||||
userName: VideoService.userName(),
|
userName: VideoService.userName(),
|
||||||
enableVideoStats: getFromUserSettings('enableVideoStats', Meteor.settings.public.kurento.enableVideoStats),
|
enableVideoStats: getFromUserSettings('enableVideoStats', Meteor.settings.public.kurento.enableVideoStats),
|
||||||
voiceBridge: VideoService.voiceBridge(),
|
voiceBridge: VideoService.voiceBridge(),
|
||||||
onMount: props.onMount,
|
|
||||||
}))(VideoProviderContainer);
|
}))(VideoProviderContainer);
|
||||||
|
@ -5,6 +5,7 @@ import cx from 'classnames';
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { styles } from './styles';
|
import { styles } from './styles';
|
||||||
import VideoListItem from './video-list-item/component';
|
import VideoListItem from './video-list-item/component';
|
||||||
|
import { withConsumer } from '../../media/webcam-draggable-overlay/context';
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
users: PropTypes.arrayOf(PropTypes.object).isRequired,
|
users: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
@ -77,6 +78,14 @@ class VideoList extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
const { webcamDraggableDispatch } = this.props;
|
||||||
|
webcamDraggableDispatch(
|
||||||
|
{
|
||||||
|
type: 'setVideoListRef',
|
||||||
|
value: this.grid,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
this.handleCanvasResize();
|
this.handleCanvasResize();
|
||||||
window.addEventListener('resize', this.handleCanvasResize, false);
|
window.addEventListener('resize', this.handleCanvasResize, false);
|
||||||
}
|
}
|
||||||
@ -93,6 +102,7 @@ class VideoList extends Component {
|
|||||||
}
|
}
|
||||||
const { focusedId } = this.state;
|
const { focusedId } = this.state;
|
||||||
const { width: canvasWidth, height: canvasHeight } = this.canvas.getBoundingClientRect();
|
const { width: canvasWidth, height: canvasHeight } = this.canvas.getBoundingClientRect();
|
||||||
|
|
||||||
const gridGutter = parseInt(window.getComputedStyle(this.grid)
|
const gridGutter = parseInt(window.getComputedStyle(this.grid)
|
||||||
.getPropertyValue('grid-row-gap'), 10);
|
.getPropertyValue('grid-row-gap'), 10);
|
||||||
const hasFocusedItem = numItems > 2 && focusedId;
|
const hasFocusedItem = numItems > 2 && focusedId;
|
||||||
@ -142,8 +152,6 @@ class VideoList extends Component {
|
|||||||
getStats,
|
getStats,
|
||||||
stopGettingStats,
|
stopGettingStats,
|
||||||
enableVideoStats,
|
enableVideoStats,
|
||||||
cursor,
|
|
||||||
swapLayout,
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { focusedId } = this.state;
|
const { focusedId } = this.state;
|
||||||
|
|
||||||
@ -167,9 +175,6 @@ class VideoList extends Component {
|
|||||||
[styles.videoListItem]: true,
|
[styles.videoListItem]: true,
|
||||||
[styles.focused]: focusedId === user.id && users.length > 2,
|
[styles.focused]: focusedId === user.id && users.length > 2,
|
||||||
})}
|
})}
|
||||||
style={{
|
|
||||||
cursor,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<VideoListItem
|
<VideoListItem
|
||||||
numOfUsers={users.length}
|
numOfUsers={users.length}
|
||||||
@ -182,7 +187,6 @@ class VideoList extends Component {
|
|||||||
getStats={(videoRef, callback) => getStats(user.id, videoRef, callback)}
|
getStats={(videoRef, callback) => getStats(user.id, videoRef, callback)}
|
||||||
stopGettingStats={() => stopGettingStats(user.id)}
|
stopGettingStats={() => stopGettingStats(user.id)}
|
||||||
enableVideoStats={enableVideoStats}
|
enableVideoStats={enableVideoStats}
|
||||||
swapLayout={swapLayout}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@ -231,4 +235,4 @@ class VideoList extends Component {
|
|||||||
|
|
||||||
VideoList.propTypes = propTypes;
|
VideoList.propTypes = propTypes;
|
||||||
|
|
||||||
export default injectIntl(VideoList);
|
export default injectIntl(withConsumer(VideoList));
|
||||||
|
@ -39,6 +39,9 @@
|
|||||||
|
|
||||||
.videoListItem {
|
.videoListItem {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
&.focused {
|
&.focused {
|
||||||
grid-column: 1 / span 2;
|
grid-column: 1 / span 2;
|
||||||
@ -50,7 +53,6 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
min-width: 100%;
|
min-width: 100%;
|
||||||
height: 100%;
|
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
@ -83,6 +85,14 @@
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cursorGrab{
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cursorGrabbing{
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
from {
|
from {
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
@ -129,6 +139,7 @@
|
|||||||
|
|
||||||
.media {
|
.media {
|
||||||
@extend %media-area;
|
@extend %media-area;
|
||||||
|
background-color: var(--color-gray);
|
||||||
}
|
}
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
|
@ -16,6 +16,7 @@ import logger from '/imports/startup/client/logger';
|
|||||||
import VideoListItemStats from './video-list-item-stats/component';
|
import VideoListItemStats from './video-list-item-stats/component';
|
||||||
import FullscreenButtonContainer from '../../fullscreen-button/container';
|
import FullscreenButtonContainer from '../../fullscreen-button/container';
|
||||||
import { styles } from '../styles';
|
import { styles } from '../styles';
|
||||||
|
import { withConsumer } from '../../../media/webcam-draggable-overlay/context';
|
||||||
|
|
||||||
const intlMessages = defineMessages({
|
const intlMessages = defineMessages({
|
||||||
connectionStatsLabel: {
|
connectionStatsLabel: {
|
||||||
@ -40,7 +41,15 @@ class VideoListItem extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { onMount } = this.props;
|
const { onMount, webcamDraggableDispatch } = this.props;
|
||||||
|
|
||||||
|
webcamDraggableDispatch(
|
||||||
|
{
|
||||||
|
type: 'setVideoRef',
|
||||||
|
value: this.videoTag,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
onMount(this.videoTag);
|
onMount(this.videoTag);
|
||||||
|
|
||||||
this.videoTag.addEventListener('loadeddata', () => this.setVideoIsReady());
|
this.videoTag.addEventListener('loadeddata', () => this.setVideoIsReady());
|
||||||
@ -130,7 +139,9 @@ class VideoListItem extends Component {
|
|||||||
render() {
|
render() {
|
||||||
const { showStats, stats, videoIsReady } = this.state;
|
const { showStats, stats, videoIsReady } = this.state;
|
||||||
const {
|
const {
|
||||||
user, numOfUsers,
|
user,
|
||||||
|
numOfUsers,
|
||||||
|
webcamDraggableState,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const availableActions = this.getAvailableActions();
|
const availableActions = this.getAvailableActions();
|
||||||
const enableVideoMenu = Meteor.settings.public.kurento.enableVideoMenu || false;
|
const enableVideoMenu = Meteor.settings.public.kurento.enableVideoMenu || false;
|
||||||
@ -149,6 +160,8 @@ class VideoListItem extends Component {
|
|||||||
muted
|
muted
|
||||||
className={cx({
|
className={cx({
|
||||||
[styles.media]: true,
|
[styles.media]: true,
|
||||||
|
[styles.cursorGrab]: !webcamDraggableState.dragging,
|
||||||
|
[styles.cursorGrabbing]: webcamDraggableState.dragging,
|
||||||
})}
|
})}
|
||||||
ref={(ref) => { this.videoTag = ref; }}
|
ref={(ref) => { this.videoTag = ref; }}
|
||||||
autoPlay
|
autoPlay
|
||||||
@ -198,7 +211,7 @@ class VideoListItem extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default injectIntl(VideoListItem);
|
export default injectIntl(withConsumer(VideoListItem));
|
||||||
|
|
||||||
VideoListItem.defaultProps = {
|
VideoListItem.defaultProps = {
|
||||||
numOfUsers: 0,
|
numOfUsers: 0,
|
||||||
|
Loading…
Reference in New Issue
Block a user