diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 101065afc3a2..85e20ffbad92 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1662,7 +1662,7 @@ pref("loop.enabled", true); pref("loop.server", "https://loop.services.mozilla.com/v0"); pref("loop.seenToS", "unseen"); pref("loop.gettingStarted.seen", false); -pref("loop.gettingStarted.url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/hello/start"); +pref("loop.gettingStarted.url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/hello/start/"); pref("loop.gettingStarted.resumeOnFirstJoin", false); pref("loop.learnMoreUrl", "https://www.firefox.com/hello/"); pref("loop.legal.ToS_url", "https://www.mozilla.org/about/legal/terms/firefox-hello/"); diff --git a/browser/base/content/browser-fxaccounts.js b/browser/base/content/browser-fxaccounts.js index 5873206bc890..537705d85fb7 100644 --- a/browser/base/content/browser-fxaccounts.js +++ b/browser/base/content/browser-fxaccounts.js @@ -2,19 +2,12 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -XPCOMUtils.defineLazyGetter(this, "FxAccountsCommon", function () { - return Cu.import("resource://gre/modules/FxAccountsCommon.js", {}); -}); - -XPCOMUtils.defineLazyModuleGetter(this, "fxaMigrator", - "resource://services-sync/FxaMigrator.jsm"); - -const PREF_SYNC_START_DOORHANGER = "services.sync.ui.showSyncStartDoorhanger"; -const DOORHANGER_ACTIVATE_DELAY_MS = 5000; -const SYNC_MIGRATION_NOTIFICATION_TITLE = "fxa-migration"; - let gFxAccounts = { + PREF_SYNC_START_DOORHANGER: "services.sync.ui.showSyncStartDoorhanger", + DOORHANGER_ACTIVATE_DELAY_MS: 5000, + SYNC_MIGRATION_NOTIFICATION_TITLE: "fxa-migration", + _initialized: false, _inCustomizationMode: false, @@ -34,8 +27,8 @@ let gFxAccounts = { "weave:service:login:error", "weave:service:setup-complete", "fxa-migration:state-changed", - FxAccountsCommon.ONVERIFIED_NOTIFICATION, - FxAccountsCommon.ONLOGOUT_NOTIFICATION + this.FxAccountsCommon.ONVERIFIED_NOTIFICATION, + this.FxAccountsCommon.ONLOGOUT_NOTIFICATION ]; }, @@ -108,8 +101,8 @@ let gFxAccounts = { observe: function (subject, topic, data) { switch (topic) { - case FxAccountsCommon.ONVERIFIED_NOTIFICATION: - Services.prefs.setBoolPref(PREF_SYNC_START_DOORHANGER, true); + case this.FxAccountsCommon.ONVERIFIED_NOTIFICATION: + Services.prefs.setBoolPref(this.PREF_SYNC_START_DOORHANGER, true); break; case "weave:service:sync:start": this.onSyncStart(); @@ -131,11 +124,11 @@ let gFxAccounts = { let showDoorhanger = false; try { - showDoorhanger = Services.prefs.getBoolPref(PREF_SYNC_START_DOORHANGER); + showDoorhanger = Services.prefs.getBoolPref(this.PREF_SYNC_START_DOORHANGER); } catch (e) { /* The pref might not exist. */ } if (showDoorhanger) { - Services.prefs.clearUserPref(PREF_SYNC_START_DOORHANGER); + Services.prefs.clearUserPref(this.PREF_SYNC_START_DOORHANGER); this.showSyncStartedDoorhanger(); } }, @@ -155,7 +148,7 @@ let gFxAccounts = { // a short delay. Without this delay the doorhanger would not show up // or with a too small delay show up while we're still animating the // window. - setTimeout(() => this.onSyncStart(), DOORHANGER_ACTIVATE_DELAY_MS); + setTimeout(() => this.onSyncStart(), this.DOORHANGER_ACTIVATE_DELAY_MS); } else { this._inCustomizationMode = event.type == "customizationstarting"; this.updateAppMenuItem(); @@ -251,12 +244,12 @@ let gFxAccounts = { let status = null; let label = null; switch (this._migrationInfo.state) { - case fxaMigrator.STATE_USER_FXA: + case this.fxaMigrator.STATE_USER_FXA: status = "migrate-signup"; label = this.strings.formatStringFromName("needUserShort", [this.button.getAttribute("fxabrandname")], 1); break; - case fxaMigrator.STATE_USER_FXA_VERIFIED: + case this.fxaMigrator.STATE_USER_FXA_VERIFIED: status = "migrate-verify"; label = this.strings.formatStringFromName("needVerifiedUserShort", [this._migrationInfo.email], @@ -270,7 +263,7 @@ let gFxAccounts = { updateMigrationNotification: Task.async(function* () { if (!this._migrationInfo) { - Weave.Notifications.removeAll(SYNC_MIGRATION_NOTIFICATION_TITLE); + Weave.Notifications.removeAll(this.SYNC_MIGRATION_NOTIFICATION_TITLE); return; } if (gBrowser.currentURI.spec.split("?")[0] == "about:accounts") { @@ -280,7 +273,7 @@ let gFxAccounts = { } let note = null; switch (this._migrationInfo.state) { - case fxaMigrator.STATE_USER_FXA: { + case this.fxaMigrator.STATE_USER_FXA: { let msg = this.strings.GetStringFromName("needUserLong"); let upgradeLabel = this.strings.GetStringFromName("upgradeToFxA.label"); @@ -289,13 +282,13 @@ let gFxAccounts = { note = new Weave.Notification( undefined, msg, undefined, Weave.Notifications.PRIORITY_WARNING, [ new Weave.NotificationButton(upgradeLabel, upgradeAccessKey, () => { - fxaMigrator.createFxAccount(window); + this.fxaMigrator.createFxAccount(window); }), ] ); break; } - case fxaMigrator.STATE_USER_FXA_VERIFIED: { + case this.fxaMigrator.STATE_USER_FXA_VERIFIED: { let msg = this.strings.formatStringFromName("needVerifiedUserLong", [this._migrationInfo.email], 1); @@ -306,14 +299,14 @@ let gFxAccounts = { note = new Weave.Notification( undefined, msg, undefined, Weave.Notifications.PRIORITY_INFO, [ new Weave.NotificationButton(resendLabel, resendAccessKey, () => { - fxaMigrator.resendVerificationMail(); + this.fxaMigrator.resendVerificationMail(); }), ] ); break; } } - note.title = SYNC_MIGRATION_NOTIFICATION_TITLE; + note.title = this.SYNC_MIGRATION_NOTIFICATION_TITLE; Weave.Notifications.replaceTitle(note); }), @@ -328,7 +321,7 @@ let gFxAccounts = { this.openSignInAgainPage("menupanel"); break; case "migrate-signup": - fxaMigrator.createFxAccount(window); + this.fxaMigrator.createFxAccount(window); break; case "migrate-verify": // Instead of using the migrator module directly here the UX calls for @@ -374,3 +367,10 @@ let gFxAccounts = { this.openAccountsPage("reauth", { entryPoint: entryPoint }); }, }; + +XPCOMUtils.defineLazyGetter(gFxAccounts, "FxAccountsCommon", function () { + return Cu.import("resource://gre/modules/FxAccountsCommon.js", {}); +}); + +XPCOMUtils.defineLazyModuleGetter(gFxAccounts, "fxaMigrator", + "resource://services-sync/FxaMigrator.jsm"); diff --git a/browser/base/content/test/general/browser_fxa_migrate.js b/browser/base/content/test/general/browser_fxa_migrate.js index d631a4ea53db..778bad5ab136 100644 --- a/browser/base/content/test/general/browser_fxa_migrate.js +++ b/browser/base/content/test/general/browser_fxa_migrate.js @@ -11,7 +11,7 @@ add_task(function* test() { // Fake the state where we need an FxA user. let buttonPromise = promiseButtonMutation(); Services.obs.notifyObservers(null, STATE_CHANGED_TOPIC, - fxaMigrator.STATE_USER_FXA); + imports.fxaMigrator.STATE_USER_FXA); let buttonState = yield buttonPromise; assertButtonState(buttonState, "migrate-signup", true); Assert.ok(Weave.Notifications.notifications.some(n => { @@ -24,7 +24,7 @@ add_task(function* test() { createInstance(Ci.nsISupportsString); email.data = "foo@example.com"; Services.obs.notifyObservers(email, STATE_CHANGED_TOPIC, - fxaMigrator.STATE_USER_FXA_VERIFIED); + imports.fxaMigrator.STATE_USER_FXA_VERIFIED); buttonState = yield buttonPromise; assertButtonState(buttonState, "migrate-verify", true, "foo@example.com not verified"); diff --git a/browser/components/loop/content/js/roomViews.js b/browser/components/loop/content/js/roomViews.js index 5327d09582e2..f5d078e5c3df 100644 --- a/browser/components/loop/content/js/roomViews.js +++ b/browser/components/loop/content/js/roomViews.js @@ -300,10 +300,16 @@ loop.roomViews = (function(mozL10n) { ); } case ROOM_STATES.ENDED: { - return sharedViews.FeedbackView({ - feedbackStore: this.props.feedbackStore, - onAfterFeedbackReceived: this.closeWindow} - ); + if (this.state.used) + return sharedViews.FeedbackView({ + feedbackStore: this.props.feedbackStore, + onAfterFeedbackReceived: this.closeWindow} + ); + + // In case the room was not used (no one was here), we + // bypass the feedback form. + this.closeWindow(); + return null; } default: { return ( diff --git a/browser/components/loop/content/js/roomViews.jsx b/browser/components/loop/content/js/roomViews.jsx index 7d23fbe62819..8d4e1ddec230 100644 --- a/browser/components/loop/content/js/roomViews.jsx +++ b/browser/components/loop/content/js/roomViews.jsx @@ -300,10 +300,16 @@ loop.roomViews = (function(mozL10n) { />; } case ROOM_STATES.ENDED: { - return ; + if (this.state.used) + return ; + + // In case the room was not used (no one was here), we + // bypass the feedback form. + this.closeWindow(); + return null; } default: { return ( diff --git a/browser/components/loop/content/shared/js/activeRoomStore.js b/browser/components/loop/content/shared/js/activeRoomStore.js index 05713606a4cd..cdcc5f88cfcd 100644 --- a/browser/components/loop/content/shared/js/activeRoomStore.js +++ b/browser/components/loop/content/shared/js/activeRoomStore.js @@ -67,7 +67,12 @@ loop.store.ActiveRoomStore = (function() { roomState: ROOM_STATES.INIT, audioMuted: false, videoMuted: false, - failureReason: undefined + failureReason: undefined, + // Tracks if the room has been used during this + // session. 'Used' means at least one call has been placed + // with it. Entering and leaving the room without seeing + // anyone is not considered as 'used' + used: false }; }, @@ -361,7 +366,10 @@ loop.store.ActiveRoomStore = (function() { * Handles recording when a remote peer has connected to the servers. */ remotePeerConnected: function() { - this.setStoreState({roomState: ROOM_STATES.HAS_PARTICIPANTS}); + this.setStoreState({ + roomState: ROOM_STATES.HAS_PARTICIPANTS, + used: true + }); // We've connected with a third-party, therefore stop displaying the ToS etc. this._mozLoop.setLoopPref("seenToS", "seen"); diff --git a/browser/components/loop/standalone/content/js/standaloneRoomViews.js b/browser/components/loop/standalone/content/js/standaloneRoomViews.js index 27e8441d9691..fdfa50c66f0b 100644 --- a/browser/components/loop/standalone/content/js/standaloneRoomViews.js +++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.js @@ -115,14 +115,20 @@ loop.standaloneRoomViews = (function(mozL10n) { ); } case ROOM_STATES.ENDED: { - return ( - React.DOM.div({className: "ended-conversation"}, - sharedViews.FeedbackView({ - feedbackStore: this.props.feedbackStore, - onAfterFeedbackReceived: this.onFeedbackSent} + if (this.props.roomUsed) + return ( + React.DOM.div({className: "ended-conversation"}, + sharedViews.FeedbackView({ + feedbackStore: this.props.feedbackStore, + onAfterFeedbackReceived: this.onFeedbackSent} + ) ) - ) - ); + ); + + // In case the room was not used (no one was here), we + // bypass the feedback form. + this.onFeedbackSent(); + return null; } case ROOM_STATES.FAILED: { return ( @@ -362,7 +368,8 @@ loop.standaloneRoomViews = (function(mozL10n) { joinRoom: this.joinRoom, helper: this.props.helper, activeRoomStore: this.props.activeRoomStore, - feedbackStore: this.props.feedbackStore}), + feedbackStore: this.props.feedbackStore, + roomUsed: this.state.used}), React.DOM.div({className: "video-layout-wrapper"}, React.DOM.div({className: "conversation room-conversation"}, React.DOM.h2({className: "room-name"}, this.state.roomName), diff --git a/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx b/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx index f4f93c86a26d..894305a7ba5b 100644 --- a/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx +++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx @@ -115,14 +115,20 @@ loop.standaloneRoomViews = (function(mozL10n) { ); } case ROOM_STATES.ENDED: { - return ( -
- -
- ); + if (this.props.roomUsed) + return ( +
+ +
+ ); + + // In case the room was not used (no one was here), we + // bypass the feedback form. + this.onFeedbackSent(); + return null; } case ROOM_STATES.FAILED: { return ( @@ -362,7 +368,8 @@ loop.standaloneRoomViews = (function(mozL10n) { joinRoom={this.joinRoom} helper={this.props.helper} activeRoomStore={this.props.activeRoomStore} - feedbackStore={this.props.feedbackStore} /> + feedbackStore={this.props.feedbackStore} + roomUsed={this.state.used} />

{this.state.roomName}

diff --git a/browser/components/loop/test/desktop-local/roomViews_test.js b/browser/components/loop/test/desktop-local/roomViews_test.js index 23c6c2dd72e9..f69c429340ab 100644 --- a/browser/components/loop/test/desktop-local/roomViews_test.js +++ b/browser/components/loop/test/desktop-local/roomViews_test.js @@ -64,6 +64,7 @@ describe("loop.roomViews", function () { audioMuted: false, videoMuted: false, failureReason: undefined, + used: false, foo: "bar" }); }); @@ -355,13 +356,28 @@ describe("loop.roomViews", function () { it("should render the FeedbackView if roomState is `ENDED`", function() { - activeRoomStore.setStoreState({roomState: ROOM_STATES.ENDED}); + activeRoomStore.setStoreState({ + roomState: ROOM_STATES.ENDED, + used: true + }); view = mountTestComponent(); TestUtils.findRenderedComponentWithType(view, loop.shared.views.FeedbackView); }); + + it("should NOT render the FeedbackView if the room has not been used", + function() { + activeRoomStore.setStoreState({ + roomState: ROOM_STATES.ENDED, + used: false + }); + + view = mountTestComponent(); + + expect(view.getDOMNode()).eql(null); + }); }); describe("Mute", function() { diff --git a/browser/components/loop/test/standalone/standaloneRoomViews_test.js b/browser/components/loop/test/standalone/standaloneRoomViews_test.js index 3d40c90b7819..b9e0a2901f51 100644 --- a/browser/components/loop/test/standalone/standaloneRoomViews_test.js +++ b/browser/components/loop/test/standalone/standaloneRoomViews_test.js @@ -300,7 +300,10 @@ describe("loop.standaloneRoomViews", function() { describe("Feedback", function() { beforeEach(function() { - activeRoomStore.setStoreState({roomState: ROOM_STATES.ENDED}); + activeRoomStore.setStoreState({ + roomState: ROOM_STATES.ENDED, + used: true + }); }); it("should display a feedback form when the user leaves the room", @@ -318,6 +321,13 @@ describe("loop.standaloneRoomViews", function() { sinon.assert.calledOnce(dispatch); sinon.assert.calledWithExactly(dispatch, new sharedActions.FeedbackComplete()); }); + + it("should NOT display a feedback form if the room has not been used", + function() { + activeRoomStore.setStoreState({used: false}); + expect(view.getDOMNode().querySelector(".faces")).eql(null); + }); + }); describe("Mute", function() { diff --git a/browser/components/preferences/in-content/search.xul b/browser/components/preferences/in-content/search.xul index f733b086c1ed..e13c22c7ea21 100644 --- a/browser/components/preferences/in-content/search.xul +++ b/browser/components/preferences/in-content/search.xul @@ -15,6 +15,8 @@