Added inline chrome extension install. Added screen sharing button. Added new actions to handle screen sharing.

master
Håvar Aambø Fosstveit 2018-03-04 22:11:03 +01:00
parent 687e85c3ec
commit 56ce361e3c
10 changed files with 240 additions and 51 deletions

View File

@ -16,7 +16,7 @@
if (window.antiglobal) if (window.antiglobal)
{ {
window.antiglobal('___browserSync___oldSocketIo', 'io', '___browserSync___', '__core-js_shared__'); window.antiglobal('__multipartyMeetingScreenShareExtensionAvailable__', '___browserSync___oldSocketIo', 'io', '___browserSync___', '__core-js_shared__');
setInterval(window.antiglobal, 180000); setInterval(window.antiglobal, 180000);
} }
</script> </script>

View File

@ -6,7 +6,6 @@ import { getProtooUrl } from './urlFactory';
import * as cookiesManager from './cookiesManager'; import * as cookiesManager from './cookiesManager';
import * as requestActions from './redux/requestActions'; import * as requestActions from './redux/requestActions';
import * as stateActions from './redux/stateActions'; import * as stateActions from './redux/stateActions';
import { getBrowserType } from './utils';
const logger = new Logger('RoomClient'); const logger = new Logger('RoomClient');
@ -81,8 +80,7 @@ export default class RoomClient
// Local Webcam. Object with: // Local Webcam. Object with:
// - {MediaDeviceInfo} [device] // - {MediaDeviceInfo} [device]
// - {String} [resolution] - 'qvga' / 'vga' / 'hd'. // - {String} [resolution] - 'qvga' / 'vga' / 'hd'.
this._webcam = this._webcam = {
{
device : null, device : null,
resolution : 'hd' resolution : 'hd'
}; };
@ -195,6 +193,57 @@ export default class RoomClient
this._micProducer.resume(); this._micProducer.resume();
} }
installExtension()
{
logger.debug('installExtension()');
return Promise.resolve()
.then(() =>
{
window.addEventListener('message', _onExtensionMessage, false);
function _onExtensionMessage({ data })
{
if (data.type === 'ScreenShareInjected')
{
logger.debug('installExtension() | installation succeeded');
return;
}
}
function _failedInstall(reason)
{
window.removeEventListener('message', _onExtensionMessage);
return Promise.reject(
new Error('Failed to install extension: %s', reason));
}
function _successfulInstall()
{
logger.debug('installExtension() | installation accepted');
}
// eslint-disable-next-line no-undef
chrome.webstore.install(null, _successfulInstall, _failedInstall);
})
.then(() =>
{
this._dispatch(stateActions.setScreenCapabilities(
{
canShareScreen : true,
needExtension : false
}));
})
.catch((error) =>
{
logger.error('enableScreenSharing() | failed: %o', error);
this._dispatch(
stateActions.setScreenShareInProgress(false));
});
}
enableScreenSharing() enableScreenSharing()
{ {
logger.debug('enableScreenSharing()'); logger.debug('enableScreenSharing()');
@ -203,37 +252,6 @@ export default class RoomClient
stateActions.setScreenShareInProgress(true)); stateActions.setScreenShareInProgress(true));
return Promise.resolve() return Promise.resolve()
.then(() =>
{
const browser = getBrowserType();
switch (browser)
{
case 'chrome':
{
// Check if we have extension, if not, try to install
// if (!('__multipartyMeetingScreenShareExtensionAvailable__' in window))
// {
// window.addEventListener('message', function(ev)
// {
// if (ev.data.type === 'ScreenShareInjected')
// {
// }
// }, false);
// }
break;
}
case 'firefox':
{
break;
}
default:
{
return Promise.reject(
new Error('Unsupported browser for screen sharing'));
}
}
})
.then(() => .then(() =>
{ {
return this._setScreenShareProducer(); return this._setScreenShareProducer();
@ -820,9 +838,14 @@ export default class RoomClient
// Set our media capabilities. // Set our media capabilities.
this._dispatch(stateActions.setMediaCapabilities( this._dispatch(stateActions.setMediaCapabilities(
{ {
canSendMic : this._room.canSend('audio'), canSendMic : this._room.canSend('audio'),
canSendWebcam : this._room.canSend('video'), canSendWebcam : this._room.canSend('video')
canShareScreen : this._room.canSend('video') }));
this._dispatch(stateActions.setScreenCapabilities(
{
canShareScreen : this._room.canSend('video') &&
this._screenSharing.isScreenShareAvailable(),
needExtension : this._screenSharing.needExtension()
})); }));
}) })
.then(() => .then(() =>
@ -1007,7 +1030,8 @@ export default class RoomClient
return Promise.resolve() return Promise.resolve()
.then(() => .then(() =>
{ {
const available = this._screenSharing.isScreenShareAvailable(); const available = this._screenSharing.isScreenShareAvailable() &&
!this._screenSharing.needExtension();
if (!available) if (!available)
throw new Error('screen sharing not available'); throw new Error('screen sharing not available');

View File

@ -63,6 +63,16 @@ class ChromeScreenShare
return false; return false;
} }
needExtension()
{
if ('__multipartyMeetingScreenShareExtensionAvailable__' in window)
{
return false;
}
return true;
}
_toConstraints(options, streamId) _toConstraints(options, streamId)
{ {
@ -135,6 +145,11 @@ class FirefoxScreenShare
return true; return true;
} }
needExtension()
{
return false;
}
_toConstraints(options) _toConstraints(options)
{ {
const constraints = { const constraints = {

View File

@ -20,12 +20,27 @@ class Room extends React.Component
room, room,
me, me,
amActiveSpeaker, amActiveSpeaker,
screenProducer,
onRoomLinkCopy, onRoomLinkCopy,
onSetAudioMode, onSetAudioMode,
onRestartIce, onRestartIce,
onLeaveMeeting onLeaveMeeting,
onShareScreen,
onUnShareScreen,
onNeedExtension
} = this.props; } = this.props;
let screenState;
if (me.needExtension)
screenState = 'need-extension';
else if (!me.canShareScreen)
screenState = 'unsupported';
else if (screenProducer)
screenState = 'on';
else
screenState = 'off';
return ( return (
<Appear duration={300}> <Appear duration={300}>
<div data-component='Room'> <div data-component='Room'>
@ -79,6 +94,37 @@ class Room extends React.Component
</div> </div>
<div className='sidebar'> <div className='sidebar'>
<div
className={classnames('button', 'screen', screenState)}
data-tip='Toggle screen sharing'
data-type='dark'
onClick={() =>
{
switch (screenState)
{
case 'on':
{
onUnShareScreen();
break;
}
case 'off':
{
onShareScreen();
break;
}
case 'need-extension':
{
onNeedExtension();
break;
}
default:
{
break;
}
}
}}
/>
<div <div
className={classnames('button', 'audio-only', { className={classnames('button', 'audio-only', {
on : me.audioOnly, on : me.audioOnly,
@ -122,18 +168,27 @@ Room.propTypes =
room : appPropTypes.Room.isRequired, room : appPropTypes.Room.isRequired,
me : appPropTypes.Me.isRequired, me : appPropTypes.Me.isRequired,
amActiveSpeaker : PropTypes.bool.isRequired, amActiveSpeaker : PropTypes.bool.isRequired,
screenProducer : appPropTypes.Producer,
onRoomLinkCopy : PropTypes.func.isRequired, onRoomLinkCopy : PropTypes.func.isRequired,
onSetAudioMode : PropTypes.func.isRequired, onSetAudioMode : PropTypes.func.isRequired,
onRestartIce : PropTypes.func.isRequired, onRestartIce : PropTypes.func.isRequired,
onLeaveMeeting : PropTypes.func.isRequired onLeaveMeeting : PropTypes.func.isRequired,
onShareScreen : PropTypes.func.isRequired,
onUnShareScreen : PropTypes.func.isRequired,
onNeedExtension : PropTypes.func.isRequired
}; };
const mapStateToProps = (state) => const mapStateToProps = (state) =>
{ {
const producersArray = Object.values(state.producers);
const screenProducer =
producersArray.find((producer) => producer.source === 'screen');
return { return {
room : state.room, room : state.room,
me : state.me, me : state.me,
amActiveSpeaker : state.me.name === state.room.activeSpeakerName amActiveSpeaker : state.me.name === state.room.activeSpeakerName,
screenProducer : screenProducer
}; };
}; };
@ -161,6 +216,18 @@ const mapDispatchToProps = (dispatch) =>
onLeaveMeeting : () => onLeaveMeeting : () =>
{ {
dispatch(requestActions.leaveRoom()); dispatch(requestActions.leaveRoom());
},
onShareScreen : () =>
{
dispatch(requestActions.enableScreenSharing());
},
onUnShareScreen : () =>
{
dispatch(requestActions.disableScreenSharing());
},
onNeedExtension : () =>
{
dispatch(requestActions.installExtension());
} }
}; };
}; };

View File

@ -6,7 +6,8 @@ const initialState =
device : null, device : null,
canSendMic : false, canSendMic : false,
canSendWebcam : false, canSendWebcam : false,
canShareScreen : true, canShareScreen : false,
needExtension : false,
canChangeWebcam : false, canChangeWebcam : false,
webcamInProgress : false, webcamInProgress : false,
screenShareInProgress : false, screenShareInProgress : false,
@ -28,9 +29,16 @@ const me = (state = initialState, action) =>
case 'SET_MEDIA_CAPABILITIES': case 'SET_MEDIA_CAPABILITIES':
{ {
const { canSendMic, canSendWebcam, canShareScreen } = action.payload; const { canSendMic, canSendWebcam } = action.payload;
return { ...state, canSendMic, canSendWebcam, canShareScreen }; return { ...state, canSendMic, canSendWebcam };
}
case 'SET_SCREEN_CAPABILITIES':
{
const { canShareScreen, needExtension } = action.payload;
return { ...state, canShareScreen, needExtension };
} }
case 'SET_CAN_CHANGE_WEBCAM': case 'SET_CAN_CHANGE_WEBCAM':

View File

@ -99,6 +99,13 @@ export const disableScreenSharing = () =>
}; };
}; };
export const installExtension = () =>
{
return {
type : 'INSTALL_EXTENSION'
};
};
export const sendChatMessage = (text, name) => export const sendChatMessage = (text, name) =>
{ {
const message = createNewMessage(text, 'response', name); const message = createNewMessage(text, 'response', name);

View File

@ -123,6 +123,13 @@ export default ({ dispatch, getState }) => (next) =>
break; break;
} }
case 'INSTALL_EXTENSION':
{
client.installExtension();
break;
}
case 'SEND_CHAT_MESSAGE': case 'SEND_CHAT_MESSAGE':
{ {
const { message } = action.payload; const { message } = action.payload;

View File

@ -30,11 +30,19 @@ export const setMe = ({ peerName, displayName, displayNameSet, device }) =>
}; };
}; };
export const setMediaCapabilities = ({ canSendMic, canSendWebcam, canShareScreen }) => export const setMediaCapabilities = ({ canSendMic, canSendWebcam }) =>
{ {
return { return {
type : 'SET_MEDIA_CAPABILITIES', type : 'SET_MEDIA_CAPABILITIES',
payload : { canSendMic, canSendWebcam, canShareScreen } payload : { canSendMic, canSendWebcam }
};
};
export const setScreenCapabilities = ({ canShareScreen, needExtension }) =>
{
return {
type : 'SET_SCREEN_CAPABILITIES',
payload : { canShareScreen, needExtension }
}; };
}; };

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 83.208 83.208" style="enable-background:new 0 0 83.208 83.208;" xml:space="preserve">
<g>
<g>
<g>
<polygon style="fill:#D80027;" points="25.052,69.154 17.894,76.312 53.683,76.312 46.525,69.154 "/>
</g>
<g>
<path style="fill:#D80027;" d="M64.419,6.896c-7.831,0-14.537,4.814-17.357,11.631H0v46.525h71.577v-22.01
c6.814-2.82,11.631-9.53,11.631-17.357C83.208,15.325,74.78,6.896,64.419,6.896z M64.419,42.685c-9.373,0-17-7.627-17-17
s7.627-17,17-17s17,7.627,17,17S73.792,42.685,64.419,42.685z"/>
</g>
<g>
<polygon style="fill:#D80027;" points="66.338,29.372 67.068,14.258 61.764,14.258 62.533,29.372 "/>
</g>
<g>
<path style="fill:#D80027;" d="M64.434,31.039c-1.764,0-3.003,1.267-3.003,3.035c0,1.732,1.199,3.035,3.003,3.035
c1.804,0,2.97-1.303,2.97-3.035C67.368,32.306,66.202,31.039,64.434,31.039z"/>
</g>
</g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -234,20 +234,20 @@
&.screen { &.screen {
&.on { &.on {
background-image: url('/resources/images/unshare-screen.svg'); background-image: url('/resources/images/no-share-screen-black.svg');
} }
&.off { &.off {
background-image: url('/resources/images/share-screen.svg'); background-image: url('/resources/images/share-screen-black.svg');
} }
&.unsupported { &.unsupported {
background-image: url('/resources/images/unshare-screen.svg'); background-image: url('/resources/images/no-share-screen-white.svg');
background-color: rgba(#d42241, 0.7); background-color: rgba(#d42241, 0.7);
} }
&.need-extension { &.need-extension {
background-image: url('/resources/images/share-screen.svg'); background-image: url('/resources/images/share-screen-extension.svg');
} }
} }