This commit is contained in:
Mark Banner 2015-04-21 21:53:15 +01:00
Родитель 34e6d5de56
Коммит 87a741e0a1
7 изменённых файлов: 166 добавлений и 61 удалений

Просмотреть файл

@ -707,6 +707,19 @@ function injectLoopAPI(targetWindow) {
},
},
/**
* Opens a URL in a new tab in the browser.
*
* @param {String} url The new url to open
*/
openURL: {
enumerable: true,
writable: true,
value: function(url) {
return MozLoopService.openURL(url);
}
},
/**
* Copies passed string onto the system clipboard.
*

Просмотреть файл

@ -1650,6 +1650,16 @@ this.MozLoopService = {
}
}),
/**
* Opens a URL in a new tab in the browser.
*
* @param {String} url The new url to open
*/
openURL: function(url) {
let win = Services.wm.getMostRecentWindow("navigator:browser");
win.openUILinkIn(url, "tab");
},
/**
* Performs a hawk based request to the loop server.
*

Просмотреть файл

@ -11,6 +11,8 @@ var loop = loop || {};
loop.contacts = (function(_, mozL10n) {
"use strict";
var sharedMixins = loop.shared.mixins;
const Button = loop.shared.views.Button;
const ButtonGroup = loop.shared.views.ButtonGroup;
const CALL_TYPES = loop.shared.utils.CALL_TYPES;
@ -82,6 +84,8 @@ loop.contacts = (function(_, mozL10n) {
};
const GravatarPromo = React.createClass({displayName: "GravatarPromo",
mixins: [sharedMixins.WindowCloseMixin],
propTypes: {
handleUse: React.PropTypes.func.isRequired
},
@ -98,6 +102,16 @@ loop.contacts = (function(_, mozL10n) {
this.setState({ showMe: false });
},
handleLinkClick: function(event) {
if (!event.target || !event.target.href) {
return;
}
event.preventDefault();
navigator.mozLoop.openURL(event.target.href);
this.closeWindow();
},
handleUseButtonClick: function() {
navigator.mozLoop.setLoopPref("contacts.gravatars.promo", false);
navigator.mozLoop.setLoopPref("contacts.gravatars.show", true);
@ -121,7 +135,8 @@ loop.contacts = (function(_, mozL10n) {
return (
React.createElement("div", {className: "contacts-gravatar-promo"},
React.createElement(Button, {additionalClass: "button-close", onClick: this.handleCloseButtonClick}),
React.createElement("p", {dangerouslySetInnerHTML: {__html: message}}),
React.createElement("p", {dangerouslySetInnerHTML: {__html: message},
onClick: this.handleLinkClick}),
React.createElement(ButtonGroup, null,
React.createElement(Button, {caption: mozL10n.get("gravatars_promo_button_nothanks"),
onClick: this.handleCloseButtonClick}),

Просмотреть файл

@ -11,6 +11,8 @@ var loop = loop || {};
loop.contacts = (function(_, mozL10n) {
"use strict";
var sharedMixins = loop.shared.mixins;
const Button = loop.shared.views.Button;
const ButtonGroup = loop.shared.views.ButtonGroup;
const CALL_TYPES = loop.shared.utils.CALL_TYPES;
@ -82,6 +84,8 @@ loop.contacts = (function(_, mozL10n) {
};
const GravatarPromo = React.createClass({
mixins: [sharedMixins.WindowCloseMixin],
propTypes: {
handleUse: React.PropTypes.func.isRequired
},
@ -98,6 +102,16 @@ loop.contacts = (function(_, mozL10n) {
this.setState({ showMe: false });
},
handleLinkClick: function(event) {
if (!event.target || !event.target.href) {
return;
}
event.preventDefault();
navigator.mozLoop.openURL(event.target.href);
this.closeWindow();
},
handleUseButtonClick: function() {
navigator.mozLoop.setLoopPref("contacts.gravatars.promo", false);
navigator.mozLoop.setLoopPref("contacts.gravatars.show", true);
@ -121,7 +135,8 @@ loop.contacts = (function(_, mozL10n) {
return (
<div className="contacts-gravatar-promo">
<Button additionalClass="button-close" onClick={this.handleCloseButtonClick}/>
<p dangerouslySetInnerHTML={{__html: message}}></p>
<p dangerouslySetInnerHTML={{__html: message}}
onClick={this.handleLinkClick}></p>
<ButtonGroup>
<Button caption={mozL10n.get("gravatars_promo_button_nothanks")}
onClick={this.handleCloseButtonClick}/>

Просмотреть файл

@ -215,6 +215,8 @@ loop.panel = (function(_, mozL10n) {
});
var ToSView = React.createClass({displayName: "ToSView",
mixins: [sharedMixins.WindowCloseMixin],
getInitialState: function() {
var getPref = navigator.mozLoop.getLoopPref.bind(navigator.mozLoop);
@ -225,6 +227,16 @@ loop.panel = (function(_, mozL10n) {
};
},
handleLinkClick: function(event) {
if (!event.target || !event.target.href) {
return;
}
event.preventDefault();
navigator.mozLoop.openURL(event.target.href);
this.closeWindow();
},
renderPartnerLogo: function() {
if (!this.state.showPartnerLogo) {
return null;
@ -262,7 +274,8 @@ loop.panel = (function(_, mozL10n) {
React.createElement("div", {id: "powered-by-wrapper"},
this.renderPartnerLogo(),
React.createElement("p", {className: "terms-service",
dangerouslySetInnerHTML: {__html: tosHTML}})
dangerouslySetInnerHTML: {__html: tosHTML},
onClick: this.handleLinkClick})
)
);
} else {
@ -305,6 +318,10 @@ loop.panel = (function(_, mozL10n) {
* Panel settings (gear) menu.
*/
var SettingsDropdown = React.createClass({displayName: "SettingsDropdown",
propTypes: {
mozLoop: React.PropTypes.object.isRequired
},
mixins: [sharedMixins.DropdownMenuMixin, sharedMixins.WindowCloseMixin],
handleClickSettingsEntry: function() {
@ -312,30 +329,31 @@ loop.panel = (function(_, mozL10n) {
},
handleClickAccountEntry: function() {
navigator.mozLoop.openFxASettings();
this.props.mozLoop.openFxASettings();
this.closeWindow();
},
handleClickAuthEntry: function() {
if (this._isSignedIn()) {
navigator.mozLoop.logOutFromFxA();
this.props.mozLoop.logOutFromFxA();
} else {
navigator.mozLoop.logInToFxA();
this.props.mozLoop.logInToFxA();
}
},
handleHelpEntry: function(event) {
event.preventDefault();
var helloSupportUrl = navigator.mozLoop.getLoopPref('support_url');
window.open(helloSupportUrl);
window.close();
var helloSupportUrl = this.props.mozLoop.getLoopPref("support_url");
this.props.mozLoop.openURL(helloSupportUrl);
this.closeWindow();
},
_isSignedIn: function() {
return !!navigator.mozLoop.userProfile;
return !!this.props.mozLoop.userProfile;
},
openGettingStartedTour: function() {
navigator.mozLoop.openGettingStartedTour("settings-menu");
this.props.mozLoop.openGettingStartedTour("settings-menu");
this.closeWindow();
},
@ -356,7 +374,7 @@ loop.panel = (function(_, mozL10n) {
React.createElement(SettingsDropdownEntry, {label: mozL10n.get("settings_menu_item_account"),
onClick: this.handleClickAccountEntry,
icon: "account",
displayed: this._isSignedIn() && navigator.mozLoop.fxAEnabled}),
displayed: this._isSignedIn() && this.props.mozLoop.fxAEnabled}),
React.createElement(SettingsDropdownEntry, {icon: "tour",
label: mozL10n.get("tour_label"),
onClick: this.openGettingStartedTour}),
@ -364,7 +382,7 @@ loop.panel = (function(_, mozL10n) {
mozL10n.get("settings_menu_item_signout") :
mozL10n.get("settings_menu_item_signin"),
onClick: this.handleClickAuthEntry,
displayed: navigator.mozLoop.fxAEnabled,
displayed: this.props.mozLoop.fxAEnabled,
icon: this._isSignedIn() ? "signout" : "signin"}),
React.createElement(SettingsDropdownEntry, {label: mozL10n.get("help_label"),
onClick: this.handleHelpEntry,
@ -689,7 +707,7 @@ loop.panel = (function(_, mozL10n) {
showTabButtons: React.PropTypes.bool,
selectedTab: React.PropTypes.string,
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
mozLoop: React.PropTypes.object,
mozLoop: React.PropTypes.object.isRequired,
roomStore:
React.PropTypes.instanceOf(loop.store.RoomStore).isRequired
},
@ -848,7 +866,7 @@ loop.panel = (function(_, mozL10n) {
React.createElement("div", {className: "signin-details"},
React.createElement(AuthLink, null),
React.createElement("div", {className: "footer-signin-separator"}),
React.createElement(SettingsDropdown, null)
React.createElement(SettingsDropdown, {mozLoop: this.props.mozLoop})
)
)
)

Просмотреть файл

@ -215,6 +215,8 @@ loop.panel = (function(_, mozL10n) {
});
var ToSView = React.createClass({
mixins: [sharedMixins.WindowCloseMixin],
getInitialState: function() {
var getPref = navigator.mozLoop.getLoopPref.bind(navigator.mozLoop);
@ -225,6 +227,16 @@ loop.panel = (function(_, mozL10n) {
};
},
handleLinkClick: function(event) {
if (!event.target || !event.target.href) {
return;
}
event.preventDefault();
navigator.mozLoop.openURL(event.target.href);
this.closeWindow();
},
renderPartnerLogo: function() {
if (!this.state.showPartnerLogo) {
return null;
@ -262,7 +274,8 @@ loop.panel = (function(_, mozL10n) {
<div id="powered-by-wrapper">
{this.renderPartnerLogo()}
<p className="terms-service"
dangerouslySetInnerHTML={{__html: tosHTML}}></p>
dangerouslySetInnerHTML={{__html: tosHTML}}
onClick={this.handleLinkClick}></p>
</div>
);
} else {
@ -305,6 +318,10 @@ loop.panel = (function(_, mozL10n) {
* Panel settings (gear) menu.
*/
var SettingsDropdown = React.createClass({
propTypes: {
mozLoop: React.PropTypes.object.isRequired
},
mixins: [sharedMixins.DropdownMenuMixin, sharedMixins.WindowCloseMixin],
handleClickSettingsEntry: function() {
@ -312,30 +329,31 @@ loop.panel = (function(_, mozL10n) {
},
handleClickAccountEntry: function() {
navigator.mozLoop.openFxASettings();
this.props.mozLoop.openFxASettings();
this.closeWindow();
},
handleClickAuthEntry: function() {
if (this._isSignedIn()) {
navigator.mozLoop.logOutFromFxA();
this.props.mozLoop.logOutFromFxA();
} else {
navigator.mozLoop.logInToFxA();
this.props.mozLoop.logInToFxA();
}
},
handleHelpEntry: function(event) {
event.preventDefault();
var helloSupportUrl = navigator.mozLoop.getLoopPref('support_url');
window.open(helloSupportUrl);
window.close();
var helloSupportUrl = this.props.mozLoop.getLoopPref("support_url");
this.props.mozLoop.openURL(helloSupportUrl);
this.closeWindow();
},
_isSignedIn: function() {
return !!navigator.mozLoop.userProfile;
return !!this.props.mozLoop.userProfile;
},
openGettingStartedTour: function() {
navigator.mozLoop.openGettingStartedTour("settings-menu");
this.props.mozLoop.openGettingStartedTour("settings-menu");
this.closeWindow();
},
@ -356,7 +374,7 @@ loop.panel = (function(_, mozL10n) {
<SettingsDropdownEntry label={mozL10n.get("settings_menu_item_account")}
onClick={this.handleClickAccountEntry}
icon="account"
displayed={this._isSignedIn() && navigator.mozLoop.fxAEnabled} />
displayed={this._isSignedIn() && this.props.mozLoop.fxAEnabled} />
<SettingsDropdownEntry icon="tour"
label={mozL10n.get("tour_label")}
onClick={this.openGettingStartedTour} />
@ -364,7 +382,7 @@ loop.panel = (function(_, mozL10n) {
mozL10n.get("settings_menu_item_signout") :
mozL10n.get("settings_menu_item_signin")}
onClick={this.handleClickAuthEntry}
displayed={navigator.mozLoop.fxAEnabled}
displayed={this.props.mozLoop.fxAEnabled}
icon={this._isSignedIn() ? "signout" : "signin"} />
<SettingsDropdownEntry label={mozL10n.get("help_label")}
onClick={this.handleHelpEntry}
@ -689,7 +707,7 @@ loop.panel = (function(_, mozL10n) {
showTabButtons: React.PropTypes.bool,
selectedTab: React.PropTypes.string,
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
mozLoop: React.PropTypes.object,
mozLoop: React.PropTypes.object.isRequired,
roomStore:
React.PropTypes.instanceOf(loop.store.RoomStore).isRequired
},
@ -848,7 +866,7 @@ loop.panel = (function(_, mozL10n) {
<div className="signin-details">
<AuthLink />
<div className="footer-signin-separator" />
<SettingsDropdown />
<SettingsDropdown mozLoop={this.props.mozLoop}/>
</div>
</div>
</div>

Просмотреть файл

@ -71,6 +71,7 @@ describe("loop.panel", function() {
},
confirm: sandbox.stub(),
notifyUITour: sandbox.stub(),
openURL: sandbox.stub(),
getSelectedTabMetadata: sandbox.stub()
};
@ -188,6 +189,19 @@ describe("loop.panel", function() {
}));
}
it("should hide the account entry when FxA is not enabled", function() {
navigator.mozLoop.userProfile = {email: "test@example.com"};
navigator.mozLoop.fxAEnabled = false;
var view = TestUtils.renderIntoDocument(
React.createElement(loop.panel.SettingsDropdown, {
mozLoop: fakeMozLoop
}));
expect(view.getDOMNode().querySelectorAll(".icon-account"))
.to.have.length.of(0);
});
describe('TabView', function() {
var view, callTab, roomsTab, contactsTab;
@ -268,18 +282,14 @@ describe("loop.panel", function() {
});
});
it("should hide the account entry when FxA is not enabled", function() {
navigator.mozLoop.userProfile = {email: "test@example.com"};
navigator.mozLoop.fxAEnabled = false;
var view = TestUtils.renderIntoDocument(
React.createElement(loop.panel.SettingsDropdown));
expect(view.getDOMNode().querySelectorAll(".icon-account"))
.to.have.length.of(0);
});
describe("SettingsDropdown", function() {
function mountTestComponent() {
return TestUtils.renderIntoDocument(
React.createElement(loop.panel.SettingsDropdown, {
mozLoop: fakeMozLoop
}));
}
beforeEach(function() {
navigator.mozLoop.logInToFxA = sandbox.stub();
navigator.mozLoop.logOutFromFxA = sandbox.stub();
@ -294,8 +304,7 @@ describe("loop.panel", function() {
function() {
navigator.mozLoop.loggedInToFxA = false;
var view = TestUtils.renderIntoDocument(
React.createElement(loop.panel.SettingsDropdown));
var view = mountTestComponent();
expect(view.getDOMNode().querySelectorAll(".icon-signout"))
.to.have.length.of(0);
@ -306,8 +315,7 @@ describe("loop.panel", function() {
it("should show a signout entry when user is authenticated", function() {
navigator.mozLoop.userProfile = {email: "test@example.com"};
var view = TestUtils.renderIntoDocument(
React.createElement(loop.panel.SettingsDropdown));
var view = mountTestComponent();
expect(view.getDOMNode().querySelectorAll(".icon-signout"))
.to.have.length.of(1);
@ -318,8 +326,7 @@ describe("loop.panel", function() {
it("should show an account entry when user is authenticated", function() {
navigator.mozLoop.userProfile = {email: "test@example.com"};
var view = TestUtils.renderIntoDocument(
React.createElement(loop.panel.SettingsDropdown));
var view = mountTestComponent();
expect(view.getDOMNode().querySelectorAll(".icon-account"))
.to.have.length.of(1);
@ -328,8 +335,7 @@ describe("loop.panel", function() {
it("should open the FxA settings when the account entry is clicked", function() {
navigator.mozLoop.userProfile = {email: "test@example.com"};
var view = TestUtils.renderIntoDocument(
React.createElement(loop.panel.SettingsDropdown));
var view = mountTestComponent();
TestUtils.Simulate.click(
view.getDOMNode().querySelector(".icon-account"));
@ -341,8 +347,7 @@ describe("loop.panel", function() {
function() {
navigator.mozLoop.loggedInToFxA = false;
var view = TestUtils.renderIntoDocument(
React.createElement(loop.panel.SettingsDropdown));
var view = mountTestComponent();
expect(view.getDOMNode().querySelectorAll(".icon-account"))
.to.have.length.of(0);
@ -350,8 +355,7 @@ describe("loop.panel", function() {
it("should sign in the user on click when unauthenticated", function() {
navigator.mozLoop.loggedInToFxA = false;
var view = TestUtils.renderIntoDocument(
React.createElement(loop.panel.SettingsDropdown));
var view = mountTestComponent();
TestUtils.Simulate.click(
view.getDOMNode().querySelector(".icon-signin"));
@ -361,8 +365,7 @@ describe("loop.panel", function() {
it("should sign out the user on click when authenticated", function() {
navigator.mozLoop.userProfile = {email: "test@example.com"};
var view = TestUtils.renderIntoDocument(
React.createElement(loop.panel.SettingsDropdown));
var view = mountTestComponent();
TestUtils.Simulate.click(
view.getDOMNode().querySelector(".icon-signout"));
@ -372,28 +375,41 @@ describe("loop.panel", function() {
});
describe("Help", function() {
var supportUrl = "https://example.com";
var view, supportUrl;
function mountTestComponent() {
return TestUtils.renderIntoDocument(
React.createElement(loop.panel.SettingsDropdown, {
mozLoop: fakeMozLoop
}));
}
beforeEach(function() {
supportUrl = "https://example.com";
navigator.mozLoop.getLoopPref = function(pref) {
if (pref === "support_url")
return supportUrl;
return "unseen";
};
sandbox.stub(window, "open");
sandbox.stub(window, "close");
});
it("should open a tab to the support page", function() {
var view = TestUtils.renderIntoDocument(
React.createElement(loop.panel.SettingsDropdown));
view = mountTestComponent();
TestUtils.Simulate
.click(view.getDOMNode().querySelector(".icon-help"));
sinon.assert.calledOnce(window.open);
sinon.assert.calledWithExactly(window.open, supportUrl);
sinon.assert.calledOnce(fakeMozLoop.openURL);
sinon.assert.calledWithExactly(fakeMozLoop.openURL, supportUrl);
});
it("should close the panel", function() {
view = mountTestComponent();
TestUtils.Simulate
.click(view.getDOMNode().querySelector(".icon-help"));
sinon.assert.calledOnce(fakeWindow.close);
});
});