Follow-up to bug 1079225 - Fix formatting of the waiting for media message in Loop rooms, and ensure feedback can be given for multiple conversations in a row. r=abr
authorMark Banner <standard8@mozilla.com>
Wed, 26 Nov 2014 21:09:09 +0000
changeset 217695 ce3eeca4f793
parent 217694 5c804aa57e4b
child 217696 96f7ccef47ad
push id27887
push userryanvm@gmail.com
push dateThu, 27 Nov 2014 02:08:38 +0000
treeherdermozilla-central@c63e741bca2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersabr
bugs1079225
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Follow-up to bug 1079225 - Fix formatting of the waiting for media message in Loop rooms, and ensure feedback can be given for multiple conversations in a row. r=abr
browser/components/loop/content/shared/js/actions.js
browser/components/loop/content/shared/js/activeRoomStore.js
browser/components/loop/content/shared/js/feedbackStore.js
browser/components/loop/content/shared/js/store.js
browser/components/loop/standalone/content/js/standaloneRoomViews.js
browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
browser/components/loop/standalone/content/js/webapp.js
browser/components/loop/standalone/content/js/webapp.jsx
browser/components/loop/test/shared/activeRoomStore_test.js
browser/components/loop/test/shared/feedbackStore_test.js
browser/components/loop/test/standalone/standaloneRoomViews_test.js
--- a/browser/components/loop/content/shared/js/actions.js
+++ b/browser/components/loop/content/shared/js/actions.js
@@ -326,19 +326,20 @@ loop.shared.actions = (function() {
     JoinedRoom: Action.define("joinedRoom", {
       apiKey: String,
       sessionToken: String,
       sessionId: String,
       expires: Number
     }),
 
     /**
-     * Resets current room.
+     * Used to indicate that the feedback cycle is completed and the countdown
+     * finished.
      */
-    ResetRoom: Action.define("resetRoom", {
+    FeedbackComplete: Action.define("feedbackComplete", {
     }),
 
     /**
      * Used to indicate the user wishes to leave the room.
      */
     LeaveRoom: Action.define("leaveRoom", {
     }),
 
--- a/browser/components/loop/content/shared/js/activeRoomStore.js
+++ b/browser/components/loop/content/shared/js/activeRoomStore.js
@@ -135,17 +135,17 @@ loop.store.ActiveRoomStore = (function()
         "joinedRoom",
         "connectedToSdkServers",
         "connectionFailure",
         "setMute",
         "remotePeerDisconnected",
         "remotePeerConnected",
         "windowUnload",
         "leaveRoom",
-        "resetRoom"
+        "feedbackComplete"
       ]);
     },
 
     /**
      * Execute setupWindowData event action from the dispatcher. This gets
      * the room data from the mozLoop api, and dispatches an UpdateRoomInfo event.
      * It also dispatches JoinRoom as this action is only applicable to the desktop
      * client, and needs to auto-join.
@@ -448,17 +448,19 @@ loop.store.ActiveRoomStore = (function()
         this._mozLoop.rooms.leave(this._storeState.roomToken,
           this._storeState.sessionToken);
       }
 
       this.setStoreState({roomState: nextState || ROOM_STATES.ENDED});
     },
 
     /**
-     * Resets current room.
+     * When feedback is complete, we reset the room to the initial state.
      */
-    resetRoom: function() {
+    feedbackComplete: function() {
+      // Note, that we want some values, such as the windowId, so we don't
+      // do a full reset here.
       this.setStoreState(this.getInitialStoreState());
     }
   });
 
   return ActiveRoomStore;
 })();
