Commit 1443fdd4 authored by JC Brand's avatar JC Brand

(WIP) Wait for promises before opening chats in API methods

parent 41154614
......@@ -36,34 +36,19 @@
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
......@@ -2575,7 +2560,13 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
if (_.isFunction(this.beforeRender)) {
this.beforeRender();
}
const new_vnode = tovnode.toVNode(parseHTMLToDOM(this.toHTML()));
let new_vnode;
if (!_.isNil(this.toHTML)) {
new_vnode = tovnode.toVNode(parseHTMLToDOM(this.toHTML()));
} else {
new_vnode = tovnode.toVNode(this.toDOM());
}
new_vnode.data.hook = _.extend({
create: this.updateEventListeners.bind(this),
update: this.updateEventListeners.bind(this)
......@@ -4529,7 +4520,7 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(global) {var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// Native Javascript for Bootstrap 4 v2.0.22 | © dnp_theme | MIT-License
/* WEBPACK VAR INJECTION */(function(global) {var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// Native Javascript for Bootstrap 4 v2.0.23 | © dnp_theme | MIT-License
(function (root, factory) {
if (true) {
// AMD support:
......@@ -4638,12 +4629,14 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
getBoundingClientRect = 'getBoundingClientRect',
querySelectorAll = 'querySelectorAll',
getElementsByCLASSNAME = 'getElementsByClassName',
getComputedStyle = 'getComputedStyle',
indexOf = 'indexOf',
parentNode = 'parentNode',
length = 'length',
toLowerCase = 'toLowerCase',
Transition = 'Transition',
Duration = 'Duration',
Webkit = 'Webkit',
style = 'style',
push = 'push',
......@@ -4672,6 +4665,7 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
// transitionEnd since 2.0.4
supportTransitions = Webkit+Transition in HTML[style] || Transition[toLowerCase]() in HTML[style],
transitionEndEvent = Webkit+Transition in HTML[style] ? Webkit[toLowerCase]()+Transition+'End' : Transition[toLowerCase]()+'end',
transitionDuration = Webkit+Duration in HTML[style] ? Webkit[toLowerCase]()+Transition+Duration : Transition[toLowerCase]()+Duration,
// set new focus element since 2.0.3
setFocus = function(element){
......@@ -4725,9 +4719,16 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
off(element, event, handlerWrapper);
});
},
getTransitionDurationFromElement = function(element) {
var duration = globalObject[getComputedStyle](element)[transitionDuration];
duration = parseFloat(duration);
duration = typeof duration === 'number' && !isNaN(duration) ? duration * 1000 : 0;
return duration + 50; // we take a short offset to make sure we fire on the next frame after animation
},
emulateTransitionEnd = function(element,handler){ // emulateTransitionEnd since 2.0.4
if (supportTransitions) { one(element, transitionEndEvent, function(e){ handler(e); }); }
else { handler(); }
var called = 0, duration = getTransitionDurationFromElement(element);
supportTransitions && one(element, transitionEndEvent, function(e){ handler(e); called = 1; });
setTimeout(function() { !called && handler(); }, duration);
},
bootstrapCustomEvent = function (eventName, componentName, related) {
var OriginalCustomEvent = new CustomEvent( eventName + '.bs.' + componentName);
......@@ -4823,7 +4824,7 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
arrowLeft && (arrow[style][left] = arrowLeft + 'px');
};
BSN.version = '2.0.22';
BSN.version = '2.0.23';
/* Native Javascript for Bootstrap 4 | Alert
-------------------------------------------*/
......@@ -4993,7 +4994,7 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
// DATA API
var intervalAttribute = element[getAttribute](dataInterval),
intervalOption = options[interval],
intervalData = intervalAttribute === 'false' ? 0 : parseInt(intervalAttribute) || 5000, // bootstrap carousel default interval
intervalData = intervalAttribute === 'false' ? 0 : parseInt(intervalAttribute),
pauseData = element[getAttribute](dataPause) === hoverEvent || false,
keyboardData = element[getAttribute](dataKeyboard) === 'true' || false,
......@@ -5008,8 +5009,8 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
this[pause] = (options[pause] === hoverEvent || pauseData) ? hoverEvent : false; // false / hover
this[interval] = typeof intervalOption === 'number' ? intervalOption
: intervalData === 0 ? 0
: intervalData;
: intervalOption === false || intervalData === 0 || intervalData === false ? 0
: 5000; // bootstrap carousel default interval
// bind, event targets
var self = this, index = element.index = 0, timer = element.timer = 0,
......@@ -5128,10 +5129,10 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
addClass(slides[next],carouselItem +'-'+ slideDirection);
addClass(slides[activeItem],carouselItem +'-'+ slideDirection);
one(slides[activeItem], transitionEndEvent, function(e) {
var timeout = e[target] !== slides[activeItem] ? e.elapsedTime*1000 : 0;
one(slides[next], transitionEndEvent, function(e) {
var timeout = e[target] !== slides[next] ? e.elapsedTime*1000+100 : 20;
setTimeout(function(){
isSliding && setTimeout(function(){
isSliding = false;
addClass(slides[next],active);
......@@ -5146,7 +5147,7 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
if ( !DOC.hidden && self[interval] && !hasClass(element,paused) ) {
self.cycle();
}
},timeout+100);
}, timeout);
});
} else {
......@@ -5211,23 +5212,24 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
// event targets and constants
var accordion = null, collapse = null, self = this,
isAnimating = false, // when true it will prevent click handlers
accordionData = element[getAttribute]('data-parent'),
activeCollapse, activeElement,
// component strings
component = 'collapse',
collapsed = 'collapsed',
isAnimating = 'isAnimating',
// private methods
openAction = function(collapseElement,toggle) {
bootstrapCustomEvent.call(collapseElement, showEvent, component);
isAnimating = true;
collapseElement[isAnimating] = true;
addClass(collapseElement,collapsing);
removeClass(collapseElement,component);
collapseElement[style][height] = collapseElement[scrollHeight] + 'px';
emulateTransitionEnd(collapseElement, function() {
isAnimating = false;
collapseElement[isAnimating] = false;
collapseElement[setAttribute](ariaExpanded,'true');
toggle[setAttribute](ariaExpanded,'true');
removeClass(collapseElement,collapsing);
......@@ -5239,7 +5241,7 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
},
closeAction = function(collapseElement,toggle) {
bootstrapCustomEvent.call(collapseElement, hideEvent, component);
isAnimating = true;
collapseElement[isAnimating] = true;
collapseElement[style][height] = collapseElement[scrollHeight] + 'px'; // set height first
removeClass(collapseElement,component);
removeClass(collapseElement,showClass);
......@@ -5248,7 +5250,7 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
collapseElement[style][height] = '0px';
emulateTransitionEnd(collapseElement, function() {
isAnimating = false;
collapseElement[isAnimating] = false;
collapseElement[setAttribute](ariaExpanded,'false');
toggle[setAttribute](ariaExpanded,'false');
removeClass(collapseElement,collapsing);
......@@ -5267,29 +5269,29 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
// public methods
this.toggle = function(e) {
e[preventDefault]();
if (isAnimating) return;
if (!hasClass(collapse,showClass)) { self.show(); }
else { self.hide(); }
};
this.hide = function() {
if ( collapse[isAnimating] ) return;
closeAction(collapse,element);
addClass(element,collapsed);
};
this.show = function() {
if ( accordion ) {
var activeCollapse = queryElement('.'+component+'.'+showClass,accordion),
toggle = activeCollapse && (queryElement('['+dataToggle+'="'+component+'"]['+dataTarget+'="#'+activeCollapse.id+'"]',accordion)
|| queryElement('['+dataToggle+'="'+component+'"][href="#'+activeCollapse.id+'"]',accordion) ),
correspondingCollapse = toggle && (toggle[getAttribute](dataTarget) || toggle.href);
if ( activeCollapse && toggle && activeCollapse !== collapse ) {
closeAction(activeCollapse,toggle);
if ( correspondingCollapse.split('#')[1] !== collapse.id ) { addClass(toggle,collapsed); }
else { removeClass(toggle,collapsed); }
}
activeCollapse = queryElement('.'+component+'.'+showClass,accordion);
activeElement = activeCollapse && (queryElement('['+dataToggle+'="'+component+'"]['+dataTarget+'="#'+activeCollapse.id+'"]',accordion)
|| queryElement('['+dataToggle+'="'+component+'"][href="#'+activeCollapse.id+'"]',accordion) );
}
if ( !collapse[isAnimating] || activeCollapse && !activeCollapse[isAnimating] ) {
if ( activeElement && activeCollapse !== collapse ) {
closeAction(activeCollapse,activeElement);
addClass(activeElement,collapsed);
}
openAction(collapse,element);
removeClass(element,collapsed);
}
};
// init
......@@ -5297,6 +5299,7 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
on(element, clickEvent, self.toggle);
}
collapse = getTarget();
collapse[isAnimating] = false; // when true it will prevent click handlers
accordion = queryElement(options.parent) || accordionData && getClosest(element, accordionData);
element[stringCollapse] = self;
};
......@@ -5454,6 +5457,7 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
var btnCheck = element[getAttribute](dataTarget)||element[getAttribute]('href'),
checkModal = queryElement( btnCheck ),
modal = hasClass(element,'modal') ? element : checkModal,
overlayDelay,
// strings
component = 'modal',
......@@ -5487,13 +5491,13 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
return globalObject[innerWidth] || (htmlRect[right] - Math.abs(htmlRect[left]));
},
setScrollbar = function () {
var bodyStyle = globalObject.getComputedStyle(DOC[body]),
var bodyStyle = globalObject[getComputedStyle](DOC[body]),
bodyPad = parseInt((bodyStyle[paddingRight]), 10), itemPad;
if (bodyIsOverflowing) {
DOC[body][style][paddingRight] = (bodyPad + scrollbarWidth) + 'px';
if (fixedItems[length]){
for (var i = 0; i < fixedItems[length]; i++) {
itemPad = globalObject.getComputedStyle(fixedItems[i])[paddingRight];
itemPad = globalObject[getComputedStyle](fixedItems[i])[paddingRight];
fixedItems[i][style][paddingRight] = ( parseInt(itemPad) + scrollbarWidth) + 'px';
}
}
......@@ -5635,6 +5639,7 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
if ( overlay && modalOverlay && !hasClass(overlay,showClass)) {
overlay[offsetWidth]; // force reflow to enable trasition
overlayDelay = getTransitionDurationFromElement(overlay);
addClass(overlay, showClass);
}
......@@ -5654,18 +5659,19 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
keydownHandlerToggle();
hasClass(modal,'fade') ? emulateTransitionEnd(modal, triggerShow) : triggerShow();
}, supportTransitions ? 150 : 0);
}, supportTransitions && overlay ? overlayDelay : 0);
};
this.hide = function() {
bootstrapCustomEvent.call(modal, hideEvent, component);
overlay = queryElement('.'+modalBackdropString);
overlayDelay = overlay && getTransitionDurationFromElement(overlay);
removeClass(modal,showClass);
modal[setAttribute](ariaHidden, true);
(function(){
setTimeout(function(){
hasClass(modal,'fade') ? emulateTransitionEnd(modal, triggerHide) : triggerHide();
}());
}, supportTransitions && overlay ? overlayDelay : 0);
};
this.setContent = function( content ) {
queryElement('.'+component+'-content',modal)[innerHTML] = content;
......@@ -6021,7 +6027,7 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
tabsContentContainer[style][height] = nextHeight + 'px'; // height animation
tabsContentContainer[offsetWidth];
emulateTransitionEnd(tabsContentContainer, triggerEnd);
},1);
},50);
}
} else {
tabs[isAnimating] = false;
......@@ -33088,12 +33094,13 @@ var map = {
function webpackContext(req) {
var id = webpackContextResolve(req);
return __webpack_require__(id);
var module = __webpack_require__(id);
return module;
}
function webpackContextResolve(req) {
var id = map[req];
if(!(id + 1)) { // check for number or string
var e = new Error("Cannot find module '" + req + "'");
var e = new Error('Cannot find module "' + req + '".');
e.code = 'MODULE_NOT_FOUND';
throw e;
}
......@@ -68354,9 +68361,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
return _converse.log(`Invalid JID "${jid}" provided in URL fragment`, Strophe.LogLevel.WARN);
}
Promise.all([_converse.api.waitUntil('rosterContactsFetched'), _converse.api.waitUntil('chatBoxesFetched')]).then(() => {
_converse.api.chats.open(jid);
});
}
_converse.router.route('converse/chat?jid=:jid', openChat);
......@@ -69279,18 +69284,21 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
},
'open'(jids, attrs) {
return new Promise((resolve, reject) => {
Promise.all([_converse.api.waitUntil('rosterContactsFetched'), _converse.api.waitUntil('chatBoxesFetched')]).then(() => {
if (_.isUndefined(jids)) {
_converse.log("chats.open: You need to provide at least one JID", Strophe.LogLevel.ERROR);
const err_msg = "chats.open: You need to provide at least one JID";
return null;
} else if (_.isString(jids)) {
const chatbox = _converse.api.chats.create(jids, attrs);
_converse.log(err_msg, Strophe.LogLevel.ERROR);
chatbox.trigger('show');
return chatbox;
reject(new Error(err_msg));
} else if (_.isString(jids)) {
resolve(_converse.api.chats.create(jids, attrs).trigger('show'));
} else {
resolve(_.map(jids, jid => _converse.api.chats.create(jid, attrs).trigger('show')));
}
return _.map(jids, jid => _converse.api.chats.create(jid, attrs).trigger('show'));
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
});
},
'get'(jids) {
......@@ -73835,8 +73843,8 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
throw new Error("converse-embedded: auto_join_rooms must be an Array");
}
if (_converse.auto_join_rooms.length !== 1 && _converse.auto_join_private_chats.length !== 1) {
throw new Error("converse-embedded: It doesn't make " + "sense to have the auto_join_rooms setting to zero or " + "more then one, since only one chat room can be open " + "at any time.");
if (_converse.auto_join_rooms.length > 1 && _converse.auto_join_private_chats.length > 1) {
throw new Error("converse-embedded: It doesn't make " + "sense to have the auto_join_rooms setting more then one, " + "since only one chat room can be open at any time.");
}
}
......@@ -79019,13 +79027,21 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
},
'open'(jids, attrs) {
return new Promise((resolve, reject) => {
_converse.api.waitUntil('chatBoxesFetched').then(() => {
if (_.isUndefined(jids)) {
throw new TypeError('rooms.open: You need to provide at least one JID');
const err_msg = 'rooms.open: You need to provide at least one JID';
_converse.log(err_msg, Strophe.LogLevel.ERROR);
reject(new TypeError(err_msg));
} else if (_.isString(jids)) {
return _converse.api.rooms.create(jids, attrs).trigger('show');
resolve(_converse.api.rooms.create(jids, attrs).trigger('show'));
} else {
resolve(_.map(jids, jid => _converse.api.rooms.create(jid, attrs).trigger('show')));
}
return _.map(jids, jid => _converse.api.rooms.create(jid, attrs).trigger('show'));
});
});
},
'get'(jids, attrs, create) {
......@@ -816,86 +816,75 @@ Note, for MUC chatrooms, you need to use the "rooms" grouping instead.
get
~~~
Returns an object representing a chatbox. The chatbox should already be open.
Returns an object representing a chat. The chat should already be open.
To return a single chatbox, provide the JID of the contact you're chatting
with in that chatbox:
To return a single chat, provide the JID of the contact you're chatting
with in that chat:
.. code-block:: javascript
_converse.api.chats.get('buddy@example.com')
To return an array of chatboxes, provide an array of JIDs:
To return an array of chats, provide an array of JIDs:
.. code-block:: javascript
_converse.api.chats.get(['buddy1@example.com', 'buddy2@example.com'])
To return all open chatboxes, call the method without any JIDs::
To return all open chats, call the method without any JIDs::
_converse.api.chats.get()
open
~~~~
Opens a chatbox and returns a `Backbone.View <http://backbonejs.org/#View>`_ object
representing a chatbox.
Opens a new chat.
It returns an promise which will resolve with a `Backbone.Model <https://backbonejs.org/#Model>`_ representing the chat.
Note that converse doesn't allow opening chats with users who aren't in your roster
(unless you have set :ref:`allow_non_roster_messaging` to ``true``).
Before opening a chat, you should first wait until the roster has been populated.
This is the :ref:`rosterContactsFetched` event/promise.
Besides that, it's a good idea to also first wait until already opened chatboxes
(which are cached in sessionStorage) have also been fetched from the cache.
This is the :ref:`chatBoxesFetched` event/promise.
These two events fire only once per session, so they're also available as promises.
So, to open a single chatbox:
So, to open a single chat:
.. code-block:: javascript
converse.plugins.add('myplugin', {
initialize: function() {
var _converse = this._converse;
Promise.all([
_converse.api.waitUntil('rosterContactsFetched'),
_converse.api.waitUntil('chatBoxesFetched')
]).then(function() {
// Note, buddy@example.org must be in your contacts roster!
_converse.api.chats.open('buddy@example.com')
_converse.api.chats.open('buddy@example.com').then((chat) => {
// Now you can do something with the chat model
});
}
});
To return an array of chatboxes, provide an array of JIDs:
To return an array of chats, provide an array of JIDs:
.. code-block:: javascript
converse.plugins.add('myplugin', {
initialize: function () {
var _converse = this._converse;
Promise.all([
_converse.api.waitUntil('rosterContactsFetched'),
_converse.api.waitUntil('chatBoxesFetched')
]).then(function() {
// Note, these users must first be in your contacts roster!
_converse.api.chats.open(['buddy1@example.com', 'buddy2@example.com'])
_converse.api.chats.open(['buddy1@example.com', 'buddy2@example.com']).then((chats) => {
// Now you can do something with the chat models
});
}
});
*The returned chatbox object contains the following methods:*
*The returned chat object contains the following methods:*
+-------------------+------------------------------------------+
| Method | Description |
+===================+==========================================+
| close | Close the chatbox. |
| close | Close the chat. |
+-------------------+------------------------------------------+
| focus | Focuses the chatbox textarea |
| focus | Focuses the chat textarea |
+-------------------+------------------------------------------+
| model.endOTR | End an OTR (Off-the-record) session. |
+-------------------+------------------------------------------+
......@@ -903,13 +892,13 @@ To return an array of chatboxes, provide an array of JIDs:
+-------------------+------------------------------------------+
| model.initiateOTR | Start an OTR (off-the-record) session. |
+-------------------+------------------------------------------+
| model.maximize | Minimize the chatbox. |
| model.maximize | Minimize the chat. |
+-------------------+------------------------------------------+
| model.minimize | Maximize the chatbox. |
| model.minimize | Maximize the chat. |
+-------------------+------------------------------------------+
| model.set | Set an attribute (i.e. mutator). |
+-------------------+------------------------------------------+
| show | Opens/shows the chatbox. |
| show | Opens/shows the chat. |
+-------------------+------------------------------------------+
*The get and set methods can be used to retrieve and change the following attributes:*
......@@ -917,9 +906,9 @@ To return an array of chatboxes, provide an array of JIDs:
+-------------+-----------------------------------------------------+
| Attribute | Description |
+=============+=====================================================+
| height | The height of the chatbox. |
| height | The height of the chat. |
+-------------+-----------------------------------------------------+
| url | The URL of the chatbox heading. |
| url | The URL of the chat heading. |
+-------------+-----------------------------------------------------+
The **chatviews** grouping
......@@ -1014,7 +1003,7 @@ The **rooms** grouping
get
~~~
Returns an object representing a multi user chatbox (room).
Returns an object representing a multi user chat (room).
It takes 3 parameters:
* the room JID (if not specified, all rooms will be returned).
......@@ -1046,7 +1035,7 @@ It takes 3 parameters:
open
~~~~
Opens a multi user chatbox and returns an object representing it.
Opens a multi user chat and returns an object representing it.
Similar to the ``chats.get`` API.
It takes 2 parameters:
......@@ -1055,7 +1044,7 @@ It takes 2 parameters:
* A map (object) containing any extra room attributes. For example, if you want
to specify the nickname, use ``{'nick': 'bloodninja'}``.
To open a single multi user chatbox, provide the JID of the room:
To open a single multi user chat, provide the JID of the room:
.. code-block:: javascript
......@@ -1328,7 +1317,7 @@ Parameters:
Returns a Promise which results with the VCard data for a particular JID or for
a `Backbone.Model` instance which represents an entity with a JID (such as a roster contact,
chatbox or chatroom occupant).
chat or chatroom occupant).
If a `Backbone.Model` instance is passed in, then it must have either a `jid`
attribute or a `muc_jid` attribute.
......
......@@ -2978,9 +2978,9 @@
"dev": true
},
"bootstrap.native": {
"version": "2.0.22",
"resolved": "https://registry.npmjs.org/bootstrap.native/-/bootstrap.native-2.0.22.tgz",
"integrity": "sha512-eypi4y1eKJoRt8cTwkZCI3QQ7W04rbv4VU1nBjBshqNXkONI7jO6tG3qZTwq9Zd+gDoeaQASyk6V185y+Y7KHQ==",
"version": "2.0.23",
"resolved": "https://registry.npmjs.org/bootstrap.native/-/bootstrap.native-2.0.23.tgz",
"integrity": "sha512-bcbVgqIjRkyiHd6DN8Y18BYjJTKvvnN3Msb7Yh6K5vGGsjRT3gV0IFKR3rcEYgJ5Kvg1egL9exIfN/Hvwn4wNA==",
"dev": true
},
"bourbon": {
......
......@@ -19,14 +19,16 @@
it("has a /help command to show the available commands",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
function (done, _converse) {
test_utils.createContacts(_converse, 'current');
test_utils.createContacts(_converse, 'current', 1);
_converse.emit('rosterContactsFetched');
test_utils.openControlBox();
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(_converse, contact_jid);
test_utils.waitUntil(() => _converse.chatboxes.length == 2).then(() => {
var view = _converse.chatboxviews.get(contact_jid);
test_utils.sendMessage(view, '/help');
......@@ -45,6 +47,7 @@
_converse.chatboxes.onMessage(msg);
expect(view.content.lastElementChild.textContent.trim().indexOf('hello world')).not.toBe(-1);
done();
});
}));
......@@ -108,38 +111,48 @@
});
}));
it("is created when you click on a roster item",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
it("is created when you click on a roster item", mock.initConverseWithPromises(
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
function (done, _converse) {
test_utils.createContacts(_converse, 'current');
_converse.emit('rosterContactsFetched');
test_utils.openControlBox();
var i, $el, jid, chatboxview;
let jid, online_contacts;
// openControlBox was called earlier, so the controlbox is
// visible, but no other chat boxes have been created.
expect(_converse.chatboxes.length).toEqual(1);
spyOn(_converse.chatboxviews, 'trimChats');
expect($("#conversejs .chatbox").length).toBe(1); // Controlbox is open
expect(document.querySelectorAll("#conversejs .chatbox").length).toBe(1); // Controlbox is open
test_utils.waitUntil(function () {
return $(_converse.rosterview.el).find('.roster-group li').length;
}, 700).then(function () {
var online_contacts = $(_converse.rosterview.el).find('.roster-group .current-xmpp-contact a.open-chat');
test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group li').length, 700).then(function () {
online_contacts = _converse.rosterview.el.querySelectorAll('.roster-group .current-xmpp-contact a.open-chat');
expect(online_contacts.length).toBe(15);
for (i=0; i<online_contacts.length; i++) {
$el = $(online_contacts[i]);
jid = $el.text().trim().replace(/ /g,'.').toLowerCase() + '@localhost';
$el[0].click();
chatboxview = _converse.chatboxviews.get(jid);
expect(_converse.chatboxes.length).toEqual(i+2);
const el = online_contacts[0];
jid = el.textContent.trim().replace(/ /g,'.').toLowerCase() + '@localhost';
el.click();
return test_utils.waitUntil(() => _converse.chatboxes.length == 2);
}).then(() => {
const chatboxview = _converse.chatboxviews.get(jid);
expect(_converse.chatboxviews.trimChats).toHaveBeenCalled();
// Check that new chat boxes are created to the left of the
// controlbox (but to the right of all existing chat boxes)
expect($("#conversejs .chatbox").length).toBe(i+2);
expect($("#conversejs .chatbox")[1].id).toBe(chatboxview.model.get('box_id'));
}
expect(document.querySelectorAll("#conversejs .chatbox").length).toBe(2);
expect(document.querySelectorAll("#conversejs .chatbox")[1].id).toBe(chatboxview.model.get('box_id'));
online_contacts[1].click();
return test_utils.waitUntil(() => _converse.chatboxes.length == 3);
}).then(() => {
const el = online_contacts[1];
const new_jid = el.textContent.trim().replace(/ /g,'.').toLowerCase() + '@localhost';
const chatboxview = _converse.chatboxviews.get(jid);
const new_chatboxview = _converse.chatboxviews.get(new_jid);
expect(_converse.chatboxviews.trimChats).toHaveBeenCalled();
// Check that new chat boxes are created to the left of the
// controlbox (but to the right of all existing chat boxes)
expect(document.querySelectorAll("#conversejs .chatbox").length).toBe(3);
expect(document.querySelectorAll("#conversejs .chatbox")[2].id).toBe(chatboxview.model.get('box_id'));
expect(document.querySelectorAll("#conversejs .chatbox")[1].id).toBe(new_chatboxview.model.get('box_id'));
done();
});
}));
......
......@@ -66,16 +66,18 @@
it("shows the number of unread mentions received",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
function (done, _converse) {
test_utils.createContacts(_converse, 'all').openControlBox();
_converse.emit('rosterContactsFetched');
var contacts_panel = _converse.chatboxviews.get('controlbox').contactspanel;
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(_converse, sender_jid);
var chatview = _converse.chatboxviews.get(sender_jid);
return test_utils.waitUntil(() => _converse.chatboxes.length).then(() => {
const chatview = _converse.chatboxviews.get(sender_jid);
chatview.model.set({'minimized': true});
expect(_.isNull(_converse.chatboxviews.el.querySelector('.restore-chat .message-count'))).toBeTruthy();
......@@ -107,6 +109,7 @@
expect(_.isNull(_converse.chatboxviews.el.querySelector('.restore-chat .message-count'))).toBeTruthy();
expect(_.isNull(_converse.rosterview.el.querySelector('.msgs-indicator'))).toBeTruthy();
done();
});
}));
});
......
(function (root, factory) {
define([
"jquery",
"jasmine",
"mock",
"test-utils"], factory);
} (this, function ($, jasmine, mock, test_utils) {
var b64_sha1 = converse.env.b64_sha1;
var _ = converse.env._;
} (this, function (jasmine, mock, test_utils) {
const b64_sha1 = converse.env.b64_sha1,
_ = converse.env._,
u = converse.env.utils;
describe("Converse", function() {
......@@ -274,59 +274,72 @@
describe("The \"chats\" API", function() {
it("has a method 'get' which returns the chatbox model", mock.initConverseWithPromises(
null, ['rosterInitialized'], {}, function (done, _converse) {
it("has a method 'get' which returns the promise that resolves to a chat model", mock.initConverseWithPromises(
null, ['rosterInitialized', 'chatBoxesInitialized'], {}, function (done, _converse) {
test_utils.openControlBox();
test_utils.createContacts(_converse, 'current');
test_utils.createContacts(_converse, 'current', 2);
_converse.emit('rosterContactsFetched');
// Test on chat that doesn't exist.
expect(_converse.api.chats.get('non-existing@jabber.org')).toBeFalsy();
var jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
const jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
const jid2 = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
// Test on chat that's not open
var box = _converse.api.chats.get(jid);
let box = _converse.api.chats.get(jid);
expect(typeof box === 'undefined').toBeTruthy();
var chatboxview = _converse.chatboxviews.get(jid);
// Test for single JID
expect(_converse.chatboxes.length).toBe(1);
// Test for one JID
test_utils.openChatBoxFor(_converse, jid);
test_utils.waitUntil(() => _converse.chatboxes.length == 1).then(() => {
box = _converse.api.chats.get(jid);
expect(box instanceof Object).toBeTruthy();
expect(box.get('box_id')).toBe(b64_sha1(jid));
chatboxview = _converse.chatboxviews.get(jid);
expect($(chatboxview.el).is(':visible')).toBeTruthy();
const chatboxview = _converse.chatboxviews.get(jid);
expect(u.isVisible(chatboxview.el)).toBeTruthy();
// Test for multiple JIDs
var jid2 = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(_converse, jid2);
var list = _converse.api.chats.get([jid, jid2]);
return test_utils.waitUntil(() => _converse.chatboxes.length == 2);
}).then(() => {
const list = _converse.api.chats.get([jid, jid2]);
expect(_.isArray(list)).toBeTruthy();
expect(list[0].get('box_id')).toBe(b64_sha1(jid));
expect(list[1].get('box_id')).toBe(b64_sha1(jid2));
done();
}).catch(_.partial(console.error, _));
}));
it("has a method 'open' which opens and returns the chatbox model", mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {}, function (done, _converse) {
it("has a method 'open' which opens and returns promise that resolves to a chat model", mock.initConverseWithPromises(
null, ['rosterGroupsFetched', 'chatBoxesInitialized'], {}, function (done, _converse) {
test_utils.openControlBox();
test_utils.createContacts(_converse, 'current');
var jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
var chatboxview;
test_utils.createContacts(_converse, 'current', 2);
_converse.emit('rosterContactsFetched');
const jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
const jid2 = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
// Test on chat that doesn't exist.
expect(_converse.api.chats.get('non-existing@jabber.org')).toBeFalsy();
var box = _converse.api.chats.open(jid);
return _converse.api.chats.open(jid).then((box) => {
expect(box instanceof Object).toBeTruthy();
expect(box.get('box_id')).toBe(b64_sha1(jid));
expect(
_.keys(box),
['close', 'endOTR', 'focus', 'get', 'initiateOTR', 'is_chatroom', 'maximize', 'minimize', 'open', 'set']
);
chatboxview = _converse.chatboxviews.get(jid);
expect($(chatboxview.el).is(':visible')).toBeTruthy();
const chatboxview = _converse.chatboxviews.get(jid);
expect(u.isVisible(chatboxview.el)).toBeTruthy();
// Test for multiple JIDs
var jid2 = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
var list = _converse.api.chats.open([jid, jid2]);
return _converse.api.chats.open([jid, jid2]);
}).then((list) => {
expect(_.isArray(list)).toBeTruthy();
expect(list[0].get('box_id')).toBe(b64_sha1(jid));
expect(list[1].get('box_id')).toBe(b64_sha1(jid2));
done();
});
}));
});
......
(function (root, factory) {
define(["jquery", "jasmine", "mock", "test-utils"], factory);
} (this, function ($, jasmine, mock, test_utils) {
var _ = converse.env._;
var $msg = converse.env.$msg;
const _ = converse.env._;
const $msg = converse.env.$msg;
const u = converse.env.utils;
describe("The Minimized Chats Widget", function () {
......@@ -62,9 +63,8 @@
expect(_converse.minimized_chats.toggleview.model.get('collapsed')).toBeFalsy();
_converse.minimized_chats.el.querySelector('#toggle-minimized-chats').click();
return test_utils.waitUntil(function () {
return $(_converse.minimized_chats.el.querySelector('.minimized-chats-flyout')).is(':visible');
}, 500).then(function () {
return test_utils.waitUntil(() => u.isVisible(u.isVisible(_converse.minimized_chats.el.querySelector('.minimized-chats-flyout'))))
.then(function () {
expect(_converse.minimized_chats.toggleview.model.get('collapsed')).toBeTruthy();
done();
});
......@@ -72,20 +72,28 @@
it("shows the number messages received to minimized chats",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
function (done, _converse) {
test_utils.createContacts(_converse, 'current');
_converse.emit('rosterContactsFetched');
test_utils.openControlBox();
_converse.minimized_chats.toggleview.model.browserStorage._clear();
_converse.minimized_chats.initToggle();
var i, contact_jid, chatview, msg;
_converse.minimized_chats.toggleview.model.set({'collapsed': true});
expect($(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count')).is(':visible')).toBeFalsy();
const unread_el = _converse.minimized_chats.toggleview.el.querySelector('.unread-message-count');
expect(_.isNull(unread_el)).toBe(true);
for (i=0; i<3; i++) {
contact_jid = mock.cur_names[i].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(_converse, contact_jid);
}
return test_utils.waitUntil(() => _converse.chatboxes.length == 4).then(() => {
for (i=0; i<3; i++) {
chatview = _converse.chatboxviews.get(contact_jid);
chatview.model.set({'minimized': true});
msg = $msg({
......@@ -136,6 +144,7 @@
}).c('inactive', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree());
expect($(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count')).text()).toBe((i).toString());
done();
});
}));
it("shows the number messages received to minimized groupchats",
......
......@@ -16,22 +16,23 @@
it("can be used to remove a contact",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
function (done, _converse) {
test_utils.createContacts(_converse, 'current');
_converse.emit('rosterContactsFetched');
let view, show_modal_button, modal;
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(_converse, contact_jid);
const view = _converse.chatboxviews.get(contact_jid);
const show_modal_button = view.el.querySelector('.show-user-details-modal');
return test_utils.waitUntil(() => _converse.chatboxes.length).then(() => {
view = _converse.chatboxviews.get(contact_jid);
show_modal_button = view.el.querySelector('.show-user-details-modal');
expect(u.isVisible(show_modal_button)).toBeTruthy();
show_modal_button.click();
const modal = view.user_details_modal;
test_utils.waitUntil(() => u.isVisible(modal.el), 1000)
.then(function () {
modal = view.user_details_modal;
return test_utils.waitUntil(() => u.isVisible(modal.el), 1000);
}).then(function () {
spyOn(window, 'confirm').and.returnValue(true);
spyOn(view.model.contact, 'removeFromRoster').and.callFake(function (callback) {
callback();
......
......@@ -69,12 +69,7 @@
Strophe.LogLevel.WARN
);
}
Promise.all([
_converse.api.waitUntil('rosterContactsFetched'),
_converse.api.waitUntil('chatBoxesFetched')
]).then(() => {
_converse.api.chats.open(jid);
});
}
_converse.router.route('converse/chat?jid=:jid', openChat);
......@@ -905,15 +900,22 @@
});
},
'open' (jids, attrs) {
return new Promise((resolve, reject) => {
Promise.all([
_converse.api.waitUntil('rosterContactsFetched'),
_converse.api.waitUntil('chatBoxesFetched')
]).then(() => {
if (_.isUndefined(jids)) {
_converse.log("chats.open: You need to provide at least one JID", Strophe.LogLevel.ERROR);
return null;
const err_msg = "chats.open: You need to provide at least one JID";
_converse.log(err_msg, Strophe.LogLevel.ERROR);
reject(new Error(err_msg));
} else if (_.isString(jids)) {
const chatbox = _converse.api.chats.create(jids, attrs);
chatbox.trigger('show');
return chatbox;
resolve(_converse.api.chats.create(jids, attrs).trigger('show'));
} else {
resolve(_.map(jids, (jid) => _converse.api.chats.create(jid, attrs).trigger('show')));
}
return _.map(jids, (jid) => _converse.api.chats.create(jid, attrs).trigger('show'));
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
});
},
'get' (jids) {
if (_.isUndefined(jids)) {
......
......@@ -1254,14 +1254,23 @@
}
return _.map(jids, _.partial(createChatRoom, _, attrs));
},
'open' (jids, attrs) {
return new Promise((resolve, reject) => {
_converse.api.waitUntil('chatBoxesFetched').then(() => {
if (_.isUndefined(jids)) {
throw new TypeError('rooms.open: You need to provide at least one JID');
const err_msg = 'rooms.open: You need to provide at least one JID';
_converse.log(err_msg, Strophe.LogLevel.ERROR);
reject(new TypeError(err_msg));
} else if (_.isString(jids)) {
return _converse.api.rooms.create(jids, attrs).trigger('show');
resolve(_converse.api.rooms.create(jids, attrs).trigger('show'));
} else {
resolve(_.map(jids, (jid) => _converse.api.rooms.create(jid, attrs).trigger('show')));
}
return _.map(jids, (jid) => _converse.api.rooms.create(jid, attrs).trigger('show'));
});
});
},
'get' (jids, attrs, create) {
if (_.isString(attrs)) {
attrs = {'nick': attrs};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment