Use promises to determine when to create the controlbox

This commit is contained in:
JC Brand 2017-08-16 12:31:17 +02:00
parent 956340aa8c
commit 694eabfc0e
6 changed files with 163 additions and 90 deletions

View File

@ -3,20 +3,42 @@
} (this, function ($, jasmine, mock, converse, test_utils) { } (this, function ($, jasmine, mock, converse, test_utils) {
var Strophe = converse.env.Strophe; var Strophe = converse.env.Strophe;
var $iq = converse.env.$iq; var $iq = converse.env.$iq;
var _ = converse.env._;
describe("The Registration Panel", function () { 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(); test_utils.openControlBox();
var cbview = _converse.chatboxviews.get('controlbox'); var cbview = _converse.chatboxviews.get('controlbox');
expect(cbview.$('#controlbox-tabs li').length).toBe(1); expect(cbview.$('#controlbox-tabs li').length).toBe(1);
expect(cbview.$('#controlbox-tabs li').text().trim()).toBe("Sign in"); expect(cbview.$('#controlbox-tabs li').text().trim()).toBe("Sign in");
done();
}, { auto_login: false, });
allow_registration: false,
})); }));
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'); var cbview = _converse.chatboxviews.get('controlbox');
test_utils.openControlBox(); test_utils.openControlBox();
var $tabs = cbview.$('#controlbox-tabs'); var $tabs = cbview.$('#controlbox-tabs');
@ -32,12 +54,22 @@
expect($login.is(':visible')).toBe(false); expect($login.is(':visible')).toBe(false);
expect($registration.is(':visible')).toBe(true); expect($registration.is(':visible')).toBe(true);
expect(cbview.switchTab).toHaveBeenCalled(); expect(cbview.switchTab).toHaveBeenCalled();
done();
}, { auto_login: false, });
allow_registration: true,
})); }));
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 cbview = _converse.chatboxviews.get('controlbox');
var registerview = cbview.registerpanel; var registerview = cbview.registerpanel;
spyOn(registerview, 'onProviderChosen').and.callThrough(); spyOn(registerview, 'onProviderChosen').and.callThrough();
@ -59,11 +91,22 @@
$form.find('input[type=submit]').click(); $form.find('input[type=submit]').click();
expect(registerview.onProviderChosen).toHaveBeenCalled(); expect(registerview.onProviderChosen).toHaveBeenCalled();
expect(_converse.connection.connect).toHaveBeenCalled(); expect(_converse.connection.connect).toHaveBeenCalled();
}, { auto_login: false, done();
allow_registration: true, });
})); }));
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'); var cbview = _converse.chatboxviews.get('controlbox');
cbview.$('#controlbox-tabs').find('li').last().find('a').click(); // Click the Register tab cbview.$('#controlbox-tabs').find('li').last().find('a').click(); // Click the Register tab
var registerview = _converse.chatboxviews.get('controlbox').registerpanel; var registerview = _converse.chatboxviews.get('controlbox').registerpanel;
@ -107,14 +150,25 @@
expect(registerview.$('input').length).toBe(5); expect(registerview.$('input').length).toBe(5);
expect(registerview.$('input[type=submit]').length).toBe(1); expect(registerview.$('input[type=submit]').length).toBe(1);
expect(registerview.$('input[type=button]').length).toBe(1); expect(registerview.$('input[type=button]').length).toBe(1);
}, { auto_login: false, done();
allow_registration: true, });
})); }));
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'); var cbview = _converse.chatboxviews.get('controlbox');
cbview.$('#controlbox-tabs').find('li').last().find('a').click(); // Click the Register tab 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, 'onProviderChosen').and.callThrough();
spyOn(registerview, 'getRegistrationFields').and.callThrough(); spyOn(registerview, 'getRegistrationFields').and.callThrough();
spyOn(registerview, 'onRegistrationFields').and.callThrough(); spyOn(registerview, 'onRegistrationFields').and.callThrough();
@ -156,11 +210,22 @@
var $stanza = $(_converse.connection.send.calls.argsFor(0)[0].tree()); var $stanza = $(_converse.connection.send.calls.argsFor(0)[0].tree());
expect($stanza.children('query').children().length).toBe(3); expect($stanza.children('query').children().length).toBe(3);
expect($stanza.children('query').children()[0].tagName).toBe('username'); expect($stanza.children('query').children()[0].tagName).toBe('username');
}, { auto_login: false, done();
allow_registration: true, });
})); }));
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'); var cbview = _converse.chatboxviews.get('controlbox');
cbview.$('#controlbox-tabs').find('li').last().find('a').click(); // Click the Register tab cbview.$('#controlbox-tabs').find('li').last().find('a').click(); // Click the Register tab
var registerview = _converse.chatboxviews.get('controlbox').registerpanel; var registerview = _converse.chatboxviews.get('controlbox').registerpanel;
@ -208,8 +273,8 @@
expect($stanza.children('query').children().length).toBe(1); expect($stanza.children('query').children().length).toBe(1);
expect($stanza.children('query').children().children().length).toBe(3); expect($stanza.children('query').children().children().length).toBe(3);
expect($stanza.children('query').children().children()[0].tagName).toBe('field'); expect($stanza.children('query').children().children()[0].tagName).toBe('field');
}, { auto_login: false, done();
allow_registration: true, });
})); }));
}); });
})); }));

