Bug 1047406 - Remove notifications in Loop conversation window. r=Standard8
authorNicolas Perriault <nperriault@gmail.com>
Thu, 02 Oct 2014 19:55:21 +0100
changeset 218131 4d7e58f67c0a2b5f92fbc0987c8eb0f78652d093
parent 218130 16b361184a4518bb62b1d8d6b0f7a9e7c5e9ab2c
child 218132 4fda0b1548612cc3c7f3aa34180b88945e8c2dff
push id2
push usergszorc@mozilla.com
push dateWed, 12 Nov 2014 19:43:22 +0000
treeherderfig@7a5f4d72e05d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersStandard8
bugs1047406
milestone34.0a2
Bug 1047406 - Remove notifications in Loop conversation window. r=Standard8
browser/components/loop/content/js/conversation.js
browser/components/loop/content/js/conversation.jsx
browser/components/loop/test/desktop-local/conversation_test.js
--- a/browser/components/loop/content/js/conversation.js
+++ b/browser/components/loop/content/js/conversation.js
@@ -173,25 +173,24 @@ loop.conversation = (function(mozL10n) {
    *
    * At the moment, it does more than that, these parts need refactoring out.
    */
   var IncomingConversationView = React.createClass({displayName: 'IncomingConversationView',
     propTypes: {
       client: React.PropTypes.instanceOf(loop.Client).isRequired,
       conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
                          .isRequired,
-      notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
-                          .isRequired,
       sdk: React.PropTypes.object.isRequired
     },
 
     getInitialState: function() {
       return {
+        callFailed: false, // XXX this should be removed when bug 1047410 lands.
         callStatus: "start"
-      }
+      };
     },
 
     componentDidMount: function() {
       this.props.conversation.on("accept", this.accept, this);
       this.props.conversation.on("decline", this.decline, this);
       this.props.conversation.on("declineAndBlock", this.declineAndBlock, this);
       this.props.conversation.on("call:accepted", this.accepted, this);
       this.props.conversation.on("change:publishedStream", this._checkConnected, this);
@@ -238,17 +237,22 @@ loop.conversation = (function(mozL10n) {
               initiate: true, 
               sdk: this.props.sdk, 
               model: this.props.conversation, 
               video: {enabled: callType !== "audio"}}
             )
           );
         }
         case "end": {
-          document.title = mozL10n.get("conversation_has_ended");
+          // XXX To be handled with the "failed" view state when bug 1047410 lands
+          if (this.state.callFailed) {
+            document.title = mozL10n.get("generic_failure_title");
+          } else {
+            document.title = mozL10n.get("conversation_has_ended");
+          }
 
           var feebackAPIBaseUrl = navigator.mozLoop.getLoopCharPref(
             "feedback.baseUrl");
 
           var appVersionInfo = navigator.mozLoop.appVersionInfo;
 
           var feedbackClient = new loop.FeedbackAPIClient(feebackAPIBaseUrl, {
             product: navigator.mozLoop.getLoopCharPref("feedback.product"),
@@ -271,52 +275,52 @@ loop.conversation = (function(mozL10n) {
       }
     },
 
     /**
      * Notify the user that the connection was not possible
      * @param {{code: number, message: string}} error
      */
     _notifyError: function(error) {
+      // XXX Not the ideal response, but bug 1047410 will be replacing
+      // this by better "call failed" UI.
       console.error(error);
-      this.props.notifications.errorL10n("connection_error_see_console_notification");
-      this.setState({callStatus: "end"});
+      this.setState({callFailed: true, callStatus: "end"});
     },
 
     /**
      * Peer hung up. Notifies the user and ends the call.
      *
      * Event properties:
      * - {String} connectionId: OT session id
      */
     _onPeerHungup: function() {
-      this.props.notifications.warnL10n("peer_ended_conversation2");
-      this.setState({callStatus: "end"});
+      this.setState({callFailed: false, callStatus: "end"});
     },
 
     /**
      * Network disconnected. Notifies the user and ends the call.
      */
     _onNetworkDisconnected: function() {
-      this.props.notifications.warnL10n("network_disconnected");
-      this.setState({callStatus: "end"});
+      // XXX Not the ideal response, but bug 1047410 will be replacing
+      // this by better "call failed" UI.
+      this.setState({callFailed: true, callStatus: "end"});
     },
 
     /**
      * Incoming call route.
      */
     setupIncomingCall: function() {
       navigator.mozLoop.startAlerting();
 
       var callData = navigator.mozLoop.getCallData(this.props.conversation.get("callId"));
       if (!callData) {
-        console.error("Failed to get the call data");
         // XXX Not the ideal response, but bug 1047410 will be replacing
         // this by better "call failed" UI.
-        this.props.notifications.errorL10n("cannot_start_call_session_not_ready");
+        console.error("Failed to get the call data");
         return;
       }
       this.props.conversation.setIncomingSessionData(callData);
       this._setupWebSocket();
     },
 
     /**
      * Starts the actual conversation
@@ -455,32 +459,30 @@ loop.conversation = (function(mozL10n) {
     },
 
     /**
      * Handles a error starting the session
      */
     _handleSessionError: function() {
       // XXX Not the ideal response, but bug 1047410 will be replacing
       // this by better "call failed" UI.
-      this.props.notifications.errorL10n("cannot_start_call_session_not_ready");
+      console.error("Failed initiating the call session.");
     },
   });
 
   /**
    * Master controller view for handling if incoming or outgoing calls are
    * in progress, and hence, which view to display.
    */
   var ConversationControllerView = React.createClass({displayName: 'ConversationControllerView',
     propTypes: {
       // XXX Old types required for incoming call view.
       client: React.PropTypes.instanceOf(loop.Client).isRequired,
       conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
                          .isRequired,
-      notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
-                          .isRequired,
       sdk: React.PropTypes.object.isRequired,
 
       // XXX New types for OutgoingConversationView
       store: React.PropTypes.instanceOf(loop.store.ConversationStore).isRequired
     },
 
     getInitialState: function() {
       return this.props.store.attributes;
@@ -502,17 +504,16 @@ loop.conversation = (function(mozL10n) {
         return (OutgoingConversationView({
           store: this.props.store}
         ));
       }
 
       return (IncomingConversationView({
         client: this.props.client, 
         conversation: this.props.conversation, 
-        notifications: this.props.notifications, 
         sdk: this.props.sdk}
       ));
     }
   });
 
   /**
    * Panel initialisation.
    */
@@ -545,17 +546,16 @@ loop.conversation = (function(mozL10n) {
     var outgoingEmail = navigator.mozLoop.getLoopCharPref("outgoingemail");
 
     // XXX Old class creation for the incoming conversation view, whilst
     // we transition across (bug 1072323).
     var conversation = new sharedModels.ConversationModel(
       {},                // Model attributes
       {sdk: window.OT}   // Model dependencies
     );
-    var notifications = new sharedModels.NotificationCollection();
 
     // Obtain the callId and pass it through
     var helper = new loop.shared.utils.Helper();
     var locationHash = helper.locationHash();
     var callId;
     if (locationHash) {
       callId = locationHash.match(/\#incoming\/(.*)/)[1]
       conversation.set("callId", callId);
@@ -567,17 +567,16 @@ loop.conversation = (function(mozL10n) {
     });
 
     document.body.classList.add(loop.shared.utils.getTargetPlatform());
 
     React.renderComponent(ConversationControllerView({
       store: conversationStore, 
       client: client, 
       conversation: conversation, 
-      notifications: notifications, 
       sdk: window.OT}
     ), document.querySelector('#main'));
 
     dispatcher.dispatch(new loop.shared.actions.GatherCallData({
       callId: callId,
       calleeId: outgoingEmail
     }));
   }
--- a/browser/components/loop/content/js/conversation.jsx
+++ b/browser/components/loop/content/js/conversation.jsx
@@ -173,25 +173,24 @@ loop.conversation = (function(mozL10n) {
    *
    * At the moment, it does more than that, these parts need refactoring out.
    */
   var IncomingConversationView = React.createClass({
     propTypes: {
       client: React.PropTypes.instanceOf(loop.Client).isRequired,
       conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
                          .isRequired,
-      notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
-                          .isRequired,
       sdk: React.PropTypes.object.isRequired
     },
 
     getInitialState: function() {
       return {
+        callFailed: false, // XXX this should be removed when bug 1047410 lands.
         callStatus: "start"
-      }
+      };
     },
 
     componentDidMount: function() {
       this.props.conversation.on("accept", this.accept, this);
       this.props.conversation.on("decline", this.decline, this);
       this.props.conversation.on("declineAndBlock", this.declineAndBlock, this);
       this.props.conversation.on("call:accepted", this.accepted, this);
       this.props.conversation.on("change:publishedStream", this._checkConnected, this);
@@ -238,17 +237,22 @@ loop.conversation = (function(mozL10n) {
               initiate={true}
               sdk={this.props.sdk}
               model={this.props.conversation}
               video={{enabled: callType !== "audio"}}
             />
           );
         }
         case "end": {
-          document.title = mozL10n.get("conversation_has_ended");
+          // XXX To be handled with the "failed" view state when bug 1047410 lands
+          if (this.state.callFailed) {
+            document.title = mozL10n.get("generic_failure_title");
+          } else {
+            document.title = mozL10n.get("conversation_has_ended");
+          }
 
           var feebackAPIBaseUrl = navigator.mozLoop.getLoopCharPref(
             "feedback.baseUrl");
 
           var appVersionInfo = navigator.mozLoop.appVersionInfo;
 
           var feedbackClient = new loop.FeedbackAPIClient(feebackAPIBaseUrl, {
             product: navigator.mozLoop.getLoopCharPref("feedback.product"),
@@ -271,52 +275,52 @@ loop.conversation = (function(mozL10n) {
       }
     },
 
     /**
      * Notify the user that the connection was not possible
      * @param {{code: number, message: string}} error
      */
     _notifyError: function(error) {
+      // XXX Not the ideal response, but bug 1047410 will be replacing
+      // this by better "call failed" UI.
       console.error(error);
-      this.props.notifications.errorL10n("connection_error_see_console_notification");
-      this.setState({callStatus: "end"});
+      this.setState({callFailed: true, callStatus: "end"});
     },
 
     /**
      * Peer hung up. Notifies the user and ends the call.
      *
      * Event properties:
      * - {String} connectionId: OT session id
      */
     _onPeerHungup: function() {
-      this.props.notifications.warnL10n("peer_ended_conversation2");
-      this.setState({callStatus: "end"});
+      this.setState({callFailed: false, callStatus: "end"});
     },
 
     /**
      * Network disconnected. Notifies the user and ends the call.
      */
     _onNetworkDisconnected: function() {
-      this.props.notifications.warnL10n("network_disconnected");
-      this.setState({callStatus: "end"});
+      // XXX Not the ideal response, but bug 1047410 will be replacing
+      // this by better "call failed" UI.
+      this.setState({callFailed: true, callStatus: "end"});
     },
 
     /**
      * Incoming call route.
      */
     setupIncomingCall: function() {
       navigator.mozLoop.startAlerting();
 
       var callData = navigator.mozLoop.getCallData(this.props.conversation.get("callId"));
       if (!callData) {
-        console.error("Failed to get the call data");
         // XXX Not the ideal response, but bug 1047410 will be replacing
         // this by better "call failed" UI.
-        this.props.notifications.errorL10n("cannot_start_call_session_not_ready");
+        console.error("Failed to get the call data");
         return;
       }
       this.props.conversation.setIncomingSessionData(callData);
       this._setupWebSocket();
     },
 
     /**
      * Starts the actual conversation
@@ -455,32 +459,30 @@ loop.conversation = (function(mozL10n) {
     },
 
     /**
      * Handles a error starting the session
      */
     _handleSessionError: function() {
       // XXX Not the ideal response, but bug 1047410 will be replacing
       // this by better "call failed" UI.
-      this.props.notifications.errorL10n("cannot_start_call_session_not_ready");
+      console.error("Failed initiating the call session.");
     },
   });
 
   /**
    * Master controller view for handling if incoming or outgoing calls are
    * in progress, and hence, which view to display.
    */
   var ConversationControllerView = React.createClass({
     propTypes: {
       // XXX Old types required for incoming call view.
       client: React.PropTypes.instanceOf(loop.Client).isRequired,
       conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
                          .isRequired,
-      notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
-                          .isRequired,
       sdk: React.PropTypes.object.isRequired,
 
       // XXX New types for OutgoingConversationView
       store: React.PropTypes.instanceOf(loop.store.ConversationStore).isRequired
     },
 
     getInitialState: function() {
       return this.props.store.attributes;
@@ -502,17 +504,16 @@ loop.conversation = (function(mozL10n) {
         return (<OutgoingConversationView
           store={this.props.store}
         />);
       }
 
       return (<IncomingConversationView
         client={this.props.client}
         conversation={this.props.conversation}
-        notifications={this.props.notifications}
         sdk={this.props.sdk}
       />);
     }
   });
 
   /**
    * Panel initialisation.
    */
@@ -545,17 +546,16 @@ loop.conversation = (function(mozL10n) {
     var outgoingEmail = navigator.mozLoop.getLoopCharPref("outgoingemail");
 
     // XXX Old class creation for the incoming conversation view, whilst
     // we transition across (bug 1072323).
     var conversation = new sharedModels.ConversationModel(
       {},                // Model attributes
       {sdk: window.OT}   // Model dependencies
     );
-    var notifications = new sharedModels.NotificationCollection();
 
     // Obtain the callId and pass it through
     var helper = new loop.shared.utils.Helper();
     var locationHash = helper.locationHash();
     var callId;
     if (locationHash) {
       callId = locationHash.match(/\#incoming\/(.*)/)[1]
       conversation.set("callId", callId);
@@ -567,17 +567,16 @@ loop.conversation = (function(mozL10n) {
     });
 
     document.body.classList.add(loop.shared.utils.getTargetPlatform());
 
     React.renderComponent(<ConversationControllerView
       store={conversationStore}
       client={client}
       conversation={conversation}
-      notifications={notifications}
       sdk={window.OT}
     />, document.querySelector('#main'));
 
     dispatcher.dispatch(new loop.shared.actions.GatherCallData({
       callId: callId,
       calleeId: outgoingEmail
     }));
   }
--- a/browser/components/loop/test/desktop-local/conversation_test.js
+++ b/browser/components/loop/test/desktop-local/conversation_test.js
@@ -6,18 +6,17 @@
 
 var expect = chai.expect;
 
 describe("loop.conversation", function() {
   "use strict";
 
   var sharedModels = loop.shared.models,
       sharedView = loop.shared.views,
-      sandbox,
-      notifications;
+      sandbox;
 
   // XXX refactor to Just Work with "sandbox.stubComponent" or else
   // just pass in the sandbox and put somewhere generally usable
 
   function stubComponent(obj, component, mockTagName){
     var reactClass = React.createClass({
       render: function() {
         var mockTagName = mockTagName || "div";
@@ -25,17 +24,16 @@ describe("loop.conversation", function()
       }
     });
     return sandbox.stub(obj, component, reactClass);
   }
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
     sandbox.useFakeTimers();
-    notifications = new loop.shared.models.NotificationCollection();
 
     navigator.mozLoop = {
       doNotDisturb: true,
       getStrings: function() {
         return JSON.stringify({textContent: "fakeText"});
       },
       get locale() {
         return "en-US";
@@ -54,16 +52,19 @@ describe("loop.conversation", function()
           channel: "test",
           platform: "test"
         };
       }
     };
 
     // XXX These stubs should be hoisted in a common file
     // Bug 1040968
+    sandbox.stub(document.mozL10n, "get", function(x) {
+      return x;
+    });
     document.mozL10n.initialize(navigator.mozLoop);
   });
 
   afterEach(function() {
     delete navigator.mozLoop;
     sandbox.restore();
   });
 
@@ -123,17 +124,16 @@ describe("loop.conversation", function()
   describe("ConversationControllerView", function() {
     var store, conversation, client, ccView, oldTitle, dispatcher;
 
     function mountTestComponent() {
       return TestUtils.renderIntoDocument(
         loop.conversation.ConversationControllerView({
           client: client,
           conversation: conversation,
-          notifications: notifications,
           sdk: {},
           store: store
         }));
     }
 
     beforeEach(function() {
       oldTitle = document.title;
       client = new loop.Client();
@@ -174,17 +174,16 @@ describe("loop.conversation", function()
   describe("IncomingConversationView", function() {
     var conversation, client, icView, oldTitle;
 
     function mountTestComponent() {
       return TestUtils.renderIntoDocument(
         loop.conversation.IncomingConversationView({
           client: client,
           conversation: conversation,
-          notifications: notifications,
           sdk: {}
         }));
     }
 
     beforeEach(function() {
       oldTitle = document.title;
       client = new loop.Client();
       conversation = new loop.shared.models.ConversationModel({}, {
@@ -196,19 +195,23 @@ describe("loop.conversation", function()
 
     afterEach(function() {
       icView = undefined;
       document.title = oldTitle;
     });
 
     describe("start", function() {
       it("should set the title to incoming_call_title2", function() {
-        sandbox.stub(document.mozL10n, "get", function(x) {
-          return x;
-        });
+        navigator.mozLoop.getCallData = function() {
+          return {
+            progressURL:    "fake",
+            websocketToken: "fake",
+            callId: 42
+          };
+        };
 
         icView = mountTestComponent();
 
         expect(document.title).eql("incoming_call_title2");
       });
     });
 
     describe("componentDidMount", function() {
@@ -309,27 +312,23 @@ describe("loop.conversation", function()
               resolveWebSocketConnect("terminated");
 
               promise.then(function () {
                 expect(icView.state.callStatus).eql("close");
                 done();
               });
             });
 
-          it("should display an error if the websocket failed to connect", function(done) {
-            sandbox.stub(notifications, "errorL10n");
-
+          // XXX implement me as part of bug 1047410
+          // see https://hg.mozilla.org/integration/fx-team/rev/5d2c69ebb321#l18.259
+          it.skip("should should switch view state to failed", function(done) {
             icView = mountTestComponent();
             rejectWebSocketConnect();
 
-            promise.then(function() {
-            }, function () {
-              sinon.assert.calledOnce(notifications.errorL10n);
-              sinon.assert.calledWithExactly(notifications.errorL10n,
-                "cannot_start_call_session_not_ready");
+            promise.then(function() {}, function() {
               done();
             });
           });
         });
 
         describe("WebSocket Events", function() {
           describe("Call cancelled or timed out before acceptance", function() {
             beforeEach(function() {
@@ -603,24 +602,31 @@ describe("loop.conversation", function()
           function() {
             conversation.trigger("session:peer-hungup");
 
               TestUtils.findRenderedComponentWithType(icView,
                 sharedView.FeedbackView);
           });
       });
 
-      describe("session:peer-hungup", function() {
+      describe("session:network-disconnected", function() {
         it("should navigate to call/feedback when network disconnects",
           function() {
             conversation.trigger("session:network-disconnected");
 
               TestUtils.findRenderedComponentWithType(icView,
                 sharedView.FeedbackView);
           });
+
+        it("should update the conversation window toolbar title",
+          function() {
+            conversation.trigger("session:network-disconnected");
+
+            expect(document.title).eql("generic_failure_title");
+          });
       });
 
       describe("Published and Subscribed Streams", function() {
         beforeEach(function() {
           icView._websocket = {
             mediaUp: sinon.spy()
           };
         });