Bug 1109849 - Bypass the feedback form if no-one has entered the room yet. r=nperriault
authorRomain Gauthier <romain.gauthier@monkeypatch.me>
Mon, 22 Dec 2014 16:56:16 +0100
changeset 220914 6436dc7bcf9128449f46b2e534591f426f4dfad2
parent 220913 3c7defb79660702e9ccfce599b241a9a80dfbcea
child 220915 8a26a418cc6851227ce74604a7a6aa2ebb68a187
push id28003
push userryanvm@gmail.com
push dateMon, 22 Dec 2014 22:44:16 +0000
treeherdermozilla-central@d5167dc0ded3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnperriault
bugs1109849
milestone37.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
Bug 1109849 - Bypass the feedback form if no-one has entered the room yet. r=nperriault
browser/components/loop/content/js/roomViews.js
browser/components/loop/content/js/roomViews.jsx
browser/components/loop/content/shared/js/activeRoomStore.js
browser/components/loop/standalone/content/js/standaloneRoomViews.js
browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
browser/components/loop/test/desktop-local/roomViews_test.js
browser/components/loop/test/standalone/standaloneRoomViews_test.js
--- a/browser/components/loop/content/js/roomViews.js
+++ b/browser/components/loop/content/js/roomViews.js
@@ -295,20 +295,26 @@ loop.roomViews = (function(mozL10n) {
         case ROOM_STATES.FULL: {
           // Note: While rooms are set to hold a maximum of 2 participants, the
           //       FULL case should never happen on desktop.
           return loop.conversation.GenericFailureView({
             cancelCall: this.closeWindow}
           );
         }
         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 (
             React.DOM.div({className: "room-conversation-wrapper"}, 
               this._renderInvitationOverlay(), 
               React.DOM.div({className: "video-layout-wrapper"}, 
                 React.DOM.div({className: "conversation room-conversation"}, 
                   React.DOM.div({className: "media nested"}, 
--- a/browser/components/loop/content/js/roomViews.jsx
+++ b/browser/components/loop/content/js/roomViews.jsx
@@ -295,20 +295,26 @@ loop.roomViews = (function(mozL10n) {
         case ROOM_STATES.FULL: {
           // Note: While rooms are set to hold a maximum of 2 participants, the
           //       FULL case should never happen on desktop.
           return <loop.conversation.GenericFailureView
             cancelCall={this.closeWindow}
           />;
         }
         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 (
             <div className="room-conversation-wrapper">
               {this._renderInvitationOverlay()}
               <div className="video-layout-wrapper">
                 <div className="conversation room-conversation">
                   <div className="media nested">
--- a/browser/components/loop/content/shared/js/activeRoomStore.js
+++ b/browser/components/loop/content/shared/js/activeRoomStore.js
@@ -62,17 +62,22 @@ loop.store.ActiveRoomStore = (function()
     /**
      * Returns initial state data for this active room.
      */
     getInitialStoreState: function() {
       return {
         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
       };
     },
 
     /**
      * Handles a room failure.
      *
      * @param {sharedActions.RoomFailure} actionData
      */
@@ -356,17 +361,20 @@ loop.store.ActiveRoomStore = (function()
       muteState[actionData.type + "Muted"] = !actionData.enabled;
       this.setStoreState(muteState);
     },
 
     /**
      * 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");
     },
 
     /**
      * Handles a remote peer disconnecting from the session. As we currently only
      * support 2 participants, we declare the room as SESSION_CONNECTED as soon as
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.js
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.js
@@ -110,24 +110,30 @@ loop.standaloneRoomViews = (function(moz
               React.DOM.p({className: "full-room-message"}, 
                 mozL10n.get("rooms_room_full_label")
               ), 
               React.DOM.p(null, this._renderCallToActionLink())
             )
           );
         }
         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 (
             React.DOM.div({className: "room-inner-info-area"}, 
               React.DOM.p({className: "failed-room-message"}, 
                 this._getFailureString()
               ), 
               React.DOM.button({className: "btn btn-join btn-info", 
@@ -357,17 +363,18 @@ loop.standaloneRoomViews = (function(moz
         React.DOM.div({className: "room-conversation-wrapper"}, 
           React.DOM.div({className: "beta-logo"}), 
           StandaloneRoomHeader(null), 
           StandaloneRoomInfoArea({roomState: this.state.roomState, 
                                   failureReason: this.state.failureReason, 
                                   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), 
               React.DOM.div({className: "media nested"}, 
                 React.DOM.span({className: "self-view-hidden-message"}, 
                   mozL10n.get("self_view_hidden_message")
                 ), 
                 React.DOM.div({className: "video_wrapper remote_wrapper"}, 
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
@@ -110,24 +110,30 @@ loop.standaloneRoomViews = (function(moz
               <p className="full-room-message">
                 {mozL10n.get("rooms_room_full_label")}
               </p>
               <p>{this._renderCallToActionLink()}</p>
             </div>
           );
         }
         case ROOM_STATES.ENDED: {
-          return (
-            <div className="ended-conversation">
-              <sharedViews.FeedbackView
-                feedbackStore={this.props.feedbackStore}
-                onAfterFeedbackReceived={this.onFeedbackSent}
-              />
-            </div>
-          );
+          if (this.props.roomUsed)
+            return (
+              <div className="ended-conversation">
+                <sharedViews.FeedbackView
+                  feedbackStore={this.props.feedbackStore}
+                  onAfterFeedbackReceived={this.onFeedbackSent}
+                />
+              </div>
+            );
+
+          // 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 (
             <div className="room-inner-info-area">
               <p className="failed-room-message">
                 {this._getFailureString()}
               </p>
               <button className="btn btn-join btn-info"
@@ -357,17 +363,18 @@ loop.standaloneRoomViews = (function(moz
         <div className="room-conversation-wrapper">
           <div className="beta-logo" />
           <StandaloneRoomHeader />
           <StandaloneRoomInfoArea roomState={this.state.roomState}
                                   failureReason={this.state.failureReason}
                                   joinRoom={this.joinRoom}
                                   helper={this.props.helper}
                                   activeRoomStore={this.props.activeRoomStore}
-                                  feedbackStore={this.props.feedbackStore} />
+                                  feedbackStore={this.props.feedbackStore}
+                                  roomUsed={this.state.used} />
           <div className="video-layout-wrapper">
             <div className="conversation room-conversation">
               <h2 className="room-name">{this.state.roomName}</h2>
               <div className="media nested">
                 <span className="self-view-hidden-message">
                   {mozL10n.get("self_view_hidden_message")}
                 </span>
                 <div className="video_wrapper remote_wrapper">
--- a/browser/components/loop/test/desktop-local/roomViews_test.js
+++ b/browser/components/loop/test/desktop-local/roomViews_test.js
@@ -59,16 +59,17 @@ describe("loop.roomViews", function () {
         roomStore: roomStore
       }));
 
       expect(testView.state).eql({
         roomState: ROOM_STATES.INIT,
         audioMuted: false,
         videoMuted: false,
         failureReason: undefined,
+        used: false,
         foo: "bar"
       });
     });
 
     it("should listen to store changes", function() {
       var TestView = React.createClass({
         mixins: [loop.roomViews.ActiveRoomStoreMixin],
         render: function() { return React.DOM.div(); }
@@ -350,23 +351,38 @@ describe("loop.roomViews", function () {
           view = mountTestComponent();
 
           TestUtils.findRenderedComponentWithType(view,
             loop.roomViews.DesktopRoomConversationView);
         });
 
       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() {
       it("should render local media as audio-only if video is muted",
         function() {
           activeRoomStore.setStoreState({
             roomState: ROOM_STATES.SESSION_CONNECTED,
             videoMuted: true
--- a/browser/components/loop/test/standalone/standaloneRoomViews_test.js
+++ b/browser/components/loop/test/standalone/standaloneRoomViews_test.js
@@ -295,17 +295,20 @@ describe("loop.standaloneRoomViews", fun
 
           sinon.assert.calledOnce(dispatch);
           sinon.assert.calledWithExactly(dispatch, new sharedActions.LeaveRoom());
         });
       });
 
       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",
           function() {
             expect(view.getDOMNode().querySelector(".faces")).not.eql(null);
           });
 
         it("should dispatch a `FeedbackComplete` action after feedback is sent",
@@ -313,16 +316,23 @@ describe("loop.standaloneRoomViews", fun
             feedbackStore.setStoreState({feedbackState: FEEDBACK_STATES.SENT});
 
             sandbox.clock.tick(
               loop.shared.views.WINDOW_AUTOCLOSE_TIMEOUT_IN_SECONDS * 1000 + 1000);
 
             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() {
         it("should render local media as audio-only if video is muted",
           function() {
             activeRoomStore.setStoreState({
               roomState: ROOM_STATES.SESSION_CONNECTED,
               videoMuted: true