--- a/browser/components/loop/content/shared/js/feedbackStore.js
+++ b/browser/components/loop/content/shared/js/feedbackStore.js
@@ -32,17 +32,18 @@ loop.store.FeedbackStore = (function() {
    * @param {Object} options Options object:
    * - {mozLoop}        mozLoop                 The MozLoop API object.
    * - {feedbackClient} loop.FeedbackAPIClient  The feedback API client.
    */
   var FeedbackStore = loop.store.createStore({
     actions: [
       "requireFeedbackDetails",
       "sendFeedback",
-      "sendFeedbackError"
+      "sendFeedbackError",
+      "feedbackComplete"
     ],
 
     initialize: function(options) {
       if (!options.feedbackClient) {
         throw new Error("Missing option feedbackClient");
       }
       this._feedbackClient = options.feedbackClient;
     },
@@ -86,13 +87,21 @@ loop.store.FeedbackStore = (function() {
      *
      * @param {sharedActions.SendFeedback} actionData The action data.
      */
     sendFeedbackError: function(actionData) {
       this.setStoreState({
         feedbackState: FEEDBACK_STATES.FAILED,
         error: actionData.error
       });
+    },
+
+    /**
+     * Resets the store to its initial state as feedback has been completed,
+     * i.e. ready for the next round of feedback.
+     */
+    feedbackComplete: function() {
+      this.resetStoreState();
     }
   });
 
   return FeedbackStore;
 })();
--- a/browser/components/loop/content/shared/js/store.js
+++ b/browser/components/loop/content/shared/js/store.js
@@ -49,16 +49,27 @@ loop.store.createStore = (function() {
      * @param {Object} newState The new store state object.
      */
     setStoreState: function(newState) {
       for (var key in newState) {
         this._storeState[key] = newState[key];
         this.trigger("change:" + key);
       }
       this.trigger("change");
+    },
+
+    /**
+     * Resets the store state to the initially defined state.
+     */
+    resetStoreState: function() {
+      if (typeof this.getInitialStoreState === "function") {
+        this._storeState = this.getInitialStoreState();
+      } else {
+        this._storeState = {};
+      }
     }
   };
 
   /**
    * Creates a new Store constructor.
    *
    * @param  {Object}   storeProto The store prototype.
    * @return {Function}            A store constructor.
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.js
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.js
@@ -24,17 +24,17 @@ loop.standaloneRoomViews = (function(moz
         React.PropTypes.instanceOf(loop.store.ActiveRoomStore).isRequired,
       feedbackStore:
         React.PropTypes.instanceOf(loop.store.FeedbackStore).isRequired
     },
 
     onFeedbackSent: function() {
       // We pass a tick to prevent React warnings regarding nested updates.
       setTimeout(function() {
-        this.props.activeRoomStore.dispatchAction(new sharedActions.ResetRoom());
+        this.props.activeRoomStore.dispatchAction(new sharedActions.FeedbackComplete());
       }.bind(this));
     },
 
     _renderCallToActionLink: function() {
       if (this.props.helper.isFirefox(navigator.userAgent)) {
         return (
           React.DOM.a({href: loop.config.learnMoreUrl, className: "btn btn-info"}, 
             mozL10n.get("rooms_room_full_call_to_action_label", {
@@ -80,18 +80,20 @@ loop.standaloneRoomViews = (function(moz
             )
           );
         }
         case ROOM_STATES.MEDIA_WAIT: {
           var msg = mozL10n.get("call_progress_getting_media_description",
                                 {clientShortname: mozL10n.get("clientShortname2")});
           // XXX Bug 1047040 will add images to help prompt the user.
           return (
-            React.DOM.p({className: "prompt-media-message"}, 
-              msg
+            React.DOM.div({className: "room-inner-info-area"}, 
+              React.DOM.p({className: "prompt-media-message"}, 
+                msg
+              )
             )
           );
         }
         case ROOM_STATES.JOINED:
         case ROOM_STATES.SESSION_CONNECTED: {
           return (
             React.DOM.div({className: "room-inner-info-area"}, 
               React.DOM.p({className: "empty-room-message"}, 
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
@@ -24,17 +24,17 @@ loop.standaloneRoomViews = (function(moz
         React.PropTypes.instanceOf(loop.store.ActiveRoomStore).isRequired,
       feedbackStore:
         React.PropTypes.instanceOf(loop.store.FeedbackStore).isRequired
     },
 
     onFeedbackSent: function() {
       // We pass a tick to prevent React warnings regarding nested updates.
       setTimeout(function() {
-        this.props.activeRoomStore.dispatchAction(new sharedActions.ResetRoom());
+        this.props.activeRoomStore.dispatchAction(new sharedActions.FeedbackComplete());
       }.bind(this));
     },
 
     _renderCallToActionLink: function() {
       if (this.props.helper.isFirefox(navigator.userAgent)) {
         return (
           <a href={loop.config.learnMoreUrl} className="btn btn-info">
             {mozL10n.get("rooms_room_full_call_to_action_label", {
@@ -80,19 +80,21 @@ loop.standaloneRoomViews = (function(moz
             </div>
           );
         }
         case ROOM_STATES.MEDIA_WAIT: {
           var msg = mozL10n.get("call_progress_getting_media_description",
                                 {clientShortname: mozL10n.get("clientShortname2")});
           // XXX Bug 1047040 will add images to help prompt the user.
           return (
-            <p className="prompt-media-message">
-              {msg}
-            </p>
+            <div className="room-inner-info-area">
+              <p className="prompt-media-message">
+                {msg}
+              </p>
+            </div>
           );
         }
         case ROOM_STATES.JOINED:
         case ROOM_STATES.SESSION_CONNECTED: {
           return (
             <div className="room-inner-info-area">
               <p className="empty-room-message">
                 {mozL10n.get("rooms_only_occupant_label")}
--- a/browser/components/loop/standalone/content/js/webapp.js
+++ b/browser/components/loop/standalone/content/js/webapp.js
@@ -685,19 +685,20 @@ loop.webapp = (function($, _, OT, mozL10
       this.props.conversation.off(null, null, this);
     },
 
     shouldComponentUpdate: function(nextProps, nextState) {
       // Only rerender if current state has actually changed
       return nextState.callStatus !== this.state.callStatus;
     },
 
-    callStatusSwitcher: function(status) {
+    resetCallStatus: function() {
+      this.props.feedbackStore.dispatchAction(new sharedActions.FeedbackComplete());
       return function() {
-        this.setState({callStatus: status});
+        this.setState({callStatus: "start"});
       }.bind(this);
     },
 
     /**
      * Renders the conversation views.
      */
     render: function() {
       switch (this.state.callStatus) {
@@ -739,17 +740,17 @@ loop.webapp = (function($, _, OT, mozL10
           );
         }
         case "end": {
           return (
             EndedConversationView({
               sdk: this.props.sdk, 
               conversation: this.props.conversation, 
               feedbackStore: this.props.feedbackStore, 
-              onAfterFeedbackReceived: this.callStatusSwitcher("start")}
+              onAfterFeedbackReceived: this.resetCallStatus()}
             )
           );
         }
         case "expired": {
           return (
             CallUrlExpiredView({helper: this.props.helper})
           );
         }
--- a/browser/components/loop/standalone/content/js/webapp.jsx
+++ b/browser/components/loop/standalone/content/js/webapp.jsx
@@ -685,19 +685,20 @@ loop.webapp = (function($, _, OT, mozL10
       this.props.conversation.off(null, null, this);
     },
 
     shouldComponentUpdate: function(nextProps, nextState) {
       // Only rerender if current state has actually changed
       return nextState.callStatus !== this.state.callStatus;
     },
 
-    callStatusSwitcher: function(status) {
+    resetCallStatus: function() {
+      this.props.feedbackStore.dispatchAction(new sharedActions.FeedbackComplete());
       return function() {
-        this.setState({callStatus: status});
+        this.setState({callStatus: "start"});
       }.bind(this);
     },
 
     /**
      * Renders the conversation views.
      */
     render: function() {
       switch (this.state.callStatus) {
@@ -739,17 +740,17 @@ loop.webapp = (function($, _, OT, mozL10
           );
         }
         case "end": {
           return (
             <EndedConversationView
               sdk={this.props.sdk}
               conversation={this.props.conversation}
               feedbackStore={this.props.feedbackStore}
-              onAfterFeedbackReceived={this.callStatusSwitcher("start")}
+              onAfterFeedbackReceived={this.resetCallStatus()}
             />
           );
         }
         case "expired": {
           return (
             <CallUrlExpiredView helper={this.props.helper} />
           );
         }
--- a/browser/components/loop/test/shared/activeRoomStore_test.js
+++ b/browser/components/loop/test/shared/activeRoomStore_test.js
@@ -259,27 +259,27 @@ describe("loop.store.ActiveRoomStore", f
         windowType: "room",
         token: "fakeToken"
       }));
 
       expect(store.getStoreState().roomState).eql(ROOM_STATES.READY);
     });
   });
 
-  describe("#resetRoom", function() {
+  describe("#feedbackComplete", function() {
     it("should reset the room store state", function() {
       var initialState = store.getInitialStoreState();
       store.setStoreState({
         roomState: ROOM_STATES.ENDED,
         audioMuted: true,
         videoMuted: true,
         failureReason: "foo"
       });
 
-      store.resetRoom(new sharedActions.ResetRoom());
+      store.feedbackComplete(new sharedActions.FeedbackComplete());
 
       expect(store.getStoreState()).eql(initialState);
     });
   });
 
   describe("#setupRoomInfo", function() {
     var fakeRoomInfo;
 
--- a/browser/components/loop/test/shared/feedbackStore_test.js
+++ b/browser/components/loop/test/shared/feedbackStore_test.js
@@ -100,9 +100,21 @@ describe("loop.store.FeedbackStore", fun
       store.once("change:feedbackState", function() {
         expect(store.getStoreState("feedbackState")).eql(FEEDBACK_STATES.FAILED);
         done();
       });
 
       store.sendFeedback(new sharedActions.SendFeedback(sadFeedbackData));
     });
   });
+
+  describe("feedbackComplete", function() {
+    it("should reset the store state", function() {
+      store.setStoreState({feedbackState: FEEDBACK_STATES.SENT});
+
+      store.feedbackComplete();
+
+      expect(store.getStoreState()).eql({
+        feedbackState: FEEDBACK_STATES.INIT
+      });
+    });
+  });
 });
--- a/browser/components/loop/test/standalone/standaloneRoomViews_test.js
+++ b/browser/components/loop/test/standalone/standaloneRoomViews_test.js
@@ -286,27 +286,31 @@ describe("loop.standaloneRoomViews", fun
           TestUtils.Simulate.click(getLeaveButton(view));
 
           sinon.assert.calledOnce(dispatch);
           sinon.assert.calledWithExactly(dispatch, new sharedActions.LeaveRoom());
         });
       });
 
       describe("Feedback", function() {
+        beforeEach(function() {
+          activeRoomStore.setStoreState({roomState: ROOM_STATES.ENDED});
+        });
+
         it("should display a feedback form when the user leaves the room",
           function() {
-            activeRoomStore.setStoreState({roomState: ROOM_STATES.ENDED});
-
             expect(view.getDOMNode().querySelector(".faces")).not.eql(null);
           });
 
-        it("should reinit the view after feedback is sent", function() {
-          feedbackStore.setStoreState({feedbackState: FEEDBACK_STATES.SENT});
+        it("should dispatch a `FeedbackComplete` action after feedback is sent",
+          function() {
+            feedbackStore.setStoreState({feedbackState: FEEDBACK_STATES.SENT});
 
-          sandbox.clock.tick(
-            loop.shared.views.WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS * 1000);
+            sandbox.clock.tick(
+              loop.shared.views.WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS * 1000 + 1000);
 
-          expect(view.getDOMNode().querySelector(".btn-join")).not.eql(null);
-        });
+            sinon.assert.calledOnce(dispatch);
+            sinon.assert.calledWithExactly(dispatch, new sharedActions.FeedbackComplete());
+          });
       });
     });
   });
 });