Added support for setting separate window in fullscreen
parent
8035fd39bc
commit
ca54a49dd3
|
|
@ -0,0 +1,107 @@
|
|||
const key = {
|
||||
fullscreenEnabled : 0,
|
||||
fullscreenElement : 1,
|
||||
requestFullscreen : 2,
|
||||
exitFullscreen : 3,
|
||||
fullscreenchange : 4,
|
||||
fullscreenerror : 5
|
||||
};
|
||||
|
||||
const webkit = [
|
||||
'webkitFullscreenEnabled',
|
||||
'webkitFullscreenElement',
|
||||
'webkitRequestFullscreen',
|
||||
'webkitExitFullscreen',
|
||||
'webkitfullscreenchange',
|
||||
'webkitfullscreenerror'
|
||||
];
|
||||
|
||||
const moz = [
|
||||
'mozFullScreenEnabled',
|
||||
'mozFullScreenElement',
|
||||
'mozRequestFullScreen',
|
||||
'mozCancelFullScreen',
|
||||
'mozfullscreenchange',
|
||||
'mozfullscreenerror'
|
||||
];
|
||||
|
||||
const ms = [
|
||||
'msFullscreenEnabled',
|
||||
'msFullscreenElement',
|
||||
'msRequestFullscreen',
|
||||
'msExitFullscreen',
|
||||
'MSFullscreenChange',
|
||||
'MSFullscreenError'
|
||||
];
|
||||
|
||||
export default class FullScreen
|
||||
{
|
||||
constructor(document)
|
||||
{
|
||||
this.document = document;
|
||||
this.vendor = (
|
||||
('fullscreenEnabled' in this.document && Object.keys(key)) ||
|
||||
(webkit[0] in this.document && webkit) ||
|
||||
(moz[0] in this.document && moz) ||
|
||||
(ms[0] in this.document && ms) ||
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
requestFullscreen(element)
|
||||
{
|
||||
element[this.vendor[key.requestFullscreen]]();
|
||||
}
|
||||
|
||||
requestFullscreenFunction(element)
|
||||
{
|
||||
element[this.vendor[key.requestFullscreen]];
|
||||
}
|
||||
|
||||
addEventListener(type, handler)
|
||||
{
|
||||
this.document.addEventListener(this.vendor[key[type]], handler);
|
||||
}
|
||||
|
||||
removeEventListener(type, handler)
|
||||
{
|
||||
this.document.removeEventListener(this.vendor[key[type]], handler);
|
||||
}
|
||||
|
||||
get exitFullscreen()
|
||||
{
|
||||
return this.document[this.vendor[key.exitFullscreen]].bind(this.document);
|
||||
}
|
||||
|
||||
get fullscreenEnabled()
|
||||
{
|
||||
return Boolean(this.document[this.vendor[key.fullscreenEnabled]]);
|
||||
}
|
||||
set fullscreenEnabled(val) {}
|
||||
|
||||
get fullscreenElement()
|
||||
{
|
||||
return this.document[this.vendor[key.fullscreenElement]];
|
||||
}
|
||||
set fullscreenElement(val) {}
|
||||
|
||||
get onfullscreenchange()
|
||||
{
|
||||
return this.document[`on${this.vendor[key.fullscreenchange]}`.toLowerCase()];
|
||||
}
|
||||
|
||||
set onfullscreenchange(handler)
|
||||
{
|
||||
this.document[`on${this.vendor[key.fullscreenchange]}`.toLowerCase()] = handler;
|
||||
}
|
||||
|
||||
get onfullscreenerror()
|
||||
{
|
||||
return this.document[`on${this.vendor[key.fullscreenerror]}`.toLowerCase()];
|
||||
}
|
||||
|
||||
set onfullscreenerror(handler)
|
||||
{
|
||||
this.document[`on${this.vendor[key.fullscreenerror]}`.toLowerCase()] = handler;
|
||||
}
|
||||
}
|
||||
|
|
@ -40,7 +40,7 @@ const FullScreenView = (props) =>
|
|||
|
||||
<div className='controls'>
|
||||
<div
|
||||
className={classnames('button', 'fullscreen', 'room-controls', {
|
||||
className={classnames('button', 'exitfullscreen', 'room-controls', {
|
||||
visible : toolbarsVisible
|
||||
})}
|
||||
onClick={(e) =>
|
||||
|
|
|
|||
|
|
@ -4,46 +4,52 @@ import { connect } from 'react-redux';
|
|||
import classnames from 'classnames';
|
||||
import * as appPropTypes from './appPropTypes';
|
||||
import * as requestActions from '../redux/requestActions';
|
||||
import fscreen from 'fscreen';
|
||||
import FullScreen from './FullScreen';
|
||||
|
||||
class Sidebar extends Component
|
||||
{
|
||||
state = {
|
||||
constructor(props)
|
||||
{
|
||||
super(props);
|
||||
|
||||
this.fullscreen = new FullScreen(document);
|
||||
this.state = {
|
||||
fullscreen : false
|
||||
};
|
||||
}
|
||||
|
||||
handleToggleFullscreen = () =>
|
||||
{
|
||||
if (fscreen.fullscreenElement)
|
||||
if (this.fullscreen.fullscreenElement)
|
||||
{
|
||||
fscreen.exitFullscreen();
|
||||
this.fullscreen.exitFullscreen();
|
||||
}
|
||||
else
|
||||
{
|
||||
fscreen.requestFullscreen(document.documentElement);
|
||||
this.fullscreen.requestFullscreen(document.documentElement);
|
||||
}
|
||||
};
|
||||
|
||||
handleFullscreenChange = () =>
|
||||
{
|
||||
this.setState({
|
||||
fullscreen : fscreen.fullscreenElement !== null
|
||||
fullscreen : this.fullscreen.fullscreenElement !== null
|
||||
});
|
||||
};
|
||||
|
||||
componentDidMount()
|
||||
{
|
||||
if (fscreen.fullscreenEnabled)
|
||||
if (this.fullscreen.fullscreenEnabled)
|
||||
{
|
||||
fscreen.addEventListener('fullscreenchange', this.handleFullscreenChange);
|
||||
this.fullscreen.addEventListener('fullscreenchange', this.handleFullscreenChange);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount()
|
||||
{
|
||||
if (fscreen.fullscreenEnabled)
|
||||
if (this.fullscreen.fullscreenEnabled)
|
||||
{
|
||||
fscreen.removeEventListener('fullscreenchange', this.handleFullscreenChange);
|
||||
this.fullscreen.removeEventListener('fullscreenchange', this.handleFullscreenChange);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -85,7 +91,7 @@ class Sidebar extends Component
|
|||
})}
|
||||
data-component='Sidebar'
|
||||
>
|
||||
{fscreen.fullscreenEnabled && (
|
||||
{this.fullscreen.fullscreenEnabled && (
|
||||
<div
|
||||
className={classnames('button', 'fullscreen', {
|
||||
on : this.state.fullscreen
|
||||
|
|
|
|||
|
|
@ -0,0 +1,286 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import FullScreen from '../FullScreen';
|
||||
import classnames from 'classnames';
|
||||
|
||||
class NewWindow extends React.PureComponent
|
||||
{
|
||||
static defaultProps =
|
||||
{
|
||||
url : '',
|
||||
name : '',
|
||||
title : '',
|
||||
features : { width: '800px', height: '600px' },
|
||||
onBlock : null,
|
||||
onUnload : null,
|
||||
center : 'parent',
|
||||
copyStyles : true
|
||||
};
|
||||
|
||||
handleToggleFullscreen = () =>
|
||||
{
|
||||
if (this.fullscreen.fullscreenElement)
|
||||
{
|
||||
this.fullscreen.exitFullscreen();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.fullscreen.requestFullscreen(this.window.document.documentElement);
|
||||
}
|
||||
};
|
||||
|
||||
handleFullscreenChange = () =>
|
||||
{
|
||||
this.setState({
|
||||
fullscreen : this.fullscreen.fullscreenElement !== null
|
||||
});
|
||||
};
|
||||
|
||||
constructor(props)
|
||||
{
|
||||
super(props);
|
||||
|
||||
this.container = document.createElement('div');
|
||||
this.window = null;
|
||||
this.windowCheckerInterval = null;
|
||||
this.released = false;
|
||||
this.fullscreen = null;
|
||||
|
||||
this.state = {
|
||||
mounted : false,
|
||||
fullscreen : false
|
||||
};
|
||||
}
|
||||
|
||||
render()
|
||||
{
|
||||
if (!this.state.mounted)
|
||||
return null;
|
||||
|
||||
return ReactDOM.createPortal([
|
||||
<div key='newwindow' data-component='FullScreenView'>
|
||||
<div className='controls'>
|
||||
{this.fullscreen.fullscreenEnabled && (
|
||||
<div
|
||||
className={classnames('button', {
|
||||
fullscreen : !this.state.fullscreen,
|
||||
exitFullscreen : this.state.fullscreen
|
||||
})}
|
||||
onClick={this.handleToggleFullscreen}
|
||||
data-tip='Fullscreen'
|
||||
data-place='right'
|
||||
data-type='dark'
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{this.props.children}
|
||||
</div>
|
||||
], this.container);
|
||||
}
|
||||
|
||||
componentDidMount()
|
||||
{
|
||||
this.openChild();
|
||||
// eslint-disable-next-line react/no-did-mount-set-state
|
||||
this.setState({ mounted: true });
|
||||
|
||||
this.fullscreen = new FullScreen(this.window.document);
|
||||
|
||||
if (this.fullscreen.fullscreenEnabled)
|
||||
{
|
||||
this.fullscreen.addEventListener('fullscreenchange', this.handleFullscreenChange);
|
||||
}
|
||||
}
|
||||
|
||||
openChild()
|
||||
{
|
||||
const {
|
||||
url,
|
||||
title,
|
||||
name,
|
||||
features,
|
||||
onBlock,
|
||||
center
|
||||
} = this.props;
|
||||
|
||||
if (center === 'parent')
|
||||
{
|
||||
features.left =
|
||||
(window.top.outerWidth / 2) + window.top.screenX - (features.width / 2);
|
||||
features.top =
|
||||
(window.top.outerHeight / 2) + window.top.screenY - (features.height / 2);
|
||||
}
|
||||
else if (center === 'screen')
|
||||
{
|
||||
const screenLeft =
|
||||
window.screenLeft !== undefined ? window.screenLeft : screen.left;
|
||||
const screenTop =
|
||||
window.screenTop !== undefined ? window.screenTop : screen.top;
|
||||
|
||||
const width = window.innerWidth
|
||||
? window.innerWidth
|
||||
: document.documentElement.clientWidth
|
||||
? document.documentElement.clientWidth
|
||||
: screen.width;
|
||||
const height = window.innerHeight
|
||||
? window.innerHeight
|
||||
: document.documentElement.clientHeight
|
||||
? document.documentElement.clientHeight
|
||||
: screen.height;
|
||||
|
||||
features.left = (width / 2) - (features.width / 2) + screenLeft;
|
||||
features.top = (height / 2) - (features.height / 2) + screenTop;
|
||||
}
|
||||
|
||||
this.window = window.open(url, name, toWindowFeatures(features));
|
||||
|
||||
this.windowCheckerInterval = setInterval(() =>
|
||||
{
|
||||
if (!this.window || this.window.closed)
|
||||
{
|
||||
this.release();
|
||||
}
|
||||
}, 50);
|
||||
|
||||
if (this.window)
|
||||
{
|
||||
this.window.document.title = title;
|
||||
this.window.document.body.appendChild(this.container);
|
||||
|
||||
if (this.props.copyStyles)
|
||||
{
|
||||
setTimeout(() => copyStyles(document, this.window.document), 0);
|
||||
}
|
||||
|
||||
this.window.addEventListener('beforeunload', () => this.release());
|
||||
}
|
||||
else if (typeof onBlock === 'function')
|
||||
{
|
||||
onBlock(null);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount()
|
||||
{
|
||||
if (this.window)
|
||||
{
|
||||
if (this.fullscreen.fullscreenEnabled)
|
||||
{
|
||||
this.fullscreen.removeEventListener('fullscreenchange', this.handleFullscreenChange);
|
||||
}
|
||||
|
||||
this.window.close();
|
||||
}
|
||||
}
|
||||
|
||||
release()
|
||||
{
|
||||
if (this.released)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.released = true;
|
||||
|
||||
clearInterval(this.windowCheckerInterval);
|
||||
|
||||
const { onUnload } = this.props;
|
||||
|
||||
if (typeof onUnload === 'function')
|
||||
{
|
||||
onUnload(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NewWindow.propTypes = {
|
||||
children : PropTypes.node,
|
||||
url : PropTypes.string,
|
||||
name : PropTypes.string,
|
||||
title : PropTypes.string,
|
||||
features : PropTypes.object,
|
||||
onUnload : PropTypes.func,
|
||||
onBlock : PropTypes.func,
|
||||
center : PropTypes.oneOf([ 'parent', 'screen' ]),
|
||||
copyStyles : PropTypes.bool
|
||||
};
|
||||
|
||||
function copyStyles(source, target)
|
||||
{
|
||||
Array.from(source.styleSheets).forEach((styleSheet) =>
|
||||
{
|
||||
let rules;
|
||||
|
||||
try
|
||||
{
|
||||
rules = styleSheet.cssRules;
|
||||
}
|
||||
catch (err) {}
|
||||
|
||||
if (rules)
|
||||
{
|
||||
const newStyleEl = source.createElement('style');
|
||||
|
||||
Array.from(styleSheet.cssRules).forEach((cssRule) =>
|
||||
{
|
||||
const { cssText, type } = cssRule;
|
||||
|
||||
let returnText = cssText;
|
||||
|
||||
if ([ 3, 5 ].includes(type))
|
||||
{
|
||||
returnText = cssText
|
||||
.split('url(')
|
||||
.map((line) =>
|
||||
{
|
||||
if (line[1] === '/')
|
||||
{
|
||||
return `${line.slice(0, 1)}${
|
||||
window.location.origin
|
||||
}${line.slice(1)}`;
|
||||
}
|
||||
|
||||
return line;
|
||||
})
|
||||
.join('url(');
|
||||
}
|
||||
|
||||
newStyleEl.appendChild(source.createTextNode(returnText));
|
||||
});
|
||||
|
||||
target.head.appendChild(newStyleEl);
|
||||
}
|
||||
else if (styleSheet.href)
|
||||
{
|
||||
const newLinkEl = source.createElement('link');
|
||||
|
||||
newLinkEl.rel = 'stylesheet';
|
||||
newLinkEl.href = styleSheet.href;
|
||||
target.head.appendChild(newLinkEl);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function toWindowFeatures(obj)
|
||||
{
|
||||
return Object.keys(obj)
|
||||
.reduce((features, name) =>
|
||||
{
|
||||
const value = obj[name];
|
||||
|
||||
if (typeof value === 'boolean')
|
||||
{
|
||||
features.push(`${name}=${value ? 'yes' : 'no'}`);
|
||||
}
|
||||
else
|
||||
{
|
||||
features.push(`${name}=${value}`);
|
||||
}
|
||||
|
||||
return features;
|
||||
}, [])
|
||||
.join(',');
|
||||
}
|
||||
|
||||
export default NewWindow;
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import NewWindow from 'react-new-window';
|
||||
import NewWindow from './NewWindow';
|
||||
import PropTypes from 'prop-types';
|
||||
import classnames from 'classnames';
|
||||
import * as appPropTypes from '../appPropTypes';
|
||||
import * as stateActions from '../../redux/stateActions';
|
||||
import FullView from '../FullView';
|
||||
|
|
@ -12,8 +11,7 @@ const VideoWindow = (props) =>
|
|||
const {
|
||||
advancedMode,
|
||||
consumer,
|
||||
toggleConsumerWindow,
|
||||
toolbarsVisible
|
||||
toggleConsumerWindow
|
||||
} = props;
|
||||
|
||||
if (!consumer)
|
||||
|
|
@ -32,34 +30,12 @@ const VideoWindow = (props) =>
|
|||
|
||||
return (
|
||||
<NewWindow onUnload={toggleConsumerWindow}>
|
||||
<div data-component='FullScreenView'>
|
||||
{consumerVisible && !consumer.supported ?
|
||||
<div className='incompatible-video'>
|
||||
<p>incompatible video</p>
|
||||
</div>
|
||||
:null
|
||||
}
|
||||
|
||||
<div className='controls'>
|
||||
<div
|
||||
className={classnames('button', 'fullscreen', 'room-controls', {
|
||||
visible : toolbarsVisible
|
||||
})}
|
||||
onClick={(e) =>
|
||||
{
|
||||
e.stopPropagation();
|
||||
toggleConsumerWindow();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<FullView
|
||||
advancedMode={advancedMode}
|
||||
videoTrack={consumer ? consumer.track : null}
|
||||
videoVisible={consumerVisible}
|
||||
videoProfile={consumerProfile}
|
||||
/>
|
||||
</div>
|
||||
</NewWindow>
|
||||
);
|
||||
};
|
||||
|
|
@ -68,15 +44,13 @@ VideoWindow.propTypes =
|
|||
{
|
||||
advancedMode : PropTypes.bool,
|
||||
consumer : appPropTypes.Consumer,
|
||||
toggleConsumerWindow : PropTypes.func.isRequired,
|
||||
toolbarsVisible : PropTypes.bool
|
||||
toggleConsumerWindow : PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = (state) =>
|
||||
{
|
||||
return {
|
||||
consumer : state.consumers[state.room.windowConsumer],
|
||||
toolbarsVisible : state.room.toolbarsVisible
|
||||
consumer : state.consumers[state.room.windowConsumer]
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -85,7 +59,7 @@ const mapDispatchToProps = (dispatch) =>
|
|||
return {
|
||||
toggleConsumerWindow : () =>
|
||||
{
|
||||
dispatch(stateActions.toggleConsumerWindow(null));
|
||||
dispatch(stateActions.toggleConsumerWindow());
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@
|
|||
"domready": "^1.0.8",
|
||||
"drag-drop": "^4.2.0",
|
||||
"file-saver": "^1.3.8",
|
||||
"fscreen": "^1.0.2",
|
||||
"hark": "^1.2.2",
|
||||
"js-cookie": "^2.2.0",
|
||||
"magnet-uri": "^5.2.3",
|
||||
|
|
|
|||
|
|
@ -44,10 +44,15 @@
|
|||
height: 5vmin;
|
||||
}
|
||||
|
||||
&.fullscreen {
|
||||
&.exitfullscreen {
|
||||
background-image: url('/resources/images/icon_fullscreen_exit_black.svg');
|
||||
background-color: rgba(#fff, 0.7);
|
||||
}
|
||||
|
||||
&.fullscreen {
|
||||
background-image: url('/resources/images/icon_fullscreen_black.svg');
|
||||
background-color: rgba(#fff, 0.7);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue