From 694eabfc0e8226cfa1de4182470bb62517397bcd Mon Sep 17 00:00:00 2001 From: JC Brand Date: Wed, 16 Aug 2017 12:31:17 +0200 Subject: [PATCH] Use promises to determine when to create the controlbox --- spec/register.js | 115 +++++++++++++++++++++++++++++-------- src/converse-chatboxes.js | 17 ++++-- src/converse-controlbox.js | 43 +++++++------- src/converse-core.js | 51 ++++++++-------- src/converse-minimize.js | 25 ++++---- src/converse-rosterview.js | 2 + 6 files changed, 163 insertions(+), 90 deletions(-) diff --git a/spec/register.js b/spec/register.js index bc735428e..241a10621 100644 --- a/spec/register.js +++ b/spec/register.js @@ -3,20 +3,42 @@ } (this, function ($, jasmine, mock, converse, test_utils) { var Strophe = converse.env.Strophe; var $iq = converse.env.$iq; + var _ = converse.env._; describe("The Registration Panel", function () { - it("is not available unless allow_registration=true", mock.initConverse(function (_converse) { + it("is not available unless allow_registration=true", + mock.initConverseWithPromises( + null, ['connectionInitialized', 'chatBoxesInitialized'], + { auto_login: false, + allow_registration: false }, + function (done, _converse) { + + test_utils.waitUntil(function () { + return _converse.chatboxviews.get('controlbox'); + }, 300) + .then(function () { + test_utils.openControlBox(); var cbview = _converse.chatboxviews.get('controlbox'); expect(cbview.$('#controlbox-tabs li').length).toBe(1); expect(cbview.$('#controlbox-tabs li').text().trim()).toBe("Sign in"); - - }, { auto_login: false, - allow_registration: false, + done(); + }); })); - it("can be opened by clicking on the registration tab", mock.initConverse(function (_converse) { + it("can be opened by clicking on the registration tab", + mock.initConverseWithPromises( + null, ['connectionInitialized', 'chatBoxesInitialized'], + { auto_login: false, + allow_registration: true }, + function (done, _converse) { + + test_utils.waitUntil(function () { + return _.get(_converse.chatboxviews.get('controlbox'), 'registerpanel'); + }, 300) + .then(function () { + var cbview = _converse.chatboxviews.get('controlbox'); test_utils.openControlBox(); var $tabs = cbview.$('#controlbox-tabs'); @@ -32,12 +54,22 @@ expect($login.is(':visible')).toBe(false); expect($registration.is(':visible')).toBe(true); expect(cbview.switchTab).toHaveBeenCalled(); - - }, { auto_login: false, - allow_registration: true, + done(); + }); })); - it("allows the user to choose an XMPP provider's domain", mock.initConverse(function (_converse) { + it("allows the user to choose an XMPP provider's domain", + mock.initConverseWithPromises( + null, ['connectionInitialized', 'chatBoxesInitialized'], + { auto_login: false, + allow_registration: true }, + function (done, _converse) { + + test_utils.waitUntil(function () { + return _.get(_converse.chatboxviews.get('controlbox'), 'registerpanel'); + }, 300) + .then(function () { + var cbview = _converse.chatboxviews.get('controlbox'); var registerview = cbview.registerpanel; spyOn(registerview, 'onProviderChosen').and.callThrough(); @@ -59,11 +91,22 @@ $form.find('input[type=submit]').click(); expect(registerview.onProviderChosen).toHaveBeenCalled(); expect(_converse.connection.connect).toHaveBeenCalled(); - }, { auto_login: false, - allow_registration: true, - })); + done(); + }); + })); + + it("will render a registration form as received from the XMPP provider", + mock.initConverseWithPromises( + null, ['connectionInitialized', 'chatBoxesInitialized'], + { auto_login: false, + allow_registration: true }, + function (done, _converse) { + + test_utils.waitUntil(function () { + return _.get(_converse.chatboxviews.get('controlbox'), 'registerpanel'); + }, 300) + .then(function () { - it("will render a registration form as received from the XMPP provider", mock.initConverse(function (_converse) { var cbview = _converse.chatboxviews.get('controlbox'); cbview.$('#controlbox-tabs').find('li').last().find('a').click(); // Click the Register tab var registerview = _converse.chatboxviews.get('controlbox').registerpanel; @@ -107,14 +150,25 @@ expect(registerview.$('input').length).toBe(5); expect(registerview.$('input[type=submit]').length).toBe(1); expect(registerview.$('input[type=button]').length).toBe(1); - }, { auto_login: false, - allow_registration: true, - })); + done(); + }); + })); + + it("will set form_type to legacy and submit it as legacy", + mock.initConverseWithPromises( + null, ['connectionInitialized', 'chatBoxesInitialized'], + { auto_login: false, + allow_registration: true }, + function (done, _converse) { + + test_utils.waitUntil(function () { + return _.get(_converse.chatboxviews.get('controlbox'), 'registerpanel'); + }, 300) + .then(function () { - it("will set form_type to legacy and submit it as legacy", mock.initConverse(function (_converse) { var cbview = _converse.chatboxviews.get('controlbox'); cbview.$('#controlbox-tabs').find('li').last().find('a').click(); // Click the Register tab - var registerview = _converse.chatboxviews.get('controlbox').registerpanel; + var registerview = cbview.registerpanel; spyOn(registerview, 'onProviderChosen').and.callThrough(); spyOn(registerview, 'getRegistrationFields').and.callThrough(); spyOn(registerview, 'onRegistrationFields').and.callThrough(); @@ -156,11 +210,22 @@ var $stanza = $(_converse.connection.send.calls.argsFor(0)[0].tree()); expect($stanza.children('query').children().length).toBe(3); expect($stanza.children('query').children()[0].tagName).toBe('username'); - }, { auto_login: false, - allow_registration: true, - })); + done(); + }); + })); + + it("will set form_type to xform and submit it as xform", + mock.initConverseWithPromises( + null, ['connectionInitialized', 'chatBoxesInitialized'], + { auto_login: false, + allow_registration: true }, + function (done, _converse) { + + test_utils.waitUntil(function () { + return _.get(_converse.chatboxviews.get('controlbox'), 'registerpanel'); + }, 300) + .then(function () { - it("will set form_type to xform and submit it as xform", mock.initConverse(function (_converse) { var cbview = _converse.chatboxviews.get('controlbox'); cbview.$('#controlbox-tabs').find('li').last().find('a').click(); // Click the Register tab var registerview = _converse.chatboxviews.get('controlbox').registerpanel; @@ -208,8 +273,8 @@ expect($stanza.children('query').children().length).toBe(1); expect($stanza.children('query').children().children().length).toBe(3); expect($stanza.children('query').children().children()[0].tagName).toBe('field'); - }, { auto_login: false, - allow_registration: true, - })); + done(); + }); + })); }); })); diff --git a/src/converse-chatboxes.js b/src/converse-chatboxes.js index 2ce5ce99c..e039f7ec1 100644 --- a/src/converse-chatboxes.js +++ b/src/converse-chatboxes.js @@ -50,7 +50,10 @@ */ const { _converse } = this; - _converse.api.promises.add('chatBoxesFetched'); + _converse.api.promises.add([ + 'chatBoxesFetched', + 'chatBoxesInitialized' + ]); _converse.ChatBoxes = Backbone.Collection.extend({ comparator: 'time_opened', @@ -312,15 +315,20 @@ } }); - _converse.chatboxes = new _converse.ChatBoxes(); - _converse.chatboxviews = new _converse.ChatBoxViews({ - 'model': this.chatboxes + // BEGIN: Event handlers + _converse.api.listen.on('pluginsInitialized', () => { + _converse.chatboxes = new _converse.ChatBoxes(); + _converse.chatboxviews = new _converse.ChatBoxViews({ + 'model': _converse.chatboxes + }); + _converse.emit('chatBoxesInitialized'); }); _converse.api.listen.on('beforeTearDown', () => { this.chatboxes.remove(); // Don't call off(), events won't get re-registered upon reconnect. delete this.chatboxes.browserStorage; }); + // END: Event handlers _converse.getViewForChatBox = function (chatbox) { if (!chatbox) { return; } @@ -331,7 +339,6 @@ _.extend(_converse.api, { 'chats': { 'open' (jids, attrs) { - debugger; if (_.isUndefined(jids)) { _converse.log("chats.open: You need to provide at least one JID", Strophe.LogLevel.ERROR); return null; diff --git a/src/converse-controlbox.js b/src/converse-controlbox.js index a5bdc4f66..f7d424751 100644 --- a/src/converse-controlbox.js +++ b/src/converse-controlbox.js @@ -48,7 +48,7 @@ const USERS_PANEL_ID = 'users'; const CHATBOX_TYPE = 'chatbox'; - const { Strophe, Backbone, utils, _, moment } = converse.env; + const { Strophe, Backbone, Promise, utils, _, moment } = converse.env; converse.plugins.add('converse-controlbox', { @@ -60,18 +60,6 @@ // // New functions which don't exist yet can also be added. - initChatBoxes () { - this.__super__.initChatBoxes.apply(this, arguments); - this.controlboxtoggle = new this.ControlBoxToggle(); - }, - - initConnection () { - this.__super__.initConnection.apply(this, arguments); - if (this.connection) { - this.addControlBox(); - } - }, - _tearDown () { this.__super__._tearDown.apply(this, arguments); if (this.rosterview) { @@ -215,6 +203,7 @@ }, initialize () { + _converse.controlboxtoggle = new _converse.ControlBoxToggle(); this.$el.insertAfter(_converse.controlboxtoggle.$el); this.model.on('change:connected', this.onConnected, this); this.model.on('destroy', this.hide, this); @@ -222,9 +211,9 @@ this.model.on('show', this.show, this); this.model.on('change:closed', this.ensureClosedState, this); this.render(); - if (this.model.get('connected')) { - this.insertRoster(); - } + _converse.api.waitUntil('rosterViewInitialized') + .then(this.insertRoster.bind(this)) + .catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL)); }, render () { @@ -256,7 +245,10 @@ onConnected () { if (this.model.get('connected')) { - this.render().insertRoster(); + this.render(); + _converse.api.waitUntil('rosterViewInitialized') + .then(this.insertRoster.bind(this)) + .catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL)); this.model.save(); } }, @@ -732,7 +724,7 @@ }, initialize () { - _converse.chatboxviews.$el.prepend(this.render()); + _converse.chatboxviews.$el.prepend(this.render().el); this.updateOnlineCount(); const that = this; _converse.api.waitUntil('initialized').then(() => { @@ -748,11 +740,10 @@ // the ControlBox or the Toggle must be shown. This prevents // artifacts (i.e. on page load the toggle is shown only to then // seconds later be hidden in favor of the control box). - return this.$el.html( - tpl_controlbox_toggle({ - 'label_toggle': __('Toggle chat') - }) - ); + this.el.innerHTML = tpl_controlbox_toggle({ + 'label_toggle': __('Toggle chat') + }) + return this; }, updateOnlineCount: _.debounce(function () { @@ -802,6 +793,12 @@ } }); + Promise.all([ + _converse.api.waitUntil('connectionInitialized'), + _converse.api.waitUntil('chatBoxesInitialized') + ]).then(_converse.addControlBox) + .catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL)); + const disconnect = function () { /* Upon disconnection, set connected to `false`, so that if * we reconnect, diff --git a/src/converse-core.js b/src/converse-core.js index c6b1f0614..2e40405d6 100755 --- a/src/converse-core.js +++ b/src/converse-core.js @@ -152,6 +152,7 @@ const PROMISES = [ 'initialized', 'cachedRoster', + 'connectionInitialized', 'pluginsInitialized', 'roster', 'rosterContactsFetched', @@ -575,11 +576,11 @@ }); this.initSession = function () { - this.session = new Backbone.Model(); + _converse.session = new Backbone.Model(); const id = b64_sha1('converse.bosh-session'); - this.session.id = id; // Appears to be necessary for backbone.browserStorage - this.session.browserStorage = new Backbone.BrowserStorage[_converse.storage](id); - this.session.fetch(); + _converse.session.id = id; // Appears to be necessary for backbone.browserStorage + _converse.session.browserStorage = new Backbone.BrowserStorage[_converse.storage](id); + _converse.session.fetch(); }; this.clearSession = function () { @@ -1778,22 +1779,22 @@ }; this.initConnection = function () { - if (this.connection) { - return; - } - if (!this.bosh_service_url && ! this.websocket_url) { - throw new Error("initConnection: you must supply a value for either the bosh_service_url or websocket_url or both."); - } - if (('WebSocket' in window || 'MozWebSocket' in window) && this.websocket_url) { - this.connection = new Strophe.Connection(this.websocket_url, this.connection_options); - } else if (this.bosh_service_url) { - this.connection = new Strophe.Connection( - this.bosh_service_url, - _.assignIn(this.connection_options, {'keepalive': this.keepalive}) - ); - } else { - throw new Error("initConnection: this browser does not support websockets and bosh_service_url wasn't specified."); + if (!this.connection) { + if (!this.bosh_service_url && ! this.websocket_url) { + throw new Error("initConnection: you must supply a value for either the bosh_service_url or websocket_url or both."); + } + if (('WebSocket' in window || 'MozWebSocket' in window) && this.websocket_url) { + this.connection = new Strophe.Connection(this.websocket_url, this.connection_options); + } else if (this.bosh_service_url) { + this.connection = new Strophe.Connection( + this.bosh_service_url, + _.assignIn(this.connection_options, {'keepalive': this.keepalive}) + ); + } else { + throw new Error("initConnection: this browser does not support websockets and bosh_service_url wasn't specified."); + } } + _converse.emit('connectionInitialized'); }; this._tearDown = function () { @@ -1801,11 +1802,13 @@ * connection. */ _converse.emit('beforeTearDown'); - this.unregisterPresenceHandler(); - if (this.roster) { - this.roster.off().reset(); // Removes roster contacts + _converse.unregisterPresenceHandler(); + if (_converse.roster) { + _converse.roster.off().reset(); // Removes roster contacts + } + if (!_.isUndefined(_converse.session)) { + _converse.session.destroy(); } - this.session.destroy(); window.removeEventListener('click', _converse.onUserActivity); window.removeEventListener('focus', _converse.onUserActivity); window.removeEventListener('keypress', _converse.onUserActivity); @@ -1813,7 +1816,7 @@ window.removeEventListener(unloadevent, _converse.onUserActivity); window.clearInterval(_converse.everySecondTrigger); _converse.emit('afterTearDown'); - return this; + return _converse; }; this.initPlugins = function () { diff --git a/src/converse-minimize.js b/src/converse-minimize.js index eb7a323ea..8314ddd7a 100644 --- a/src/converse-minimize.js +++ b/src/converse-minimize.js @@ -27,7 +27,7 @@ ) { "use strict"; - const { _ , utils, Backbone, b64_sha1, moment } = converse.env; + const { _ , utils, Backbone, Promise, Strophe, b64_sha1, moment } = converse.env; converse.plugins.add('converse-minimize', { overrides: { @@ -37,15 +37,6 @@ // // New functions which don't exist yet can also be added. - initChatBoxes () { - const { _converse } = this.__super__; - const result = this.__super__.initChatBoxes.apply(this, arguments); - _converse.minimized_chats = new _converse.MinimizedChats({ - model: _converse.chatboxes - }); - return result; - }, - registerGlobalEventHandlers () { const { _converse } = this.__super__; $(window).on("resize", _.debounce(function (ev) { @@ -492,7 +483,16 @@ } }); - const renderMinimizeButton = function (view) { + Promise.all([ + _converse.api.waitUntil('connectionInitialized'), + _converse.api.waitUntil('chatBoxesInitialized') + ]).then(() => { + _converse.minimized_chats = new _converse.MinimizedChats({ + model: _converse.chatboxes + }); + }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL)); + + _converse.on('chatBoxOpened', function renderMinimizeButton (view) { // Inserts a "minimize" button in the chatview's header const $el = view.$el.find('.toggle-chatbox-button'); const $new_el = tpl_chatbox_minimize( @@ -503,8 +503,7 @@ } else { view.$el.find('.close-chatbox-button').after($new_el); } - }; - _converse.on('chatBoxOpened', renderMinimizeButton); + }); _converse.on('controlBoxOpened', function (chatbox) { // Wrapped in anon method because at scan time, chatboxviews diff --git a/src/converse-rosterview.js b/src/converse-rosterview.js index 76c2f3771..db3f554b2 100644 --- a/src/converse-rosterview.js +++ b/src/converse-rosterview.js @@ -76,6 +76,7 @@ allow_contact_removal: true, show_toolbar: true, }); + _converse.api.promises.add('rosterViewInitialized'); const STATUSES = { 'dnd': __('This contact is busy'), @@ -950,6 +951,7 @@ 'model': _converse.rostergroups }); _converse.rosterview.render(); + _converse.emit('rosterViewInitialized'); }; _converse.api.listen.on('rosterInitialized', initRoster); _converse.api.listen.on('rosterReadyAfterReconnection', initRoster);