зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1121071 Part 1 - Remove old call url code from the Loop panel and related UI areas. r=mikedeboer
This commit is contained in:
Родитель
68d3ae3040
Коммит
bfc73a696e
|
@ -1677,7 +1677,6 @@ pref("loop.CSP", "default-src 'self' about: file: chrome:; img-src 'self' data:
|
|||
#endif
|
||||
pref("loop.oauth.google.redirect_uri", "urn:ietf:wg:oauth:2.0:oob:auto");
|
||||
pref("loop.oauth.google.scope", "https://www.google.com/m8/feeds");
|
||||
pref("loop.rooms.enabled", true);
|
||||
pref("loop.fxa_oauth.tokendata", "");
|
||||
pref("loop.fxa_oauth.profile", "");
|
||||
pref("loop.support_url", "https://support.mozilla.org/kb/group-conversations-firefox-hello-webrtc");
|
||||
|
|
|
@ -416,26 +416,6 @@ function injectLoopAPI(targetWindow) {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Used to note a call url expiry time. If the time is later than the current
|
||||
* latest expiry time, then the stored expiry time is increased. For times
|
||||
* sooner, this function is a no-op; this ensures we always have the latest
|
||||
* expiry time for a url.
|
||||
*
|
||||
* This is used to determine whether or not we should be registering with the
|
||||
* push server on start.
|
||||
*
|
||||
* @param {Integer} expiryTimeSeconds The seconds since epoch of the expiry time
|
||||
* of the url.
|
||||
*/
|
||||
noteCallUrlExpiry: {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: function(expiryTimeSeconds) {
|
||||
MozLoopService.noteCallUrlExpiry(expiryTimeSeconds);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Set any preference under "loop."
|
||||
*
|
||||
|
|
|
@ -1243,22 +1243,6 @@ this.MozLoopService = {
|
|||
return MozLoopServiceInternal.promiseRegisteredWithServers(sessionType);
|
||||
},
|
||||
|
||||
/**
|
||||
* Used to note a call url expiry time. If the time is later than the current
|
||||
* latest expiry time, then the stored expiry time is increased. For times
|
||||
* sooner, this function is a no-op; this ensures we always have the latest
|
||||
* expiry time for a url.
|
||||
*
|
||||
* This is used to determine whether or not we should be registering with the
|
||||
* push server on start.
|
||||
*
|
||||
* @param {Integer} expiryTimeSeconds The seconds since epoch of the expiry time
|
||||
* of the url.
|
||||
*/
|
||||
noteCallUrlExpiry: function(expiryTimeSeconds) {
|
||||
MozLoopServiceInternal.expiryTimeSeconds = expiryTimeSeconds;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the strings for the specified element. Designed for use with l10n.js.
|
||||
*
|
||||
|
|
|
@ -9,12 +9,6 @@ var loop = loop || {};
|
|||
loop.Client = (function($) {
|
||||
"use strict";
|
||||
|
||||
// The expected properties to be returned from the POST /call-url/ request.
|
||||
var expectedCallUrlProperties = ["callUrl", "expiresAt"];
|
||||
|
||||
// The expected properties to be returned from the GET /calls request.
|
||||
var expectedCallProperties = ["calls"];
|
||||
|
||||
// THe expected properties to be returned from the POST /calls request.
|
||||
var expectedPostCallProperties = [
|
||||
"apiKey", "callId", "progressURL",
|
||||
|
@ -81,56 +75,6 @@ loop.Client = (function($) {
|
|||
cb(error);
|
||||
},
|
||||
|
||||
/**
|
||||
* Requests a call URL from the Loop server. It will note the
|
||||
* expiry time for the url with the mozLoop api. It will select the
|
||||
* appropriate hawk session to use based on whether or not the user
|
||||
* is currently logged into a Firefox account profile.
|
||||
*
|
||||
* Callback parameters:
|
||||
* - err null on successful request, non-null otherwise.
|
||||
* - callUrlData an object of the obtained call url data if successful:
|
||||
* -- callUrl: The url of the call
|
||||
* -- expiresAt: The amount of hours until expiry of the url
|
||||
*
|
||||
* @param {String} simplepushUrl a registered Simple Push URL
|
||||
* @param {string} nickname the nickname of the future caller
|
||||
* @param {Function} cb Callback(err, callUrlData)
|
||||
*/
|
||||
requestCallUrl: function(nickname, cb) {
|
||||
var sessionType;
|
||||
if (this.mozLoop.userProfile) {
|
||||
sessionType = this.mozLoop.LOOP_SESSION_TYPE.FXA;
|
||||
} else {
|
||||
sessionType = this.mozLoop.LOOP_SESSION_TYPE.GUEST;
|
||||
}
|
||||
|
||||
this.mozLoop.hawkRequest(sessionType, "/call-url/", "POST",
|
||||
{callerId: nickname},
|
||||
function (error, responseText) {
|
||||
if (error) {
|
||||
this._telemetryAdd("LOOP_CLIENT_CALL_URL_REQUESTS_SUCCESS", false);
|
||||
this._failureHandler(cb, error);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
var urlData = JSON.parse(responseText);
|
||||
|
||||
// This throws if the data is invalid, in which case only the failure
|
||||
// telemetry will be recorded.
|
||||
var returnData = this._validate(urlData, expectedCallUrlProperties);
|
||||
|
||||
this._telemetryAdd("LOOP_CLIENT_CALL_URL_REQUESTS_SUCCESS", true);
|
||||
cb(null, returnData);
|
||||
} catch (err) {
|
||||
this._telemetryAdd("LOOP_CLIENT_CALL_URL_REQUESTS_SUCCESS", false);
|
||||
console.log("Error requesting call info", err);
|
||||
cb(err);
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Block call URL based on the token identifier
|
||||
*
|
||||
|
@ -203,20 +147,6 @@ loop.Client = (function($) {
|
|||
}.bind(this)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a value to a telemetry histogram, ignoring errors.
|
||||
*
|
||||
* @param {string} histogramId Name of the telemetry histogram to update.
|
||||
* @param {integer} value Value to add to the histogram.
|
||||
*/
|
||||
_telemetryAdd: function(histogramId, value) {
|
||||
try {
|
||||
this.mozLoop.telemetryAdd(histogramId, value);
|
||||
} catch (err) {
|
||||
console.error("Error recording telemetry", err);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return Client;
|
||||
|
|
|
@ -39,9 +39,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
// When we don't need to rely on the pref, this can move back to
|
||||
// getDefaultProps (bug 1100258).
|
||||
return {
|
||||
selectedTab: this.props.selectedTab ||
|
||||
(navigator.mozLoop.getLoopPref("rooms.enabled") ?
|
||||
"rooms" : "call")
|
||||
selectedTab: this.props.selectedTab || "rooms"
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -358,157 +356,6 @@ loop.panel = (function(_, mozL10n) {
|
|||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Call url result view.
|
||||
*/
|
||||
var CallUrlResult = React.createClass({displayName: "CallUrlResult",
|
||||
mixins: [sharedMixins.DocumentVisibilityMixin],
|
||||
|
||||
propTypes: {
|
||||
callUrl: React.PropTypes.string,
|
||||
callUrlExpiry: React.PropTypes.number,
|
||||
notifications: React.PropTypes.object.isRequired,
|
||||
client: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
pending: false,
|
||||
copied: false,
|
||||
callUrl: this.props.callUrl || "",
|
||||
callUrlExpiry: 0
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Provided by DocumentVisibilityMixin. Schedules retrieval of a new call
|
||||
* URL everytime the panel is reopened.
|
||||
*/
|
||||
onDocumentVisible: function() {
|
||||
this._fetchCallUrl();
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
// If we've already got a callURL, don't bother requesting a new one.
|
||||
// As of this writing, only used for visual testing in the UI showcase.
|
||||
if (this.state.callUrl.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._fetchCallUrl();
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetches a call URL.
|
||||
*/
|
||||
_fetchCallUrl: function() {
|
||||
this.setState({pending: true});
|
||||
// XXX This is an empty string as a conversation identifier. Bug 1015938 implements
|
||||
// a user-set string.
|
||||
this.props.client.requestCallUrl("",
|
||||
this._onCallUrlReceived);
|
||||
},
|
||||
|
||||
_onCallUrlReceived: function(err, callUrlData) {
|
||||
if (err) {
|
||||
if (err.code != 401) {
|
||||
// 401 errors are already handled in hawkRequest and show an error
|
||||
// message about the session.
|
||||
this.props.notifications.errorL10n("unable_retrieve_url");
|
||||
}
|
||||
this.setState(this.getInitialState());
|
||||
} else {
|
||||
try {
|
||||
var callUrl = new window.URL(callUrlData.callUrl);
|
||||
// XXX the current server vers does not implement the callToken field
|
||||
// but it exists in the API. This workaround should be removed in the future
|
||||
var token = callUrlData.callToken ||
|
||||
callUrl.pathname.split('/').pop();
|
||||
|
||||
// Now that a new URL is available, indicate it has not been shared.
|
||||
this.linkExfiltrated = false;
|
||||
|
||||
this.setState({pending: false, copied: false,
|
||||
callUrl: callUrl.href,
|
||||
callUrlExpiry: callUrlData.expiresAt});
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
this.props.notifications.errorL10n("unable_retrieve_url");
|
||||
this.setState(this.getInitialState());
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
handleEmailButtonClick: function(event) {
|
||||
this.handleLinkExfiltration(event);
|
||||
|
||||
sharedUtils.composeCallUrlEmail(this.state.callUrl);
|
||||
},
|
||||
|
||||
handleCopyButtonClick: function(event) {
|
||||
this.handleLinkExfiltration(event);
|
||||
// XXX the mozLoop object should be passed as a prop, to ease testing and
|
||||
// using a fake implementation in UI components showcase.
|
||||
navigator.mozLoop.copyString(this.state.callUrl);
|
||||
this.setState({copied: true});
|
||||
},
|
||||
|
||||
linkExfiltrated: false,
|
||||
|
||||
handleLinkExfiltration: function(event) {
|
||||
// Update the count of shared URLs only once per generated URL.
|
||||
if (!this.linkExfiltrated) {
|
||||
this.linkExfiltrated = true;
|
||||
try {
|
||||
navigator.mozLoop.telemetryAdd("LOOP_CLIENT_CALL_URL_SHARED", true);
|
||||
} catch (err) {
|
||||
console.error("Error recording telemetry", err);
|
||||
}
|
||||
}
|
||||
|
||||
// Note URL expiration every time it is shared.
|
||||
if (this.state.callUrlExpiry) {
|
||||
navigator.mozLoop.noteCallUrlExpiry(this.state.callUrlExpiry);
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
// XXX setting elem value from a state (in the callUrl input)
|
||||
// makes it immutable ie read only but that is fine in our case.
|
||||
// readOnly attr will suppress a warning regarding this issue
|
||||
// from the react lib.
|
||||
var cx = React.addons.classSet;
|
||||
return (
|
||||
React.createElement("div", {className: "generate-url"},
|
||||
React.createElement("header", {id: "share-link-header"}, mozL10n.get("share_link_header_text")),
|
||||
React.createElement("div", {className: "generate-url-stack"},
|
||||
React.createElement("input", {type: "url", value: this.state.callUrl, readOnly: "true",
|
||||
onCopy: this.handleLinkExfiltration,
|
||||
className: cx({"generate-url-input": true,
|
||||
pending: this.state.pending,
|
||||
// Used in functional testing, signals that
|
||||
// call url was received from loop server
|
||||
callUrl: !this.state.pending})}),
|
||||
React.createElement("div", {className: cx({"generate-url-spinner": true,
|
||||
spinner: true,
|
||||
busy: this.state.pending})})
|
||||
),
|
||||
React.createElement(ButtonGroup, {additionalClass: "url-actions"},
|
||||
React.createElement(Button, {additionalClass: "button-email",
|
||||
disabled: !this.state.callUrl,
|
||||
onClick: this.handleEmailButtonClick,
|
||||
caption: mozL10n.get("share_button")}),
|
||||
React.createElement(Button, {additionalClass: "button-copy",
|
||||
disabled: !this.state.callUrl,
|
||||
onClick: this.handleCopyButtonClick,
|
||||
caption: this.state.copied ? mozL10n.get("copied_url_button") :
|
||||
mozL10n.get("copy_url_button")})
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* FxA sign in/up link component.
|
||||
*/
|
||||
|
@ -820,9 +667,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
var PanelView = React.createClass({displayName: "PanelView",
|
||||
propTypes: {
|
||||
notifications: React.PropTypes.object.isRequired,
|
||||
client: React.PropTypes.object.isRequired,
|
||||
// Mostly used for UI components showcase and unit tests
|
||||
callUrl: React.PropTypes.string,
|
||||
userProfile: React.PropTypes.object,
|
||||
// Used only for unit tests.
|
||||
showTabButtons: React.PropTypes.bool,
|
||||
|
@ -869,17 +714,13 @@ loop.panel = (function(_, mozL10n) {
|
|||
}
|
||||
},
|
||||
|
||||
_roomsEnabled: function() {
|
||||
return this.props.mozLoop.getLoopPref("rooms.enabled");
|
||||
},
|
||||
|
||||
_onStatusChanged: function() {
|
||||
var profile = this.props.mozLoop.userProfile;
|
||||
var currUid = this.state.userProfile ? this.state.userProfile.uid : null;
|
||||
var newUid = profile ? profile.uid : null;
|
||||
if (currUid != newUid) {
|
||||
// On profile change (login, logout), switch back to the default tab.
|
||||
this.selectTab(this._roomsEnabled() ? "rooms" : "call");
|
||||
this.selectTab("rooms");
|
||||
this.setState({userProfile: profile});
|
||||
}
|
||||
this.updateServiceErrors();
|
||||
|
@ -902,34 +743,6 @@ loop.panel = (function(_, mozL10n) {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The rooms feature is hidden by default for now. Once it gets mainstream,
|
||||
* this method can be simplified.
|
||||
*/
|
||||
_renderRoomsOrCallTab: function() {
|
||||
if (!this._roomsEnabled()) {
|
||||
return (
|
||||
React.createElement(Tab, {name: "call"},
|
||||
React.createElement("div", {className: "content-area"},
|
||||
React.createElement(CallUrlResult, {client: this.props.client,
|
||||
notifications: this.props.notifications,
|
||||
callUrl: this.props.callUrl}),
|
||||
React.createElement(ToSView, null)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
React.createElement(Tab, {name: "rooms"},
|
||||
React.createElement(RoomList, {dispatcher: this.props.dispatcher,
|
||||
store: this.props.roomStore,
|
||||
userDisplayName: this._getUserDisplayName()}),
|
||||
React.createElement(ToSView, null)
|
||||
)
|
||||
);
|
||||
},
|
||||
|
||||
startForm: function(name, contact) {
|
||||
this.refs[name].initForm(contact);
|
||||
this.selectTab(name);
|
||||
|
@ -986,7 +799,12 @@ loop.panel = (function(_, mozL10n) {
|
|||
clearOnDocumentHidden: true}),
|
||||
React.createElement(TabView, {ref: "tabView", selectedTab: this.props.selectedTab,
|
||||
buttonsHidden: hideButtons},
|
||||
this._renderRoomsOrCallTab(),
|
||||
React.createElement(Tab, {name: "rooms"},
|
||||
React.createElement(RoomList, {dispatcher: this.props.dispatcher,
|
||||
store: this.props.roomStore,
|
||||
userDisplayName: this._getUserDisplayName()}),
|
||||
React.createElement(ToSView, null)
|
||||
),
|
||||
React.createElement(Tab, {name: "contacts"},
|
||||
React.createElement(ContactsList, {selectTab: this.selectTab,
|
||||
startForm: this.startForm,
|
||||
|
@ -1029,7 +847,6 @@ loop.panel = (function(_, mozL10n) {
|
|||
// else to ensure the L10n environment is setup correctly.
|
||||
mozL10n.initialize(navigator.mozLoop);
|
||||
|
||||
var client = new loop.Client();
|
||||
var notifications = new sharedModels.NotificationCollection();
|
||||
var dispatcher = new loop.Dispatcher();
|
||||
var roomStore = new loop.store.RoomStore(dispatcher, {
|
||||
|
@ -1038,7 +855,6 @@ loop.panel = (function(_, mozL10n) {
|
|||
});
|
||||
|
||||
React.render(React.createElement(PanelView, {
|
||||
client: client,
|
||||
notifications: notifications,
|
||||
roomStore: roomStore,
|
||||
mozLoop: navigator.mozLoop,
|
||||
|
@ -1057,7 +873,6 @@ loop.panel = (function(_, mozL10n) {
|
|||
init: init,
|
||||
AuthLink: AuthLink,
|
||||
AvailabilityDropdown: AvailabilityDropdown,
|
||||
CallUrlResult: CallUrlResult,
|
||||
GettingStartedView: GettingStartedView,
|
||||
PanelView: PanelView,
|
||||
RoomEntry: RoomEntry,
|
||||
|
|
|
@ -39,9 +39,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
// When we don't need to rely on the pref, this can move back to
|
||||
// getDefaultProps (bug 1100258).
|
||||
return {
|
||||
selectedTab: this.props.selectedTab ||
|
||||
(navigator.mozLoop.getLoopPref("rooms.enabled") ?
|
||||
"rooms" : "call")
|
||||
selectedTab: this.props.selectedTab || "rooms"
|
||||
};
|
||||
},
|
||||
|
||||
|
@ -358,157 +356,6 @@ loop.panel = (function(_, mozL10n) {
|
|||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Call url result view.
|
||||
*/
|
||||
var CallUrlResult = React.createClass({
|
||||
mixins: [sharedMixins.DocumentVisibilityMixin],
|
||||
|
||||
propTypes: {
|
||||
callUrl: React.PropTypes.string,
|
||||
callUrlExpiry: React.PropTypes.number,
|
||||
notifications: React.PropTypes.object.isRequired,
|
||||
client: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
pending: false,
|
||||
copied: false,
|
||||
callUrl: this.props.callUrl || "",
|
||||
callUrlExpiry: 0
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Provided by DocumentVisibilityMixin. Schedules retrieval of a new call
|
||||
* URL everytime the panel is reopened.
|
||||
*/
|
||||
onDocumentVisible: function() {
|
||||
this._fetchCallUrl();
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
// If we've already got a callURL, don't bother requesting a new one.
|
||||
// As of this writing, only used for visual testing in the UI showcase.
|
||||
if (this.state.callUrl.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._fetchCallUrl();
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetches a call URL.
|
||||
*/
|
||||
_fetchCallUrl: function() {
|
||||
this.setState({pending: true});
|
||||
// XXX This is an empty string as a conversation identifier. Bug 1015938 implements
|
||||
// a user-set string.
|
||||
this.props.client.requestCallUrl("",
|
||||
this._onCallUrlReceived);
|
||||
},
|
||||
|
||||
_onCallUrlReceived: function(err, callUrlData) {
|
||||
if (err) {
|
||||
if (err.code != 401) {
|
||||
// 401 errors are already handled in hawkRequest and show an error
|
||||
// message about the session.
|
||||
this.props.notifications.errorL10n("unable_retrieve_url");
|
||||
}
|
||||
this.setState(this.getInitialState());
|
||||
} else {
|
||||
try {
|
||||
var callUrl = new window.URL(callUrlData.callUrl);
|
||||
// XXX the current server vers does not implement the callToken field
|
||||
// but it exists in the API. This workaround should be removed in the future
|
||||
var token = callUrlData.callToken ||
|
||||
callUrl.pathname.split('/').pop();
|
||||
|
||||
// Now that a new URL is available, indicate it has not been shared.
|
||||
this.linkExfiltrated = false;
|
||||
|
||||
this.setState({pending: false, copied: false,
|
||||
callUrl: callUrl.href,
|
||||
callUrlExpiry: callUrlData.expiresAt});
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
this.props.notifications.errorL10n("unable_retrieve_url");
|
||||
this.setState(this.getInitialState());
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
handleEmailButtonClick: function(event) {
|
||||
this.handleLinkExfiltration(event);
|
||||
|
||||
sharedUtils.composeCallUrlEmail(this.state.callUrl);
|
||||
},
|
||||
|
||||
handleCopyButtonClick: function(event) {
|
||||
this.handleLinkExfiltration(event);
|
||||
// XXX the mozLoop object should be passed as a prop, to ease testing and
|
||||
// using a fake implementation in UI components showcase.
|
||||
navigator.mozLoop.copyString(this.state.callUrl);
|
||||
this.setState({copied: true});
|
||||
},
|
||||
|
||||
linkExfiltrated: false,
|
||||
|
||||
handleLinkExfiltration: function(event) {
|
||||
// Update the count of shared URLs only once per generated URL.
|
||||
if (!this.linkExfiltrated) {
|
||||
this.linkExfiltrated = true;
|
||||
try {
|
||||
navigator.mozLoop.telemetryAdd("LOOP_CLIENT_CALL_URL_SHARED", true);
|
||||
} catch (err) {
|
||||
console.error("Error recording telemetry", err);
|
||||
}
|
||||
}
|
||||
|
||||
// Note URL expiration every time it is shared.
|
||||
if (this.state.callUrlExpiry) {
|
||||
navigator.mozLoop.noteCallUrlExpiry(this.state.callUrlExpiry);
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
// XXX setting elem value from a state (in the callUrl input)
|
||||
// makes it immutable ie read only but that is fine in our case.
|
||||
// readOnly attr will suppress a warning regarding this issue
|
||||
// from the react lib.
|
||||
var cx = React.addons.classSet;
|
||||
return (
|
||||
<div className="generate-url">
|
||||
<header id="share-link-header">{mozL10n.get("share_link_header_text")}</header>
|
||||
<div className="generate-url-stack">
|
||||
<input type="url" value={this.state.callUrl} readOnly="true"
|
||||
onCopy={this.handleLinkExfiltration}
|
||||
className={cx({"generate-url-input": true,
|
||||
pending: this.state.pending,
|
||||
// Used in functional testing, signals that
|
||||
// call url was received from loop server
|
||||
callUrl: !this.state.pending})} />
|
||||
<div className={cx({"generate-url-spinner": true,
|
||||
spinner: true,
|
||||
busy: this.state.pending})} />
|
||||
</div>
|
||||
<ButtonGroup additionalClass="url-actions">
|
||||
<Button additionalClass="button-email"
|
||||
disabled={!this.state.callUrl}
|
||||
onClick={this.handleEmailButtonClick}
|
||||
caption={mozL10n.get("share_button")} />
|
||||
<Button additionalClass="button-copy"
|
||||
disabled={!this.state.callUrl}
|
||||
onClick={this.handleCopyButtonClick}
|
||||
caption={this.state.copied ? mozL10n.get("copied_url_button") :
|
||||
mozL10n.get("copy_url_button")} />
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* FxA sign in/up link component.
|
||||
*/
|
||||
|
@ -820,9 +667,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
var PanelView = React.createClass({
|
||||
propTypes: {
|
||||
notifications: React.PropTypes.object.isRequired,
|
||||
client: React.PropTypes.object.isRequired,
|
||||
// Mostly used for UI components showcase and unit tests
|
||||
callUrl: React.PropTypes.string,
|
||||
userProfile: React.PropTypes.object,
|
||||
// Used only for unit tests.
|
||||
showTabButtons: React.PropTypes.bool,
|
||||
|
@ -869,17 +714,13 @@ loop.panel = (function(_, mozL10n) {
|
|||
}
|
||||
},
|
||||
|
||||
_roomsEnabled: function() {
|
||||
return this.props.mozLoop.getLoopPref("rooms.enabled");
|
||||
},
|
||||
|
||||
_onStatusChanged: function() {
|
||||
var profile = this.props.mozLoop.userProfile;
|
||||
var currUid = this.state.userProfile ? this.state.userProfile.uid : null;
|
||||
var newUid = profile ? profile.uid : null;
|
||||
if (currUid != newUid) {
|
||||
// On profile change (login, logout), switch back to the default tab.
|
||||
this.selectTab(this._roomsEnabled() ? "rooms" : "call");
|
||||
this.selectTab("rooms");
|
||||
this.setState({userProfile: profile});
|
||||
}
|
||||
this.updateServiceErrors();
|
||||
|
@ -902,34 +743,6 @@ loop.panel = (function(_, mozL10n) {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The rooms feature is hidden by default for now. Once it gets mainstream,
|
||||
* this method can be simplified.
|
||||
*/
|
||||
_renderRoomsOrCallTab: function() {
|
||||
if (!this._roomsEnabled()) {
|
||||
return (
|
||||
<Tab name="call">
|
||||
<div className="content-area">
|
||||
<CallUrlResult client={this.props.client}
|
||||
notifications={this.props.notifications}
|
||||
callUrl={this.props.callUrl} />
|
||||
<ToSView />
|
||||
</div>
|
||||
</Tab>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Tab name="rooms">
|
||||
<RoomList dispatcher={this.props.dispatcher}
|
||||
store={this.props.roomStore}
|
||||
userDisplayName={this._getUserDisplayName()}/>
|
||||
<ToSView />
|
||||
</Tab>
|
||||
);
|
||||
},
|
||||
|
||||
startForm: function(name, contact) {
|
||||
this.refs[name].initForm(contact);
|
||||
this.selectTab(name);
|
||||
|
@ -986,7 +799,12 @@ loop.panel = (function(_, mozL10n) {
|
|||
clearOnDocumentHidden={true} />
|
||||
<TabView ref="tabView" selectedTab={this.props.selectedTab}
|
||||
buttonsHidden={hideButtons}>
|
||||
{this._renderRoomsOrCallTab()}
|
||||
<Tab name="rooms">
|
||||
<RoomList dispatcher={this.props.dispatcher}
|
||||
store={this.props.roomStore}
|
||||
userDisplayName={this._getUserDisplayName()}/>
|
||||
<ToSView />
|
||||
</Tab>
|
||||
<Tab name="contacts">
|
||||
<ContactsList selectTab={this.selectTab}
|
||||
startForm={this.startForm}
|
||||
|
@ -1029,7 +847,6 @@ loop.panel = (function(_, mozL10n) {
|
|||
// else to ensure the L10n environment is setup correctly.
|
||||
mozL10n.initialize(navigator.mozLoop);
|
||||
|
||||
var client = new loop.Client();
|
||||
var notifications = new sharedModels.NotificationCollection();
|
||||
var dispatcher = new loop.Dispatcher();
|
||||
var roomStore = new loop.store.RoomStore(dispatcher, {
|
||||
|
@ -1038,7 +855,6 @@ loop.panel = (function(_, mozL10n) {
|
|||
});
|
||||
|
||||
React.render(<PanelView
|
||||
client={client}
|
||||
notifications={notifications}
|
||||
roomStore={roomStore}
|
||||
mozLoop={navigator.mozLoop}
|
||||
|
@ -1057,7 +873,6 @@ loop.panel = (function(_, mozL10n) {
|
|||
init: init,
|
||||
AuthLink: AuthLink,
|
||||
AvailabilityDropdown: AvailabilityDropdown,
|
||||
CallUrlResult: CallUrlResult,
|
||||
GettingStartedView: GettingStartedView,
|
||||
PanelView: PanelView,
|
||||
RoomEntry: RoomEntry,
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
<script type="text/javascript" src="loop/shared/js/roomStates.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/fxOSActiveRoomStore.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/activeRoomStore.js"></script>
|
||||
<script type="text/javascript" src="loop/js/client.js"></script>
|
||||
<script type="text/javascript;version=1.8" src="loop/js/contacts.js"></script>
|
||||
<script type="text/javascript" src="loop/js/panel.js"></script>
|
||||
</body>
|
||||
|
|
|
@ -32,7 +32,6 @@ describe("loop.Client", function() {
|
|||
.returns(null)
|
||||
.withArgs("hawk-session-token")
|
||||
.returns(fakeToken),
|
||||
noteCallUrlExpiry: sinon.spy(),
|
||||
hawkRequest: sinon.stub(),
|
||||
LOOP_SESSION_TYPE: {
|
||||
GUEST: 1,
|
||||
|
@ -89,140 +88,6 @@ describe("loop.Client", function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe("#requestCallUrl", function() {
|
||||
it("should post to /call-url/", function() {
|
||||
client.requestCallUrl("foo", callback);
|
||||
|
||||
sinon.assert.calledOnce(hawkRequestStub);
|
||||
sinon.assert.calledWithExactly(hawkRequestStub, sinon.match.number,
|
||||
"/call-url/", "POST", {callerId: "foo"}, sinon.match.func);
|
||||
});
|
||||
|
||||
it("should send a sessionType of LOOP_SESSION_TYPE.GUEST when " +
|
||||
"mozLoop.userProfile returns null", function() {
|
||||
mozLoop.userProfile = null;
|
||||
|
||||
client.requestCallUrl("foo", callback);
|
||||
|
||||
sinon.assert.calledOnce(hawkRequestStub);
|
||||
sinon.assert.calledWithExactly(hawkRequestStub,
|
||||
mozLoop.LOOP_SESSION_TYPE.GUEST, "/call-url/", "POST",
|
||||
{callerId: "foo"}, sinon.match.func);
|
||||
});
|
||||
|
||||
it("should send a sessionType of LOOP_SESSION_TYPE.FXA when " +
|
||||
"mozLoop.userProfile returns an object", function () {
|
||||
mozLoop.userProfile = {};
|
||||
|
||||
client.requestCallUrl("foo", callback);
|
||||
|
||||
sinon.assert.calledOnce(hawkRequestStub);
|
||||
sinon.assert.calledWithExactly(hawkRequestStub,
|
||||
mozLoop.LOOP_SESSION_TYPE.FXA, "/call-url/", "POST",
|
||||
{callerId: "foo"}, sinon.match.func);
|
||||
});
|
||||
|
||||
it("should call the callback with the url when the request succeeds",
|
||||
function() {
|
||||
var callUrlData = {
|
||||
"callUrl": "fakeCallUrl",
|
||||
"expiresAt": 60
|
||||
};
|
||||
|
||||
// Sets up the hawkRequest stub to trigger the callback with no error
|
||||
// and the url.
|
||||
hawkRequestStub.callsArgWith(4, null, JSON.stringify(callUrlData));
|
||||
|
||||
client.requestCallUrl("foo", callback);
|
||||
|
||||
sinon.assert.calledWithExactly(callback, null, callUrlData);
|
||||
});
|
||||
|
||||
it("should not update call url expiry when the request succeeds",
|
||||
function() {
|
||||
var callUrlData = {
|
||||
"callUrl": "fakeCallUrl",
|
||||
"expiresAt": 6000
|
||||
};
|
||||
|
||||
// Sets up the hawkRequest stub to trigger the callback with no error
|
||||
// and the url.
|
||||
hawkRequestStub.callsArgWith(4, null, JSON.stringify(callUrlData));
|
||||
|
||||
client.requestCallUrl("foo", callback);
|
||||
|
||||
sinon.assert.notCalled(mozLoop.noteCallUrlExpiry);
|
||||
});
|
||||
|
||||
it("should call mozLoop.telemetryAdd when the request succeeds",
|
||||
function(done) {
|
||||
var callUrlData = {
|
||||
"callUrl": "fakeCallUrl",
|
||||
"expiresAt": 60
|
||||
};
|
||||
|
||||
// Sets up the hawkRequest stub to trigger the callback with no error
|
||||
// and the url.
|
||||
hawkRequestStub.callsArgWith(4, null,
|
||||
JSON.stringify(callUrlData));
|
||||
|
||||
client.requestCallUrl("foo", function(err) {
|
||||
expect(err).to.be.null;
|
||||
|
||||
sinon.assert.calledOnce(mozLoop.telemetryAdd);
|
||||
sinon.assert.calledWith(mozLoop.telemetryAdd,
|
||||
"LOOP_CLIENT_CALL_URL_REQUESTS_SUCCESS",
|
||||
true);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should send an error when the request fails", function() {
|
||||
// Sets up the hawkRequest stub to trigger the callback with
|
||||
// an error
|
||||
hawkRequestStub.callsArgWith(4, fakeErrorRes);
|
||||
|
||||
client.requestCallUrl("foo", callback);
|
||||
|
||||
sinon.assert.calledOnce(callback);
|
||||
sinon.assert.calledWithMatch(callback, sinon.match(function(err) {
|
||||
return err.code == 400 && "invalid token" == err.message;
|
||||
}));
|
||||
});
|
||||
|
||||
it("should send an error if the data is not valid", function() {
|
||||
// Sets up the hawkRequest stub to trigger the callback with
|
||||
// an error
|
||||
hawkRequestStub.callsArgWith(4, null, "{}");
|
||||
|
||||
client.requestCallUrl("foo", callback);
|
||||
|
||||
sinon.assert.calledOnce(callback);
|
||||
sinon.assert.calledWithMatch(callback, sinon.match(function(err) {
|
||||
return /Invalid data received/.test(err.message);
|
||||
}));
|
||||
});
|
||||
|
||||
it("should call mozLoop.telemetryAdd when the request fails",
|
||||
function(done) {
|
||||
// Sets up the hawkRequest stub to trigger the callback with
|
||||
// an error
|
||||
hawkRequestStub.callsArgWith(4, fakeErrorRes);
|
||||
|
||||
client.requestCallUrl("foo", function(err) {
|
||||
expect(err).not.to.be.null;
|
||||
|
||||
sinon.assert.calledOnce(mozLoop.telemetryAdd);
|
||||
sinon.assert.calledWith(mozLoop.telemetryAdd,
|
||||
"LOOP_CLIENT_CALL_URL_REQUESTS_SUCCESS",
|
||||
false);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#setupOutgoingCall", function() {
|
||||
var calleeIds, callType;
|
||||
|
||||
|
|
|
@ -48,10 +48,6 @@ describe("loop.panel", function() {
|
|||
getPluralForm: function() {
|
||||
return "fakeText";
|
||||
},
|
||||
copyString: sandbox.stub(),
|
||||
noteCallUrlExpiry: sinon.spy(),
|
||||
composeEmail: sinon.spy(),
|
||||
telemetryAdd: sinon.spy(),
|
||||
contacts: {
|
||||
getAll: function(callback) {
|
||||
callback(null, []);
|
||||
|
@ -186,69 +182,33 @@ describe("loop.panel", function() {
|
|||
describe('TabView', function() {
|
||||
var view, callTab, roomsTab, contactsTab;
|
||||
|
||||
describe("loop.rooms.enabled on", function() {
|
||||
beforeEach(function() {
|
||||
navigator.mozLoop.getLoopPref = function(pref) {
|
||||
if (pref === "rooms.enabled" ||
|
||||
pref === "gettingStarted.seen") {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
beforeEach(function() {
|
||||
navigator.mozLoop.getLoopPref = function(pref) {
|
||||
if (pref === "gettingStarted.seen") {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
view = createTestPanelView();
|
||||
view = createTestPanelView();
|
||||
|
||||
[roomsTab, contactsTab] =
|
||||
TestUtils.scryRenderedDOMComponentsWithClass(view, "tab");
|
||||
});
|
||||
|
||||
it("should select contacts tab when clicking tab button", function() {
|
||||
TestUtils.Simulate.click(
|
||||
view.getDOMNode().querySelector("li[data-tab-name=\"contacts\"]"));
|
||||
|
||||
expect(contactsTab.getDOMNode().classList.contains("selected"))
|
||||
.to.be.true;
|
||||
});
|
||||
|
||||
it("should select rooms tab when clicking tab button", function() {
|
||||
TestUtils.Simulate.click(
|
||||
view.getDOMNode().querySelector("li[data-tab-name=\"rooms\"]"));
|
||||
|
||||
expect(roomsTab.getDOMNode().classList.contains("selected"))
|
||||
.to.be.true;
|
||||
});
|
||||
[roomsTab, contactsTab] =
|
||||
TestUtils.scryRenderedDOMComponentsWithClass(view, "tab");
|
||||
});
|
||||
|
||||
describe("loop.rooms.enabled off", function() {
|
||||
beforeEach(function() {
|
||||
navigator.mozLoop.getLoopPref = function(pref) {
|
||||
if (pref === "rooms.enabled") {
|
||||
return false;
|
||||
} else if (pref === "gettingStarted.seen") {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
it("should select contacts tab when clicking tab button", function() {
|
||||
TestUtils.Simulate.click(
|
||||
view.getDOMNode().querySelector("li[data-tab-name=\"contacts\"]"));
|
||||
|
||||
view = createTestPanelView();
|
||||
expect(contactsTab.getDOMNode().classList.contains("selected"))
|
||||
.to.be.true;
|
||||
});
|
||||
|
||||
[callTab, contactsTab] =
|
||||
TestUtils.scryRenderedDOMComponentsWithClass(view, "tab");
|
||||
});
|
||||
it("should select rooms tab when clicking tab button", function() {
|
||||
TestUtils.Simulate.click(
|
||||
view.getDOMNode().querySelector("li[data-tab-name=\"rooms\"]"));
|
||||
|
||||
it("should select contacts tab when clicking tab button", function() {
|
||||
TestUtils.Simulate.click(
|
||||
view.getDOMNode().querySelector("li[data-tab-name=\"contacts\"]"));
|
||||
|
||||
expect(contactsTab.getDOMNode().classList.contains("selected"))
|
||||
.to.be.true;
|
||||
});
|
||||
|
||||
it("should select call tab when clicking tab button", function() {
|
||||
TestUtils.Simulate.click(
|
||||
view.getDOMNode().querySelector("li[data-tab-name=\"call\"]"));
|
||||
|
||||
expect(callTab.getDOMNode().classList.contains("selected"))
|
||||
.to.be.true;
|
||||
});
|
||||
expect(roomsTab.getDOMNode().classList.contains("selected"))
|
||||
.to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -468,284 +428,6 @@ describe("loop.panel", function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe("loop.panel.CallUrlResult", function() {
|
||||
var fakeClient, callUrlData, view;
|
||||
|
||||
beforeEach(function() {
|
||||
callUrlData = {
|
||||
callUrl: "http://call.invalid/fakeToken",
|
||||
expiresAt: 1000
|
||||
};
|
||||
|
||||
fakeClient = {
|
||||
requestCallUrl: function(_, cb) {
|
||||
cb(null, callUrlData);
|
||||
}
|
||||
};
|
||||
|
||||
sandbox.stub(notifications, "reset");
|
||||
view = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.panel.CallUrlResult, {
|
||||
notifications: notifications,
|
||||
client: fakeClient
|
||||
}));
|
||||
});
|
||||
|
||||
describe("Rendering the component should generate a call URL", function() {
|
||||
|
||||
beforeEach(function() {
|
||||
document.mozL10n.initialize({
|
||||
getStrings: function(key) {
|
||||
var text;
|
||||
|
||||
if (key === "share_email_subject4")
|
||||
text = "email-subject";
|
||||
else if (key === "share_email_body4")
|
||||
text = "{{callUrl}}";
|
||||
|
||||
return JSON.stringify({textContent: text});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should make a request to requestCallUrl", function() {
|
||||
sandbox.stub(fakeClient, "requestCallUrl");
|
||||
var view = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.panel.CallUrlResult, {
|
||||
notifications: notifications,
|
||||
client: fakeClient
|
||||
}));
|
||||
|
||||
sinon.assert.calledOnce(view.props.client.requestCallUrl);
|
||||
sinon.assert.calledWithExactly(view.props.client.requestCallUrl,
|
||||
sinon.match.string, sinon.match.func);
|
||||
});
|
||||
|
||||
it("should set the call url form in a pending state", function() {
|
||||
// Cancel requestCallUrl effect to keep the state pending
|
||||
fakeClient.requestCallUrl = sandbox.stub();
|
||||
var view = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.panel.CallUrlResult, {
|
||||
notifications: notifications,
|
||||
client: fakeClient
|
||||
}));
|
||||
|
||||
expect(view.state.pending).eql(true);
|
||||
});
|
||||
|
||||
it("should update state with the call url received", function() {
|
||||
expect(view.state.pending).eql(false);
|
||||
expect(view.state.callUrl).eql(callUrlData.callUrl);
|
||||
});
|
||||
|
||||
it("should clear the pending state when a response is received",
|
||||
function() {
|
||||
expect(view.state.pending).eql(false);
|
||||
});
|
||||
|
||||
it("should update CallUrlResult with the call url", function() {
|
||||
var urlField = view.getDOMNode().querySelector("input[type='url']");
|
||||
|
||||
expect(urlField.value).eql(callUrlData.callUrl);
|
||||
});
|
||||
|
||||
it("should have 0 pending notifications", function() {
|
||||
expect(view.props.notifications.length).eql(0);
|
||||
});
|
||||
|
||||
it("should display a share button for email", function() {
|
||||
fakeClient.requestCallUrl = sandbox.stub();
|
||||
var composeCallUrlEmail = sandbox.stub(sharedUtils, "composeCallUrlEmail");
|
||||
var view = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.panel.CallUrlResult, {
|
||||
notifications: notifications,
|
||||
client: fakeClient
|
||||
}));
|
||||
view.setState({pending: false, callUrl: "http://example.com"});
|
||||
|
||||
TestUtils.findRenderedDOMComponentWithClass(view, "button-email");
|
||||
TestUtils.Simulate.click(view.getDOMNode().querySelector(".button-email"));
|
||||
|
||||
sinon.assert.calledOnce(composeCallUrlEmail);
|
||||
sinon.assert.calledWithExactly(composeCallUrlEmail, "http://example.com");
|
||||
});
|
||||
|
||||
it("should feature a copy button capable of copying the call url when clicked", function() {
|
||||
fakeClient.requestCallUrl = sandbox.stub();
|
||||
var view = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.panel.CallUrlResult, {
|
||||
notifications: notifications,
|
||||
client: fakeClient
|
||||
}));
|
||||
view.setState({
|
||||
pending: false,
|
||||
copied: false,
|
||||
callUrl: "http://example.com",
|
||||
callUrlExpiry: 6000
|
||||
});
|
||||
|
||||
TestUtils.Simulate.click(view.getDOMNode().querySelector(".button-copy"));
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.copyString);
|
||||
sinon.assert.calledWithExactly(navigator.mozLoop.copyString,
|
||||
view.state.callUrl);
|
||||
});
|
||||
|
||||
it("should note the call url expiry when the url is copied via button",
|
||||
function() {
|
||||
var view = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.panel.CallUrlResult, {
|
||||
notifications: notifications,
|
||||
client: fakeClient
|
||||
}));
|
||||
view.setState({
|
||||
pending: false,
|
||||
copied: false,
|
||||
callUrl: "http://example.com",
|
||||
callUrlExpiry: 6000
|
||||
});
|
||||
|
||||
TestUtils.Simulate.click(view.getDOMNode().querySelector(".button-copy"));
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.noteCallUrlExpiry);
|
||||
sinon.assert.calledWithExactly(navigator.mozLoop.noteCallUrlExpiry,
|
||||
6000);
|
||||
});
|
||||
|
||||
it("should call mozLoop.telemetryAdd when the url is copied via button",
|
||||
function() {
|
||||
var view = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.panel.CallUrlResult, {
|
||||
notifications: notifications,
|
||||
client: fakeClient
|
||||
}));
|
||||
view.setState({
|
||||
pending: false,
|
||||
copied: false,
|
||||
callUrl: "http://example.com",
|
||||
callUrlExpiry: 6000
|
||||
});
|
||||
|
||||
// Multiple clicks should result in the URL being counted only once.
|
||||
TestUtils.Simulate.click(view.getDOMNode().querySelector(".button-copy"));
|
||||
TestUtils.Simulate.click(view.getDOMNode().querySelector(".button-copy"));
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.telemetryAdd);
|
||||
sinon.assert.calledWith(navigator.mozLoop.telemetryAdd,
|
||||
"LOOP_CLIENT_CALL_URL_SHARED",
|
||||
true);
|
||||
});
|
||||
|
||||
it("should note the call url expiry when the url is emailed",
|
||||
function() {
|
||||
var view = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.panel.CallUrlResult, {
|
||||
notifications: notifications,
|
||||
client: fakeClient
|
||||
}));
|
||||
view.setState({
|
||||
pending: false,
|
||||
copied: false,
|
||||
callUrl: "http://example.com",
|
||||
callUrlExpiry: 6000
|
||||
});
|
||||
|
||||
TestUtils.Simulate.click(view.getDOMNode().querySelector(".button-email"));
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.noteCallUrlExpiry);
|
||||
sinon.assert.calledWithExactly(navigator.mozLoop.noteCallUrlExpiry,
|
||||
6000);
|
||||
});
|
||||
|
||||
it("should call mozLoop.telemetryAdd when the url is emailed",
|
||||
function() {
|
||||
var view = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.panel.CallUrlResult, {
|
||||
notifications: notifications,
|
||||
client: fakeClient
|
||||
}));
|
||||
view.setState({
|
||||
pending: false,
|
||||
copied: false,
|
||||
callUrl: "http://example.com",
|
||||
callUrlExpiry: 6000
|
||||
});
|
||||
|
||||
// Multiple clicks should result in the URL being counted only once.
|
||||
TestUtils.Simulate.click(view.getDOMNode().querySelector(".button-email"));
|
||||
TestUtils.Simulate.click(view.getDOMNode().querySelector(".button-email"));
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.telemetryAdd);
|
||||
sinon.assert.calledWith(navigator.mozLoop.telemetryAdd,
|
||||
"LOOP_CLIENT_CALL_URL_SHARED",
|
||||
true);
|
||||
});
|
||||
|
||||
it("should note the call url expiry when the url is copied manually",
|
||||
function() {
|
||||
var view = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.panel.CallUrlResult, {
|
||||
notifications: notifications,
|
||||
client: fakeClient
|
||||
}));
|
||||
view.setState({
|
||||
pending: false,
|
||||
copied: false,
|
||||
callUrl: "http://example.com",
|
||||
callUrlExpiry: 6000
|
||||
});
|
||||
|
||||
var urlField = view.getDOMNode().querySelector("input[type='url']");
|
||||
TestUtils.Simulate.copy(urlField);
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.noteCallUrlExpiry);
|
||||
sinon.assert.calledWithExactly(navigator.mozLoop.noteCallUrlExpiry,
|
||||
6000);
|
||||
});
|
||||
|
||||
it("should call mozLoop.telemetryAdd when the url is copied manually",
|
||||
function() {
|
||||
var view = TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.panel.CallUrlResult, {
|
||||
notifications: notifications,
|
||||
client: fakeClient
|
||||
}));
|
||||
view.setState({
|
||||
pending: false,
|
||||
copied: false,
|
||||
callUrl: "http://example.com",
|
||||
callUrlExpiry: 6000
|
||||
});
|
||||
|
||||
// Multiple copies should result in the URL being counted only once.
|
||||
var urlField = view.getDOMNode().querySelector("input[type='url']");
|
||||
TestUtils.Simulate.copy(urlField);
|
||||
TestUtils.Simulate.copy(urlField);
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.telemetryAdd);
|
||||
sinon.assert.calledWith(navigator.mozLoop.telemetryAdd,
|
||||
"LOOP_CLIENT_CALL_URL_SHARED",
|
||||
true);
|
||||
});
|
||||
|
||||
it("should notify the user when the operation failed", function() {
|
||||
fakeClient.requestCallUrl = function(_, cb) {
|
||||
cb("fake error");
|
||||
};
|
||||
sandbox.stub(notifications, "errorL10n");
|
||||
TestUtils.renderIntoDocument(
|
||||
React.createElement(loop.panel.CallUrlResult, {
|
||||
notifications: notifications,
|
||||
client: fakeClient
|
||||
}));
|
||||
|
||||
sinon.assert.calledOnce(notifications.errorL10n);
|
||||
sinon.assert.calledWithExactly(notifications.errorL10n,
|
||||
"unable_retrieve_url");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("loop.panel.RoomEntry", function() {
|
||||
var dispatcher, roomData;
|
||||
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function expiryTimePref() {
|
||||
return Services.prefs.getIntPref("loop.urlsExpiryTimeSeconds");
|
||||
}
|
||||
|
||||
function run_test()
|
||||
{
|
||||
setupFakeLoopServer();
|
||||
|
||||
Services.prefs.setIntPref("loop.urlsExpiryTimeSeconds", 0);
|
||||
|
||||
MozLoopService.noteCallUrlExpiry(1000);
|
||||
|
||||
Assert.equal(expiryTimePref(), 1000, "should be equal to set value");
|
||||
|
||||
MozLoopService.noteCallUrlExpiry(900);
|
||||
|
||||
Assert.equal(expiryTimePref(), 1000, "should remain the same value");
|
||||
|
||||
MozLoopService.noteCallUrlExpiry(1500);
|
||||
|
||||
Assert.equal(expiryTimePref(), 1500, "should be the increased value");
|
||||
}
|
|
@ -16,8 +16,8 @@ function test_getStrings() {
|
|||
|
||||
// XXX This depends on the L10n values, which I'd prefer not to do, but is the
|
||||
// simplest way for now.
|
||||
Assert.equal(MozLoopService.getStrings("share_link_header_text"),
|
||||
'{"textContent":"Share this link to invite someone to talk:"}');
|
||||
Assert.equal(MozLoopService.getStrings("display_name_guest"),
|
||||
'{"textContent":"Guest"}');
|
||||
}
|
||||
|
||||
function run_test()
|
||||
|
|
|
@ -9,7 +9,6 @@ skip-if = toolkit == 'gonk'
|
|||
[test_looprooms.js]
|
||||
[test_loopservice_directcall.js]
|
||||
[test_loopservice_dnd.js]
|
||||
[test_loopservice_expiry.js]
|
||||
[test_loopservice_hawk_errors.js]
|
||||
[test_loopservice_hawk_request.js]
|
||||
[test_loopservice_loop_prefs.js]
|
||||
|
|
|
@ -48,14 +48,10 @@ var fakeRooms = [
|
|||
* @type {Object}
|
||||
*/
|
||||
navigator.mozLoop = {
|
||||
roomsEnabled: false,
|
||||
ensureRegistered: function() {},
|
||||
getAudioBlob: function(){},
|
||||
getLoopPref: function(pref) {
|
||||
switch(pref) {
|
||||
// Ensure UI for rooms is displayed in the showcase.
|
||||
case "rooms.enabled":
|
||||
return this.roomsEnabled;
|
||||
// Ensure we skip FTE completely.
|
||||
case "gettingStarted.seen":
|
||||
return true;
|
||||
|
|
|
@ -83,7 +83,6 @@
|
|||
// Local mocks
|
||||
|
||||
var mockMozLoopRooms = _.extend({}, navigator.mozLoop);
|
||||
mockMozLoopRooms.roomsEnabled = true;
|
||||
|
||||
var mockContact = {
|
||||
name: ["Mr Smith"],
|
||||
|
@ -93,7 +92,6 @@
|
|||
};
|
||||
|
||||
var mockClient = {
|
||||
requestCallUrl: noop,
|
||||
requestCallUrlInfo: noop
|
||||
};
|
||||
|
||||
|
@ -220,33 +218,21 @@
|
|||
React.createElement("p", {className: "note"},
|
||||
React.createElement("strong", null, "Note:"), " 332px wide."
|
||||
),
|
||||
React.createElement(Example, {summary: "Call URL retrieved", dashed: "true", style: {width: "332px"}},
|
||||
React.createElement(PanelView, {client: mockClient, notifications: notifications,
|
||||
callUrl: "http://invalid.example.url/",
|
||||
mozLoop: navigator.mozLoop,
|
||||
dispatcher: dispatcher,
|
||||
roomStore: roomStore})
|
||||
),
|
||||
React.createElement(Example, {summary: "Call URL retrieved - authenticated", dashed: "true", style: {width: "332px"}},
|
||||
React.createElement(PanelView, {client: mockClient, notifications: notifications,
|
||||
callUrl: "http://invalid.example.url/",
|
||||
userProfile: {email: "test@example.com"},
|
||||
mozLoop: navigator.mozLoop,
|
||||
dispatcher: dispatcher,
|
||||
roomStore: roomStore})
|
||||
),
|
||||
React.createElement(Example, {summary: "Pending call url retrieval", dashed: "true", style: {width: "332px"}},
|
||||
React.createElement(PanelView, {client: mockClient, notifications: notifications,
|
||||
mozLoop: navigator.mozLoop,
|
||||
dispatcher: dispatcher,
|
||||
roomStore: roomStore})
|
||||
),
|
||||
React.createElement(Example, {summary: "Pending call url retrieval - authenticated", dashed: "true", style: {width: "332px"}},
|
||||
React.createElement(Example, {summary: "Room list tab", dashed: "true", style: {width: "332px"}},
|
||||
React.createElement(PanelView, {client: mockClient, notifications: notifications,
|
||||
userProfile: {email: "test@example.com"},
|
||||
mozLoop: navigator.mozLoop,
|
||||
mozLoop: mockMozLoopRooms,
|
||||
dispatcher: dispatcher,
|
||||
roomStore: roomStore})
|
||||
roomStore: roomStore,
|
||||
selectedTab: "rooms"})
|
||||
),
|
||||
React.createElement(Example, {summary: "Contact list tab", dashed: "true", style: {width: "332px"}},
|
||||
React.createElement(PanelView, {client: mockClient, notifications: notifications,
|
||||
userProfile: {email: "test@example.com"},
|
||||
mozLoop: mockMozLoopRooms,
|
||||
dispatcher: dispatcher,
|
||||
roomStore: roomStore,
|
||||
selectedTab: "contacts"})
|
||||
),
|
||||
React.createElement(Example, {summary: "Error Notification", dashed: "true", style: {width: "332px"}},
|
||||
React.createElement(PanelView, {client: mockClient, notifications: errNotifications,
|
||||
|
@ -261,14 +247,6 @@
|
|||
dispatcher: dispatcher,
|
||||
roomStore: roomStore})
|
||||
),
|
||||
React.createElement(Example, {summary: "Room list tab", dashed: "true", style: {width: "332px"}},
|
||||
React.createElement(PanelView, {client: mockClient, notifications: notifications,
|
||||
userProfile: {email: "test@example.com"},
|
||||
mozLoop: mockMozLoopRooms,
|
||||
dispatcher: dispatcher,
|
||||
roomStore: roomStore,
|
||||
selectedTab: "rooms"})
|
||||
),
|
||||
React.createElement(Example, {summary: "Contact import success", dashed: "true", style: {width: "332px"}},
|
||||
React.createElement(PanelView, {notifications: new loop.shared.models.NotificationCollection([{level: "success", message: "Import success"}]),
|
||||
userProfile: {email: "test@example.com"},
|
||||
|
|
|
@ -83,7 +83,6 @@
|
|||
// Local mocks
|
||||
|
||||
var mockMozLoopRooms = _.extend({}, navigator.mozLoop);
|
||||
mockMozLoopRooms.roomsEnabled = true;
|
||||
|
||||
var mockContact = {
|
||||
name: ["Mr Smith"],
|
||||
|
@ -93,7 +92,6 @@
|
|||
};
|
||||
|
||||
var mockClient = {
|
||||
requestCallUrl: noop,
|
||||
requestCallUrlInfo: noop
|
||||
};
|
||||
|
||||
|
@ -220,33 +218,21 @@
|
|||
<p className="note">
|
||||
<strong>Note:</strong> 332px wide.
|
||||
</p>
|
||||
<Example summary="Call URL retrieved" dashed="true" style={{width: "332px"}}>
|
||||
<PanelView client={mockClient} notifications={notifications}
|
||||
callUrl="http://invalid.example.url/"
|
||||
mozLoop={navigator.mozLoop}
|
||||
dispatcher={dispatcher}
|
||||
roomStore={roomStore} />
|
||||
</Example>
|
||||
<Example summary="Call URL retrieved - authenticated" dashed="true" style={{width: "332px"}}>
|
||||
<PanelView client={mockClient} notifications={notifications}
|
||||
callUrl="http://invalid.example.url/"
|
||||
userProfile={{email: "test@example.com"}}
|
||||
mozLoop={navigator.mozLoop}
|
||||
dispatcher={dispatcher}
|
||||
roomStore={roomStore} />
|
||||
</Example>
|
||||
<Example summary="Pending call url retrieval" dashed="true" style={{width: "332px"}}>
|
||||
<PanelView client={mockClient} notifications={notifications}
|
||||
mozLoop={navigator.mozLoop}
|
||||
dispatcher={dispatcher}
|
||||
roomStore={roomStore} />
|
||||
</Example>
|
||||
<Example summary="Pending call url retrieval - authenticated" dashed="true" style={{width: "332px"}}>
|
||||
<Example summary="Room list tab" dashed="true" style={{width: "332px"}}>
|
||||
<PanelView client={mockClient} notifications={notifications}
|
||||
userProfile={{email: "test@example.com"}}
|
||||
mozLoop={navigator.mozLoop}
|
||||
mozLoop={mockMozLoopRooms}
|
||||
dispatcher={dispatcher}
|
||||
roomStore={roomStore} />
|
||||
roomStore={roomStore}
|
||||
selectedTab="rooms" />
|
||||
</Example>
|
||||
<Example summary="Contact list tab" dashed="true" style={{width: "332px"}}>
|
||||
<PanelView client={mockClient} notifications={notifications}
|
||||
userProfile={{email: "test@example.com"}}
|
||||
mozLoop={mockMozLoopRooms}
|
||||
dispatcher={dispatcher}
|
||||
roomStore={roomStore}
|
||||
selectedTab="contacts" />
|
||||
</Example>
|
||||
<Example summary="Error Notification" dashed="true" style={{width: "332px"}}>
|
||||
<PanelView client={mockClient} notifications={errNotifications}
|
||||
|
@ -261,14 +247,6 @@
|
|||
dispatcher={dispatcher}
|
||||
roomStore={roomStore} />
|
||||
</Example>
|
||||
<Example summary="Room list tab" dashed="true" style={{width: "332px"}}>
|
||||
<PanelView client={mockClient} notifications={notifications}
|
||||
userProfile={{email: "test@example.com"}}
|
||||
mozLoop={mockMozLoopRooms}
|
||||
dispatcher={dispatcher}
|
||||
roomStore={roomStore}
|
||||
selectedTab="rooms" />
|
||||
</Example>
|
||||
<Example summary="Contact import success" dashed="true" style={{width: "332px"}}>
|
||||
<PanelView notifications={new loop.shared.models.NotificationCollection([{level: "success", message: "Import success"}])}
|
||||
userProfile={{email: "test@example.com"}}
|
||||
|
|
|
@ -13,30 +13,8 @@ clientShortname2=Firefox Hello
|
|||
first_time_experience_title={{clientShortname}} — Join the conversation
|
||||
first_time_experience_button_label=Get Started
|
||||
|
||||
share_link_header_text=Share this link to invite someone to talk:
|
||||
invite_header_text=Invite someone to join you.
|
||||
|
||||
## LOCALIZATION NOTE(invitee_name_label): Displayed when obtaining a url.
|
||||
## See https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#precall-firstrun
|
||||
## Click the label icon at the end of the url field.
|
||||
invitee_name_label=Who are you inviting?
|
||||
## LOCALIZATION NOTE(invitee_expire_days_label): Allows the user to adjust
|
||||
## the expiry time. Click the label icon at the end of the url field to see where
|
||||
## this is:
|
||||
## https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#precall-firstrun
|
||||
## Semicolon-separated list of plural forms. See:
|
||||
## http://developer.mozilla.org/en/docs/Localization_and_Plurals
|
||||
## In this item, don't translate the part between {{..}}
|
||||
invitee_expire_days_label=Invitation will expire in {{expiry_time}} day;Invitation will expire in {{expiry_time}} days
|
||||
## LOCALIZATION NOTE(invitee_expire_hours_label): Allows the user to adjust
|
||||
## the expiry time. Click the label icon are the end of the url field to see where
|
||||
## this is:
|
||||
## https://people.mozilla.org/~dhenein/labs/loop-mvp-spec/#precall-firstrun
|
||||
## Semicolon-separated list of plural forms. See:
|
||||
## http://developer.mozilla.org/en/docs/Localization_and_Plurals
|
||||
## In this item, don't translate the part between {{..}}
|
||||
invitee_expire_hours_label=Invitation will expire in {{expiry_time}} hour;Invitation will expire in {{expiry_time}} hours
|
||||
|
||||
# Status text
|
||||
display_name_guest=Guest
|
||||
display_name_dnd_status=Do Not Disturb
|
||||
|
@ -66,9 +44,7 @@ share_email_subject4={{clientShortname}} — Join the conversation
|
|||
## LOCALIZATION NOTE (share_email_body4): In this item, don't translate the
|
||||
## part between {{..}} and leave the \r\n\r\n part alone
|
||||
share_email_body4=Hello!\r\n\r\nJoin me for a video conversation using {{clientShortname}}:\r\n\r\nYou don't have to download or install anything. Just copy and paste this URL into your browser:\r\n\r\n{{callUrl}}\r\n\r\nIf you want, you can also learn more about {{clientShortname}} at {{learnMoreUrl}}\r\n\r\nTalk to you soon!
|
||||
share_button=Email
|
||||
share_button2=Email Link
|
||||
copy_url_button=Copy
|
||||
copy_url_button2=Copy Link
|
||||
copied_url_button=Copied!
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче