Bug 1088672 - Part 7. Rewrite Loop's incoming call handling in the flux style. Remove the now redundant non-flux based code for incoming calls. r=mikedeboer
authorMark Banner <standard8@mozilla.com>
Thu, 12 Mar 2015 14:01:38 +0000
changeset 233271 9303663e42d3091197bb1283776f92d1f8857b02
parent 233270 ea81bbb5d288cf80edb126c6d75a45f4b19fa53c
child 233272 c154c877d4e473084f4d436f564e14594584739f
push id28409
push userryanvm@gmail.com
push dateThu, 12 Mar 2015 21:55:43 +0000
treeherdermozilla-central@849053cb635d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmikedeboer
bugs1088672
milestone39.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 1088672 - Part 7. Rewrite Loop's incoming call handling in the flux style. Remove the now redundant non-flux based code for incoming calls. r=mikedeboer
browser/components/loop/content/conversation.html
browser/components/loop/content/js/client.js
browser/components/loop/content/js/conversation.js
browser/components/loop/content/js/conversation.jsx
browser/components/loop/content/js/conversationAppStore.js
browser/components/loop/content/js/conversationViews.js
browser/components/loop/content/js/conversationViews.jsx
browser/components/loop/test/desktop-local/client_test.js
browser/components/loop/test/desktop-local/conversationAppStore_test.js
browser/components/loop/test/desktop-local/conversationViews_test.js
--- a/browser/components/loop/content/conversation.html
+++ b/browser/components/loop/content/conversation.html
@@ -21,17 +21,16 @@
     <script type="text/javascript" src="loop/js/otconfig.js"></script>
     <script type="text/javascript" src="loop/libs/sdk.js"></script>
     <script type="text/javascript" src="loop/shared/libs/react-0.12.2.js"></script>
     <script type="text/javascript" src="loop/shared/libs/jquery-2.1.0.js"></script>
     <script type="text/javascript" src="loop/shared/libs/lodash-2.4.1.js"></script>
     <script type="text/javascript" src="loop/shared/libs/backbone-1.1.2.js"></script>
 
     <script type="text/javascript" src="loop/shared/js/utils.js"></script>
-    <script type="text/javascript" src="loop/shared/js/models.js"></script>
     <script type="text/javascript" src="loop/shared/js/mixins.js"></script>
     <script type="text/javascript" src="loop/shared/js/feedbackApiClient.js"></script>
     <script type="text/javascript" src="loop/shared/js/actions.js"></script>
     <script type="text/javascript" src="loop/shared/js/validate.js"></script>
     <script type="text/javascript" src="loop/shared/js/dispatcher.js"></script>
     <script type="text/javascript" src="loop/shared/js/otSdkDriver.js"></script>
     <script type="text/javascript" src="loop/shared/js/store.js"></script>
     <script type="text/javascript" src="loop/shared/js/roomStore.js"></script>
