', + ' ', + ' ', + '
' + ].join("")), + + className: "incoming-call", + + events: { + "click .btn-accept": "handleAccept", + "click .btn-decline": "handleDecline" + }, + + /** + * User clicked on the "accept" button. + * @param {MouseEvent} event + */ + handleAccept: function(event) { + event.preventDefault(); + this.model.trigger("accept"); + }, + + /** + * User clicked on the "decline" button. + * @param {MouseEvent} event + */ + handleDecline: function(event) { + event.preventDefault(); + // XXX For now, we just close the window. + window.close(); + } + }); + + /** + * Call ended view. + * @type {loop.shared.views.BaseView} + */ + var EndedCallView = sharedViews.BaseView.extend({ + template: _.template([ + '', + ' ', + '
' + ].join("")), + + className: "call-ended", + + events: { + "click button": "closeWindow" + }, + + closeWindow: function(event) { + event.preventDefault(); + // XXX For now, we just close the window. + window.close(); + } + }); + + /** + * Conversation router. + * + * Required options: + * - {loop.shared.models.ConversationModel} conversation Conversation model. + * - {loop.shared.components.Notifier} notifier Notifier component. + * + * @type {loop.shared.router.BaseConversationRouter} + */ + var ConversationRouter = loop.desktopRouter.DesktopConversationRouter.extend({ + routes: { + "incoming/:version": "incoming", + "call/accept": "accept", + "call/ongoing": "conversation", + "call/ended": "ended" + }, + + /** + * @override {loop.shared.router.BaseConversationRouter.startCall} + */ + startCall: function() { + this.navigate("call/ongoing", {trigger: true}); + }, + + /** + * @override {loop.shared.router.BaseConversationRouter.endCall} + */ + endCall: function() { + this.navigate("call/ended", {trigger: true}); + }, + + /** + * Incoming call route. + * + * @param {String} loopVersion The version from the push notification, set + * by the router from the URL. + */ + incoming: function(loopVersion) { + this._conversation.set({loopVersion: loopVersion}); + this._conversation.once("accept", function() { + this.navigate("call/accept", {trigger: true}); + }.bind(this)); + this.loadView(new IncomingCallView({model: this._conversation})); + }, + + /** + * Accepts an incoming call. + */ + accept: function() { + this._conversation.initiate({ + baseServerUrl: window.navigator.mozLoop.serverUrl, + outgoing: false + }); + }, + + /** + * conversation is the route when the conversation is active. The start + * route should be navigated to first. + */ + conversation: function() { + if (!this._conversation.isSessionReady()) { + console.error("Error: navigated to conversation route without " + + "the start route to initialise the call first"); + this._notifier.errorL10n("cannot_start_call_session_not_ready"); + return; + } + + this.loadView( + new loop.shared.views.ConversationView({ + sdk: OT, + model: this._conversation + })); + }, + + /** + * XXX: load a view with a close button for now? + */ + ended: function() { + this.loadView(new EndedCallView()); + } + }); + + /** + * Panel initialisation. + */ + function init() { + // Do the initial L10n setup, we do this before anything + // else to ensure the L10n environment is setup correctly. + mozL10n.initialize(window.navigator.mozLoop); + + router = new ConversationRouter({ + conversation: new loop.shared.models.ConversationModel({}, {sdk: OT}), + notifier: new sharedViews.NotificationListView({el: "#messages"}) + }); + Backbone.history.start(); + } + + return { + ConversationRouter: ConversationRouter, + EndedCallView: EndedCallView, + IncomingCallView: IncomingCallView, + init: init + }; +})(window.OT, document.mozL10n); diff --git a/browser/components/loop/content/js/desktopRouter.js b/browser/components/loop/content/js/desktopRouter.js new file mode 100644 index 000000000000..294418685d5a --- /dev/null +++ b/browser/components/loop/content/js/desktopRouter.js @@ -0,0 +1,34 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* global loop:true */ + +var loop = loop || {}; +loop.desktopRouter = (function() { + "use strict"; + + /** + * On the desktop app, the use of about: uris prevents us from changing the + * url of the location. As a result, we change the navigate function to simply + * activate the new routes, and not try changing the url. + * + * XXX It is conceivable we might be able to remove this in future, if we + * can either swap to resource uris or remove the limitation on the about uris. + */ + var extendedRouter = { + navigate: function(to) { + this[this.routes[to]](); + } + }; + + var DesktopRouter = loop.shared.router.BaseRouter.extend(extendedRouter); + + var DesktopConversationRouter = + loop.shared.router.BaseConversationRouter.extend(extendedRouter); + + return { + DesktopRouter: DesktopRouter, + DesktopConversationRouter: DesktopConversationRouter + }; +})(); diff --git a/browser/components/loop/content/js/panel.js b/browser/components/loop/content/js/panel.js new file mode 100644 index 000000000000..60456bd8dc52 --- /dev/null +++ b/browser/components/loop/content/js/panel.js @@ -0,0 +1,248 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* global loop:true */ + +var loop = loop || {}; +loop.panel = (function(_, mozL10n) { + "use strict"; + + var sharedViews = loop.shared.views, + // aliasing translation function as __ for concision + __ = mozL10n.get; + + /** + * Panel router. + * @type {loop.desktopRouter.DesktopRouter} + */ + var router; + + /** + * Do not disturb panel subview. + */ + var DoNotDisturbView = sharedViews.BaseView.extend({ + template: _.template([ + '', + ].join('')), + + events: { + "click input[type=checkbox]": "toggle" + }, + + /** + * Toggles mozLoop activation status. + */ + toggle: function() { + navigator.mozLoop.doNotDisturb = !navigator.mozLoop.doNotDisturb; + this.render(); + }, + + render: function() { + this.$el.html(this.template({ + checked: navigator.mozLoop.doNotDisturb ? "checked" : "" + })); + return this; + } + }); + + /** + * Panel view. + */ + var PanelView = sharedViews.BaseView.extend({ + template: _.template([ + '