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