From d55d4faa865c2168c6e423d653b716746ed3b79f Mon Sep 17 00:00:00 2001 From: Manuel Casas Date: Fri, 16 Oct 2015 17:06:48 +0100 Subject: [PATCH] Bug 1131542 - Loop button on toolbar needs different tooltips to explain colours/state. r=Standard8 --- browser/base/content/browser-loop.js | 30 ++++++++++++ browser/components/loop/modules/LoopRooms.jsm | 2 + .../components/loop/test/mochitest/.eslintrc | 3 +- .../test/mochitest/browser_toolbarbutton.js | 46 +++++++++++++++++-- .../uitour/test/browser_UITour_loop.js | 2 + .../customizableWidgets.properties | 5 ++ 6 files changed, 84 insertions(+), 4 deletions(-) diff --git a/browser/base/content/browser-loop.js b/browser/base/content/browser-loop.js index bf971356e60b..5f2d87fd6396 100644 --- a/browser/base/content/browser-loop.js +++ b/browser/base/content/browser-loop.js @@ -276,18 +276,48 @@ var LoopUI; return; } let state = ""; + let mozL10nId = "loop-call-button3"; + let suffix = ".tooltiptext"; if (this.MozLoopService.errors.size) { state = "error"; + mozL10nId += "-error"; } else if (this.MozLoopService.screenShareActive) { state = "action"; + mozL10nId += "-screensharing"; } else if (aReason == "login" && this.MozLoopService.userProfile) { state = "active"; + mozL10nId += "-active"; } else if (this.MozLoopService.doNotDisturb) { state = "disabled"; + mozL10nId += "-donotdisturb"; } else if (this.MozLoopService.roomsParticipantsCount > 0) { state = "active"; + this.roomsWithNonOwners().then(roomsWithNonOwners => { + if (roomsWithNonOwners.length > 0) { + mozL10nId += "-participantswaiting"; + } else { + mozL10nId += "-active"; + } + + this.updateTooltiptext(mozL10nId + suffix); + this.toolbarButton.node.setAttribute("state", state); + }); + return; } this.toolbarButton.node.setAttribute("state", state); + this.updateTooltiptext(mozL10nId + suffix); + }, + + /** + * Updates the tootltiptext to reflect Loop status. + * + * @param {string} [mozL10nId] l10n ID that refelct the current + * Loop status. + */ + updateTooltiptext: function(mozL10nId) { + this.toolbarButton.node.setAttribute("tooltiptext", mozL10nId); + var tooltiptext = CustomizableUI.getLocalizedProperty(this.toolbarButton, "tooltiptext"); + this.toolbarButton.node.setAttribute("tooltiptext", tooltiptext); }, /** diff --git a/browser/components/loop/modules/LoopRooms.jsm b/browser/components/loop/modules/LoopRooms.jsm index b147a3b2f45e..70367e4866f9 100644 --- a/browser/components/loop/modules/LoopRooms.jsm +++ b/browser/components/loop/modules/LoopRooms.jsm @@ -1116,12 +1116,14 @@ this.LoopRooms = { */ _setRoomsCache: function(roomsCache) { LoopRoomsInternal.rooms.clear(); + gDirty = true; if (roomsCache) { // Need a clone as the internal map is read-only. for (let [key, value] of roomsCache) { LoopRoomsInternal.rooms.set(key, value); } + gDirty = false; } } }; diff --git a/browser/components/loop/test/mochitest/.eslintrc b/browser/components/loop/test/mochitest/.eslintrc index 14d39d6109cb..5e6c3747b554 100644 --- a/browser/components/loop/test/mochitest/.eslintrc +++ b/browser/components/loop/test/mochitest/.eslintrc @@ -34,6 +34,7 @@ "LoopUI": false, // Other items "Chat": true, - "WebChannel": true + "WebChannel": true, + "executeSoon": true } } diff --git a/browser/components/loop/test/mochitest/browser_toolbarbutton.js b/browser/components/loop/test/mochitest/browser_toolbarbutton.js index aae19da75c64..d1e21a0eb268 100644 --- a/browser/components/loop/test/mochitest/browser_toolbarbutton.js +++ b/browser/components/loop/test/mochitest/browser_toolbarbutton.js @@ -43,75 +43,112 @@ add_task(function* test_LoopUI_getters() { add_task(function* test_doNotDisturb() { Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Start a conversation", "Check button has default tooltiptext"); yield MozLoopService.doNotDisturb = true; Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "disabled", "Check button is in disabled state"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Do not disturb", "Check button has disabled tooltiptext"); yield MozLoopService.doNotDisturb = false; Assert.notStrictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "disabled", "Check button is not in disabled state"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Start a conversation", "Check button has default tooltiptext"); }); add_task(function* test_doNotDisturb_with_login() { Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Start a conversation", "Check button has default tooltiptext"); yield MozLoopService.doNotDisturb = true; Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "disabled", "Check button is in disabled state"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Do not disturb", "Check button has disabled tooltiptext"); MozLoopServiceInternal.fxAOAuthTokenData = fxASampleToken; MozLoopServiceInternal.fxAOAuthProfile = fxASampleProfile; yield MozLoopServiceInternal.notifyStatusChanged("login"); Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "active", "Check button is in active state"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Active conversation", "Check button has active tooltiptext"); yield loadLoopPanel(); Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "disabled", "Check button is in disabled state after opening panel"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Do not disturb", "Check button has disabled tooltiptext"); LoopUI.panel.hidePopup(); yield MozLoopService.doNotDisturb = false; Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Start a conversation", "Check button has default tooltiptext"); MozLoopServiceInternal.fxAOAuthTokenData = null; yield MozLoopServiceInternal.notifyStatusChanged(); Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Start a conversation", "Check button has default tooltiptext"); }); add_task(function* test_error() { Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Start a conversation", "Check button has default tooltiptext"); yield MozLoopServiceInternal.setError("testing", {}); Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "error", "Check button is in error state"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Error!", "Check button has error tooltiptext"); yield MozLoopServiceInternal.clearError("testing"); Assert.notStrictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "error", "Check button is not in error state"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Start a conversation", "Check button has default tooltiptext"); }); add_task(function* test_error_with_login() { Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Start a conversation", "Check button has default tooltiptext"); yield MozLoopServiceInternal.setError("testing", {}); Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "error", "Check button is in error state"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Error!", "Check button has error tooltiptext"); MozLoopServiceInternal.fxAOAuthProfile = fxASampleProfile; MozLoopServiceInternal.notifyStatusChanged("login"); Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "error", "Check button is in error state"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Error!", "Check button has error tooltiptext"); yield MozLoopServiceInternal.clearError("testing"); Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Start a conversation", "Check button has default tooltiptext"); MozLoopServiceInternal.fxAOAuthProfile = null; MozLoopServiceInternal.notifyStatusChanged(); Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Start a conversation", "Check button has default tooltiptext"); }); add_task(function* test_active() { Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Start a conversation", "Check button has default tooltiptext"); MozLoopServiceInternal.fxAOAuthTokenData = fxASampleToken; MozLoopServiceInternal.fxAOAuthProfile = fxASampleProfile; yield MozLoopServiceInternal.notifyStatusChanged("login"); Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "active", "Check button is in active state"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Active conversation", "Check button has active tooltiptext"); yield loadLoopPanel(); Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state after opening panel"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Start a conversation", "Check button has default tooltiptext"); LoopUI.panel.hidePopup(); MozLoopServiceInternal.fxAOAuthTokenData = null; MozLoopServiceInternal.notifyStatusChanged(); Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Start a conversation", "Check button has default tooltiptext"); }); add_task(function* test_room_participants() { Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state"); - LoopRoomsInternal.rooms.set("test_room", {participants: [{displayName: "hugh", id: "008"}]}); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Start a conversation", "Check button has default tooltiptext"); + let roomsCache = new Map([[ "test_room", {participants: [{displayName: "hugh", id: "008", owner: true}]} ]]); + LoopRooms._setRoomsCache(roomsCache); MozLoopServiceInternal.notifyStatusChanged(); + // Since we're changing the rooms map directly, we're expecting it to be a synchronous operation. + // But Promises have the inherent property of then-ables being async so even though the operation returns immediately, + // because the cache is hit, the promise won't be resolved until after the next tick. + // And that's what the line below does, waits until the next tick + yield new Promise(resolve => executeSoon(resolve)); Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "active", "Check button is in active state"); - LoopRoomsInternal.rooms.set("test_room", {participants: []}); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Active conversation", "Check button has active tooltiptext"); + roomsCache.set("test_room", {participants: [{displayName: "hugh", id: "008", owner: false}]}); + LoopRooms._setRoomsCache(roomsCache); + MozLoopServiceInternal.notifyStatusChanged(); + yield new Promise(resolve => executeSoon(resolve)); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "active", "Check button is in active state"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Someone is waiting for you in a conversation", "Check button has participantswaiting tooltiptext"); + roomsCache.set("test_room", {participants: []}); + LoopRooms._setRoomsCache(roomsCache); MozLoopServiceInternal.notifyStatusChanged(); Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state"); - LoopRoomsInternal.rooms.delete("test_room"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Start a conversation", "Check button has default tooltiptext"); + LoopRooms._setRoomsCache(); }); add_task(function* test_panelToggle_on_click() { @@ -130,10 +167,13 @@ add_task(function* test_panelToggle_on_click() { add_task(function* test_screen_share() { Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Start a conversation", "Check button has default tooltiptext"); MozLoopService.setScreenShareState("1", true); Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "action", "Check button is in action state"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "You are sharing your screen", "Check button has sharingscreen tooltiptext"); MozLoopService.setScreenShareState("1", false); Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state"); + Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Start a conversation", "Check button has default tooltiptext"); }); add_task(function* test_private_browsing_window() { diff --git a/browser/components/uitour/test/browser_UITour_loop.js b/browser/components/uitour/test/browser_UITour_loop.js index 287dbd035f56..0f6a6ba38c11 100644 --- a/browser/components/uitour/test/browser_UITour_loop.js +++ b/browser/components/uitour/test/browser_UITour_loop.js @@ -251,6 +251,7 @@ var tests = [ chatWin.document.querySelector(".btn-email").click(); }); }); + setupFakeRoom(); LoopRooms.open("fakeTourRoom"); }), taskify(function* test_arrow_panel_position() { @@ -349,6 +350,7 @@ function setupFakeRoom() { for (let prop of ["roomToken", "roomOwner", "roomUrl", "participants"]) room[prop] = "fakeTourRoom"; room.decryptedContext = {roomName: "fakeTourRoom"}; + room.participants = []; let roomsMap = new Map([ [room.roomToken, room] ]); diff --git a/browser/locales/en-US/chrome/browser/customizableui/customizableWidgets.properties b/browser/locales/en-US/chrome/browser/customizableui/customizableWidgets.properties index f0ca0b10826b..39a922e658ce 100644 --- a/browser/locales/en-US/chrome/browser/customizableui/customizableWidgets.properties +++ b/browser/locales/en-US/chrome/browser/customizableui/customizableWidgets.properties @@ -97,6 +97,11 @@ quit-button.tooltiptext.mac = Quit %1$S (%2$S) # approval before you change it. loop-call-button3.label = Hello loop-call-button3.tooltiptext = Start a conversation +loop-call-button3-error.tooltiptext = Error! +loop-call-button3-donotdisturb.tooltiptext = Do not disturb +loop-call-button3-screensharing.tooltiptext = You are sharing your screen +loop-call-button3-active.tooltiptext = Active conversation +loop-call-button3-participantswaiting.tooltiptext = Someone is waiting for you in a conversation # LOCALIZATION NOTE(loop-call-button3-pb.tooltiptext): Shown when the button is # placed inside a Private Browsing window. %S is the value of loop-call-button3.label. loop-call-button3-pb.tooltiptext = %S is not available in Private Browsing