--- a/browser/components/loop/content/js/client.js
+++ b/browser/components/loop/content/js/client.js
@@ -71,47 +71,16 @@ loop.Client = (function($) {
      */
     _failureHandler: function(cb, error) {
       var message = "HTTP " + error.code + " " + error.error + "; " + error.message;
       console.error(message);
       cb(error);
     },
 
     /**
-     * Block call URL based on the token identifier
-     *
-     * @param {string} token Conversation identifier used to block the URL
-     * @param {mozLoop.LOOP_SESSION_TYPE} sessionType The type of session which
-     *                                                the url belongs to.
-     * @param {function} cb Callback function used for handling an error
-     *                      response. XXX The incoming call panel does not
-     *                      exist after the block button is clicked therefore
-     *                      it does not make sense to display an error.
-     **/
-    deleteCallUrl: function(token, sessionType, cb) {
-      function deleteRequestCallback(error, responseText) {
-        if (error) {
-          this._failureHandler(cb, error);
-          return;
-        }
-
-        try {
-          cb(null);
-        } catch (err) {
-          console.log("Error deleting call info", err);
-          cb(err);
-        }
-      }
-
-      this.mozLoop.hawkRequest(sessionType,
-                               "/call-url/" + token, "DELETE", null,
-                               deleteRequestCallback.bind(this));
-    },
-
-    /**
      * Sets up an outgoing call, getting the relevant data from the server.
      *
      * Callback parameters:
      * - err null on successful registration, non-null otherwise.
      * - result an object of the obtained data for starting the call, if successful
      *
      * @param {Array} calleeIds an array of emails and phone numbers.
      * @param {String} callType the type of call.
--- a/browser/components/loop/content/js/conversation.js
+++ b/browser/components/loop/content/js/conversation.js
@@ -11,17 +11,16 @@ var loop = loop || {};
 loop.conversation = (function(mozL10n) {
   "use strict";
 
   var sharedViews = loop.shared.views;
   var sharedMixins = loop.shared.mixins;
   var sharedModels = loop.shared.models;
   var sharedActions = loop.shared.actions;
 
-  var IncomingConversationView = loop.conversationViews.IncomingConversationView;
   var CallControllerView = loop.conversationViews.CallControllerView;
   var CallIdentifierView = loop.conversationViews.CallIdentifierView;
   var DesktopRoomConversationView = loop.roomViews.DesktopRoomConversationView;
   var GenericFailureView = loop.conversationViews.GenericFailureView;
 
   /**
    * Master controller view for handling if incoming or outgoing calls are
    * in progress, and hence, which view to display.
--- a/browser/components/loop/content/js/conversation.jsx
+++ b/browser/components/loop/content/js/conversation.jsx
@@ -11,17 +11,16 @@ var loop = loop || {};
 loop.conversation = (function(mozL10n) {
   "use strict";
 
   var sharedViews = loop.shared.views;
   var sharedMixins = loop.shared.mixins;
   var sharedModels = loop.shared.models;
   var sharedActions = loop.shared.actions;
 
-  var IncomingConversationView = loop.conversationViews.IncomingConversationView;
   var CallControllerView = loop.conversationViews.CallControllerView;
   var CallIdentifierView = loop.conversationViews.CallIdentifierView;
   var DesktopRoomConversationView = loop.roomViews.DesktopRoomConversationView;
   var GenericFailureView = loop.conversationViews.GenericFailureView;
 
   /**
    * Master controller view for handling if incoming or outgoing calls are
    * in progress, and hence, which view to display.
--- a/browser/components/loop/content/js/conversationAppStore.js
+++ b/browser/components/loop/content/js/conversationAppStore.js
@@ -64,22 +64,17 @@ loop.store.ConversationAppStore = (funct
       var windowData = this._mozLoop.getConversationWindowData(actionData.windowId);
 
       if (!windowData) {
         console.error("Failed to get the window data");
         this.setStoreState({windowType: "failed"});
         return;
       }
 
-      // XXX windowData is a hack for the IncomingConversationView until
-      // we rework it for the flux model in bug 1088672.
-      this.setStoreState({
-        windowType: windowData.type,
-        windowData: windowData
-      });
+      this.setStoreState({windowType: windowData.type});
 
       this._dispatcher.dispatch(new loop.shared.actions.SetupWindowData(_.extend({
         windowId: actionData.windowId}, windowData)));
     }
   }, Backbone.Events);
 
   return ConversationAppStore;
 
--- a/browser/components/loop/content/js/conversationViews.js
+++ b/browser/components/loop/content/js/conversationViews.js
@@ -309,19 +309,16 @@ loop.conversationViews = (function(mozL1
         )
         /* jshint ignore:end */
       );
     }
   });
 
   /**
    * Something went wrong view. Displayed when there's a big problem.
-   *
-   * XXX Based on CallFailedView, but built specially until we flux-ify the
-   * incoming call views (bug 1088672).
    */
   var GenericFailureView = React.createClass({displayName: "GenericFailureView",
     mixins: [sharedMixins.AudioMixin],
 
     propTypes: {
       cancelCall: React.PropTypes.func.isRequired
     },
 
@@ -343,337 +340,16 @@ loop.conversationViews = (function(mozL1
             )
           )
         )
       );
     }
   });
 
   /**
-   * This view manages the incoming conversation views - from
-   * call initiation through to the actual conversation and call end.
-   *
-   * At the moment, it does more than that, these parts need refactoring out.
-   */
-  var IncomingConversationView = React.createClass({displayName: "IncomingConversationView",
-    mixins: [sharedMixins.AudioMixin, sharedMixins.WindowCloseMixin],
-
-    propTypes: {
-      client: React.PropTypes.instanceOf(loop.Client).isRequired,
-      conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
-                         .isRequired,
-      sdk: React.PropTypes.object.isRequired,
-      isDesktop: React.PropTypes.bool,
-      conversationAppStore: React.PropTypes.instanceOf(
-        loop.store.ConversationAppStore).isRequired
-    },
-
-    getDefaultProps: function() {
-      return {
-        isDesktop: false
-      };
-    },
-
-    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);
-      this.props.conversation.on("change:subscribedStream", this._checkConnected, this);
-      this.props.conversation.on("session:ended", this.endCall, this);
-      this.props.conversation.on("session:peer-hungup", this._onPeerHungup, this);
-      this.props.conversation.on("session:network-disconnected", this._onNetworkDisconnected, this);
-      this.props.conversation.on("session:connection-error", this._notifyError, this);
-
-      this.setupIncomingCall();
-    },
-
-    componentDidUnmount: function() {
-      this.props.conversation.off(null, null, this);
-    },
-
-    render: function() {
-      switch (this.state.callStatus) {
-        case "start": {
-          document.title = mozL10n.get("incoming_call_title2");
-
-          // XXX Don't render anything initially, though this should probably
-          // be some sort of pending view, whilst we connect the websocket.
-          return null;
-        }
-        case "incoming": {
-          document.title = mozL10n.get("incoming_call_title2");
-
-          return (
-            React.createElement(AcceptCallView, {
-              model: this.props.conversation, 
-              video: this.props.conversation.hasVideoStream("incoming")}
-            )
-          );
-        }
-        case "connected": {
-          document.title = this.props.conversation.getCallIdentifier();
-
-          var callType = this.props.conversation.get("selectedCallType");
-
-          return (
-            React.createElement(sharedViews.ConversationView, {
-              isDesktop: this.props.isDesktop, 
-              initiate: true, 
-              sdk: this.props.sdk, 
-              model: this.props.conversation, 
-              video: {enabled: callType !== "audio"}}
-            )
-          );
-        }
-        case "end": {
-          // XXX To be handled with the "failed" view state when bug 1047410 lands
-          if (this.state.callFailed) {
-            return React.createElement(GenericFailureView, {
-              cancelCall: this.closeWindow.bind(this)}
-            );
-          }
-
-          document.title = mozL10n.get("conversation_has_ended");
-
-          this.play("terminated");
-
-          return (
-            React.createElement(sharedViews.FeedbackView, {
-              onAfterFeedbackReceived: this.closeWindow.bind(this)}
-            )
-          );
-        }
-        case "close": {
-          this.closeWindow();
-          return (React.createElement("div", null));
-        }
-      }
-    },
-
-    /**
-     * 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.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.setState({callFailed: false, callStatus: "end"});
-    },
-
-    /**
-     * Network disconnected. Notifies the user and ends the call.
-     */
-    _onNetworkDisconnected: function() {
-      // 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();
-
-      // XXX This is a hack until we rework for the flux model in bug 1088672.
-      var callData = this.props.conversationAppStore.getStoreState().windowData;
-
-      this.props.conversation.setIncomingSessionData(callData);
-      this._setupWebSocket();
-    },
-
-    /**
-     * Starts the actual conversation
-     */
-    accepted: function() {
-      this.setState({callStatus: "connected"});
-    },
-
-    /**
-     * Moves the call to the end state
-     */
-    endCall: function() {
-      navigator.mozLoop.calls.clearCallInProgress(
-        this.props.conversation.get("windowId"));
-      this.setState({callStatus: "end"});
-    },
-
-    /**
-     * Used to set up the web socket connection and navigate to the
-     * call view if appropriate.
-     */
-    _setupWebSocket: function() {
-      this._websocket = new loop.CallConnectionWebSocket({
-        url: this.props.conversation.get("progressURL"),
-        websocketToken: this.props.conversation.get("websocketToken"),
-        callId: this.props.conversation.get("callId"),
-      });
-      this._websocket.promiseConnect().then(function(progressStatus) {
-        this.setState({
-          callStatus: progressStatus === "terminated" ? "close" : "incoming"
-        });
-      }.bind(this), function() {
-        this._handleSessionError();
-        return;
-      }.bind(this));
-
-      this._websocket.on("progress", this._handleWebSocketProgress, this);
-    },
-
-    /**
-     * Checks if the streams have been connected, and notifies the
-     * websocket that the media is now connected.
-     */
-    _checkConnected: function() {
-      // Check we've had both local and remote streams connected before
-      // sending the media up message.
-      if (this.props.conversation.streamsConnected()) {
-        this._websocket.mediaUp();
-      }
-    },
-
-    /**
-     * Used to receive websocket progress and to determine how to handle
-     * it if appropraite.
-     * If we add more cases here, then we should refactor this function.
-     *
-     * @param {Object} progressData The progress data from the websocket.
-     * @param {String} previousState The previous state from the websocket.
-     */
-    _handleWebSocketProgress: function(progressData, previousState) {
-      // We only care about the terminated state at the moment.
-      if (progressData.state !== "terminated")
-        return;
-
-      // XXX This would be nicer in the _abortIncomingCall function, but we need to stop
-      // it here for now due to server-side issues that are being fixed in bug 1088351.
-      // This is before the abort call to ensure that it happens before the window is
-      // closed.
-      navigator.mozLoop.stopAlerting();
-
-      // If we hit any of the termination reasons, and the user hasn't accepted
-      // then it seems reasonable to close the window/abort the incoming call.
-      //
-      // If the user has accepted the call, and something's happened, display
-      // the call failed view.
-      //
-      // https://wiki.mozilla.org/Loop/Architecture/MVP#Termination_Reasons
-      if (previousState === "init" || previousState === "alerting") {
-        this._abortIncomingCall();
-      } else {
-        this.setState({callFailed: true, callStatus: "end"});
-      }
-
-    },
-
-    /**
-     * Silently aborts an incoming call - stops the alerting, and
-     * closes the websocket.
-     */
-    _abortIncomingCall: function() {
-      this._websocket.close();
-      // Having a timeout here lets the logging for the websocket complete and be
-      // displayed on the console if both are on.
-      setTimeout(this.closeWindow, 0);
-    },
-
-    /**
-     * Accepts an incoming call.
-     */
-    accept: function() {
-      navigator.mozLoop.stopAlerting();
-      this._websocket.accept();
-      this.props.conversation.accepted();
-    },
-
-    /**
-     * Declines a call and handles closing of the window.
-     */
-    _declineCall: function() {
-      this._websocket.decline();
-      navigator.mozLoop.calls.clearCallInProgress(
-        this.props.conversation.get("windowId"));
-      this._websocket.close();
-      // Having a timeout here lets the logging for the websocket complete and be
-      // displayed on the console if both are on.
-      setTimeout(this.closeWindow, 0);
-    },
-
-    /**
-     * Declines an incoming call.
-     */
-    decline: function() {
-      navigator.mozLoop.stopAlerting();
-      this._declineCall();
-    },
-
-    /**
-     * Decline and block an incoming call
-     * @note:
-     * - loopToken is the callUrl identifier. It gets set in the panel
-     *   after a callUrl is received
-     */
-    declineAndBlock: function() {
-      navigator.mozLoop.stopAlerting();
-      var token = this.props.conversation.get("callToken");
-      var callerId = this.props.conversation.get("callerId");
-
-      // If this is a direct call, we'll need to block the caller directly.
-      if (callerId && EMAIL_OR_PHONE_RE.test(callerId)) {
-        navigator.mozLoop.calls.blockDirectCaller(callerId, function(err) {
-          // XXX The conversation window will be closed when this cb is triggered
-          // figure out if there is a better way to report the error to the user
-          // (bug 1103150).
-          console.log(err.fileName + ":" + err.lineNumber + ": " + err.message);
-        });
-      } else {
-        this.props.client.deleteCallUrl(token,
-          this.props.conversation.get("sessionType"),
-          function(error) {
-            // XXX The conversation window will be closed when this cb is triggered
-            // figure out if there is a better way to report the error to the user
-            // (bug 1048909).
-            console.log(error);
-          });
-      }
-
-      this._declineCall();
-    },
-
-    /**
-     * 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.
-      console.error("Failed initiating the call session.");
-    },
-  });
-
-  /**
    * View for pending conversations. Displays a cancel button and appropriate
    * pending/ringing strings.
    */
   var PendingConversationView = React.createClass({displayName: "PendingConversationView",
     mixins: [sharedMixins.AudioMixin],
 
     propTypes: {
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
@@ -1078,14 +754,13 @@ loop.conversationViews = (function(mozL1
   return {
     PendingConversationView: PendingConversationView,
     CallIdentifierView: CallIdentifierView,
     ConversationDetailView: ConversationDetailView,
     CallFailedView: CallFailedView,
     _getContactDisplayName: _getContactDisplayName,
     GenericFailureView: GenericFailureView,
     AcceptCallView: AcceptCallView,
-    IncomingConversationView: IncomingConversationView,
     OngoingConversationView: OngoingConversationView,
     CallControllerView: CallControllerView
   };
 
 })(document.mozL10n || navigator.mozL10n);
--- a/browser/components/loop/content/js/conversationViews.jsx
+++ b/browser/components/loop/content/js/conversationViews.jsx
@@ -309,19 +309,16 @@ loop.conversationViews = (function(mozL1
         </div>
         /* jshint ignore:end */
       );
     }
   });
 
   /**
    * Something went wrong view. Displayed when there's a big problem.
-   *
-   * XXX Based on CallFailedView, but built specially until we flux-ify the
-   * incoming call views (bug 1088672).
    */
   var GenericFailureView = React.createClass({
     mixins: [sharedMixins.AudioMixin],
 
     propTypes: {
       cancelCall: React.PropTypes.func.isRequired
     },
 
@@ -343,337 +340,16 @@ loop.conversationViews = (function(mozL1
             </button>
           </div>
         </div>
       );
     }
   });
 
   /**
-   * This view manages the incoming conversation views - from
-   * call initiation through to the actual conversation and call end.
-   *
-   * At the moment, it does more than that, these parts need refactoring out.
-   */
-  var IncomingConversationView = React.createClass({
-    mixins: [sharedMixins.AudioMixin, sharedMixins.WindowCloseMixin],
-
-    propTypes: {
-      client: React.PropTypes.instanceOf(loop.Client).isRequired,
-      conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
-                         .isRequired,
-      sdk: React.PropTypes.object.isRequired,
-      isDesktop: React.PropTypes.bool,
-      conversationAppStore: React.PropTypes.instanceOf(
-        loop.store.ConversationAppStore).isRequired
-    },
-
-    getDefaultProps: function() {
-      return {
-        isDesktop: false
-      };
-    },
-
-    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);
-      this.props.conversation.on("change:subscribedStream", this._checkConnected, this);
-      this.props.conversation.on("session:ended", this.endCall, this);
-      this.props.conversation.on("session:peer-hungup", this._onPeerHungup, this);
-      this.props.conversation.on("session:network-disconnected", this._onNetworkDisconnected, this);
-      this.props.conversation.on("session:connection-error", this._notifyError, this);
-
-      this.setupIncomingCall();
-    },
-
-    componentDidUnmount: function() {
-      this.props.conversation.off(null, null, this);
-    },
-
-    render: function() {
-      switch (this.state.callStatus) {
-        case "start": {
-          document.title = mozL10n.get("incoming_call_title2");
-
-          // XXX Don't render anything initially, though this should probably
-          // be some sort of pending view, whilst we connect the websocket.
-          return null;
-        }
-        case "incoming": {
-          document.title = mozL10n.get("incoming_call_title2");
-
-          return (
-            <AcceptCallView
-              model={this.props.conversation}
-              video={this.props.conversation.hasVideoStream("incoming")}
-            />
-          );
-        }
-        case "connected": {
-          document.title = this.props.conversation.getCallIdentifier();
-
-          var callType = this.props.conversation.get("selectedCallType");
-
-          return (
-            <sharedViews.ConversationView
-              isDesktop={this.props.isDesktop}
-              initiate={true}
-              sdk={this.props.sdk}
-              model={this.props.conversation}
-              video={{enabled: callType !== "audio"}}
-            />
-          );
-        }
-        case "end": {
-          // XXX To be handled with the "failed" view state when bug 1047410 lands
-          if (this.state.callFailed) {
-            return <GenericFailureView
-              cancelCall={this.closeWindow.bind(this)}
-            />;
-          }
-
-          document.title = mozL10n.get("conversation_has_ended");
-
-          this.play("terminated");
-
-          return (
-            <sharedViews.FeedbackView
-              onAfterFeedbackReceived={this.closeWindow.bind(this)}
-            />
-          );
-        }
-        case "close": {
-          this.closeWindow();
-          return (<div/>);
-        }
-      }
-    },
-
-    /**
-     * 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.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.setState({callFailed: false, callStatus: "end"});
-    },
-
-    /**
-     * Network disconnected. Notifies the user and ends the call.
-     */
-    _onNetworkDisconnected: function() {
-      // 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();
-
-      // XXX This is a hack until we rework for the flux model in bug 1088672.
-      var callData = this.props.conversationAppStore.getStoreState().windowData;
-
-      this.props.conversation.setIncomingSessionData(callData);
-      this._setupWebSocket();
-    },
-
-    /**
-     * Starts the actual conversation
-     */
-    accepted: function() {
-      this.setState({callStatus: "connected"});
-    },
-
-    /**
-     * Moves the call to the end state
-     */
-    endCall: function() {
-      navigator.mozLoop.calls.clearCallInProgress(
-        this.props.conversation.get("windowId"));
-      this.setState({callStatus: "end"});
-    },
-
-    /**
-     * Used to set up the web socket connection and navigate to the
-     * call view if appropriate.
-     */
-    _setupWebSocket: function() {
-      this._websocket = new loop.CallConnectionWebSocket({
-        url: this.props.conversation.get("progressURL"),
-        websocketToken: this.props.conversation.get("websocketToken"),
-        callId: this.props.conversation.get("callId"),
-      });
-      this._websocket.promiseConnect().then(function(progressStatus) {
-        this.setState({
-          callStatus: progressStatus === "terminated" ? "close" : "incoming"
-        });
-      }.bind(this), function() {
-        this._handleSessionError();
-        return;
-      }.bind(this));
-
-      this._websocket.on("progress", this._handleWebSocketProgress, this);
-    },
-
-    /**
-     * Checks if the streams have been connected, and notifies the
-     * websocket that the media is now connected.
-     */
-    _checkConnected: function() {
-      // Check we've had both local and remote streams connected before
-      // sending the media up message.
-      if (this.props.conversation.streamsConnected()) {
-        this._websocket.mediaUp();
-      }
-    },
-
-    /**
-     * Used to receive websocket progress and to determine how to handle
-     * it if appropraite.
-     * If we add more cases here, then we should refactor this function.
-     *
-     * @param {Object} progressData The progress data from the websocket.
-     * @param {String} previousState The previous state from the websocket.
-     */
-    _handleWebSocketProgress: function(progressData, previousState) {
-      // We only care about the terminated state at the moment.
-      if (progressData.state !== "terminated")
-        return;
-
-      // XXX This would be nicer in the _abortIncomingCall function, but we need to stop
-      // it here for now due to server-side issues that are being fixed in bug 1088351.
-      // This is before the abort call to ensure that it happens before the window is
-      // closed.
-      navigator.mozLoop.stopAlerting();
-
-      // If we hit any of the termination reasons, and the user hasn't accepted
-      // then it seems reasonable to close the window/abort the incoming call.
-      //
-      // If the user has accepted the call, and something's happened, display
-      // the call failed view.
-      //
-      // https://wiki.mozilla.org/Loop/Architecture/MVP#Termination_Reasons
-      if (previousState === "init" || previousState === "alerting") {
-        this._abortIncomingCall();
-      } else {
-        this.setState({callFailed: true, callStatus: "end"});
-      }
-
-    },
-
-    /**
-     * Silently aborts an incoming call - stops the alerting, and
-     * closes the websocket.
-     */
-    _abortIncomingCall: function() {
-      this._websocket.close();
-      // Having a timeout here lets the logging for the websocket complete and be
-      // displayed on the console if both are on.
-      setTimeout(this.closeWindow, 0);
-    },
-
-    /**
-     * Accepts an incoming call.
-     */
-    accept: function() {
-      navigator.mozLoop.stopAlerting();
-      this._websocket.accept();
-      this.props.conversation.accepted();
-    },
-
-    /**
-     * Declines a call and handles closing of the window.
-     */
-    _declineCall: function() {
-      this._websocket.decline();
-      navigator.mozLoop.calls.clearCallInProgress(
-        this.props.conversation.get("windowId"));
-      this._websocket.close();
-      // Having a timeout here lets the logging for the websocket complete and be
-      // displayed on the console if both are on.
-      setTimeout(this.closeWindow, 0);
-    },
-
-    /**
-     * Declines an incoming call.
-     */
-    decline: function() {
-      navigator.mozLoop.stopAlerting();
-      this._declineCall();
-    },
-
-    /**
-     * Decline and block an incoming call
-     * @note:
-     * - loopToken is the callUrl identifier. It gets set in the panel
-     *   after a callUrl is received
-     */
-    declineAndBlock: function() {
-      navigator.mozLoop.stopAlerting();
-      var token = this.props.conversation.get("callToken");
-      var callerId = this.props.conversation.get("callerId");
-
-      // If this is a direct call, we'll need to block the caller directly.
-      if (callerId && EMAIL_OR_PHONE_RE.test(callerId)) {
-        navigator.mozLoop.calls.blockDirectCaller(callerId, function(err) {
-          // XXX The conversation window will be closed when this cb is triggered
-          // figure out if there is a better way to report the error to the user
-          // (bug 1103150).
-          console.log(err.fileName + ":" + err.lineNumber + ": " + err.message);
-        });
-      } else {
-        this.props.client.deleteCallUrl(token,
-          this.props.conversation.get("sessionType"),
-          function(error) {
-            // XXX The conversation window will be closed when this cb is triggered
-            // figure out if there is a better way to report the error to the user
-            // (bug 1048909).
-            console.log(error);
-          });
-      }
-
-      this._declineCall();
-    },
-
-    /**
-     * 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.
-      console.error("Failed initiating the call session.");
-    },
-  });
-
-  /**
    * View for pending conversations. Displays a cancel button and appropriate
    * pending/ringing strings.
    */
   var PendingConversationView = React.createClass({
     mixins: [sharedMixins.AudioMixin],
 
     propTypes: {
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
@@ -1078,14 +754,13 @@ loop.conversationViews = (function(mozL1
   return {
     PendingConversationView: PendingConversationView,
     CallIdentifierView: CallIdentifierView,
     ConversationDetailView: ConversationDetailView,
     CallFailedView: CallFailedView,
     _getContactDisplayName: _getContactDisplayName,
     GenericFailureView: GenericFailureView,
     AcceptCallView: AcceptCallView,
-    IncomingConversationView: IncomingConversationView,
     OngoingConversationView: OngoingConversationView,
     CallControllerView: CallControllerView
   };
 
 })(document.mozL10n || navigator.mozL10n);
--- a/browser/components/loop/test/desktop-local/client_test.js
+++ b/browser/components/loop/test/desktop-local/client_test.js
@@ -47,52 +47,16 @@ describe("loop.Client", function() {
     });
   });
 
   afterEach(function() {
     sandbox.restore();
   });
 
   describe("loop.Client", function() {
-    describe("#deleteCallUrl", function() {
-      it("should make a delete call to /call-url/{fakeToken}", function() {
-        client.deleteCallUrl(fakeToken, mozLoop.LOOP_SESSION_TYPE.GUEST, callback);
-
-        sinon.assert.calledOnce(hawkRequestStub);
-        sinon.assert.calledWith(hawkRequestStub,
-                                mozLoop.LOOP_SESSION_TYPE.GUEST,
-                                "/call-url/" + fakeToken, "DELETE");
-      });
-
-      it("should call the callback with null when the request succeeds",
-         function() {
-
-           // Sets up the hawkRequest stub to trigger the callback with no error
-           // and the url.
-           hawkRequestStub.callsArgWith(4, null);
-
-           client.deleteCallUrl(fakeToken, mozLoop.LOOP_SESSION_TYPE.FXA, callback);
-
-           sinon.assert.calledWithExactly(callback, null);
-         });
-
-      it("should send an error when the request fails", function() {
-        // Sets up the hawkRequest stub to trigger the callback with
-        // an error
-        hawkRequestStub.callsArgWith(4, fakeErrorRes);
-
-        client.deleteCallUrl(fakeToken, mozLoop.LOOP_SESSION_TYPE.FXA, callback);
-
-        sinon.assert.calledOnce(callback);
-        sinon.assert.calledWithMatch(callback, sinon.match(function(err) {
-          return err.code == 400 && "invalid token" == err.message;
-        }));
-      });
-    });
-
     describe("#setupOutgoingCall", function() {
       var calleeIds, callType;
 
       beforeEach(function() {
         calleeIds = [
           "fakeemail", "fake phone"
         ];
         callType = "audio";
--- a/browser/components/loop/test/desktop-local/conversationAppStore_test.js
+++ b/browser/components/loop/test/desktop-local/conversationAppStore_test.js
@@ -58,18 +58,17 @@ describe("loop.store.ConversationAppStor
         mozLoop: fakeMozLoop
       });
     });
 
     it("should fetch the window type from the mozLoop API", function() {
       dispatcher.dispatch(new sharedActions.GetWindowData(fakeGetWindowData));
 
       expect(store.getStoreState()).eql({
-        windowType: "incoming",
-        windowData: fakeWindowData
+        windowType: "incoming"
       });
     });
 
     it("should dispatch a SetupWindowData action with the data from the mozLoop API",
       function() {
         sandbox.stub(dispatcher, "dispatch");
 
         store.getWindowData(new sharedActions.GetWindowData(fakeGetWindowData));
--- a/browser/components/loop/test/desktop-local/conversationViews_test.js
+++ b/browser/components/loop/test/desktop-local/conversationViews_test.js
@@ -713,538 +713,16 @@ describe("loop.conversationViews", funct
 
         store.setStoreState({callState: CALL_STATES.TERMINATED});
 
         TestUtils.findRenderedComponentWithType(view,
           loop.conversationViews.CallFailedView);
     });
   });
 
-  describe("IncomingConversationView", function() {
-    var conversationAppStore, conversation, client, icView, oldTitle,
-        feedbackStore;
-
-    function mountTestComponent() {
-      return TestUtils.renderIntoDocument(
-        React.createElement(loop.conversationViews.IncomingConversationView, {
-          client: client,
-          conversation: conversation,
-          sdk: {},
-          conversationAppStore: conversationAppStore
-        }));
-    }
-
-    beforeEach(function() {
-      oldTitle = document.title;
-      client = new loop.Client();
-      conversation = new loop.shared.models.ConversationModel({}, {
-        sdk: {}
-      });
-      conversation.set({windowId: 42});
-      var dispatcher = new loop.Dispatcher();
-      conversationAppStore = new loop.store.ConversationAppStore({
-        dispatcher: dispatcher,
-        mozLoop: navigator.mozLoop
-      });
-      feedbackStore = new loop.store.FeedbackStore(dispatcher, {
-        feedbackClient: {}
-      });
-      sandbox.stub(conversation, "setOutgoingSessionData");
-    });
-
-    afterEach(function() {
-      icView = undefined;
-      document.title = oldTitle;
-    });
-
-    describe("start", function() {
-      it("should set the title to incoming_call_title2", function() {
-        conversationAppStore.setStoreState({
-          windowData: {
-            progressURL:    "fake",
-            websocketToken: "fake",
-            callId: 42
-          }
-        });
-
-        icView = mountTestComponent();
-
-        expect(document.title).eql("incoming_call_title2");
-      });
-    });
-
-    describe("componentDidMount", function() {
-      var fakeSessionData, promise, resolveWebSocketConnect;
-      var rejectWebSocketConnect;
-
-      beforeEach(function() {
-        fakeSessionData  = {
-          sessionId:      "sessionId",
-          sessionToken:   "sessionToken",
-          apiKey:         "apiKey",
-          callType:       "callType",
-          callId:         "Hello",
-          progressURL:    "http://progress.example.com",
-          websocketToken: "7b"
-        };
-
-        conversationAppStore.setStoreState({
-          windowData: fakeSessionData
-        });
-
-        stubComponent(loop.conversationViews, "AcceptCallView");
-        stubComponent(sharedView, "ConversationView");
-      });
-
-      it("should start alerting", function() {
-        icView = mountTestComponent();
-
-        sinon.assert.calledOnce(navigator.mozLoop.startAlerting);
-      });
-
-      describe("Session Data setup", function() {
-        beforeEach(function() {
-          sandbox.stub(loop, "CallConnectionWebSocket").returns({
-            promiseConnect: function () {
-              promise = new Promise(function(resolve, reject) {
-                resolveWebSocketConnect = resolve;
-                rejectWebSocketConnect = reject;
-              });
-              return promise;
-            },
-            on: sinon.stub()
-          });
-        });
-
-        it("should store the session data", function() {
-          sandbox.stub(conversation, "setIncomingSessionData");
-
-          icView = mountTestComponent();
-
-          sinon.assert.calledOnce(conversation.setIncomingSessionData);
-          sinon.assert.calledWithExactly(conversation.setIncomingSessionData,
-                                         fakeSessionData);
-        });
-
-        it("should setup the websocket connection", function() {
-          icView = mountTestComponent();
-
-          sinon.assert.calledOnce(loop.CallConnectionWebSocket);
-          sinon.assert.calledWithExactly(loop.CallConnectionWebSocket, {
-            callId: "Hello",
-            url: "http://progress.example.com",
-            websocketToken: "7b"
-          });
-        });
-      });
-
-      describe("WebSocket Handling", function() {
-        beforeEach(function() {
-          promise = new Promise(function(resolve, reject) {
-            resolveWebSocketConnect = resolve;
-            rejectWebSocketConnect = reject;
-          });
-
-          sandbox.stub(loop.CallConnectionWebSocket.prototype, "promiseConnect").returns(promise);
-        });
-
-        it("should set the state to incoming on success", function(done) {
-          icView = mountTestComponent();
-          resolveWebSocketConnect("incoming");
-
-          promise.then(function () {
-            expect(icView.state.callStatus).eql("incoming");
-            done();
-          });
-        });
-
-        it("should set the state to close on success if the progress " +
-          "state is terminated", function(done) {
-            icView = mountTestComponent();
-            resolveWebSocketConnect("terminated");
-
-            promise.then(function () {
-              expect(icView.state.callStatus).eql("close");
-              done();
-            });
-          });
-
-        // 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() {
-            done();
-          });
-        });
-      });
-
-      describe("WebSocket Events", function() {
-        describe("Call cancelled or timed out before acceptance", function() {
-          beforeEach(function() {
-            // Mounting the test component automatically calls the required
-            // setup functions
-            icView = mountTestComponent();
-            promise = new Promise(function(resolve, reject) {
-              resolve();
-            });
-
-            sandbox.stub(loop.CallConnectionWebSocket.prototype, "promiseConnect").returns(promise);
-            sandbox.stub(loop.CallConnectionWebSocket.prototype, "close");
-          });
-
-          describe("progress - terminated (previousState = alerting)", function() {
-            it("should stop alerting", function(done) {
-              promise.then(function() {
-                icView._websocket.trigger("progress", {
-                  state: "terminated",
-                  reason: WEBSOCKET_REASONS.TIMEOUT
-                }, "alerting");
-
-                sinon.assert.calledOnce(navigator.mozLoop.stopAlerting);
-                done();
-              });
-            });
-
-            it("should close the websocket", function(done) {
-              promise.then(function() {
-                icView._websocket.trigger("progress", {
-                  state: "terminated",
-                  reason: WEBSOCKET_REASONS.CLOSED
-                }, "alerting");
-
-                sinon.assert.calledOnce(icView._websocket.close);
-                done();
-              });
-            });
-
-            it("should close the window", function(done) {
-              promise.then(function() {
-                icView._websocket.trigger("progress", {
-                  state: "terminated",
-                  reason: WEBSOCKET_REASONS.ANSWERED_ELSEWHERE
-                }, "alerting");
-
-                sandbox.clock.tick(1);
-
-                sinon.assert.calledOnce(fakeWindow.close);
-                done();
-              });
-            });
-          });
-
-
-          describe("progress - terminated (previousState not init" +
-                   " nor alerting)",
-            function() {
-              it("should set the state to end", function(done) {
-                promise.then(function() {
-                  icView._websocket.trigger("progress", {
-                    state: "terminated",
-                    reason: WEBSOCKET_REASONS.MEDIA_FAIL
-                  }, "connecting");
-
-                  expect(icView.state.callStatus).eql("end");
-                  done();
-                });
-              });
-
-              it("should stop alerting", function(done) {
-                promise.then(function() {
-                  icView._websocket.trigger("progress", {
-                    state: "terminated",
-                    reason: WEBSOCKET_REASONS.MEDIA_FAIL
-                  }, "connecting");
-
-                  sinon.assert.calledOnce(navigator.mozLoop.stopAlerting);
-                  done();
-                });
-              });
-            });
-        });
-      });
-
-      describe("#accept", function() {
-        beforeEach(function() {
-          icView = mountTestComponent();
-          conversation.setIncomingSessionData({
-            sessionId:      "sessionId",
-            sessionToken:   "sessionToken",
-            apiKey:         "apiKey",
-            callType:       "callType",
-            callId:         "Hello",
-            progressURL:    "http://progress.example.com",
-            websocketToken: 123
-          });
-
-          sandbox.stub(icView._websocket, "accept");
-          sandbox.stub(icView.props.conversation, "accepted");
-        });
-
-        it("should initiate the conversation", function() {
-          icView.accept();
-
-          sinon.assert.calledOnce(icView.props.conversation.accepted);
-        });
-
-        it("should notify the websocket of the user acceptance", function() {
-          icView.accept();
-
-          sinon.assert.calledOnce(icView._websocket.accept);
-        });
-
-        it("should stop alerting", function() {
-          icView.accept();
-
-          sinon.assert.calledOnce(navigator.mozLoop.stopAlerting);
-        });
-      });
-
-      describe("#decline", function() {
-        beforeEach(function() {
-          icView = mountTestComponent();
-
-          icView._websocket = {
-            decline: sinon.stub(),
-            close: sinon.stub()
-          };
-          conversation.set({
-            windowId: "8699"
-          });
-          conversation.setIncomingSessionData({
-            websocketToken: 123
-          });
-        });
-
-        it("should close the window", function() {
-          icView.decline();
-
-          sandbox.clock.tick(1);
-
-          sinon.assert.calledOnce(fakeWindow.close);
-        });
-
-        it("should stop alerting", function() {
-          icView.decline();
-
-          sinon.assert.calledOnce(navigator.mozLoop.stopAlerting);
-        });
-
-        it("should release callData", function() {
-          icView.decline();
-
-          sinon.assert.calledOnce(navigator.mozLoop.calls.clearCallInProgress);
-          sinon.assert.calledWithExactly(
-            navigator.mozLoop.calls.clearCallInProgress, "8699");
-        });
-      });
-
-      describe("#blocked", function() {
-        var mozLoop, deleteCallUrlStub;
-
-        beforeEach(function() {
-          icView = mountTestComponent();
-
-          icView._websocket = {
-            decline: sinon.spy(),
-            close: sinon.stub()
-          };
-
-          mozLoop = {
-            LOOP_SESSION_TYPE: {
-              GUEST: 1,
-              FXA: 2
-            }
-          };
-
-          deleteCallUrlStub = sandbox.stub(loop.Client.prototype,
-                                           "deleteCallUrl");
-        });
-
-        it("should call mozLoop.stopAlerting", function() {
-          icView.declineAndBlock();
-
-          sinon.assert.calledOnce(navigator.mozLoop.stopAlerting);
-        });
-
-        it("should call delete call", function() {
-          sandbox.stub(conversation, "get").withArgs("callToken")
-                                           .returns("fakeToken")
-                                           .withArgs("sessionType")
-                                           .returns(mozLoop.LOOP_SESSION_TYPE.FXA);
-
-          icView.declineAndBlock();
-
-          sinon.assert.calledOnce(deleteCallUrlStub);
-          sinon.assert.calledWithExactly(deleteCallUrlStub,
-            "fakeToken", mozLoop.LOOP_SESSION_TYPE.FXA, sinon.match.func);
-        });
-
-        it("should get callToken from conversation model", function() {
-          sandbox.stub(conversation, "get");
-          icView.declineAndBlock();
-
-          sinon.assert.called(conversation.get);
-          sinon.assert.calledWithExactly(conversation.get, "callToken");
-          sinon.assert.calledWithExactly(conversation.get, "windowId");
-        });
-
-        it("should trigger error handling in case of error", function() {
-          // XXX just logging to console for now
-          var log = sandbox.stub(console, "log");
-          var fakeError = {
-            error: true
-          };
-          deleteCallUrlStub.callsArgWith(2, fakeError);
-          icView.declineAndBlock();
-
-          sinon.assert.calledOnce(log);
-          sinon.assert.calledWithExactly(log, fakeError);
-        });
-
-        it("should close the window", function() {
-          icView.declineAndBlock();
-
-          sandbox.clock.tick(1);
-
-          sinon.assert.calledOnce(fakeWindow.close);
-        });
-      });
-    });
-
-    describe("Events", function() {
-      var fakeSessionData;
-
-      beforeEach(function() {
-
-        fakeSessionData = {
-          sessionId:    "sessionId",
-          sessionToken: "sessionToken",
-          apiKey:       "apiKey"
-        };
-
-        conversationAppStore.setStoreState({
-          windowData: fakeSessionData
-        });
-
-        sandbox.stub(conversation, "setIncomingSessionData");
-        sandbox.stub(loop, "CallConnectionWebSocket").returns({
-          promiseConnect: function() {
-            return new Promise(function() {});
-          },
-          on: sandbox.spy()
-        });
-
-        icView = mountTestComponent();
-
-        conversation.set("loopToken", "fakeToken");
-        stubComponent(sharedView, "ConversationView");
-      });
-
-      describe("call:accepted", function() {
-        it("should display the ConversationView",
-          function() {
-            conversation.accepted();
-
-            TestUtils.findRenderedComponentWithType(icView,
-              sharedView.ConversationView);
-          });
-
-        it("should set the title to the call identifier", function() {
-          sandbox.stub(conversation, "getCallIdentifier").returns("fakeId");
-
-          conversation.accepted();
-
-          expect(document.title).eql("fakeId");
-        });
-      });
-
-      describe("session:ended", function() {
-        it("should display the feedback view when the call session ends",
-          function() {
-            conversation.trigger("session:ended");
-
-            TestUtils.findRenderedComponentWithType(icView,
-              sharedView.FeedbackView);
-          });
-      });
-
-      describe("session:peer-hungup", function() {
-        it("should display the feedback view when the peer hangs up",
-          function() {
-            conversation.trigger("session:peer-hungup");
-
-              TestUtils.findRenderedComponentWithType(icView,
-                sharedView.FeedbackView);
-          });
-      });
-
-      describe("session:network-disconnected", function() {
-        it("should navigate to call failed when network disconnects",
-          function() {
-            conversation.trigger("session:network-disconnected");
-
-            TestUtils.findRenderedComponentWithType(icView,
-              loop.conversationViews.GenericFailureView);
-          });
-
-        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()
-          };
-        });
-
-        describe("publishStream", function() {
-          it("should not notify the websocket if only one stream is up",
-            function() {
-              conversation.set("publishedStream", true);
-
-              sinon.assert.notCalled(icView._websocket.mediaUp);
-            });
-
-          it("should notify the websocket that media is up if both streams" +
-             "are connected", function() {
-              conversation.set("subscribedStream", true);
-              conversation.set("publishedStream", true);
-
-              sinon.assert.calledOnce(icView._websocket.mediaUp);
-            });
-        });
-
-        describe("subscribedStream", function() {
-          it("should not notify the websocket if only one stream is up",
-            function() {
-              conversation.set("subscribedStream", true);
-
-              sinon.assert.notCalled(icView._websocket.mediaUp);
-            });
-
-          it("should notify the websocket that media is up if both streams" +
-             "are connected", function() {
-              conversation.set("publishedStream", true);
-              conversation.set("subscribedStream", true);
-
-              sinon.assert.calledOnce(icView._websocket.mediaUp);
-            });
-        });
-      });
-    });
-  });
-
   describe("AcceptCallView", function() {
     var view;
 
     function mountTestComponent(extraProps) {
       var props = _.extend({dispatcher: dispatcher, mozLoop: fakeMozLoop}, extraProps);
       return TestUtils.renderIntoDocument(
         React.createElement(loop.conversationViews.AcceptCallView, props));
     }