mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Backed out changeset 4c48e36e05bb (bug 1000240) for marionette failures
This commit is contained in:
parent
ef3d69de82
commit
fb1ba03717
@ -11,12 +11,10 @@ var loop = loop || {};
|
||||
loop.conversation = (function(mozL10n) {
|
||||
"use strict";
|
||||
|
||||
var sharedViews = loop.shared.views;
|
||||
var sharedMixins = loop.shared.mixins;
|
||||
var sharedModels = loop.shared.models;
|
||||
var sharedViews = loop.shared.views,
|
||||
sharedModels = loop.shared.models;
|
||||
|
||||
var IncomingCallView = React.createClass({displayName: 'IncomingCallView',
|
||||
mixins: [sharedMixins.DropdownMenuMixin],
|
||||
|
||||
propTypes: {
|
||||
model: React.PropTypes.object.isRequired,
|
||||
@ -25,11 +23,25 @@ loop.conversation = (function(mozL10n) {
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
showMenu: false,
|
||||
showDeclineMenu: false,
|
||||
video: true
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {showDeclineMenu: this.props.showDeclineMenu};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
window.addEventListener("click", this.clickHandler);
|
||||
window.addEventListener("blur", this._hideDeclineMenu);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
window.removeEventListener("click", this.clickHandler);
|
||||
window.removeEventListener("blur", this._hideDeclineMenu);
|
||||
},
|
||||
|
||||
clickHandler: function(e) {
|
||||
var target = e.target;
|
||||
if (!target.classList.contains('btn-chevron')) {
|
||||
@ -55,6 +67,15 @@ loop.conversation = (function(mozL10n) {
|
||||
return false;
|
||||
},
|
||||
|
||||
_toggleDeclineMenu: function() {
|
||||
var currentState = this.state.showDeclineMenu;
|
||||
this.setState({showDeclineMenu: !currentState});
|
||||
},
|
||||
|
||||
_hideDeclineMenu: function() {
|
||||
this.setState({showDeclineMenu: false});
|
||||
},
|
||||
|
||||
/*
|
||||
* Generate props for <AcceptCallButton> component based on
|
||||
* incoming call type. An incoming video call will render a video
|
||||
@ -88,13 +109,16 @@ loop.conversation = (function(mozL10n) {
|
||||
|
||||
render: function() {
|
||||
/* jshint ignore:start */
|
||||
var btnClassAccept = "btn btn-accept";
|
||||
var btnClassDecline = "btn btn-error btn-decline";
|
||||
var conversationPanelClass = "incoming-call";
|
||||
var dropdownMenuClassesDecline = React.addons.classSet({
|
||||
"native-dropdown-menu": true,
|
||||
"conversation-window-dropdown": true,
|
||||
"visually-hidden": !this.state.showMenu
|
||||
"visually-hidden": !this.state.showDeclineMenu
|
||||
});
|
||||
return (
|
||||
React.DOM.div({className: "incoming-call"},
|
||||
React.DOM.div({className: conversationPanelClass},
|
||||
React.DOM.h2(null, mozL10n.get("incoming_call_title2")),
|
||||
React.DOM.div({className: "btn-group incoming-call-action-group"},
|
||||
|
||||
@ -104,11 +128,13 @@ loop.conversation = (function(mozL10n) {
|
||||
React.DOM.div({className: "btn-group-chevron"},
|
||||
React.DOM.div({className: "btn-group"},
|
||||
|
||||
React.DOM.button({className: "btn btn-decline",
|
||||
React.DOM.button({className: btnClassDecline,
|
||||
onClick: this._handleDecline},
|
||||
mozL10n.get("incoming_call_cancel_button")
|
||||
),
|
||||
React.DOM.div({className: "btn-chevron", onClick: this.toggleDropdownMenu})
|
||||
React.DOM.div({className: "btn-chevron",
|
||||
onClick: this._toggleDeclineMenu}
|
||||
)
|
||||
),
|
||||
|
||||
React.DOM.ul({className: dropdownMenuClassesDecline},
|
||||
|
@ -11,12 +11,10 @@ var loop = loop || {};
|
||||
loop.conversation = (function(mozL10n) {
|
||||
"use strict";
|
||||
|
||||
var sharedViews = loop.shared.views;
|
||||
var sharedMixins = loop.shared.mixins;
|
||||
var sharedModels = loop.shared.models;
|
||||
var sharedViews = loop.shared.views,
|
||||
sharedModels = loop.shared.models;
|
||||
|
||||
var IncomingCallView = React.createClass({
|
||||
mixins: [sharedMixins.DropdownMenuMixin],
|
||||
|
||||
propTypes: {
|
||||
model: React.PropTypes.object.isRequired,
|
||||
@ -25,11 +23,25 @@ loop.conversation = (function(mozL10n) {
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
showMenu: false,
|
||||
showDeclineMenu: false,
|
||||
video: true
|
||||
};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {showDeclineMenu: this.props.showDeclineMenu};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
window.addEventListener("click", this.clickHandler);
|
||||
window.addEventListener("blur", this._hideDeclineMenu);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
window.removeEventListener("click", this.clickHandler);
|
||||
window.removeEventListener("blur", this._hideDeclineMenu);
|
||||
},
|
||||
|
||||
clickHandler: function(e) {
|
||||
var target = e.target;
|
||||
if (!target.classList.contains('btn-chevron')) {
|
||||
@ -55,6 +67,15 @@ loop.conversation = (function(mozL10n) {
|
||||
return false;
|
||||
},
|
||||
|
||||
_toggleDeclineMenu: function() {
|
||||
var currentState = this.state.showDeclineMenu;
|
||||
this.setState({showDeclineMenu: !currentState});
|
||||
},
|
||||
|
||||
_hideDeclineMenu: function() {
|
||||
this.setState({showDeclineMenu: false});
|
||||
},
|
||||
|
||||
/*
|
||||
* Generate props for <AcceptCallButton> component based on
|
||||
* incoming call type. An incoming video call will render a video
|
||||
@ -88,13 +109,16 @@ loop.conversation = (function(mozL10n) {
|
||||
|
||||
render: function() {
|
||||
/* jshint ignore:start */
|
||||
var btnClassAccept = "btn btn-accept";
|
||||
var btnClassDecline = "btn btn-error btn-decline";
|
||||
var conversationPanelClass = "incoming-call";
|
||||
var dropdownMenuClassesDecline = React.addons.classSet({
|
||||
"native-dropdown-menu": true,
|
||||
"conversation-window-dropdown": true,
|
||||
"visually-hidden": !this.state.showMenu
|
||||
"visually-hidden": !this.state.showDeclineMenu
|
||||
});
|
||||
return (
|
||||
<div className="incoming-call">
|
||||
<div className={conversationPanelClass}>
|
||||
<h2>{mozL10n.get("incoming_call_title2")}</h2>
|
||||
<div className="btn-group incoming-call-action-group">
|
||||
|
||||
@ -104,11 +128,13 @@ loop.conversation = (function(mozL10n) {
|
||||
<div className="btn-group-chevron">
|
||||
<div className="btn-group">
|
||||
|
||||
<button className="btn btn-decline"
|
||||
<button className={btnClassDecline}
|
||||
onClick={this._handleDecline}>
|
||||
{mozL10n.get("incoming_call_cancel_button")}
|
||||
</button>
|
||||
<div className="btn-chevron" onClick={this.toggleDropdownMenu} />
|
||||
<div className="btn-chevron"
|
||||
onClick={this._toggleDeclineMenu}>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul className={dropdownMenuClassesDecline}>
|
||||
|
@ -137,9 +137,7 @@ p {
|
||||
|
||||
.btn-cancel,
|
||||
.btn-error,
|
||||
.btn-decline,
|
||||
.btn-hangup,
|
||||
.btn-decline + .btn-chevron,
|
||||
.btn-error + .btn-chevron {
|
||||
background-color: #d74345;
|
||||
border: 1px solid #d74345;
|
||||
@ -147,9 +145,7 @@ p {
|
||||
|
||||
.btn-cancel:hover,
|
||||
.btn-error:hover,
|
||||
.btn-decline:hover,
|
||||
.btn-hangup:hover,
|
||||
.btn-decline + .btn-chevron:hover,
|
||||
.btn-error + .btn-chevron:hover {
|
||||
background-color: #c53436;
|
||||
border: 1px solid #c53436;
|
||||
@ -157,9 +153,7 @@ p {
|
||||
|
||||
.btn-cancel:active,
|
||||
.btn-error:active,
|
||||
.btn-decline:active,
|
||||
.btn-hangup:active,
|
||||
.btn-decline + .btn-chevron:active,
|
||||
.btn-error + .btn-chevron:active {
|
||||
background-color: #ae2325;
|
||||
border: 1px solid #ae2325;
|
||||
@ -188,7 +182,6 @@ p {
|
||||
}
|
||||
|
||||
.btn-group-chevron .btn {
|
||||
border-radius: 2px;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
flex: 2;
|
||||
@ -376,7 +369,7 @@ p {
|
||||
padding: 20px 0;
|
||||
border: 1px solid #e7e7e7;
|
||||
box-shadow: 0 2px 0 rgba(0, 0, 0, .03);
|
||||
margin: 2rem 0;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.info-panel h1 {
|
||||
|
@ -31,10 +31,6 @@ loop.shared.mixins = (function() {
|
||||
* @type {Object}
|
||||
*/
|
||||
var DropdownMenuMixin = {
|
||||
get documentBody() {
|
||||
return rootObject.document.body;
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {showMenu: false};
|
||||
},
|
||||
@ -44,13 +40,11 @@ loop.shared.mixins = (function() {
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.documentBody.addEventListener("click", this._onBodyClick);
|
||||
this.documentBody.addEventListener("blur", this.hideDropdownMenu);
|
||||
rootObject.document.body.addEventListener("click", this._onBodyClick);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
this.documentBody.removeEventListener("click", this._onBodyClick);
|
||||
this.documentBody.removeEventListener("blur", this.hideDropdownMenu);
|
||||
rootObject.document.body.removeEventListener("click", this._onBodyClick);
|
||||
},
|
||||
|
||||
showDropdownMenu: function() {
|
||||
@ -59,11 +53,7 @@ loop.shared.mixins = (function() {
|
||||
|
||||
hideDropdownMenu: function() {
|
||||
this.setState({showMenu: false});
|
||||
},
|
||||
|
||||
toggleDropdownMenu: function() {
|
||||
this.setState({showMenu: !this.state.showMenu});
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -29,8 +29,8 @@ loop.shared.models = (function(l10n) {
|
||||
// requires.
|
||||
callType: undefined, // The type of incoming call selected by
|
||||
// other peer ("audio" or "audio-video")
|
||||
selectedCallType: "audio-video", // The selected type for the call that was
|
||||
// initiated ("audio" or "audio-video")
|
||||
selectedCallType: undefined, // The selected type for the call that was
|
||||
// initiated ("audio" or "audio-video")
|
||||
callToken: undefined, // Incoming call token.
|
||||
// Used for blocking a call url
|
||||
subscribedStream: false, // Used to indicate that a stream has been
|
||||
@ -86,13 +86,8 @@ loop.shared.models = (function(l10n) {
|
||||
/**
|
||||
* Used to indicate that an outgoing call should start any necessary
|
||||
* set-up.
|
||||
*
|
||||
* @param {String} selectedCallType Call type ("audio" or "audio-video")
|
||||
*/
|
||||
setupOutgoingCall: function(selectedCallType) {
|
||||
if (selectedCallType) {
|
||||
this.set("selectedCallType", selectedCallType);
|
||||
}
|
||||
setupOutgoingCall: function() {
|
||||
this.trigger("call:outgoing:setup");
|
||||
},
|
||||
|
||||
|
@ -115,9 +115,8 @@ body,
|
||||
line-height: 2.2rem;
|
||||
}
|
||||
|
||||
p.standalone-btn-label {
|
||||
.standalone-btn-label {
|
||||
font-size: 1.2rem;
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
|
||||
.light-color-font {
|
||||
|
@ -14,10 +14,9 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
loop.config = loop.config || {};
|
||||
loop.config.serverUrl = loop.config.serverUrl || "http://localhost:5000";
|
||||
|
||||
var sharedMixins = loop.shared.mixins;
|
||||
var sharedModels = loop.shared.models;
|
||||
var sharedViews = loop.shared.views;
|
||||
var sharedUtils = loop.shared.utils;
|
||||
var sharedModels = loop.shared.models,
|
||||
sharedViews = loop.shared.views,
|
||||
sharedUtils = loop.shared.utils;
|
||||
|
||||
/**
|
||||
* Homepage view.
|
||||
@ -117,8 +116,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
render: function() {
|
||||
return (
|
||||
React.DOM.h1({className: "standalone-header-title"},
|
||||
React.DOM.strong(null, mozL10n.get("brandShortname")),
|
||||
mozL10n.get("clientShortname")
|
||||
React.DOM.strong(null, mozL10n.get("brandShortname")), " ", mozL10n.get("clientShortname")
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -307,105 +305,53 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
React.DOM.div({className: "flex-padding-1"})
|
||||
)
|
||||
),
|
||||
|
||||
ConversationFooter(null)
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var InitiateCallButton = React.createClass({displayName: 'InitiateCallButton',
|
||||
mixins: [sharedMixins.DropdownMenuMixin],
|
||||
|
||||
/**
|
||||
* Conversation launcher view. A ConversationModel is associated and attached
|
||||
* as a `model` property.
|
||||
*
|
||||
* Required properties:
|
||||
* - {loop.shared.models.ConversationModel} model Conversation model.
|
||||
* - {loop.shared.models.NotificationCollection} notifications
|
||||
*/
|
||||
var StartConversationView = React.createClass({displayName: 'StartConversationView',
|
||||
propTypes: {
|
||||
caption: React.PropTypes.string.isRequired,
|
||||
startCall: React.PropTypes.func.isRequired,
|
||||
disabled: React.PropTypes.bool
|
||||
model: React.PropTypes.oneOfType([
|
||||
React.PropTypes.instanceOf(sharedModels.ConversationModel),
|
||||
React.PropTypes.instanceOf(FxOSConversationModel)
|
||||
]).isRequired,
|
||||
// XXX Check more tightly here when we start injecting window.loop.*
|
||||
notifications: React.PropTypes.object.isRequired,
|
||||
client: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {disabled: false};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var dropdownMenuClasses = React.addons.classSet({
|
||||
"native-dropdown-large-parent": true,
|
||||
"standalone-dropdown-menu": true,
|
||||
"visually-hidden": !this.state.showMenu
|
||||
});
|
||||
var chevronClasses = React.addons.classSet({
|
||||
"btn-chevron": true,
|
||||
"disabled": this.props.disabled
|
||||
});
|
||||
return (
|
||||
React.DOM.div({className: "standalone-btn-chevron-menu-group"},
|
||||
React.DOM.div({className: "btn-group-chevron"},
|
||||
React.DOM.div({className: "btn-group"},
|
||||
React.DOM.button({className: "btn btn-large btn-accept",
|
||||
onClick: this.props.startCall("audio-video"),
|
||||
disabled: this.props.disabled,
|
||||
title: mozL10n.get("initiate_audio_video_call_tooltip2")},
|
||||
React.DOM.span({className: "standalone-call-btn-text"},
|
||||
this.props.caption
|
||||
),
|
||||
React.DOM.span({className: "standalone-call-btn-video-icon"})
|
||||
),
|
||||
React.DOM.div({className: chevronClasses,
|
||||
onClick: this.toggleDropdownMenu}
|
||||
)
|
||||
),
|
||||
React.DOM.ul({className: dropdownMenuClasses},
|
||||
React.DOM.li(null,
|
||||
React.DOM.button({className: "start-audio-only-call",
|
||||
onClick: this.props.startCall("audio"),
|
||||
disabled: this.props.disabled},
|
||||
mozL10n.get("initiate_audio_call_button2")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Initiate conversation view.
|
||||
*/
|
||||
var InitiateConversationView = React.createClass({displayName: 'InitiateConversationView',
|
||||
mixins: [Backbone.Events],
|
||||
|
||||
propTypes: {
|
||||
conversation: React.PropTypes.oneOfType([
|
||||
React.PropTypes.instanceOf(sharedModels.ConversationModel),
|
||||
React.PropTypes.instanceOf(FxOSConversationModel)
|
||||
]).isRequired,
|
||||
// XXX Check more tightly here when we start injecting window.loop.*
|
||||
notifications: React.PropTypes.object.isRequired,
|
||||
client: React.PropTypes.object.isRequired,
|
||||
title: React.PropTypes.string.isRequired,
|
||||
callButtonLabel: React.PropTypes.string.isRequired
|
||||
return {showCallOptionsMenu: false};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
urlCreationDateString: '',
|
||||
disableCallButton: false
|
||||
disableCallButton: false,
|
||||
showCallOptionsMenu: this.props.showCallOptionsMenu
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.listenTo(this.props.conversation,
|
||||
"session:error", this._onSessionError);
|
||||
this.listenTo(this.props.conversation,
|
||||
"fxos:app-needed", this._onFxOSAppNeeded);
|
||||
this.props.client.requestCallUrlInfo(
|
||||
this.props.conversation.get("loopToken"),
|
||||
this._setConversationTimestamp);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
this.stopListening(this.props.conversation);
|
||||
localStorage.setItem("has-seen-tos", "true");
|
||||
// Listen for events & hide dropdown menu if user clicks away
|
||||
window.addEventListener("click", this.clickHandler);
|
||||
this.props.model.listenTo(this.props.model, "session:error",
|
||||
this._onSessionError);
|
||||
this.props.model.listenTo(this.props.model, "fxos:app-needed",
|
||||
this._onFxOSAppNeeded);
|
||||
this.props.client.requestCallUrlInfo(this.props.model.get("loopToken"),
|
||||
this._setConversationTimestamp);
|
||||
},
|
||||
|
||||
_onSessionError: function(error, l10nProps) {
|
||||
@ -416,9 +362,11 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
|
||||
_onFxOSAppNeeded: function() {
|
||||
this.setState({
|
||||
marketplaceSrc: loop.config.marketplaceUrl,
|
||||
onMarketplaceMessage: this.props.conversation.onMarketplaceMessage.bind(
|
||||
this.props.conversation
|
||||
marketplaceSrc: loop.config.marketplaceUrl
|
||||
});
|
||||
this.setState({
|
||||
onMarketplaceMessage: this.props.model.onMarketplaceMessage.bind(
|
||||
this.props.model
|
||||
)
|
||||
});
|
||||
},
|
||||
@ -431,10 +379,11 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
*
|
||||
* @param {string} User call type choice "audio" or "audio-video"
|
||||
*/
|
||||
startCall: function(callType) {
|
||||
_initiateOutgoingCall: function(callType) {
|
||||
return function() {
|
||||
this.props.conversation.setupOutgoingCall(callType);
|
||||
this.props.model.set("selectedCallType", callType);
|
||||
this.setState({disableCallButton: true});
|
||||
this.props.model.setupOutgoingCall();
|
||||
}.bind(this);
|
||||
},
|
||||
|
||||
@ -449,21 +398,47 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
}
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
window.removeEventListener("click", this.clickHandler);
|
||||
localStorage.setItem("has-seen-tos", "true");
|
||||
},
|
||||
|
||||
clickHandler: function(e) {
|
||||
if (!e.target.classList.contains('btn-chevron') &&
|
||||
this.state.showCallOptionsMenu) {
|
||||
this._toggleCallOptionsMenu();
|
||||
}
|
||||
},
|
||||
|
||||
_toggleCallOptionsMenu: function() {
|
||||
var state = this.state.showCallOptionsMenu;
|
||||
this.setState({showCallOptionsMenu: !state});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var tosLinkName = mozL10n.get("terms_of_use_link_text");
|
||||
var privacyNoticeName = mozL10n.get("privacy_notice_link_text");
|
||||
var tos_link_name = mozL10n.get("terms_of_use_link_text");
|
||||
var privacy_notice_name = mozL10n.get("privacy_notice_link_text");
|
||||
|
||||
var tosHTML = mozL10n.get("legal_text_and_links", {
|
||||
"terms_of_use_url": "<a target=_blank href='/legal/terms/'>" +
|
||||
tosLinkName + "</a>",
|
||||
tos_link_name + "</a>",
|
||||
"privacy_notice_url": "<a target=_blank href='" +
|
||||
"https://www.mozilla.org/privacy/'>" + privacyNoticeName + "</a>"
|
||||
"https://www.mozilla.org/privacy/'>" + privacy_notice_name + "</a>"
|
||||
});
|
||||
|
||||
var dropdownMenuClasses = React.addons.classSet({
|
||||
"native-dropdown-large-parent": true,
|
||||
"standalone-dropdown-menu": true,
|
||||
"visually-hidden": !this.state.showCallOptionsMenu
|
||||
});
|
||||
var tosClasses = React.addons.classSet({
|
||||
"terms-service": true,
|
||||
hide: (localStorage.getItem("has-seen-tos") === "true")
|
||||
});
|
||||
var chevronClasses = React.addons.classSet({
|
||||
"btn-chevron": true,
|
||||
"disabled": this.state.disableCallButton
|
||||
});
|
||||
|
||||
return (
|
||||
React.DOM.div({className: "container"},
|
||||
@ -473,17 +448,47 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
urlCreationDateString: this.state.urlCreationDateString}),
|
||||
|
||||
React.DOM.p({className: "standalone-btn-label"},
|
||||
this.props.title
|
||||
mozL10n.get("initiate_call_button_label2")
|
||||
),
|
||||
|
||||
React.DOM.div({id: "messages"}),
|
||||
|
||||
React.DOM.div({className: "btn-group"},
|
||||
React.DOM.div({className: "flex-padding-1"}),
|
||||
InitiateCallButton({
|
||||
caption: this.props.callButtonLabel,
|
||||
disabled: this.state.disableCallButton,
|
||||
startCall: this.startCall}
|
||||
React.DOM.div({className: "standalone-btn-chevron-menu-group"},
|
||||
React.DOM.div({className: "btn-group-chevron"},
|
||||
React.DOM.div({className: "btn-group"},
|
||||
|
||||
React.DOM.button({className: "btn btn-large btn-accept",
|
||||
onClick: this._initiateOutgoingCall("audio-video"),
|
||||
disabled: this.state.disableCallButton,
|
||||
title: mozL10n.get("initiate_audio_video_call_tooltip2")},
|
||||
React.DOM.span({className: "standalone-call-btn-text"},
|
||||
mozL10n.get("initiate_audio_video_call_button2")
|
||||
),
|
||||
React.DOM.span({className: "standalone-call-btn-video-icon"})
|
||||
),
|
||||
|
||||
React.DOM.div({className: chevronClasses,
|
||||
onClick: this._toggleCallOptionsMenu}
|
||||
)
|
||||
|
||||
),
|
||||
|
||||
React.DOM.ul({className: dropdownMenuClasses},
|
||||
React.DOM.li(null,
|
||||
/*
|
||||
Button required for disabled state.
|
||||
*/
|
||||
React.DOM.button({className: "start-audio-only-call",
|
||||
onClick: this._initiateOutgoingCall("audio"),
|
||||
disabled: this.state.disableCallButton},
|
||||
mozL10n.get("initiate_audio_call_button2")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
)
|
||||
),
|
||||
React.DOM.div({className: "flex-padding-1"})
|
||||
),
|
||||
@ -533,26 +538,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
}
|
||||
});
|
||||
|
||||
var StartConversationView = React.createClass({displayName: 'StartConversationView',
|
||||
render: function() {
|
||||
return this.transferPropsTo(
|
||||
InitiateConversationView({
|
||||
title: mozL10n.get("initiate_call_button_label2"),
|
||||
callButtonLabel: mozL10n.get("initiate_audio_video_call_button2")})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var FailedConversationView = React.createClass({displayName: 'FailedConversationView',
|
||||
render: function() {
|
||||
return this.transferPropsTo(
|
||||
InitiateConversationView({
|
||||
title: mozL10n.get("call_failed_title"),
|
||||
callButtonLabel: mozL10n.get("retry_call_button")})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* This view manages the outgoing conversation views - from
|
||||
* call initiation through to the actual conversation and call end.
|
||||
@ -610,19 +595,11 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
*/
|
||||
render: function() {
|
||||
switch (this.state.callStatus) {
|
||||
case "failure":
|
||||
case "start": {
|
||||
return (
|
||||
StartConversationView({
|
||||
conversation: this.props.conversation,
|
||||
notifications: this.props.notifications,
|
||||
client: this.props.client}
|
||||
)
|
||||
);
|
||||
}
|
||||
case "failure": {
|
||||
return (
|
||||
FailedConversationView({
|
||||
conversation: this.props.conversation,
|
||||
model: this.props.conversation,
|
||||
notifications: this.props.notifications,
|
||||
client: this.props.client}
|
||||
)
|
||||
@ -798,17 +775,18 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
/**
|
||||
* Handles call rejection.
|
||||
*
|
||||
* @param {String} reason The reason the call was terminated (reject, busy,
|
||||
* timeout, cancel, media-fail, user-unknown, closed)
|
||||
* @param {String} reason The reason the call was terminated.
|
||||
*/
|
||||
_handleCallTerminated: function(reason) {
|
||||
if (reason === "cancel") {
|
||||
this.setState({callStatus: "start"});
|
||||
return;
|
||||
if (reason !== "cancel") {
|
||||
// XXX This should really display the call failed view - bug 1046959
|
||||
// will implement this.
|
||||
this.props.notifications.errorL10n("call_timeout_notification_text");
|
||||
}
|
||||
// XXX later, we'll want to display more meaningfull messages (needs UX)
|
||||
this.props.notifications.errorL10n("call_timeout_notification_text");
|
||||
this.setState({callStatus: "failure"});
|
||||
// redirects the user to the call start view
|
||||
// XXX should switch callStatus to failed for specific reasons when we
|
||||
// get the call failed view; for now, switch back to start.
|
||||
this.setState({callStatus: "start"});
|
||||
},
|
||||
|
||||
/**
|
||||
@ -915,7 +893,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
CallUrlExpiredView: CallUrlExpiredView,
|
||||
PendingConversationView: PendingConversationView,
|
||||
StartConversationView: StartConversationView,
|
||||
FailedConversationView: FailedConversationView,
|
||||
OutgoingConversationView: OutgoingConversationView,
|
||||
EndedConversationView: EndedConversationView,
|
||||
HomeView: HomeView,
|
||||
|
@ -14,10 +14,9 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
loop.config = loop.config || {};
|
||||
loop.config.serverUrl = loop.config.serverUrl || "http://localhost:5000";
|
||||
|
||||
var sharedMixins = loop.shared.mixins;
|
||||
var sharedModels = loop.shared.models;
|
||||
var sharedViews = loop.shared.views;
|
||||
var sharedUtils = loop.shared.utils;
|
||||
var sharedModels = loop.shared.models,
|
||||
sharedViews = loop.shared.views,
|
||||
sharedUtils = loop.shared.utils;
|
||||
|
||||
/**
|
||||
* Homepage view.
|
||||
@ -117,8 +116,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
render: function() {
|
||||
return (
|
||||
<h1 className="standalone-header-title">
|
||||
<strong>{mozL10n.get("brandShortname")}</strong>
|
||||
{mozL10n.get("clientShortname")}
|
||||
<strong>{mozL10n.get("brandShortname")}</strong> {mozL10n.get("clientShortname")}
|
||||
</h1>
|
||||
);
|
||||
}
|
||||
@ -236,7 +234,7 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
<h3 className="call-url">
|
||||
{conversationUrl}
|
||||
</h3>
|
||||
<h4 className={urlCreationDateClasses}>
|
||||
<h4 className={urlCreationDateClasses} >
|
||||
{callUrlCreationDateString}
|
||||
</h4>
|
||||
</header>
|
||||
@ -288,124 +286,72 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
<ConversationBranding />
|
||||
</header>
|
||||
|
||||
<div id="cameraPreview" />
|
||||
<div id="cameraPreview"></div>
|
||||
|
||||
<div id="messages" />
|
||||
<div id="messages"></div>
|
||||
|
||||
<p className="standalone-btn-label">
|
||||
{callState}
|
||||
</p>
|
||||
|
||||
<div className="btn-pending-cancel-group btn-group">
|
||||
<div className="flex-padding-1" />
|
||||
<div className="flex-padding-1"></div>
|
||||
<button className="btn btn-large btn-cancel"
|
||||
onClick={this._cancelOutgoingCall} >
|
||||
<span className="standalone-call-btn-text">
|
||||
{mozL10n.get("initiate_call_cancel_button")}
|
||||
</span>
|
||||
</button>
|
||||
<div className="flex-padding-1" />
|
||||
<div className="flex-padding-1"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ConversationFooter />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var InitiateCallButton = React.createClass({
|
||||
mixins: [sharedMixins.DropdownMenuMixin],
|
||||
|
||||
/**
|
||||
* Conversation launcher view. A ConversationModel is associated and attached
|
||||
* as a `model` property.
|
||||
*
|
||||
* Required properties:
|
||||
* - {loop.shared.models.ConversationModel} model Conversation model.
|
||||
* - {loop.shared.models.NotificationCollection} notifications
|
||||
*/
|
||||
var StartConversationView = React.createClass({
|
||||
propTypes: {
|
||||
caption: React.PropTypes.string.isRequired,
|
||||
startCall: React.PropTypes.func.isRequired,
|
||||
disabled: React.PropTypes.bool
|
||||
model: React.PropTypes.oneOfType([
|
||||
React.PropTypes.instanceOf(sharedModels.ConversationModel),
|
||||
React.PropTypes.instanceOf(FxOSConversationModel)
|
||||
]).isRequired,
|
||||
// XXX Check more tightly here when we start injecting window.loop.*
|
||||
notifications: React.PropTypes.object.isRequired,
|
||||
client: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {disabled: false};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var dropdownMenuClasses = React.addons.classSet({
|
||||
"native-dropdown-large-parent": true,
|
||||
"standalone-dropdown-menu": true,
|
||||
"visually-hidden": !this.state.showMenu
|
||||
});
|
||||
var chevronClasses = React.addons.classSet({
|
||||
"btn-chevron": true,
|
||||
"disabled": this.props.disabled
|
||||
});
|
||||
return (
|
||||
<div className="standalone-btn-chevron-menu-group">
|
||||
<div className="btn-group-chevron">
|
||||
<div className="btn-group">
|
||||
<button className="btn btn-large btn-accept"
|
||||
onClick={this.props.startCall("audio-video")}
|
||||
disabled={this.props.disabled}
|
||||
title={mozL10n.get("initiate_audio_video_call_tooltip2")}>
|
||||
<span className="standalone-call-btn-text">
|
||||
{this.props.caption}
|
||||
</span>
|
||||
<span className="standalone-call-btn-video-icon" />
|
||||
</button>
|
||||
<div className={chevronClasses}
|
||||
onClick={this.toggleDropdownMenu}>
|
||||
</div>
|
||||
</div>
|
||||
<ul className={dropdownMenuClasses}>
|
||||
<li>
|
||||
<button className="start-audio-only-call"
|
||||
onClick={this.props.startCall("audio")}
|
||||
disabled={this.props.disabled}>
|
||||
{mozL10n.get("initiate_audio_call_button2")}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Initiate conversation view.
|
||||
*/
|
||||
var InitiateConversationView = React.createClass({
|
||||
mixins: [Backbone.Events],
|
||||
|
||||
propTypes: {
|
||||
conversation: React.PropTypes.oneOfType([
|
||||
React.PropTypes.instanceOf(sharedModels.ConversationModel),
|
||||
React.PropTypes.instanceOf(FxOSConversationModel)
|
||||
]).isRequired,
|
||||
// XXX Check more tightly here when we start injecting window.loop.*
|
||||
notifications: React.PropTypes.object.isRequired,
|
||||
client: React.PropTypes.object.isRequired,
|
||||
title: React.PropTypes.string.isRequired,
|
||||
callButtonLabel: React.PropTypes.string.isRequired
|
||||
return {showCallOptionsMenu: false};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
urlCreationDateString: '',
|
||||
disableCallButton: false
|
||||
disableCallButton: false,
|
||||
showCallOptionsMenu: this.props.showCallOptionsMenu
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.listenTo(this.props.conversation,
|
||||
"session:error", this._onSessionError);
|
||||
this.listenTo(this.props.conversation,
|
||||
"fxos:app-needed", this._onFxOSAppNeeded);
|
||||
this.props.client.requestCallUrlInfo(
|
||||
this.props.conversation.get("loopToken"),
|
||||
this._setConversationTimestamp);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
this.stopListening(this.props.conversation);
|
||||
localStorage.setItem("has-seen-tos", "true");
|
||||
// Listen for events & hide dropdown menu if user clicks away
|
||||
window.addEventListener("click", this.clickHandler);
|
||||
this.props.model.listenTo(this.props.model, "session:error",
|
||||
this._onSessionError);
|
||||
this.props.model.listenTo(this.props.model, "fxos:app-needed",
|
||||
this._onFxOSAppNeeded);
|
||||
this.props.client.requestCallUrlInfo(this.props.model.get("loopToken"),
|
||||
this._setConversationTimestamp);
|
||||
},
|
||||
|
||||
_onSessionError: function(error, l10nProps) {
|
||||
@ -416,9 +362,11 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
|
||||
_onFxOSAppNeeded: function() {
|
||||
this.setState({
|
||||
marketplaceSrc: loop.config.marketplaceUrl,
|
||||
onMarketplaceMessage: this.props.conversation.onMarketplaceMessage.bind(
|
||||
this.props.conversation
|
||||
marketplaceSrc: loop.config.marketplaceUrl
|
||||
});
|
||||
this.setState({
|
||||
onMarketplaceMessage: this.props.model.onMarketplaceMessage.bind(
|
||||
this.props.model
|
||||
)
|
||||
});
|
||||
},
|
||||
@ -431,10 +379,11 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
*
|
||||
* @param {string} User call type choice "audio" or "audio-video"
|
||||
*/
|
||||
startCall: function(callType) {
|
||||
_initiateOutgoingCall: function(callType) {
|
||||
return function() {
|
||||
this.props.conversation.setupOutgoingCall(callType);
|
||||
this.props.model.set("selectedCallType", callType);
|
||||
this.setState({disableCallButton: true});
|
||||
this.props.model.setupOutgoingCall();
|
||||
}.bind(this);
|
||||
},
|
||||
|
||||
@ -449,21 +398,47 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
}
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
window.removeEventListener("click", this.clickHandler);
|
||||
localStorage.setItem("has-seen-tos", "true");
|
||||
},
|
||||
|
||||
clickHandler: function(e) {
|
||||
if (!e.target.classList.contains('btn-chevron') &&
|
||||
this.state.showCallOptionsMenu) {
|
||||
this._toggleCallOptionsMenu();
|
||||
}
|
||||
},
|
||||
|
||||
_toggleCallOptionsMenu: function() {
|
||||
var state = this.state.showCallOptionsMenu;
|
||||
this.setState({showCallOptionsMenu: !state});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var tosLinkName = mozL10n.get("terms_of_use_link_text");
|
||||
var privacyNoticeName = mozL10n.get("privacy_notice_link_text");
|
||||
var tos_link_name = mozL10n.get("terms_of_use_link_text");
|
||||
var privacy_notice_name = mozL10n.get("privacy_notice_link_text");
|
||||
|
||||
var tosHTML = mozL10n.get("legal_text_and_links", {
|
||||
"terms_of_use_url": "<a target=_blank href='/legal/terms/'>" +
|
||||
tosLinkName + "</a>",
|
||||
tos_link_name + "</a>",
|
||||
"privacy_notice_url": "<a target=_blank href='" +
|
||||
"https://www.mozilla.org/privacy/'>" + privacyNoticeName + "</a>"
|
||||
"https://www.mozilla.org/privacy/'>" + privacy_notice_name + "</a>"
|
||||
});
|
||||
|
||||
var dropdownMenuClasses = React.addons.classSet({
|
||||
"native-dropdown-large-parent": true,
|
||||
"standalone-dropdown-menu": true,
|
||||
"visually-hidden": !this.state.showCallOptionsMenu
|
||||
});
|
||||
var tosClasses = React.addons.classSet({
|
||||
"terms-service": true,
|
||||
hide: (localStorage.getItem("has-seen-tos") === "true")
|
||||
});
|
||||
var chevronClasses = React.addons.classSet({
|
||||
"btn-chevron": true,
|
||||
"disabled": this.state.disableCallButton
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
@ -473,19 +448,49 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
urlCreationDateString={this.state.urlCreationDateString} />
|
||||
|
||||
<p className="standalone-btn-label">
|
||||
{this.props.title}
|
||||
{mozL10n.get("initiate_call_button_label2")}
|
||||
</p>
|
||||
|
||||
<div id="messages"></div>
|
||||
|
||||
<div className="btn-group">
|
||||
<div className="flex-padding-1" />
|
||||
<InitiateCallButton
|
||||
caption={this.props.callButtonLabel}
|
||||
disabled={this.state.disableCallButton}
|
||||
startCall={this.startCall}
|
||||
/>
|
||||
<div className="flex-padding-1" />
|
||||
<div className="flex-padding-1"></div>
|
||||
<div className="standalone-btn-chevron-menu-group">
|
||||
<div className="btn-group-chevron">
|
||||
<div className="btn-group">
|
||||
|
||||
<button className="btn btn-large btn-accept"
|
||||
onClick={this._initiateOutgoingCall("audio-video")}
|
||||
disabled={this.state.disableCallButton}
|
||||
title={mozL10n.get("initiate_audio_video_call_tooltip2")} >
|
||||
<span className="standalone-call-btn-text">
|
||||
{mozL10n.get("initiate_audio_video_call_button2")}
|
||||
</span>
|
||||
<span className="standalone-call-btn-video-icon"></span>
|
||||
</button>
|
||||
|
||||
<div className={chevronClasses}
|
||||
onClick={this._toggleCallOptionsMenu}>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<ul className={dropdownMenuClasses}>
|
||||
<li>
|
||||
{/*
|
||||
Button required for disabled state.
|
||||
*/}
|
||||
<button className="start-audio-only-call"
|
||||
onClick={this._initiateOutgoingCall("audio")}
|
||||
disabled={this.state.disableCallButton} >
|
||||
{mozL10n.get("initiate_audio_call_button2")}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-padding-1"></div>
|
||||
</div>
|
||||
|
||||
<p className={tosClasses}
|
||||
@ -533,26 +538,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
}
|
||||
});
|
||||
|
||||
var StartConversationView = React.createClass({
|
||||
render: function() {
|
||||
return this.transferPropsTo(
|
||||
<InitiateConversationView
|
||||
title={mozL10n.get("initiate_call_button_label2")}
|
||||
callButtonLabel={mozL10n.get("initiate_audio_video_call_button2")} />
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var FailedConversationView = React.createClass({
|
||||
render: function() {
|
||||
return this.transferPropsTo(
|
||||
<InitiateConversationView
|
||||
title={mozL10n.get("call_failed_title")}
|
||||
callButtonLabel={mozL10n.get("retry_call_button")} />
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* This view manages the outgoing conversation views - from
|
||||
* call initiation through to the actual conversation and call end.
|
||||
@ -610,19 +595,11 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
*/
|
||||
render: function() {
|
||||
switch (this.state.callStatus) {
|
||||
case "failure":
|
||||
case "start": {
|
||||
return (
|
||||
<StartConversationView
|
||||
conversation={this.props.conversation}
|
||||
notifications={this.props.notifications}
|
||||
client={this.props.client}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case "failure": {
|
||||
return (
|
||||
<FailedConversationView
|
||||
conversation={this.props.conversation}
|
||||
model={this.props.conversation}
|
||||
notifications={this.props.notifications}
|
||||
client={this.props.client}
|
||||
/>
|
||||
@ -798,17 +775,18 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
/**
|
||||
* Handles call rejection.
|
||||
*
|
||||
* @param {String} reason The reason the call was terminated (reject, busy,
|
||||
* timeout, cancel, media-fail, user-unknown, closed)
|
||||
* @param {String} reason The reason the call was terminated.
|
||||
*/
|
||||
_handleCallTerminated: function(reason) {
|
||||
if (reason === "cancel") {
|
||||
this.setState({callStatus: "start"});
|
||||
return;
|
||||
if (reason !== "cancel") {
|
||||
// XXX This should really display the call failed view - bug 1046959
|
||||
// will implement this.
|
||||
this.props.notifications.errorL10n("call_timeout_notification_text");
|
||||
}
|
||||
// XXX later, we'll want to display more meaningfull messages (needs UX)
|
||||
this.props.notifications.errorL10n("call_timeout_notification_text");
|
||||
this.setState({callStatus: "failure"});
|
||||
// redirects the user to the call start view
|
||||
// XXX should switch callStatus to failed for specific reasons when we
|
||||
// get the call failed view; for now, switch back to start.
|
||||
this.setState({callStatus: "start"});
|
||||
},
|
||||
|
||||
/**
|
||||
@ -915,7 +893,6 @@ loop.webapp = (function($, _, OT, mozL10n) {
|
||||
CallUrlExpiredView: CallUrlExpiredView,
|
||||
PendingConversationView: PendingConversationView,
|
||||
StartConversationView: StartConversationView,
|
||||
FailedConversationView: FailedConversationView,
|
||||
OutgoingConversationView: OutgoingConversationView,
|
||||
EndedConversationView: EndedConversationView,
|
||||
HomeView: HomeView,
|
||||
|
@ -5,7 +5,6 @@ call_timeout_notification_text=Your call did not go through.
|
||||
missing_conversation_info=Missing conversation information.
|
||||
network_disconnected=The network connection terminated abruptly.
|
||||
peer_ended_conversation2=The person you were calling has ended the conversation.
|
||||
call_failed_title=Call failed.
|
||||
connection_error_see_console_notification=Call failed; see console for details.
|
||||
generic_failure_title=Something went wrong.
|
||||
generic_failure_with_reason2=You can try again or email a link to be reached at later.
|
||||
|
@ -76,19 +76,6 @@ describe("loop.shared.models", function() {
|
||||
});
|
||||
|
||||
describe("#setupOutgoingCall", function() {
|
||||
it("should set the a custom selected call type", function() {
|
||||
conversation.setupOutgoingCall("audio");
|
||||
|
||||
expect(conversation.get("selectedCallType")).eql("audio");
|
||||
});
|
||||
|
||||
it("should respect the default selected call type when none is passed",
|
||||
function() {
|
||||
conversation.setupOutgoingCall();
|
||||
|
||||
expect(conversation.get("selectedCallType")).eql("audio-video");
|
||||
});
|
||||
|
||||
it("should trigger a `call:outgoing:setup` event", function(done) {
|
||||
conversation.once("call:outgoing:setup", function() {
|
||||
done();
|
||||
|
@ -196,14 +196,14 @@ describe("loop.webapp", function() {
|
||||
sandbox.stub(notifications, "errorL10n");
|
||||
});
|
||||
|
||||
it("should display the FailedConversationView", function() {
|
||||
it("should display the StartConversationView", function() {
|
||||
ocView._websocket.trigger("progress", {
|
||||
state: "terminated",
|
||||
reason: "reject"
|
||||
});
|
||||
|
||||
TestUtils.findRenderedComponentWithType(ocView,
|
||||
loop.webapp.FailedConversationView);
|
||||
loop.webapp.StartConversationView);
|
||||
});
|
||||
|
||||
it("should display an error message if the reason is not 'cancel'",
|
||||
@ -271,14 +271,14 @@ describe("loop.webapp", function() {
|
||||
});
|
||||
|
||||
describe("call:outgoing", function() {
|
||||
it("should display FailedConversationView if session token is missing",
|
||||
it("should set display the StartConversationView if session token is missing",
|
||||
function() {
|
||||
conversation.set("loopToken", "");
|
||||
|
||||
ocView.startCall();
|
||||
|
||||
TestUtils.findRenderedComponentWithType(ocView,
|
||||
loop.webapp.FailedConversationView);
|
||||
loop.webapp.StartConversationView);
|
||||
});
|
||||
|
||||
it("should notify the user if session token is missing", function() {
|
||||
@ -400,11 +400,11 @@ describe("loop.webapp", function() {
|
||||
conversation.set("loopToken", "");
|
||||
});
|
||||
|
||||
it("should display the FailedConversationView", function() {
|
||||
it("should set display the StartConversationView", function() {
|
||||
conversation.setupOutgoingCall();
|
||||
|
||||
TestUtils.findRenderedComponentWithType(ocView,
|
||||
loop.webapp.FailedConversationView);
|
||||
loop.webapp.StartConversationView);
|
||||
});
|
||||
|
||||
it("should display an error", function() {
|
||||
@ -416,12 +416,13 @@ describe("loop.webapp", function() {
|
||||
|
||||
describe("Has loop token", function() {
|
||||
beforeEach(function() {
|
||||
conversation.set("selectedCallType", "audio-video");
|
||||
sandbox.stub(conversation, "outgoing");
|
||||
});
|
||||
|
||||
it("should call requestCallInfo on the client",
|
||||
function() {
|
||||
conversation.setupOutgoingCall("audio-video");
|
||||
conversation.setupOutgoingCall();
|
||||
|
||||
sinon.assert.calledOnce(client.requestCallInfo);
|
||||
sinon.assert.calledWith(client.requestCallInfo, "fakeToken",
|
||||
@ -439,14 +440,14 @@ describe("loop.webapp", function() {
|
||||
loop.webapp.CallUrlExpiredView);
|
||||
});
|
||||
|
||||
it("should set display the FailedConversationView on any other error",
|
||||
it("should set display the StartConversationView on any other error",
|
||||
function() {
|
||||
client.requestCallInfo.callsArgWith(2, {errno: 104});
|
||||
|
||||
conversation.setupOutgoingCall();
|
||||
|
||||
TestUtils.findRenderedComponentWithType(ocView,
|
||||
loop.webapp.FailedConversationView);
|
||||
loop.webapp.StartConversationView);
|
||||
});
|
||||
|
||||
it("should notify the user on any other error", function() {
|
||||
@ -584,7 +585,8 @@ describe("loop.webapp", function() {
|
||||
|
||||
describe("StartConversationView", function() {
|
||||
describe("#initiate", function() {
|
||||
var conversation, view, fakeSubmitEvent, requestCallUrlInfo;
|
||||
var conversation, setupOutgoingCall, view, fakeSubmitEvent,
|
||||
requestCallUrlInfo;
|
||||
|
||||
beforeEach(function() {
|
||||
conversation = new sharedModels.ConversationModel({}, {
|
||||
@ -592,6 +594,7 @@ describe("loop.webapp", function() {
|
||||
});
|
||||
|
||||
fakeSubmitEvent = {preventDefault: sinon.spy()};
|
||||
setupOutgoingCall = sinon.stub(conversation, "setupOutgoingCall");
|
||||
|
||||
var standaloneClientStub = {
|
||||
requestCallUrlInfo: function(token, cb) {
|
||||
@ -602,7 +605,7 @@ describe("loop.webapp", function() {
|
||||
|
||||
view = React.addons.TestUtils.renderIntoDocument(
|
||||
loop.webapp.StartConversationView({
|
||||
conversation: conversation,
|
||||
model: conversation,
|
||||
notifications: notifications,
|
||||
client: standaloneClientStub
|
||||
})
|
||||
@ -611,24 +614,20 @@ describe("loop.webapp", function() {
|
||||
|
||||
it("should start the audio-video conversation establishment process",
|
||||
function() {
|
||||
var setupOutgoingCall = sinon.stub(conversation, "setupOutgoingCall");
|
||||
|
||||
var button = view.getDOMNode().querySelector(".btn-accept");
|
||||
React.addons.TestUtils.Simulate.click(button);
|
||||
|
||||
sinon.assert.calledOnce(setupOutgoingCall);
|
||||
sinon.assert.calledWithExactly(setupOutgoingCall, "audio-video");
|
||||
sinon.assert.calledWithExactly(setupOutgoingCall);
|
||||
});
|
||||
|
||||
it("should start the audio-only conversation establishment process",
|
||||
function() {
|
||||
var setupOutgoingCall = sinon.stub(conversation, "setupOutgoingCall");
|
||||
|
||||
var button = view.getDOMNode().querySelector(".start-audio-only-call");
|
||||
React.addons.TestUtils.Simulate.click(button);
|
||||
|
||||
sinon.assert.calledOnce(setupOutgoingCall);
|
||||
sinon.assert.calledWithExactly(setupOutgoingCall, "audio");
|
||||
sinon.assert.calledWithExactly(setupOutgoingCall);
|
||||
});
|
||||
|
||||
it("should disable audio-video button once session is initiated",
|
||||
@ -651,35 +650,35 @@ describe("loop.webapp", function() {
|
||||
expect(button.disabled).to.eql(true);
|
||||
});
|
||||
|
||||
it("should set selectedCallType to audio", function() {
|
||||
conversation.set("loopToken", "fake");
|
||||
it("should set selectedCallType to audio", function() {
|
||||
conversation.set("loopToken", "fake");
|
||||
|
||||
var button = view.getDOMNode().querySelector(".start-audio-only-call");
|
||||
React.addons.TestUtils.Simulate.click(button);
|
||||
var button = view.getDOMNode().querySelector(".start-audio-only-call");
|
||||
React.addons.TestUtils.Simulate.click(button);
|
||||
|
||||
expect(conversation.get("selectedCallType")).to.eql("audio");
|
||||
expect(conversation.get("selectedCallType")).to.eql("audio");
|
||||
});
|
||||
|
||||
it("should set selectedCallType to audio-video", function() {
|
||||
conversation.set("loopToken", "fake");
|
||||
|
||||
var button = view.getDOMNode().querySelector(".standalone-call-btn-video-icon");
|
||||
React.addons.TestUtils.Simulate.click(button);
|
||||
|
||||
expect(conversation.get("selectedCallType")).to.eql("audio-video");
|
||||
});
|
||||
|
||||
it("should set state.urlCreationDateString to a locale date string",
|
||||
function() {
|
||||
// wrap in a jquery object because text is broken up
|
||||
// into several span elements
|
||||
var date = new Date(0);
|
||||
var options = {year: "numeric", month: "long", day: "numeric"};
|
||||
var timestamp = date.toLocaleDateString(navigator.language, options);
|
||||
|
||||
expect(view.state.urlCreationDateString).to.eql(timestamp);
|
||||
});
|
||||
|
||||
it("should set selectedCallType to audio-video", function() {
|
||||
conversation.set("loopToken", "fake");
|
||||
|
||||
var button = view.getDOMNode().querySelector(".standalone-call-btn-video-icon");
|
||||
React.addons.TestUtils.Simulate.click(button);
|
||||
|
||||
expect(conversation.get("selectedCallType")).to.eql("audio-video");
|
||||
});
|
||||
|
||||
// XXX this test breaks while the feature actually works; find a way to
|
||||
// test this properly.
|
||||
it.skip("should set state.urlCreationDateString to a locale date string",
|
||||
function() {
|
||||
var date = new Date();
|
||||
var options = {year: "numeric", month: "long", day: "numeric"};
|
||||
var timestamp = date.toLocaleDateString(navigator.language, options);
|
||||
var dateElem = view.getDOMNode().querySelector(".call-url-date");
|
||||
|
||||
expect(dateElem.textContent).to.eql(timestamp);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Events", function() {
|
||||
@ -698,7 +697,7 @@ describe("loop.webapp", function() {
|
||||
|
||||
view = React.addons.TestUtils.renderIntoDocument(
|
||||
loop.webapp.StartConversationView({
|
||||
conversation: conversation,
|
||||
model: conversation,
|
||||
notifications: notifications,
|
||||
client: {requestCallUrlInfo: requestCallUrlInfo}
|
||||
})
|
||||
@ -783,7 +782,7 @@ describe("loop.webapp", function() {
|
||||
|
||||
view = React.addons.TestUtils.renderIntoDocument(
|
||||
loop.webapp.StartConversationView({
|
||||
conversation: conversation,
|
||||
model: conversation,
|
||||
notifications: notifications,
|
||||
client: {requestCallUrlInfo: requestCallUrlInfo}
|
||||
})
|
||||
@ -799,7 +798,7 @@ describe("loop.webapp", function() {
|
||||
localStorage.setItem("has-seen-tos", "true");
|
||||
view = React.addons.TestUtils.renderIntoDocument(
|
||||
loop.webapp.StartConversationView({
|
||||
conversation: conversation,
|
||||
model: conversation,
|
||||
notifications: notifications,
|
||||
client: {requestCallUrlInfo: requestCallUrlInfo}
|
||||
})
|
||||
@ -889,7 +888,7 @@ describe("loop.webapp", function() {
|
||||
|
||||
view = React.addons.TestUtils.renderIntoDocument(
|
||||
loop.webapp.StartConversationView({
|
||||
conversation: conversation,
|
||||
model: conversation,
|
||||
notifications: notifications,
|
||||
client: standaloneClientStub
|
||||
})
|
||||
@ -1004,7 +1003,7 @@ describe("loop.webapp", function() {
|
||||
before(function() {
|
||||
view = React.addons.TestUtils.renderIntoDocument(
|
||||
loop.webapp.StartConversationView({
|
||||
conversation: model,
|
||||
model: model,
|
||||
notifications: notifications,
|
||||
client: {requestCallUrlInfo: sandbox.stub()}
|
||||
})
|
||||
|
@ -18,13 +18,12 @@
|
||||
|
||||
// 2. Standalone webapp
|
||||
var HomeView = loop.webapp.HomeView;
|
||||
var UnsupportedBrowserView = loop.webapp.UnsupportedBrowserView;
|
||||
var UnsupportedDeviceView = loop.webapp.UnsupportedDeviceView;
|
||||
var CallUrlExpiredView = loop.webapp.CallUrlExpiredView;
|
||||
var UnsupportedBrowserView = loop.webapp.UnsupportedBrowserView;
|
||||
var UnsupportedDeviceView = loop.webapp.UnsupportedDeviceView;
|
||||
var CallUrlExpiredView = loop.webapp.CallUrlExpiredView;
|
||||
var PendingConversationView = loop.webapp.PendingConversationView;
|
||||
var StartConversationView = loop.webapp.StartConversationView;
|
||||
var FailedConversationView = loop.webapp.FailedConversationView;
|
||||
var EndedConversationView = loop.webapp.EndedConversationView;
|
||||
var StartConversationView = loop.webapp.StartConversationView;
|
||||
var EndedConversationView = loop.webapp.EndedConversationView;
|
||||
|
||||
// 3. Shared components
|
||||
var ConversationToolbar = loop.shared.views.ConversationToolbar;
|
||||
@ -169,7 +168,8 @@
|
||||
Example({summary: "Default", dashed: "true", style: {width: "260px", height: "254px"}},
|
||||
React.DOM.div({className: "fx-embedded"},
|
||||
IncomingCallView({model: mockConversationModel,
|
||||
showMenu: true})
|
||||
showDeclineMenu: true,
|
||||
video: true})
|
||||
)
|
||||
)
|
||||
),
|
||||
@ -236,19 +236,10 @@
|
||||
Section({name: "StartConversationView"},
|
||||
Example({summary: "Start conversation view", dashed: "true"},
|
||||
React.DOM.div({className: "standalone"},
|
||||
StartConversationView({conversation: mockConversationModel,
|
||||
StartConversationView({model: mockConversationModel,
|
||||
client: mockClient,
|
||||
notifications: notifications})
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
Section({name: "FailedConversationView"},
|
||||
Example({summary: "Failed conversation view", dashed: "true"},
|
||||
React.DOM.div({className: "standalone"},
|
||||
FailedConversationView({conversation: mockConversationModel,
|
||||
client: mockClient,
|
||||
notifications: notifications})
|
||||
notifications: notifications,
|
||||
showCallOptionsMenu: true})
|
||||
)
|
||||
)
|
||||
),
|
||||
|
@ -18,13 +18,12 @@
|
||||
|
||||
// 2. Standalone webapp
|
||||
var HomeView = loop.webapp.HomeView;
|
||||
var UnsupportedBrowserView = loop.webapp.UnsupportedBrowserView;
|
||||
var UnsupportedDeviceView = loop.webapp.UnsupportedDeviceView;
|
||||
var CallUrlExpiredView = loop.webapp.CallUrlExpiredView;
|
||||
var UnsupportedBrowserView = loop.webapp.UnsupportedBrowserView;
|
||||
var UnsupportedDeviceView = loop.webapp.UnsupportedDeviceView;
|
||||
var CallUrlExpiredView = loop.webapp.CallUrlExpiredView;
|
||||
var PendingConversationView = loop.webapp.PendingConversationView;
|
||||
var StartConversationView = loop.webapp.StartConversationView;
|
||||
var FailedConversationView = loop.webapp.FailedConversationView;
|
||||
var EndedConversationView = loop.webapp.EndedConversationView;
|
||||
var StartConversationView = loop.webapp.StartConversationView;
|
||||
var EndedConversationView = loop.webapp.EndedConversationView;
|
||||
|
||||
// 3. Shared components
|
||||
var ConversationToolbar = loop.shared.views.ConversationToolbar;
|
||||
@ -169,7 +168,8 @@
|
||||
<Example summary="Default" dashed="true" style={{width: "260px", height: "254px"}}>
|
||||
<div className="fx-embedded" >
|
||||
<IncomingCallView model={mockConversationModel}
|
||||
showMenu={true} />
|
||||
showDeclineMenu={true}
|
||||
video={true} />
|
||||
</div>
|
||||
</Example>
|
||||
</Section>
|
||||
@ -236,19 +236,10 @@
|
||||
<Section name="StartConversationView">
|
||||
<Example summary="Start conversation view" dashed="true">
|
||||
<div className="standalone">
|
||||
<StartConversationView conversation={mockConversationModel}
|
||||
<StartConversationView model={mockConversationModel}
|
||||
client={mockClient}
|
||||
notifications={notifications} />
|
||||
</div>
|
||||
</Example>
|
||||
</Section>
|
||||
|
||||
<Section name="FailedConversationView">
|
||||
<Example summary="Failed conversation view" dashed="true">
|
||||
<div className="standalone">
|
||||
<FailedConversationView conversation={mockConversationModel}
|
||||
client={mockClient}
|
||||
notifications={notifications} />
|
||||
notifications={notifications}
|
||||
showCallOptionsMenu={true} />
|
||||
</div>
|
||||
</Example>
|
||||
</Section>
|
||||
|
Loading…
Reference in New Issue
Block a user