mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1122032 Part 1 - Setup minimal screen sharing for Loop from desktop (disabled by default). r=mikedeboer
This commit is contained in:
parent
7cfa8db02a
commit
bd23ef68d0
@ -1653,6 +1653,7 @@ pref("shumway.disabled", true);
|
||||
pref("image.mem.max_decoded_image_kb", 256000);
|
||||
|
||||
pref("loop.enabled", true);
|
||||
pref("loop.screenshare.enabled", false);
|
||||
pref("loop.server", "https://loop.services.mozilla.com/v0");
|
||||
pref("loop.seenToS", "unseen");
|
||||
pref("loop.showPartnerLogo", true);
|
||||
|
@ -28,7 +28,6 @@
|
||||
<script type="text/javascript" src="loop/shared/js/utils.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/models.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/mixins.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/views.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/feedbackApiClient.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/actions.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/validate.js"></script>
|
||||
@ -41,6 +40,7 @@
|
||||
<script type="text/javascript" src="loop/shared/js/fxOSActiveRoomStore.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/activeRoomStore.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/feedbackStore.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/views.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/feedbackViews.js"></script>
|
||||
<script type="text/javascript" src="loop/js/conversationViews.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/websocket.js"></script>
|
||||
|
@ -42,7 +42,8 @@ loop.conversation = (function(mozL10n) {
|
||||
conversationStore: React.PropTypes.instanceOf(loop.store.ConversationStore)
|
||||
.isRequired,
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
roomStore: React.PropTypes.instanceOf(loop.store.RoomStore)
|
||||
roomStore: React.PropTypes.instanceOf(loop.store.RoomStore),
|
||||
mozLoop: React.PropTypes.object.isRequired,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
@ -78,6 +79,7 @@ loop.conversation = (function(mozL10n) {
|
||||
case "room": {
|
||||
return (React.createElement(DesktopRoomConversationView, {
|
||||
dispatcher: this.props.dispatcher,
|
||||
mozLoop: this.props.mozLoop,
|
||||
roomStore: this.props.roomStore}
|
||||
));
|
||||
}
|
||||
@ -189,7 +191,8 @@ loop.conversation = (function(mozL10n) {
|
||||
client: client,
|
||||
conversation: conversation,
|
||||
dispatcher: dispatcher,
|
||||
sdk: window.OT}
|
||||
sdk: window.OT,
|
||||
mozLoop: navigator.mozLoop}
|
||||
), document.querySelector('#main'));
|
||||
|
||||
dispatcher.dispatch(new sharedActions.GetWindowData({
|
||||
|
@ -42,7 +42,8 @@ loop.conversation = (function(mozL10n) {
|
||||
conversationStore: React.PropTypes.instanceOf(loop.store.ConversationStore)
|
||||
.isRequired,
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
roomStore: React.PropTypes.instanceOf(loop.store.RoomStore)
|
||||
roomStore: React.PropTypes.instanceOf(loop.store.RoomStore),
|
||||
mozLoop: React.PropTypes.object.isRequired,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
@ -78,6 +79,7 @@ loop.conversation = (function(mozL10n) {
|
||||
case "room": {
|
||||
return (<DesktopRoomConversationView
|
||||
dispatcher={this.props.dispatcher}
|
||||
mozLoop={this.props.mozLoop}
|
||||
roomStore={this.props.roomStore}
|
||||
/>);
|
||||
}
|
||||
@ -190,6 +192,7 @@ loop.conversation = (function(mozL10n) {
|
||||
conversation={conversation}
|
||||
dispatcher={dispatcher}
|
||||
sdk={window.OT}
|
||||
mozLoop={navigator.mozLoop}
|
||||
/>, document.querySelector('#main'));
|
||||
|
||||
dispatcher.dispatch(new sharedActions.GetWindowData({
|
||||
|
@ -14,6 +14,7 @@ loop.roomViews = (function(mozL10n) {
|
||||
var sharedActions = loop.shared.actions;
|
||||
var sharedMixins = loop.shared.mixins;
|
||||
var ROOM_STATES = loop.store.ROOM_STATES;
|
||||
var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
|
||||
var sharedViews = loop.shared.views;
|
||||
|
||||
/**
|
||||
@ -169,7 +170,8 @@ loop.roomViews = (function(mozL10n) {
|
||||
],
|
||||
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
mozLoop: React.PropTypes.object.isRequired,
|
||||
},
|
||||
|
||||
_renderInvitationOverlay: function() {
|
||||
@ -193,6 +195,7 @@ loop.roomViews = (function(mozL10n) {
|
||||
publishVideo: !this.state.videoMuted
|
||||
}),
|
||||
getLocalElementFunc: this._getElement.bind(this, ".local"),
|
||||
getScreenShareElementFunc: this._getElement.bind(this, ".screen"),
|
||||
getRemoteElementFunc: this._getElement.bind(this, ".remote")
|
||||
}));
|
||||
}
|
||||
@ -238,6 +241,11 @@ loop.roomViews = (function(mozL10n) {
|
||||
"room-preview": this.state.roomState !== ROOM_STATES.HAS_PARTICIPANTS
|
||||
});
|
||||
|
||||
var screenShareData = {
|
||||
state: this.state.screenSharingState,
|
||||
visible: this.props.mozLoop.getLoopPref("screenshare.enabled")
|
||||
};
|
||||
|
||||
switch(this.state.roomState) {
|
||||
case ROOM_STATES.FAILED:
|
||||
case ROOM_STATES.FULL: {
|
||||
@ -268,13 +276,16 @@ loop.roomViews = (function(mozL10n) {
|
||||
React.createElement("div", {className: "video_wrapper remote_wrapper"},
|
||||
React.createElement("div", {className: "video_inner remote"})
|
||||
),
|
||||
React.createElement("div", {className: localStreamClasses})
|
||||
React.createElement("div", {className: localStreamClasses}),
|
||||
React.createElement("div", {className: "screen hide"})
|
||||
),
|
||||
React.createElement(sharedViews.ConversationToolbar, {
|
||||
dispatcher: this.props.dispatcher,
|
||||
video: {enabled: !this.state.videoMuted, visible: true},
|
||||
audio: {enabled: !this.state.audioMuted, visible: true},
|
||||
publishStream: this.publishStream,
|
||||
hangup: this.leaveRoom})
|
||||
hangup: this.leaveRoom,
|
||||
screenShare: screenShareData})
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -14,6 +14,7 @@ loop.roomViews = (function(mozL10n) {
|
||||
var sharedActions = loop.shared.actions;
|
||||
var sharedMixins = loop.shared.mixins;
|
||||
var ROOM_STATES = loop.store.ROOM_STATES;
|
||||
var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
|
||||
var sharedViews = loop.shared.views;
|
||||
|
||||
/**
|
||||
@ -169,7 +170,8 @@ loop.roomViews = (function(mozL10n) {
|
||||
],
|
||||
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
mozLoop: React.PropTypes.object.isRequired,
|
||||
},
|
||||
|
||||
_renderInvitationOverlay: function() {
|
||||
@ -193,6 +195,7 @@ loop.roomViews = (function(mozL10n) {
|
||||
publishVideo: !this.state.videoMuted
|
||||
}),
|
||||
getLocalElementFunc: this._getElement.bind(this, ".local"),
|
||||
getScreenShareElementFunc: this._getElement.bind(this, ".screen"),
|
||||
getRemoteElementFunc: this._getElement.bind(this, ".remote")
|
||||
}));
|
||||
}
|
||||
@ -238,6 +241,11 @@ loop.roomViews = (function(mozL10n) {
|
||||
"room-preview": this.state.roomState !== ROOM_STATES.HAS_PARTICIPANTS
|
||||
});
|
||||
|
||||
var screenShareData = {
|
||||
state: this.state.screenSharingState,
|
||||
visible: this.props.mozLoop.getLoopPref("screenshare.enabled")
|
||||
};
|
||||
|
||||
switch(this.state.roomState) {
|
||||
case ROOM_STATES.FAILED:
|
||||
case ROOM_STATES.FULL: {
|
||||
@ -269,12 +277,15 @@ loop.roomViews = (function(mozL10n) {
|
||||
<div className="video_inner remote"></div>
|
||||
</div>
|
||||
<div className={localStreamClasses}></div>
|
||||
<div className="screen hide"></div>
|
||||
</div>
|
||||
<sharedViews.ConversationToolbar
|
||||
dispatcher={this.props.dispatcher}
|
||||
video={{enabled: !this.state.videoMuted, visible: true}}
|
||||
audio={{enabled: !this.state.audioMuted, visible: true}}
|
||||
publishStream={this.publishStream}
|
||||
hangup={this.leaveRoom} />
|
||||
hangup={this.leaveRoom}
|
||||
screenShare={screenShareData} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -43,6 +43,11 @@
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.btn-screen-share-entry {
|
||||
float: right !important;
|
||||
border-left: 1px solid #5a5a5a;
|
||||
}
|
||||
|
||||
.conversation-toolbar-btn-box {
|
||||
border-right: 1px solid #5a5a5a;
|
||||
}
|
||||
@ -147,11 +152,11 @@
|
||||
}
|
||||
|
||||
/* Common media control buttons behavior */
|
||||
.conversation-toolbar .media-control {
|
||||
.conversation-toolbar .transparent-button {
|
||||
background-color: transparent;
|
||||
opacity: 1;
|
||||
}
|
||||
.conversation-toolbar .media-control:hover {
|
||||
.conversation-toolbar .transparent-button:hover {
|
||||
background-color: rgba(255,255,255,.35);
|
||||
opacity: 1;
|
||||
}
|
||||
@ -192,6 +197,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Screen share button */
|
||||
.btn-screen-share {
|
||||
/* XXX Replace this with the real button: bug 1126286 */
|
||||
background-image: url(../img/video-inverse-14x14.png);
|
||||
}
|
||||
|
||||
.btn-screen-share.active {
|
||||
background-color: #6CB23E;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.btn-screen-share.disabled {
|
||||
/* XXX Add css here for disabled state: bug 1126286 */
|
||||
}
|
||||
|
||||
.fx-embedded .remote_wrapper {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
|
@ -160,6 +160,9 @@ loop.shared.actions = (function() {
|
||||
publisherConfig: Object,
|
||||
// The local stream element
|
||||
getLocalElementFunc: Function,
|
||||
// The screen share element; optional until all conversation
|
||||
// types support it.
|
||||
// getScreenShareElementFunc: Function,
|
||||
// The remote stream element
|
||||
getRemoteElementFunc: Function
|
||||
}),
|
||||
@ -195,6 +198,26 @@ loop.shared.actions = (function() {
|
||||
enabled: Boolean
|
||||
}),
|
||||
|
||||
/**
|
||||
* Used to start a screen share.
|
||||
*/
|
||||
StartScreenShare: Action.define("startScreenShare", {
|
||||
}),
|
||||
|
||||
/**
|
||||
* Used to end a screen share.
|
||||
*/
|
||||
EndScreenShare: Action.define("endScreenShare", {
|
||||
}),
|
||||
|
||||
/**
|
||||
* Used to notifiy that screen sharing is active or not.
|
||||
*/
|
||||
ScreenSharingState: Action.define("screenSharingState", {
|
||||
// One of loop.shared.utils.SCREEN_SHARE_STATES.
|
||||
state: String
|
||||
}),
|
||||
|
||||
/**
|
||||
* Creates a new room.
|
||||
* XXX: should move to some roomActions module - refs bug 1079284
|
||||
|
@ -12,12 +12,14 @@ loop.store.ActiveRoomStore = (function() {
|
||||
|
||||
var sharedActions = loop.shared.actions;
|
||||
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
|
||||
var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
|
||||
|
||||
// Error numbers taken from
|
||||
// https://github.com/mozilla-services/loop-server/blob/master/loop/errno.json
|
||||
var REST_ERRNOS = loop.shared.utils.REST_ERRNOS;
|
||||
|
||||
var ROOM_STATES = loop.store.ROOM_STATES;
|
||||
|
||||
/**
|
||||
* Active room store.
|
||||
*
|
||||
@ -70,7 +72,8 @@ loop.store.ActiveRoomStore = (function() {
|
||||
// anyone is not considered as 'used'
|
||||
used: false,
|
||||
localVideoDimensions: {},
|
||||
remoteVideoDimensions: {}
|
||||
remoteVideoDimensions: {},
|
||||
screenSharingState: SCREEN_SHARE_STATES.INACTIVE
|
||||
};
|
||||
},
|
||||
|
||||
@ -117,6 +120,7 @@ loop.store.ActiveRoomStore = (function() {
|
||||
"connectedToSdkServers",
|
||||
"connectionFailure",
|
||||
"setMute",
|
||||
"screenSharingState",
|
||||
"remotePeerDisconnected",
|
||||
"remotePeerConnected",
|
||||
"windowUnload",
|
||||
@ -369,6 +373,13 @@ loop.store.ActiveRoomStore = (function() {
|
||||
this.setStoreState(muteState);
|
||||
},
|
||||
|
||||
/**
|
||||
* Used to note the current screensharing state.
|
||||
*/
|
||||
screenSharingState: function(actionData) {
|
||||
this.setStoreState({screenSharingState: actionData.state});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles recording when a remote peer has connected to the servers.
|
||||
*/
|
||||
|
@ -10,6 +10,7 @@ loop.OTSdkDriver = (function() {
|
||||
var sharedActions = loop.shared.actions;
|
||||
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
|
||||
var STREAM_PROPERTIES = loop.shared.utils.STREAM_PROPERTIES;
|
||||
var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
|
||||
|
||||
/**
|
||||
* This is a wrapper for the OT sdk. It is used to translate the SDK events into
|
||||
@ -30,11 +31,21 @@ loop.OTSdkDriver = (function() {
|
||||
|
||||
this.dispatcher.register(this, [
|
||||
"setupStreamElements",
|
||||
"setMute"
|
||||
"setMute",
|
||||
"startScreenShare",
|
||||
"endScreenShare"
|
||||
]);
|
||||
};
|
||||
|
||||
OTSdkDriver.prototype = {
|
||||
/**
|
||||
* Clones the publisher config into a new object, as the sdk modifies the
|
||||
* properties object.
|
||||
*/
|
||||
_getCopyPublisherConfig: function() {
|
||||
return _.extend({}, this.publisherConfig);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the setupStreamElements action. Saves the required data and
|
||||
* kicks off the initialising of the publisher.
|
||||
@ -44,6 +55,7 @@ loop.OTSdkDriver = (function() {
|
||||
*/
|
||||
setupStreamElements: function(actionData) {
|
||||
this.getLocalElement = actionData.getLocalElementFunc;
|
||||
this.getScreenShareElementFunc = actionData.getScreenShareElementFunc;
|
||||
this.getRemoteElement = actionData.getRemoteElementFunc;
|
||||
this.publisherConfig = actionData.publisherConfig;
|
||||
|
||||
@ -51,7 +63,7 @@ loop.OTSdkDriver = (function() {
|
||||
// the initial connect of the session. This saves time when setting up
|
||||
// the media.
|
||||
this.publisher = this.sdk.initPublisher(this.getLocalElement(),
|
||||
this.publisherConfig);
|
||||
this._getCopyPublisherConfig());
|
||||
this.publisher.on("streamCreated", this._onLocalStreamCreated.bind(this));
|
||||
this.publisher.on("accessAllowed", this._onPublishComplete.bind(this));
|
||||
this.publisher.on("accessDenied", this._onPublishDenied.bind(this));
|
||||
@ -74,6 +86,41 @@ loop.OTSdkDriver = (function() {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Initiates a screen sharing publisher.
|
||||
*/
|
||||
startScreenShare: function() {
|
||||
this.dispatcher.dispatch(new sharedActions.ScreenSharingState({
|
||||
state: SCREEN_SHARE_STATES.PENDING
|
||||
}));
|
||||
|
||||
var config = this._getCopyPublisherConfig();
|
||||
// This is temporary until we get a sharing type selector
|
||||
config.videoSource = "window";
|
||||
|
||||
this.screenshare = this.sdk.initPublisher(this.getScreenShareElementFunc(),
|
||||
config);
|
||||
this.screenshare.on("accessAllowed", this._onScreenShareGranted.bind(this));
|
||||
this.screenshare.on("accessDenied", this._onScreenShareDenied.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Ends an active screenshare session.
|
||||
*/
|
||||
endScreenShare: function() {
|
||||
if (!this.screenshare) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.session.unpublish(this.screenshare);
|
||||
this.screenshare.off("accessAllowed accessDenied");
|
||||
this.screenshare.destroy();
|
||||
delete this.screenshare;
|
||||
this.dispatcher.dispatch(new sharedActions.ScreenSharingState({
|
||||
state: SCREEN_SHARE_STATES.INACTIVE
|
||||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* Connects a session for the SDK, listening to the required events.
|
||||
*
|
||||
@ -104,6 +151,8 @@ loop.OTSdkDriver = (function() {
|
||||
* Disconnects the sdk session.
|
||||
*/
|
||||
disconnectSession: function() {
|
||||
this.endScreenShare();
|
||||
|
||||
if (this.session) {
|
||||
this.session.off("streamCreated streamDestroyed connectionDestroyed " +
|
||||
"sessionDisconnected streamPropertyChanged");
|
||||
@ -246,8 +295,16 @@ loop.OTSdkDriver = (function() {
|
||||
}));
|
||||
}
|
||||
|
||||
var remoteElement;
|
||||
if (event.stream.videoType === "screen") {
|
||||
// XXX Implement in part 2.
|
||||
remoteElement = "null";
|
||||
} else {
|
||||
remoteElement = this.getRemoteElement();
|
||||
}
|
||||
|
||||
this.session.subscribe(event.stream,
|
||||
this.getRemoteElement(), this.publisherConfig);
|
||||
remoteElement, this._getCopyPublisherConfig());
|
||||
|
||||
this._subscribedRemoteStream = true;
|
||||
if (this._checkAllStreamsConnected()) {
|
||||
@ -347,6 +404,25 @@ loop.OTSdkDriver = (function() {
|
||||
_checkAllStreamsConnected: function() {
|
||||
return this._publishedLocalStream &&
|
||||
this._subscribedRemoteStream;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when a screenshare is complete, publishes it to the session.
|
||||
*/
|
||||
_onScreenShareGranted: function() {
|
||||
this.session.publish(this.screenshare);
|
||||
this.dispatcher.dispatch(new sharedActions.ScreenSharingState({
|
||||
state: SCREEN_SHARE_STATES.ACTIVE
|
||||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when a screenshare is denied. Notifies the other stores.
|
||||
*/
|
||||
_onScreenShareDenied: function() {
|
||||
this.dispatcher.dispatch(new sharedActions.ScreenSharingState({
|
||||
state: SCREEN_SHARE_STATES.INACTIVE
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -48,6 +48,13 @@ loop.shared.utils = (function(mozL10n) {
|
||||
HAS_VIDEO: "hasVideo"
|
||||
};
|
||||
|
||||
var SCREEN_SHARE_STATES = {
|
||||
INACTIVE: "ss-inactive",
|
||||
// Pending is when the user is being prompted, aka gUM in progress.
|
||||
PENDING: "ss-pending",
|
||||
ACTIVE: "ss-active"
|
||||
};
|
||||
|
||||
/**
|
||||
* Format a given date into an l10n-friendly string.
|
||||
*
|
||||
@ -145,6 +152,7 @@ loop.shared.utils = (function(mozL10n) {
|
||||
REST_ERRNOS: REST_ERRNOS,
|
||||
WEBSOCKET_REASONS: WEBSOCKET_REASONS,
|
||||
STREAM_PROPERTIES: STREAM_PROPERTIES,
|
||||
SCREEN_SHARE_STATES: SCREEN_SHARE_STATES,
|
||||
Helper: Helper,
|
||||
composeCallUrlEmail: composeCallUrlEmail,
|
||||
formatDate: formatDate,
|
||||
|
@ -8,11 +8,13 @@
|
||||
/* global loop:true, React */
|
||||
var loop = loop || {};
|
||||
loop.shared = loop.shared || {};
|
||||
loop.shared.views = (function(_, OT, l10n) {
|
||||
loop.shared.views = (function(_, l10n) {
|
||||
"use strict";
|
||||
|
||||
var sharedActions = loop.shared.actions;
|
||||
var sharedModels = loop.shared.models;
|
||||
var sharedMixins = loop.shared.mixins;
|
||||
var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
|
||||
|
||||
/**
|
||||
* Media control button.
|
||||
@ -46,6 +48,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
var classesObj = {
|
||||
"btn": true,
|
||||
"media-control": true,
|
||||
"transparent-button": true,
|
||||
"local-media": this.props.scope === "local",
|
||||
"muted": !this.props.enabled,
|
||||
"hide": !this.props.visible
|
||||
@ -72,6 +75,60 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Screen sharing control button.
|
||||
*
|
||||
* Required props:
|
||||
* - {loop.Dispatcher} dispatcher The dispatcher instance
|
||||
* - {Boolean} visible Set to true to display the button
|
||||
* - {String} state One of the screen sharing states, see
|
||||
* loop.shared.utils.SCREEN_SHARE_STATES
|
||||
*/
|
||||
var ScreenShareControlButton = React.createClass({displayName: "ScreenShareControlButton",
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
visible: React.PropTypes.bool.isRequired,
|
||||
state: React.PropTypes.string.isRequired,
|
||||
},
|
||||
|
||||
handleClick: function() {
|
||||
if (this.props.state === SCREEN_SHARE_STATES.ACTIVE) {
|
||||
this.props.dispatcher.dispatch(
|
||||
new sharedActions.EndScreenShare({}));
|
||||
} else {
|
||||
this.props.dispatcher.dispatch(
|
||||
new sharedActions.StartScreenShare({}));
|
||||
}
|
||||
},
|
||||
|
||||
_getTitle: function() {
|
||||
var prefix = this.props.state === SCREEN_SHARE_STATES.ACTIVE ?
|
||||
"active" : "inactive";
|
||||
|
||||
return l10n.get(prefix + "_screenshare_button_title");
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (!this.props.visible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var screenShareClasses = React.addons.classSet({
|
||||
"btn": true,
|
||||
"btn-screen-share": true,
|
||||
"transparent-button": true,
|
||||
"active": this.props.state === SCREEN_SHARE_STATES.ACTIVE,
|
||||
"disabled": this.props.state === SCREEN_SHARE_STATES.PENDING
|
||||
});
|
||||
|
||||
return (
|
||||
React.createElement("button", {className: screenShareClasses,
|
||||
onClick: this.handleClick,
|
||||
title: this._getTitle()})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Conversation controls.
|
||||
*/
|
||||
@ -80,13 +137,16 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
return {
|
||||
video: {enabled: true, visible: true},
|
||||
audio: {enabled: true, visible: true},
|
||||
screenShare: {state: SCREEN_SHARE_STATES.INACTIVE, visible: false},
|
||||
enableHangup: true
|
||||
};
|
||||
},
|
||||
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
video: React.PropTypes.object.isRequired,
|
||||
audio: React.PropTypes.object.isRequired,
|
||||
screenShare: React.PropTypes.object,
|
||||
hangup: React.PropTypes.func.isRequired,
|
||||
publishStream: React.PropTypes.func.isRequired,
|
||||
hangupButtonLabel: React.PropTypes.string,
|
||||
@ -110,7 +170,6 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var cx = React.addons.classSet;
|
||||
return (
|
||||
React.createElement("ul", {className: "conversation-toolbar"},
|
||||
React.createElement("li", {className: "conversation-toolbar-btn-box btn-hangup-entry"},
|
||||
@ -131,6 +190,11 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
enabled: this.props.audio.enabled,
|
||||
visible: this.props.audio.visible,
|
||||
scope: "local", type: "audio"})
|
||||
),
|
||||
React.createElement("li", {className: "conversation-toolbar-btn-box btn-screen-share-entry"},
|
||||
React.createElement(ScreenShareControlButton, {dispatcher: this.props.dispatcher,
|
||||
visible: this.props.screenShare.visible,
|
||||
state: this.props.screenShare.state})
|
||||
)
|
||||
)
|
||||
);
|
||||
@ -461,6 +525,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
ConversationView: ConversationView,
|
||||
ConversationToolbar: ConversationToolbar,
|
||||
MediaControlButton: MediaControlButton,
|
||||
ScreenShareControlButton: ScreenShareControlButton,
|
||||
NotificationListView: NotificationListView
|
||||
};
|
||||
})(_, window.OT, navigator.mozL10n || document.mozL10n);
|
||||
})(_, navigator.mozL10n || document.mozL10n);
|
||||
|
@ -8,11 +8,13 @@
|
||||
/* global loop:true, React */
|
||||
var loop = loop || {};
|
||||
loop.shared = loop.shared || {};
|
||||
loop.shared.views = (function(_, OT, l10n) {
|
||||
loop.shared.views = (function(_, l10n) {
|
||||
"use strict";
|
||||
|
||||
var sharedActions = loop.shared.actions;
|
||||
var sharedModels = loop.shared.models;
|
||||
var sharedMixins = loop.shared.mixins;
|
||||
var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
|
||||
|
||||
/**
|
||||
* Media control button.
|
||||
@ -46,6 +48,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
var classesObj = {
|
||||
"btn": true,
|
||||
"media-control": true,
|
||||
"transparent-button": true,
|
||||
"local-media": this.props.scope === "local",
|
||||
"muted": !this.props.enabled,
|
||||
"hide": !this.props.visible
|
||||
@ -72,6 +75,60 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Screen sharing control button.
|
||||
*
|
||||
* Required props:
|
||||
* - {loop.Dispatcher} dispatcher The dispatcher instance
|
||||
* - {Boolean} visible Set to true to display the button
|
||||
* - {String} state One of the screen sharing states, see
|
||||
* loop.shared.utils.SCREEN_SHARE_STATES
|
||||
*/
|
||||
var ScreenShareControlButton = React.createClass({
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
visible: React.PropTypes.bool.isRequired,
|
||||
state: React.PropTypes.string.isRequired,
|
||||
},
|
||||
|
||||
handleClick: function() {
|
||||
if (this.props.state === SCREEN_SHARE_STATES.ACTIVE) {
|
||||
this.props.dispatcher.dispatch(
|
||||
new sharedActions.EndScreenShare({}));
|
||||
} else {
|
||||
this.props.dispatcher.dispatch(
|
||||
new sharedActions.StartScreenShare({}));
|
||||
}
|
||||
},
|
||||
|
||||
_getTitle: function() {
|
||||
var prefix = this.props.state === SCREEN_SHARE_STATES.ACTIVE ?
|
||||
"active" : "inactive";
|
||||
|
||||
return l10n.get(prefix + "_screenshare_button_title");
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (!this.props.visible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var screenShareClasses = React.addons.classSet({
|
||||
"btn": true,
|
||||
"btn-screen-share": true,
|
||||
"transparent-button": true,
|
||||
"active": this.props.state === SCREEN_SHARE_STATES.ACTIVE,
|
||||
"disabled": this.props.state === SCREEN_SHARE_STATES.PENDING
|
||||
});
|
||||
|
||||
return (
|
||||
<button className={screenShareClasses}
|
||||
onClick={this.handleClick}
|
||||
title={this._getTitle()}></button>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Conversation controls.
|
||||
*/
|
||||
@ -80,13 +137,16 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
return {
|
||||
video: {enabled: true, visible: true},
|
||||
audio: {enabled: true, visible: true},
|
||||
screenShare: {state: SCREEN_SHARE_STATES.INACTIVE, visible: false},
|
||||
enableHangup: true
|
||||
};
|
||||
},
|
||||
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
video: React.PropTypes.object.isRequired,
|
||||
audio: React.PropTypes.object.isRequired,
|
||||
screenShare: React.PropTypes.object,
|
||||
hangup: React.PropTypes.func.isRequired,
|
||||
publishStream: React.PropTypes.func.isRequired,
|
||||
hangupButtonLabel: React.PropTypes.string,
|
||||
@ -110,7 +170,6 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var cx = React.addons.classSet;
|
||||
return (
|
||||
<ul className="conversation-toolbar">
|
||||
<li className="conversation-toolbar-btn-box btn-hangup-entry">
|
||||
@ -132,6 +191,11 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
visible={this.props.audio.visible}
|
||||
scope="local" type="audio" />
|
||||
</li>
|
||||
<li className="conversation-toolbar-btn-box btn-screen-share-entry">
|
||||
<ScreenShareControlButton dispatcher={this.props.dispatcher}
|
||||
visible={this.props.screenShare.visible}
|
||||
state={this.props.screenShare.state} />
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
@ -461,6 +525,7 @@ loop.shared.views = (function(_, OT, l10n) {
|
||||
ConversationView: ConversationView,
|
||||
ConversationToolbar: ConversationToolbar,
|
||||
MediaControlButton: MediaControlButton,
|
||||
ScreenShareControlButton: ScreenShareControlButton,
|
||||
NotificationListView: NotificationListView
|
||||
};
|
||||
})(_, window.OT, navigator.mozL10n || document.mozL10n);
|
||||
})(_, navigator.mozL10n || document.mozL10n);
|
||||
|
@ -101,7 +101,6 @@
|
||||
<script type="text/javascript" src="shared/js/utils.js"></script>
|
||||
<script type="text/javascript" src="shared/js/models.js"></script>
|
||||
<script type="text/javascript" src="shared/js/mixins.js"></script>
|
||||
<script type="text/javascript" src="shared/js/views.js"></script>
|
||||
<script type="text/javascript" src="shared/js/feedbackApiClient.js"></script>
|
||||
<script type="text/javascript" src="shared/js/actions.js"></script>
|
||||
<script type="text/javascript" src="shared/js/validate.js"></script>
|
||||
@ -113,6 +112,7 @@
|
||||
<script type="text/javascript" src="shared/js/fxOSActiveRoomStore.js"></script>
|
||||
<script type="text/javascript" src="shared/js/activeRoomStore.js"></script>
|
||||
<script type="text/javascript" src="shared/js/feedbackStore.js"></script>
|
||||
<script type="text/javascript" src="shared/js/views.js"></script>
|
||||
<script type="text/javascript" src="shared/js/feedbackViews.js"></script>
|
||||
<script type="text/javascript" src="js/standaloneAppStore.js"></script>
|
||||
<script type="text/javascript" src="js/standaloneClient.js"></script>
|
||||
|
@ -17,6 +17,9 @@ mute_local_audio_button_title=Mute your audio
|
||||
unmute_local_audio_button_title=Unmute your audio
|
||||
mute_local_video_button_title=Mute your video
|
||||
unmute_local_video_button_title=Unmute your video
|
||||
active_screenshare_button_title=Stop sharing
|
||||
inactive_screenshare_button_title=Share your screen
|
||||
|
||||
outgoing_call_title=Start conversation?
|
||||
call_with_contact_title=Conversation with {{incomingCallIdentity}}
|
||||
welcome=Welcome to the {{clientShortname}} web client.
|
||||
|
@ -141,7 +141,8 @@ describe("loop.conversation", function() {
|
||||
sdk: {},
|
||||
conversationStore: conversationStore,
|
||||
conversationAppStore: conversationAppStore,
|
||||
dispatcher: dispatcher
|
||||
dispatcher: dispatcher,
|
||||
mozLoop: navigator.mozLoop
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,6 @@
|
||||
<script src="../../content/shared/js/feedbackApiClient.js"></script>
|
||||
<script src="../../content/shared/js/models.js"></script>
|
||||
<script src="../../content/shared/js/mixins.js"></script>
|
||||
<script src="../../content/shared/js/views.js"></script>
|
||||
<script src="../../content/shared/js/websocket.js"></script>
|
||||
<script src="../../content/shared/js/actions.js"></script>
|
||||
<script src="../../content/shared/js/validate.js"></script>
|
||||
@ -56,6 +55,7 @@
|
||||
<script src="../../content/shared/js/fxOSActiveRoomStore.js"></script>
|
||||
<script src="../../content/shared/js/activeRoomStore.js"></script>
|
||||
<script src="../../content/shared/js/feedbackStore.js"></script>
|
||||
<script src="../../content/shared/js/views.js"></script>
|
||||
<script src="../../content/shared/js/feedbackViews.js"></script>
|
||||
<script src="../../content/js/client.js"></script>
|
||||
<script src="../../content/js/conversationAppStore.js"></script>
|
||||
|
@ -6,20 +6,25 @@ describe("loop.roomViews", function () {
|
||||
"use strict";
|
||||
|
||||
var ROOM_STATES = loop.store.ROOM_STATES;
|
||||
var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
|
||||
|
||||
var sandbox, dispatcher, roomStore, activeRoomStore, fakeWindow;
|
||||
var fakeMozLoop;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
dispatcher = new loop.Dispatcher();
|
||||
|
||||
fakeMozLoop = {
|
||||
getAudioBlob: sinon.stub(),
|
||||
getLoopPref: sinon.stub()
|
||||
};
|
||||
|
||||
fakeWindow = {
|
||||
document: {},
|
||||
navigator: {
|
||||
mozLoop: {
|
||||
getAudioBlob: sinon.stub()
|
||||
}
|
||||
mozLoop: fakeMozLoop
|
||||
},
|
||||
addEventListener: function() {},
|
||||
removeEventListener: function() {}
|
||||
@ -62,16 +67,10 @@ describe("loop.roomViews", function () {
|
||||
roomStore: roomStore
|
||||
}));
|
||||
|
||||
expect(testView.state).eql({
|
||||
roomState: ROOM_STATES.INIT,
|
||||
audioMuted: false,
|
||||
videoMuted: false,
|
||||
failureReason: undefined,
|
||||
used: false,
|
||||
foo: "bar",
|
||||
localVideoDimensions: {},
|
||||
remoteVideoDimensions: {}
|
||||
});
|
||||
var expectedState = _.extend({foo: "bar"},
|
||||
activeRoomStore.getInitialStoreState());
|
||||
|
||||
expect(testView.state).eql(expectedState);
|
||||
});
|
||||
|
||||
it("should listen to store changes", function() {
|
||||
@ -216,7 +215,8 @@ describe("loop.roomViews", function () {
|
||||
return TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.roomViews.DesktopRoomConversationView, {
|
||||
dispatcher: dispatcher,
|
||||
roomStore: roomStore
|
||||
roomStore: roomStore,
|
||||
mozLoop: fakeMozLoop
|
||||
}));
|
||||
}
|
||||
|
||||
@ -276,6 +276,20 @@ describe("loop.roomViews", function () {
|
||||
expect(muteBtn.classList.contains("muted")).eql(true);
|
||||
});
|
||||
|
||||
it("should dispatch a `StartScreenShare` action when sharing is not active " +
|
||||
"and the screen share button is pressed", function() {
|
||||
view = mountTestComponent();
|
||||
|
||||
view.setState({screenSharingState: SCREEN_SHARE_STATES.INACTIVE});
|
||||
|
||||
var muteBtn = view.getDOMNode().querySelector('.btn-mute-video');
|
||||
|
||||
React.addons.TestUtils.Simulate.click(muteBtn);
|
||||
|
||||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||
sinon.match.hasOwn("name", "setMute"));
|
||||
});
|
||||
|
||||
describe("#componentWillUpdate", function() {
|
||||
function expectActionDispatched(view) {
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
|
@ -9,6 +9,7 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
var REST_ERRNOS = loop.shared.utils.REST_ERRNOS;
|
||||
var ROOM_STATES = loop.store.ROOM_STATES;
|
||||
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
|
||||
var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
|
||||
var sandbox, dispatcher, store, fakeMozLoop, fakeSdkDriver;
|
||||
var fakeMultiplexGum;
|
||||
|
||||
@ -646,6 +647,16 @@ describe("loop.store.ActiveRoomStore", function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe("#screenSharingState", function() {
|
||||
it("should save the state", function() {
|
||||
store.screenSharingState(new sharedActions.ScreenSharingState({
|
||||
state: SCREEN_SHARE_STATES.ACTIVE
|
||||
}));
|
||||
|
||||
expect(store.getStoreState().screenSharingState).eql(SCREEN_SHARE_STATES.ACTIVE);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#remotePeerConnected", function() {
|
||||
it("should set the state to `HAS_PARTICIPANTS`", function() {
|
||||
store.remotePeerConnected();
|
||||
|
@ -42,7 +42,6 @@
|
||||
<script src="../../content/shared/js/utils.js"></script>
|
||||
<script src="../../content/shared/js/models.js"></script>
|
||||
<script src="../../content/shared/js/mixins.js"></script>
|
||||
<script src="../../content/shared/js/views.js"></script>
|
||||
<script src="../../content/shared/js/websocket.js"></script>
|
||||
<script src="../../content/shared/js/feedbackApiClient.js"></script>
|
||||
<script src="../../content/shared/js/validate.js"></script>
|
||||
@ -56,6 +55,7 @@
|
||||
<script src="../../content/shared/js/roomStore.js"></script>
|
||||
<script src="../../content/shared/js/conversationStore.js"></script>
|
||||
<script src="../../content/shared/js/feedbackStore.js"></script>
|
||||
<script src="../../content/shared/js/views.js"></script>
|
||||
<script src="../../content/shared/js/feedbackViews.js"></script>
|
||||
|
||||
<!-- Test scripts -->
|
||||
|
@ -9,6 +9,7 @@ describe("loop.OTSdkDriver", function () {
|
||||
var sharedActions = loop.shared.actions;
|
||||
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
|
||||
var STREAM_PROPERTIES = loop.shared.utils.STREAM_PROPERTIES;
|
||||
var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
|
||||
var sandbox;
|
||||
var dispatcher, driver, publisher, sdk, session, sessionData;
|
||||
var fakeLocalElement, fakeRemoteElement, publisherConfig, fakeEvent;
|
||||
@ -35,6 +36,7 @@ describe("loop.OTSdkDriver", function () {
|
||||
connect: sinon.stub(),
|
||||
disconnect: sinon.stub(),
|
||||
publish: sinon.stub(),
|
||||
unpublish: sinon.stub(),
|
||||
subscribe: sinon.stub(),
|
||||
forceDisconnect: sinon.stub()
|
||||
}, Backbone.Events);
|
||||
@ -119,6 +121,74 @@ describe("loop.OTSdkDriver", function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe("#startScreenShare", function() {
|
||||
var fakeElement;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox.stub(dispatcher, "dispatch");
|
||||
|
||||
fakeElement = {
|
||||
className: "fakeVideo"
|
||||
};
|
||||
|
||||
driver.getScreenShareElementFunc = function() {
|
||||
return fakeElement;
|
||||
};
|
||||
});
|
||||
|
||||
it("should dispatch a `ScreenSharingState` action", function() {
|
||||
driver.startScreenShare(new sharedActions.StartScreenShare());
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.ScreenSharingState({
|
||||
state: SCREEN_SHARE_STATES.PENDING
|
||||
}));
|
||||
});
|
||||
|
||||
it("should initialize a publisher", function() {
|
||||
driver.startScreenShare(new sharedActions.StartScreenShare());
|
||||
|
||||
sinon.assert.calledOnce(sdk.initPublisher);
|
||||
sinon.assert.calledWithMatch(sdk.initPublisher,
|
||||
fakeElement, {videoSource: "window"});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#endScreenShare", function() {
|
||||
beforeEach(function() {
|
||||
driver.getScreenShareElementFunc = function() {};
|
||||
|
||||
driver.startScreenShare(new sharedActions.StartScreenShare());
|
||||
|
||||
sandbox.stub(dispatcher, "dispatch");
|
||||
|
||||
driver.session = session;
|
||||
});
|
||||
|
||||
it("should unpublish the share", function() {
|
||||
driver.endScreenShare(new sharedActions.EndScreenShare());
|
||||
|
||||
sinon.assert.calledOnce(session.unpublish);
|
||||
});
|
||||
|
||||
it("should destroy the share", function() {
|
||||
driver.endScreenShare(new sharedActions.EndScreenShare());
|
||||
|
||||
sinon.assert.calledOnce(publisher.destroy);
|
||||
});
|
||||
|
||||
it("should dispatch a `ScreenSharingState` action", function() {
|
||||
driver.endScreenShare(new sharedActions.EndScreenShare());
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.ScreenSharingState({
|
||||
state: SCREEN_SHARE_STATES.INACTIVE
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("#connectSession", function() {
|
||||
it("should initialise a new session", function() {
|
||||
driver.connectSession(sessionData);
|
||||
@ -214,7 +284,7 @@ describe("loop.OTSdkDriver", function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Events", function() {
|
||||
describe("Events (general media)", function() {
|
||||
beforeEach(function() {
|
||||
driver.connectSession(sessionData);
|
||||
|
||||
@ -427,4 +497,46 @@ describe("loop.OTSdkDriver", function () {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Events (screenshare)", function() {
|
||||
beforeEach(function() {
|
||||
driver.connectSession(sessionData);
|
||||
|
||||
driver.getScreenShareElementFunc = function() {};
|
||||
|
||||
driver.startScreenShare(new sharedActions.StartScreenShare());
|
||||
|
||||
sandbox.stub(dispatcher, "dispatch");
|
||||
});
|
||||
|
||||
describe("accessAllowed", function() {
|
||||
it("should publish the stream", function() {
|
||||
publisher.trigger("accessAllowed", fakeEvent);
|
||||
|
||||
sinon.assert.calledOnce(session.publish);
|
||||
});
|
||||
|
||||
it("should dispatch a `ScreenSharingState` action", function() {
|
||||
publisher.trigger("accessAllowed", fakeEvent);
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.ScreenSharingState({
|
||||
state: SCREEN_SHARE_STATES.ACTIVE
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("accessDenied", function() {
|
||||
it("should dispatch a `ScreenShareState` action", function() {
|
||||
publisher.trigger("accessDenied", fakeEvent);
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.ScreenSharingState({
|
||||
state: SCREEN_SHARE_STATES.INACTIVE
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -12,10 +12,11 @@ var TestUtils = React.addons.TestUtils;
|
||||
describe("loop.shared.views", function() {
|
||||
"use strict";
|
||||
|
||||
var sharedModels = loop.shared.models,
|
||||
sharedViews = loop.shared.views,
|
||||
getReactElementByClass = TestUtils.findRenderedDOMComponentWithClass,
|
||||
sandbox, fakeAudioXHR;
|
||||
var sharedModels = loop.shared.models;
|
||||
var sharedViews = loop.shared.views;
|
||||
var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
|
||||
var getReactElementByClass = TestUtils.findRenderedDOMComponentWithClass;
|
||||
var sandbox, fakeAudioXHR, dispatcher;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@ -23,6 +24,10 @@ describe("loop.shared.views", function() {
|
||||
sandbox.stub(l10n, "get", function(x) {
|
||||
return "translated:" + x;
|
||||
});
|
||||
|
||||
dispatcher = new loop.Dispatcher();
|
||||
sandbox.stub(dispatcher, "dispatch");
|
||||
|
||||
fakeAudioXHR = {
|
||||
open: sinon.spy(),
|
||||
send: function() {},
|
||||
@ -92,6 +97,76 @@ describe("loop.shared.views", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("ScreenShareControlButton", function() {
|
||||
it("should render a visible share button", function() {
|
||||
var comp = TestUtils.renderIntoDocument(
|
||||
React.createElement(sharedViews.ScreenShareControlButton, {
|
||||
dispatcher: dispatcher,
|
||||
visible: true,
|
||||
state: SCREEN_SHARE_STATES.INACTIVE
|
||||
}));
|
||||
|
||||
expect(comp.getDOMNode().classList.contains("active")).eql(false);
|
||||
expect(comp.getDOMNode().classList.contains("disabled")).eql(false);
|
||||
});
|
||||
|
||||
it("should render a disabled share button when share is pending", function() {
|
||||
var comp = TestUtils.renderIntoDocument(
|
||||
React.createElement(sharedViews.ScreenShareControlButton, {
|
||||
dispatcher: dispatcher,
|
||||
visible: true,
|
||||
state: SCREEN_SHARE_STATES.PENDING
|
||||
}));
|
||||
|
||||
expect(comp.getDOMNode().classList.contains("active")).eql(false);
|
||||
expect(comp.getDOMNode().classList.contains("disabled")).eql(true);
|
||||
});
|
||||
|
||||
it("should render an active share button", function() {
|
||||
var comp = TestUtils.renderIntoDocument(
|
||||
React.createElement(sharedViews.ScreenShareControlButton, {
|
||||
dispatcher: dispatcher,
|
||||
visible: true,
|
||||
state: SCREEN_SHARE_STATES.ACTIVE
|
||||
}));
|
||||
|
||||
expect(comp.getDOMNode().classList.contains("active")).eql(true);
|
||||
expect(comp.getDOMNode().classList.contains("disabled")).eql(false);
|
||||
});
|
||||
|
||||
it("should dispatch a StartScreenShare action on click when the state is not active",
|
||||
function() {
|
||||
var comp = TestUtils.renderIntoDocument(
|
||||
React.createElement(sharedViews.ScreenShareControlButton, {
|
||||
dispatcher: dispatcher,
|
||||
visible: true,
|
||||
state: SCREEN_SHARE_STATES.INACTIVE
|
||||
}));
|
||||
|
||||
TestUtils.Simulate.click(comp.getDOMNode());
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.StartScreenShare({}));
|
||||
});
|
||||
|
||||
it("should dispatch a EndScreenShare action on click when the state is active",
|
||||
function() {
|
||||
var comp = TestUtils.renderIntoDocument(
|
||||
React.createElement(sharedViews.ScreenShareControlButton, {
|
||||
dispatcher: dispatcher,
|
||||
visible: true,
|
||||
state: SCREEN_SHARE_STATES.ACTIVE
|
||||
}));
|
||||
|
||||
TestUtils.Simulate.click(comp.getDOMNode());
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.EndScreenShare({}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("ConversationToolbar", function() {
|
||||
var hangup, publishStream;
|
||||
|
||||
|
@ -41,7 +41,6 @@
|
||||
<script src="../../content/shared/js/utils.js"></script>
|
||||
<script src="../../content/shared/js/models.js"></script>
|
||||
<script src="../../content/shared/js/mixins.js"></script>
|
||||
<script src="../../content/shared/js/views.js"></script>
|
||||
<script src="../../content/shared/js/websocket.js"></script>
|
||||
<script src="../../content/shared/js/feedbackApiClient.js"></script>
|
||||
<script src="../../content/shared/js/actions.js"></script>
|
||||
@ -52,6 +51,7 @@
|
||||
<script src="../../content/shared/js/fxOSActiveRoomStore.js"></script>
|
||||
<script src="../../content/shared/js/activeRoomStore.js"></script>
|
||||
<script src="../../content/shared/js/feedbackStore.js"></script>
|
||||
<script src="../../content/shared/js/views.js"></script>
|
||||
<script src="../../content/shared/js/feedbackViews.js"></script>
|
||||
<script src="../../content/shared/js/otSdkDriver.js"></script>
|
||||
<script src="../../standalone/content/js/multiplexGum.js"></script>
|
||||
|
@ -54,6 +54,7 @@ navigator.mozLoop = {
|
||||
switch(pref) {
|
||||
// Ensure we skip FTE completely.
|
||||
case "gettingStarted.seen":
|
||||
case "screenshare.enabled":
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
@ -37,7 +37,6 @@
|
||||
<script src="../content/shared/js/utils.js"></script>
|
||||
<script src="../content/shared/js/models.js"></script>
|
||||
<script src="../content/shared/js/mixins.js"></script>
|
||||
<script src="../content/shared/js/views.js"></script>
|
||||
<script src="../content/shared/js/websocket.js"></script>
|
||||
<script src="../content/shared/js/validate.js"></script>
|
||||
<script src="../content/shared/js/dispatcher.js"></script>
|
||||
@ -48,6 +47,7 @@
|
||||
<script src="../content/shared/js/fxOSActiveRoomStore.js"></script>
|
||||
<script src="../content/shared/js/activeRoomStore.js"></script>
|
||||
<script src="../content/shared/js/feedbackStore.js"></script>
|
||||
<script src="../content/shared/js/views.js"></script>
|
||||
<script src="../content/shared/js/feedbackViews.js"></script>
|
||||
<script src="../content/js/roomViews.js"></script>
|
||||
<script src="../content/js/conversationViews.js"></script>
|
||||
|
@ -567,6 +567,7 @@
|
||||
React.createElement(DesktopRoomConversationView, {
|
||||
roomStore: roomStore,
|
||||
dispatcher: dispatcher,
|
||||
mozLoop: navigator.mozLoop,
|
||||
roomState: ROOM_STATES.INIT})
|
||||
)
|
||||
),
|
||||
@ -577,6 +578,7 @@
|
||||
React.createElement(DesktopRoomConversationView, {
|
||||
roomStore: roomStore,
|
||||
dispatcher: dispatcher,
|
||||
mozLoop: navigator.mozLoop,
|
||||
roomState: ROOM_STATES.HAS_PARTICIPANTS})
|
||||
)
|
||||
)
|
||||
|
@ -567,6 +567,7 @@
|
||||
<DesktopRoomConversationView
|
||||
roomStore={roomStore}
|
||||
dispatcher={dispatcher}
|
||||
mozLoop={navigator.mozLoop}
|
||||
roomState={ROOM_STATES.INIT} />
|
||||
</div>
|
||||
</Example>
|
||||
@ -577,6 +578,7 @@
|
||||
<DesktopRoomConversationView
|
||||
roomStore={roomStore}
|
||||
dispatcher={dispatcher}
|
||||
mozLoop={navigator.mozLoop}
|
||||
roomState={ROOM_STATES.HAS_PARTICIPANTS} />
|
||||
</div>
|
||||
</Example>
|
||||
|
@ -183,6 +183,8 @@ mute_local_audio_button_title=Mute your audio
|
||||
unmute_local_audio_button_title=Unmute your audio
|
||||
mute_local_video_button_title=Mute your video
|
||||
unmute_local_video_button_title=Unmute your video
|
||||
active_screenshare_button_title=Stop sharing
|
||||
inactive_screenshare_button_title=Share your screen
|
||||
|
||||
## LOCALIZATION NOTE (call_with_contact_title): The title displayed
|
||||
## when calling a contact. Don't translate the part between {{..}} because
|
||||
|
Loading…
Reference in New Issue
Block a user