View File

@ -50,7 +50,10 @@
*/ */
const { _converse } = this; const { _converse } = this;
_converse.api.promises.add('chatBoxesFetched'); _converse.api.promises.add([
'chatBoxesFetched',
'chatBoxesInitialized'
]);
_converse.ChatBoxes = Backbone.Collection.extend({ _converse.ChatBoxes = Backbone.Collection.extend({
comparator: 'time_opened', comparator: 'time_opened',
@ -312,15 +315,20 @@
} }
}); });
_converse.chatboxes = new _converse.ChatBoxes(); // BEGIN: Event handlers
_converse.chatboxviews = new _converse.ChatBoxViews({ _converse.api.listen.on('pluginsInitialized', () => {
'model': this.chatboxes _converse.chatboxes = new _converse.ChatBoxes();
_converse.chatboxviews = new _converse.ChatBoxViews({
'model': _converse.chatboxes
});
_converse.emit('chatBoxesInitialized');
}); });
_converse.api.listen.on('beforeTearDown', () => { _converse.api.listen.on('beforeTearDown', () => {
this.chatboxes.remove(); // Don't call off(), events won't get re-registered upon reconnect. this.chatboxes.remove(); // Don't call off(), events won't get re-registered upon reconnect.
delete this.chatboxes.browserStorage; delete this.chatboxes.browserStorage;
}); });
// END: Event handlers
_converse.getViewForChatBox = function (chatbox) { _converse.getViewForChatBox = function (chatbox) {
if (!chatbox) { return; } if (!chatbox) { return; }
@ -331,7 +339,6 @@
_.extend(_converse.api, { _.extend(_converse.api, {
'chats': { 'chats': {
'open' (jids, attrs) { 'open' (jids, attrs) {
debugger;
if (_.isUndefined(jids)) { if (_.isUndefined(jids)) {
_converse.log("chats.open: You need to provide at least one JID", Strophe.LogLevel.ERROR); _converse.log("chats.open: You need to provide at least one JID", Strophe.LogLevel.ERROR);
return null; return null;

View File

@ -48,7 +48,7 @@
const USERS_PANEL_ID = 'users'; const USERS_PANEL_ID = 'users';
const CHATBOX_TYPE = 'chatbox'; const CHATBOX_TYPE = 'chatbox';
const { Strophe, Backbone, utils, _, moment } = converse.env; const { Strophe, Backbone, Promise, utils, _, moment } = converse.env;
converse.plugins.add('converse-controlbox', { converse.plugins.add('converse-controlbox', {
@ -60,18 +60,6 @@
// //
// New functions which don't exist yet can also be added. // 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 () { _tearDown () {
this.__super__._tearDown.apply(this, arguments); this.__super__._tearDown.apply(this, arguments);
if (this.rosterview) { if (this.rosterview) {
@ -215,6 +203,7 @@
}, },
initialize () { initialize () {
_converse.controlboxtoggle = new _converse.ControlBoxToggle();
this.$el.insertAfter(_converse.controlboxtoggle.$el); this.$el.insertAfter(_converse.controlboxtoggle.$el);
this.model.on('change:connected', this.onConnected, this); this.model.on('change:connected', this.onConnected, this);
this.model.on('destroy', this.hide, this); this.model.on('destroy', this.hide, this);
@ -222,9 +211,9 @@
this.model.on('show', this.show, this); this.model.on('show', this.show, this);
this.model.on('change:closed', this.ensureClosedState, this); this.model.on('change:closed', this.ensureClosedState, this);
this.render(); this.render();
if (this.model.get('connected')) { _converse.api.waitUntil('rosterViewInitialized')
this.insertRoster(); .then(this.insertRoster.bind(this))
} .catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
}, },
render () { render () {
@ -256,7 +245,10 @@
onConnected () { onConnected () {
if (this.model.get('connected')) { 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(); this.model.save();
} }
}, },
@ -732,7 +724,7 @@
}, },
initialize () { initialize () {
_converse.chatboxviews.$el.prepend(this.render()); _converse.chatboxviews.$el.prepend(this.render().el);
this.updateOnlineCount(); this.updateOnlineCount();
const that = this; const that = this;
_converse.api.waitUntil('initialized').then(() => { _converse.api.waitUntil('initialized').then(() => {
@ -748,11 +740,10 @@
// the ControlBox or the Toggle must be shown. This prevents // the ControlBox or the Toggle must be shown. This prevents
// artifacts (i.e. on page load the toggle is shown only to then // artifacts (i.e. on page load the toggle is shown only to then
// seconds later be hidden in favor of the control box). // seconds later be hidden in favor of the control box).
return this.$el.html( this.el.innerHTML = tpl_controlbox_toggle({
tpl_controlbox_toggle({ 'label_toggle': __('Toggle chat')
'label_toggle': __('Toggle chat') })
}) return this;
);
}, },
updateOnlineCount: _.debounce(function () { 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 () { const disconnect = function () {
/* Upon disconnection, set connected to `false`, so that if /* Upon disconnection, set connected to `false`, so that if
* we reconnect, * we reconnect,

View File

@ -152,6 +152,7 @@
const PROMISES = [ const PROMISES = [
'initialized', 'initialized',
'cachedRoster', 'cachedRoster',
'connectionInitialized',
'pluginsInitialized', 'pluginsInitialized',
'roster', 'roster',
'rosterContactsFetched', 'rosterContactsFetched',
@ -575,11 +576,11 @@
}); });
this.initSession = function () { this.initSession = function () {
this.session = new Backbone.Model(); _converse.session = new Backbone.Model();
const id = b64_sha1('converse.bosh-session'); const id = b64_sha1('converse.bosh-session');
this.session.id = id; // Appears to be necessary for backbone.browserStorage _converse.session.id = id; // Appears to be necessary for backbone.browserStorage
this.session.browserStorage = new Backbone.BrowserStorage[_converse.storage](id); _converse.session.browserStorage = new Backbone.BrowserStorage[_converse.storage](id);
this.session.fetch(); _converse.session.fetch();
}; };
this.clearSession = function () { this.clearSession = function () {
@ -1778,22 +1779,22 @@
}; };
this.initConnection = function () { this.initConnection = function () {
if (this.connection) { 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 (!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);
if (('WebSocket' in window || 'MozWebSocket' in window) && this.websocket_url) { } else if (this.bosh_service_url) {
this.connection = new Strophe.Connection(this.websocket_url, this.connection_options); this.connection = new Strophe.Connection(
} else if (this.bosh_service_url) { this.bosh_service_url,
this.connection = new Strophe.Connection( _.assignIn(this.connection_options, {'keepalive': this.keepalive})
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.");
} else { }
throw new Error("initConnection: this browser does not support websockets and bosh_service_url wasn't specified.");
} }
_converse.emit('connectionInitialized');
}; };
this._tearDown = function () { this._tearDown = function () {
@ -1801,11 +1802,13 @@
* connection. * connection.
*/ */
_converse.emit('beforeTearDown'); _converse.emit('beforeTearDown');
this.unregisterPresenceHandler(); _converse.unregisterPresenceHandler();
if (this.roster) { if (_converse.roster) {
this.roster.off().reset(); // Removes roster contacts _converse.roster.off().reset(); // Removes roster contacts
}
if (!_.isUndefined(_converse.session)) {
_converse.session.destroy();
} }
this.session.destroy();
window.removeEventListener('click', _converse.onUserActivity); window.removeEventListener('click', _converse.onUserActivity);
window.removeEventListener('focus', _converse.onUserActivity); window.removeEventListener('focus', _converse.onUserActivity);
window.removeEventListener('keypress', _converse.onUserActivity); window.removeEventListener('keypress', _converse.onUserActivity);
@ -1813,7 +1816,7 @@
window.removeEventListener(unloadevent, _converse.onUserActivity); window.removeEventListener(unloadevent, _converse.onUserActivity);
window.clearInterval(_converse.everySecondTrigger); window.clearInterval(_converse.everySecondTrigger);
_converse.emit('afterTearDown'); _converse.emit('afterTearDown');
return this; return _converse;
}; };
this.initPlugins = function () { this.initPlugins = function () {

View File

@ -27,7 +27,7 @@
) { ) {
"use strict"; "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', { converse.plugins.add('converse-minimize', {
overrides: { overrides: {
@ -37,15 +37,6 @@
// //
// New functions which don't exist yet can also be added. // 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 () { registerGlobalEventHandlers () {
const { _converse } = this.__super__; const { _converse } = this.__super__;
$(window).on("resize", _.debounce(function (ev) { $(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 // Inserts a "minimize" button in the chatview's header
const $el = view.$el.find('.toggle-chatbox-button'); const $el = view.$el.find('.toggle-chatbox-button');
const $new_el = tpl_chatbox_minimize( const $new_el = tpl_chatbox_minimize(
@ -503,8 +503,7 @@
} else { } else {
view.$el.find('.close-chatbox-button').after($new_el); view.$el.find('.close-chatbox-button').after($new_el);
} }
}; });
_converse.on('chatBoxOpened', renderMinimizeButton);
_converse.on('controlBoxOpened', function (chatbox) { _converse.on('controlBoxOpened', function (chatbox) {
// Wrapped in anon method because at scan time, chatboxviews // Wrapped in anon method because at scan time, chatboxviews

View File

@ -76,6 +76,7 @@
allow_contact_removal: true, allow_contact_removal: true,
show_toolbar: true, show_toolbar: true,
}); });
_converse.api.promises.add('rosterViewInitialized');
const STATUSES = { const STATUSES = {
'dnd': __('This contact is busy'), 'dnd': __('This contact is busy'),
@ -950,6 +951,7 @@
'model': _converse.rostergroups 'model': _converse.rostergroups
}); });
_converse.rosterview.render(); _converse.rosterview.render();
_converse.emit('rosterViewInitialized');
}; };
_converse.api.listen.on('rosterInitialized', initRoster); _converse.api.listen.on('rosterInitialized', initRoster);
_converse.api.listen.on('rosterReadyAfterReconnection', initRoster); _converse.api.listen.on('rosterReadyAfterReconnection', initRoster);