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)
{
window.antiglobal('___browserSync___oldSocketIo', 'io', '___browserSync___', '__core-js_shared__');
window.antiglobal('__multipartyMeetingScreenShareExtensionAvailable__', '___browserSync___oldSocketIo', 'io', '___browserSync___', '__core-js_shared__');
setInterval(window.antiglobal, 180000);
}
</script>

View File

@ -6,7 +6,6 @@ import { getProtooUrl } from './urlFactory';
import * as cookiesManager from './cookiesManager';
import * as requestActions from './redux/requestActions';
import * as stateActions from './redux/stateActions';
import { getBrowserType } from './utils';
const logger = new Logger('RoomClient');
@ -81,8 +80,7 @@ export default class RoomClient
// Local Webcam. Object with:
// - {MediaDeviceInfo} [device]
// - {String} [resolution] - 'qvga' / 'vga' / 'hd'.
this._webcam =
{
this._webcam = {
device : null,
resolution : 'hd'
};
@ -195,6 +193,57 @@ export default class RoomClient
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()
{
logger.debug('enableScreenSharing()');
@ -203,37 +252,6 @@ export default class RoomClient
stateActions.setScreenShareInProgress(true));
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(() =>
{
return this._setScreenShareProducer();
@ -821,8 +839,13 @@ export default class RoomClient
this._dispatch(stateActions.setMediaCapabilities(
{
canSendMic : this._room.canSend('audio'),
canSendWebcam : this._room.canSend('video'),
canShareScreen : this._room.canSend('video')
canSendWebcam : this._room.canSend('video')
}));
this._dispatch(stateActions.setScreenCapabilities(
{
canShareScreen : this._room.canSend('video') &&
this._screenSharing.isScreenShareAvailable(),
needExtension : this._screenSharing.needExtension()
}));
})
.then(() =>
@ -1007,7 +1030,8 @@ export default class RoomClient
return Promise.resolve()
.then(() =>
{
const available = this._screenSharing.isScreenShareAvailable();
const available = this._screenSharing.isScreenShareAvailable() &&
!this._screenSharing.needExtension();
if (!available)
throw new Error('screen sharing not available');

View File

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

View File

@ -20,12 +20,27 @@ class Room extends React.Component
room,
me,
amActiveSpeaker,
screenProducer,
onRoomLinkCopy,
onSetAudioMode,
onRestartIce,
onLeaveMeeting
onLeaveMeeting,
onShareScreen,
onUnShareScreen,
onNeedExtension
} = 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 (
<Appear duration={300}>
<div data-component='Room'>
@ -79,6 +94,37 @@ class Room extends React.Component
</div>
<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
className={classnames('button', 'audio-only', {
on : me.audioOnly,
@ -122,18 +168,27 @@ Room.propTypes =
room : appPropTypes.Room.isRequired,
me : appPropTypes.Me.isRequired,
amActiveSpeaker : PropTypes.bool.isRequired,
screenProducer : appPropTypes.Producer,
onRoomLinkCopy : PropTypes.func.isRequired,
onSetAudioMode : 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 producersArray = Object.values(state.producers);
const screenProducer =
producersArray.find((producer) => producer.source === 'screen');
return {
room : state.room,
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 : () =>
{
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,
canSendMic : false,
canSendWebcam : false,
canShareScreen : true,
canShareScreen : false,
needExtension : false,
canChangeWebcam : false,
webcamInProgress : false,
screenShareInProgress : false,
@ -28,9 +29,16 @@ const me = (state = initialState, action) =>
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':

View File

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

View File

@ -123,6 +123,13 @@ export default ({ dispatch, getState }) => (next) =>
break;
}
case 'INSTALL_EXTENSION':
{
client.installExtension();
break;
}
case 'SEND_CHAT_MESSAGE':
{
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 {
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 {
&.on {
background-image: url('/resources/images/unshare-screen.svg');
background-image: url('/resources/images/no-share-screen-black.svg');
}
&.off {
background-image: url('/resources/images/share-screen.svg');
background-image: url('/resources/images/share-screen-black.svg');
}
&.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);
}
&.need-extension {
background-image: url('/resources/images/share-screen.svg');
background-image: url('/resources/images/share-screen-extension.svg');
}
}