Commit 1ef29bee authored by JC Brand's avatar JC Brand

Use composition instead of overrides

parent d2b1f2c9
...@@ -189,12 +189,11 @@ The code for it would look something like this: ...@@ -189,12 +189,11 @@ The code for it would look something like this:
// Commonly used utilities and variables can be found under the "env" // Commonly used utilities and variables can be found under the "env"
// namespace of the "converse" global. // namespace of the "converse" global.
var Strophe = converse.env.Strophe, const Strophe = converse.env.Strophe,
$iq = converse.env.$iq, $iq = converse.env.$iq,
$msg = converse.env.$msg, $msg = converse.env.$msg,
$pres = converse.env.$pres, $pres = converse.env.$pres,
$build = converse.env.$build, $build = converse.env.$build,
b64_sha1 = converse.env.b64_sha1,
_ = converse.env._, _ = converse.env._,
dayjs = converse.env.dayjs; dayjs = converse.env.dayjs;
...@@ -300,7 +299,7 @@ In this case, you should first listen for the ``connection`` event, and then do ...@@ -300,7 +299,7 @@ In this case, you should first listen for the ``connection`` event, and then do
converse.plugins.add('myplugin', { converse.plugins.add('myplugin', {
initialize: function () { initialize: function () {
var _converse = this._converse; const _converse = this._converse;
_converse.api.listen.on('connected', function () { _converse.api.listen.on('connected', function () {
_converse.api.archive.query({'with': 'admin2@localhost'}); _converse.api.archive.query({'with': 'admin2@localhost'});
...@@ -363,26 +362,15 @@ generated by `generator-conversejs <https://github.com/jcbrand/generator-convers ...@@ -363,26 +362,15 @@ generated by `generator-conversejs <https://github.com/jcbrand/generator-convers
.. code-block:: javascript .. code-block:: javascript
(function (root, factory) { import converse from "@converse/headless/converse-core";
if (typeof define === 'function' && define.amd) {
// AMD. Register as a module called "myplugin"
define(["converse"], factory);
} else {
// Browser globals. If you're not using a module loader such as require.js,
// then this line below executes. Make sure that your plugin's <script> tag
// appears after the one from converse.js.
factory(converse);
}
}(this, function (converse) {
// Commonly used utilities and variables can be found under the "env" // Commonly used utilities and variables can be found under the "env"
// namespace of the "converse" global. // namespace of the "converse" global.
var Strophe = converse.env.Strophe, const Strophe = converse.env.Strophe,
$iq = converse.env.$iq, $iq = converse.env.$iq,
$msg = converse.env.$msg, $msg = converse.env.$msg,
$pres = converse.env.$pres, $pres = converse.env.$pres,
$build = converse.env.$build, $build = converse.env.$build,
b64_sha1 = converse.env.b64_sha1,
_ = converse.env._, _ = converse.env._,
dayjs = converse.env.dayjs; dayjs = converse.env.dayjs;
...@@ -395,7 +383,8 @@ generated by `generator-conversejs <https://github.com/jcbrand/generator-convers ...@@ -395,7 +383,8 @@ generated by `generator-conversejs <https://github.com/jcbrand/generator-convers
* available, in which case any overrides applicable to them will be * available, in which case any overrides applicable to them will be
* ignored. * ignored.
* *
* NB: These plugins need to have already been loaded via require.js. * NB: These plugins need to have already been imported or loaded,
* either in your plugin or somewhere else.
* *
* It's possible to make these dependencies "non-optional". * It's possible to make these dependencies "non-optional".
* If the setting "strict_plugin_dependencies" is set to true, * If the setting "strict_plugin_dependencies" is set to true,
...@@ -411,7 +400,7 @@ generated by `generator-conversejs <https://github.com/jcbrand/generator-convers ...@@ -411,7 +400,7 @@ generated by `generator-conversejs <https://github.com/jcbrand/generator-convers
/* Inside this method, you have access to the private /* Inside this method, you have access to the private
* `_converse` object. * `_converse` object.
*/ */
var _converse = this._converse; const _converse = this._converse;
_converse.log("The \"myplugin\" plugin is being initialized"); _converse.log("The \"myplugin\" plugin is being initialized");
/* From the `_converse` object you can get any configuration /* From the `_converse` object you can get any configuration
...@@ -476,7 +465,7 @@ generated by `generator-conversejs <https://github.com/jcbrand/generator-convers ...@@ -476,7 +465,7 @@ generated by `generator-conversejs <https://github.com/jcbrand/generator-convers
// Top-level functions in "overrides" are bound to the // Top-level functions in "overrides" are bound to the
// inner "_converse" object. // inner "_converse" object.
var _converse = this; const _converse = this;
// Your custom code can come here ... // Your custom code can come here ...
...@@ -496,7 +485,7 @@ generated by `generator-conversejs <https://github.com/jcbrand/generator-convers ...@@ -496,7 +485,7 @@ generated by `generator-conversejs <https://github.com/jcbrand/generator-convers
'sendPresence': function (type, status_message, jid) { 'sendPresence': function (type, status_message, jid) {
// The "_converse" object is available via the __super__ // The "_converse" object is available via the __super__
// attribute. // attribute.
var _converse = this.__super__._converse; const _converse = this.__super__._converse;
// Custom code can come here ... // Custom code can come here ...
...@@ -511,4 +500,3 @@ generated by `generator-conversejs <https://github.com/jcbrand/generator-convers ...@@ -511,4 +500,3 @@ generated by `generator-conversejs <https://github.com/jcbrand/generator-convers
} }
} }
}); });
}));
...@@ -42,19 +42,66 @@ converse.plugins.add('converse-bookmark-views', { ...@@ -42,19 +42,66 @@ converse.plugins.add('converse-bookmark-views', {
// Overrides mentioned here will be picked up by converse.js's // Overrides mentioned here will be picked up by converse.js's
// plugin architecture they will replace existing methods on the // plugin architecture they will replace existing methods on the
// relevant objects or classes. // relevant objects or classes.
//
// New functions which don't exist yet can also be added.
ChatRoomView: { ChatRoomView: {
events: { events: {
'click .toggle-bookmark': 'toggleBookmark' 'click .toggle-bookmark': 'toggleBookmark'
}, },
async renderHeading () {
this.__super__.renderHeading.apply(this, arguments);
const { _converse } = this.__super__;
if (_converse.allow_bookmarks) {
const supported = await _converse.checkBookmarksSupport();
if (supported) {
this.renderBookmarkToggle();
}
}
}
}
},
initialize () { initialize () {
this.__super__.initialize.apply(this, arguments); /* The initialize function gets called as soon as the plugin is
this.model.on('change:bookmarked', this.onBookmarked, this); * loaded by converse.js's plugin machinery.
this.setBookmarkState(); */
const { _converse } = this,
{ __ } = _converse;
// Configuration values for this plugin
// ====================================
// Refer to docs/source/configuration.rst for explanations of these
// configuration settings.
_converse.api.settings.update({
hide_open_bookmarks: true,
muc_respect_autojoin: true
});
Object.assign(_converse, {
removeBookmarkViaEvent (ev) {
/* Remove a bookmark as determined by the passed in
* event.
*/
ev.preventDefault();
const name = ev.target.getAttribute('data-bookmark-name');
const jid = ev.target.getAttribute('data-room-jid');
if (confirm(__("Are you sure you want to remove the bookmark \"%1$s\"?", name))) {
_.invokeMap(_converse.bookmarks.where({'jid': jid}), Backbone.Model.prototype.destroy);
}
},
addBookmarkViaEvent (ev) {
/* Add a bookmark as determined by the passed in
* event.
*/
ev.preventDefault();
const jid = ev.target.getAttribute('data-room-jid');
const chatroom = _converse.api.rooms.open(jid, {'bring_to_foreground': true});
_converse.chatboxviews.get(jid).renderBookmarkForm();
}, },
});
const bookmarkableChatRoomView = {
renderBookmarkToggle () { renderBookmarkToggle () {
if (this.el.querySelector('.chat-head .toggle-bookmark')) { if (this.el.querySelector('.chat-head .toggle-bookmark')) {
...@@ -80,21 +127,7 @@ converse.plugins.add('converse-bookmark-views', { ...@@ -80,21 +127,7 @@ converse.plugins.add('converse-bookmark-views', {
} }
}, },
async renderHeading () {
this.__super__.renderHeading.apply(this, arguments);
const { _converse } = this.__super__;
if (_converse.allow_bookmarks) {
const supported = await _converse.checkBookmarksSupport();
if (supported) {
this.renderBookmarkToggle();
}
}
},
onBookmarked () { onBookmarked () {
const { _converse } = this.__super__,
{ __ } = _converse;
const icon = this.el.querySelector('.toggle-bookmark'); const icon = this.el.querySelector('.toggle-bookmark');
if (_.isNull(icon)) { if (_.isNull(icon)) {
return; return;
...@@ -111,7 +144,6 @@ converse.plugins.add('converse-bookmark-views', { ...@@ -111,7 +144,6 @@ converse.plugins.add('converse-bookmark-views', {
setBookmarkState () { setBookmarkState () {
/* Set whether the groupchat is bookmarked or not. /* Set whether the groupchat is bookmarked or not.
*/ */
const { _converse } = this.__super__;
if (!_.isUndefined(_converse.bookmarks)) { if (!_.isUndefined(_converse.bookmarks)) {
const models = _converse.bookmarks.where({'jid': this.model.get('jid')}); const models = _converse.bookmarks.where({'jid': this.model.get('jid')});
if (!models.length) { if (!models.length) {
...@@ -125,7 +157,6 @@ converse.plugins.add('converse-bookmark-views', { ...@@ -125,7 +157,6 @@ converse.plugins.add('converse-bookmark-views', {
renderBookmarkForm () { renderBookmarkForm () {
this.hideChatRoomContents(); this.hideChatRoomContents();
if (!this.bookmark_form) { if (!this.bookmark_form) {
const { _converse } = this.__super__;
this.bookmark_form = new _converse.MUCBookmarkForm({ this.bookmark_form = new _converse.MUCBookmarkForm({
'model': this.model, 'model': this.model,
'chatroomview': this 'chatroomview': this
...@@ -141,7 +172,6 @@ converse.plugins.add('converse-bookmark-views', { ...@@ -141,7 +172,6 @@ converse.plugins.add('converse-bookmark-views', {
ev.preventDefault(); ev.preventDefault();
ev.stopPropagation(); ev.stopPropagation();
} }
const { _converse } = this.__super__;
const models = _converse.bookmarks.where({'jid': this.model.get('jid')}); const models = _converse.bookmarks.where({'jid': this.model.get('jid')});
if (!models.length) { if (!models.length) {
this.renderBookmarkForm(); this.renderBookmarkForm();
...@@ -151,48 +181,7 @@ converse.plugins.add('converse-bookmark-views', { ...@@ -151,48 +181,7 @@ converse.plugins.add('converse-bookmark-views', {
} }
} }
} }
}, Object.assign(_converse.ChatRoomView.prototype, bookmarkableChatRoomView);
initialize () {
/* The initialize function gets called as soon as the plugin is
* loaded by converse.js's plugin machinery.
*/
const { _converse } = this,
{ __ } = _converse;
// Configuration values for this plugin
// ====================================
// Refer to docs/source/configuration.rst for explanations of these
// configuration settings.
_converse.api.settings.update({
hide_open_bookmarks: true,
muc_respect_autojoin: true
});
Object.assign(_converse, {
removeBookmarkViaEvent (ev) {
/* Remove a bookmark as determined by the passed in
* event.
*/
ev.preventDefault();
const name = ev.target.getAttribute('data-bookmark-name');
const jid = ev.target.getAttribute('data-room-jid');
if (confirm(__("Are you sure you want to remove the bookmark \"%1$s\"?", name))) {
_.invokeMap(_converse.bookmarks.where({'jid': jid}), Backbone.Model.prototype.destroy);
}
},
addBookmarkViaEvent (ev) {
/* Add a bookmark as determined by the passed in
* event.
*/
ev.preventDefault();
const jid = ev.target.getAttribute('data-room-jid');
const chatroom = _converse.api.rooms.open(jid, {'bring_to_foreground': true});
_converse.chatboxviews.get(jid).renderBookmarkForm();
},
});
_converse.MUCBookmarkForm = Backbone.VDOMView.extend({ _converse.MUCBookmarkForm = Backbone.VDOMView.extend({
...@@ -368,6 +357,7 @@ converse.plugins.add('converse-bookmark-views', { ...@@ -368,6 +357,7 @@ converse.plugins.add('converse-bookmark-views', {
} }
}); });
/************************ BEGIN Event Handlers ************************/
const initBookmarkViews = async function () { const initBookmarkViews = async function () {
await _converse.api.waitUntil('roomsPanelRendered'); await _converse.api.waitUntil('roomsPanelRendered');
_converse.bookmarksview = new _converse.BookmarksView({'model': _converse.bookmarks}); _converse.bookmarksview = new _converse.BookmarksView({'model': _converse.bookmarks});
...@@ -381,5 +371,11 @@ converse.plugins.add('converse-bookmark-views', { ...@@ -381,5 +371,11 @@ converse.plugins.add('converse-bookmark-views', {
} }
_converse.api.listen.on('bookmarksInitialized', initBookmarkViews); _converse.api.listen.on('bookmarksInitialized', initBookmarkViews);
_converse.api.listen.on('chatRoomOpened', view => {
view.model.on('change:bookmarked', view.onBookmarked, view);
view.setBookmarkState();
});
/************************ END Event Handlers ************************/
} }
}); });
...@@ -105,23 +105,6 @@ converse.plugins.add('converse-controlbox', { ...@@ -105,23 +105,6 @@ converse.plugins.add('converse-controlbox', {
view.close(); view.close();
}); });
return this; return this;
},
getChatBoxWidth (view) {
const { _converse } = this.__super__;
const controlbox = this.get('controlbox');
if (view.model.get('id') === 'controlbox') {
/* We return the width of the controlbox or its toggle,
* depending on which is visible.
*/
if (!controlbox || !u.isVisible(controlbox.el)) {
return u.getOuterWidth(_converse.controlboxtoggle.el, true);
} else {
return u.getOuterWidth(controlbox.el, true);
}
} else {
return this.__super__.getChatBoxWidth.apply(this, arguments);
}
} }
}, },
...@@ -232,11 +215,12 @@ converse.plugins.add('converse-controlbox', { ...@@ -232,11 +215,12 @@ converse.plugins.add('converse-controlbox', {
* Triggered when the _converse.ControlBoxView has been initialized and therefore * Triggered when the _converse.ControlBoxView has been initialized and therefore
* exists. The controlbox contains the login and register forms when the user is * exists. The controlbox contains the login and register forms when the user is
* logged out and a list of the user's contacts and group chats when logged in. * logged out and a list of the user's contacts and group chats when logged in.
* @event _converse#chatBoxInitialized * @event _converse#controlboxInitialized
* @type { _converse.ControlBoxView } * @type { _converse.ControlBoxView }
* @example _converse.api.listen.on('controlboxInitialized', view => { ... }); * @example _converse.api.listen.on('controlboxInitialized', view => { ... });
*/ */
_converse.api.trigger('controlboxInitialized', this); _converse.api.trigger('controlboxInitialized', this);
_converse.api.trigger('chatBoxInitialized', this);
}, },
render () { render () {
......
...@@ -45,42 +45,6 @@ converse.plugins.add('converse-dragresize', { ...@@ -45,42 +45,6 @@ converse.plugins.add('converse-dragresize', {
// Overrides mentioned here will be picked up by converse.js's // Overrides mentioned here will be picked up by converse.js's
// plugin architecture they will replace existing methods on the // plugin architecture they will replace existing methods on the
// relevant objects or classes. // relevant objects or classes.
//
// New functions which don't exist yet can also be added.
registerGlobalEventHandlers () {
const that = this;
document.addEventListener('mousemove', function (ev) {
if (!that.resizing || !that.allow_dragresize) { return true; }
ev.preventDefault();
that.resizing.chatbox.resizeChatBox(ev);
});
document.addEventListener('mouseup', function (ev) {
if (!that.resizing || !that.allow_dragresize) { return true; }
ev.preventDefault();
const height = that.applyDragResistance(
that.resizing.chatbox.height,
that.resizing.chatbox.model.get('default_height')
);
const width = that.applyDragResistance(
that.resizing.chatbox.width,
that.resizing.chatbox.model.get('default_width')
);
if (that.connection.connected) {
that.resizing.chatbox.model.save({'height': height});
that.resizing.chatbox.model.save({'width': width});
} else {
that.resizing.chatbox.model.set({'height': height});
that.resizing.chatbox.model.set({'width': width});
}
that.resizing = null;
});
return this.__super__.registerGlobalEventHandlers.apply(this, arguments);
},
ChatBox: { ChatBox: {
initialize () { initialize () {
const { _converse } = this.__super__; const { _converse } = this.__super__;
...@@ -102,9 +66,24 @@ converse.plugins.add('converse-dragresize', { ...@@ -102,9 +66,24 @@ converse.plugins.add('converse-dragresize', {
'mousedown .dragresize-topleft': 'onStartDiagonalResize' 'mousedown .dragresize-topleft': 'onStartDiagonalResize'
}, },
initialize () { render () {
window.addEventListener('resize', _.debounce(this.setDimensions.bind(this), 100)); const result = this.__super__.render.apply(this, arguments);
this.__super__.initialize.apply(this, arguments); renderDragResizeHandles(this.__super__._converse, this);
this.setWidth();
return result;
},
_show () {
this.initDragResize().setDimensions();
this.__super__._show.apply(this, arguments);
}
},
HeadlinesBoxView: {
events: {
'mousedown .dragresize-top': 'onStartVerticalResize',
'mousedown .dragresize-left': 'onStartHorizontalResize',
'mousedown .dragresize-topleft': 'onStartDiagonalResize'
}, },
render () { render () {
...@@ -112,27 +91,70 @@ converse.plugins.add('converse-dragresize', { ...@@ -112,27 +91,70 @@ converse.plugins.add('converse-dragresize', {
renderDragResizeHandles(this.__super__._converse, this); renderDragResizeHandles(this.__super__._converse, this);
this.setWidth(); this.setWidth();
return result; return result;
}
}, },
setWidth () { ControlBoxView: {
// If a custom width is applied (due to drag-resizing), events: {
// then we need to set the width of the .chatbox element as well. 'mousedown .dragresize-top': 'onStartVerticalResize',
if (this.model.get('width')) { 'mousedown .dragresize-left': 'onStartHorizontalResize',
this.el.style.width = this.model.get('width'); 'mousedown .dragresize-topleft': 'onStartDiagonalResize'
}
}, },
_show () { render () {
const result = this.__super__.render.apply(this, arguments);
renderDragResizeHandles(this.__super__._converse, this);
this.setWidth();
return result;
},
renderLoginPanel () {
const result = this.__super__.renderLoginPanel.apply(this, arguments);
this.initDragResize().setDimensions(); this.initDragResize().setDimensions();
this.__super__._show.apply(this, arguments); return result;
},
renderControlBoxPane () {
const result = this.__super__.renderControlBoxPane.apply(this, arguments);
this.initDragResize().setDimensions();
return result;
}
},
ChatRoomView: {
events: {
'mousedown .dragresize-top': 'onStartVerticalResize',
'mousedown .dragresize-left': 'onStartHorizontalResize',
'mousedown .dragresize-topleft': 'onStartDiagonalResize'
}, },
render () {
const result = this.__super__.render.apply(this, arguments);
renderDragResizeHandles(this.__super__._converse, this);
this.setWidth();
return result;
}
}
},
initialize () {
/* The initialize function gets called as soon as the plugin is
* loaded by converse.js's plugin machinery.
*/
const { _converse } = this;
_converse.api.settings.update({
'allow_dragresize': true,
});
const dragResizable = {
initDragResize () { initDragResize () {
/* Determine and store the default box size. /* Determine and store the default box size.
* We need this information for the drag-resizing feature. * We need this information for the drag-resizing feature.
*/ */
const { _converse } = this.__super__, const flyout = this.el.querySelector('.box-flyout'),
flyout = this.el.querySelector('.box-flyout'),
style = window.getComputedStyle(flyout); style = window.getComputedStyle(flyout);
if (_.isUndefined(this.model.get('height'))) { if (_.isUndefined(this.model.get('height'))) {
...@@ -157,6 +179,34 @@ converse.plugins.add('converse-dragresize', { ...@@ -157,6 +179,34 @@ converse.plugins.add('converse-dragresize', {
return this; return this;
}, },
resizeChatBox (ev) {
let diff;
if (_converse.resizing.direction.indexOf('top') === 0) {
diff = ev.pageY - this.prev_pageY;
if (diff) {
this.height = ((this.height-diff) > (this.model.get('min_height') || 0)) ? (this.height-diff) : this.model.get('min_height');
this.prev_pageY = ev.pageY;
this.setChatBoxHeight(this.height);
}
}
if (_.includes(_converse.resizing.direction, 'left')) {
diff = this.prev_pageX - ev.pageX;
if (diff) {
this.width = ((this.width+diff) > (this.model.get('min_width') || 0)) ? (this.width+diff) : this.model.get('min_width');
this.prev_pageX = ev.pageX;
this.setChatBoxWidth(this.width);
}
}
},
setWidth () {
// If a custom width is applied (due to drag-resizing),
// then we need to set the width of the .chatbox element as well.
if (this.model.get('width')) {
this.el.style.width = this.model.get('width');
}
},
setDimensions () { setDimensions () {
// Make sure the chat box has the right height and width. // Make sure the chat box has the right height and width.
this.adjustToViewport(); this.adjustToViewport();
...@@ -165,7 +215,6 @@ converse.plugins.add('converse-dragresize', { ...@@ -165,7 +215,6 @@ converse.plugins.add('converse-dragresize', {
}, },
setChatBoxHeight (height) { setChatBoxHeight (height) {
const { _converse } = this.__super__;
if (height) { if (height) {
height = _converse.applyDragResistance(height, this.model.get('default_height'))+'px'; height = _converse.applyDragResistance(height, this.model.get('default_height'))+'px';
} else { } else {
...@@ -178,7 +227,6 @@ converse.plugins.add('converse-dragresize', { ...@@ -178,7 +227,6 @@ converse.plugins.add('converse-dragresize', {
}, },
setChatBoxWidth (width) { setChatBoxWidth (width) {
const { _converse } = this.__super__;
if (width) { if (width) {
width = _converse.applyDragResistance(width, this.model.get('default_width'))+'px'; width = _converse.applyDragResistance(width, this.model.get('default_width'))+'px';
} else { } else {
...@@ -208,7 +256,6 @@ converse.plugins.add('converse-dragresize', { ...@@ -208,7 +256,6 @@ converse.plugins.add('converse-dragresize', {
}, },
onStartVerticalResize (ev) { onStartVerticalResize (ev) {
const { _converse } = this.__super__;
if (!_converse.allow_dragresize) { return true; } if (!_converse.allow_dragresize) { return true; }
// Record element attributes for mouseMove(). // Record element attributes for mouseMove().
const flyout = this.el.querySelector('.box-flyout'), const flyout = this.el.querySelector('.box-flyout'),
...@@ -222,7 +269,6 @@ converse.plugins.add('converse-dragresize', { ...@@ -222,7 +269,6 @@ converse.plugins.add('converse-dragresize', {
}, },
onStartHorizontalResize (ev) { onStartHorizontalResize (ev) {
const { _converse } = this.__super__;
if (!_converse.allow_dragresize) { return true; } if (!_converse.allow_dragresize) { return true; }
const flyout = this.el.querySelector('.box-flyout'), const flyout = this.el.querySelector('.box-flyout'),
style = window.getComputedStyle(flyout); style = window.getComputedStyle(flyout);
...@@ -235,116 +281,13 @@ converse.plugins.add('converse-dragresize', { ...@@ -235,116 +281,13 @@ converse.plugins.add('converse-dragresize', {
}, },
onStartDiagonalResize (ev) { onStartDiagonalResize (ev) {
const { _converse } = this.__super__;
this.onStartHorizontalResize(ev); this.onStartHorizontalResize(ev);
this.onStartVerticalResize(ev); this.onStartVerticalResize(ev);
_converse.resizing.direction = 'topleft'; _converse.resizing.direction = 'topleft';
}, },
};
Object.assign(_converse.ChatBoxView.prototype, dragResizable);
resizeChatBox (ev) {
let diff;
const { _converse } = this.__super__;
if (_converse.resizing.direction.indexOf('top') === 0) {
diff = ev.pageY - this.prev_pageY;
if (diff) {
this.height = ((this.height-diff) > (this.model.get('min_height') || 0)) ? (this.height-diff) : this.model.get('min_height');
this.prev_pageY = ev.pageY;
this.setChatBoxHeight(this.height);
}
}
if (_.includes(_converse.resizing.direction, 'left')) {
diff = this.prev_pageX - ev.pageX;
if (diff) {
this.width = ((this.width+diff) > (this.model.get('min_width') || 0)) ? (this.width+diff) : this.model.get('min_width');
this.prev_pageX = ev.pageX;
this.setChatBoxWidth(this.width);
}
}
}
},
HeadlinesBoxView: {
events: {
'mousedown .dragresize-top': 'onStartVerticalResize',
'mousedown .dragresize-left': 'onStartHorizontalResize',
'mousedown .dragresize-topleft': 'onStartDiagonalResize'
},
initialize () {
window.addEventListener('resize', _.debounce(this.setDimensions.bind(this), 100));
return this.__super__.initialize.apply(this, arguments);
},
render () {
const result = this.__super__.render.apply(this, arguments);
renderDragResizeHandles(this.__super__._converse, this);
this.setWidth();
return result;
}
},
ControlBoxView: {
events: {
'mousedown .dragresize-top': 'onStartVerticalResize',
'mousedown .dragresize-left': 'onStartHorizontalResize',
'mousedown .dragresize-topleft': 'onStartDiagonalResize'
},
initialize () {
window.addEventListener('resize', _.debounce(this.setDimensions.bind(this), 100));
this.__super__.initialize.apply(this, arguments);
},
render () {
const result = this.__super__.render.apply(this, arguments);
renderDragResizeHandles(this.__super__._converse, this);
this.setWidth();
return result;
},
renderLoginPanel () {
const result = this.__super__.renderLoginPanel.apply(this, arguments);
this.initDragResize().setDimensions();
return result;
},
renderControlBoxPane () {
const result = this.__super__.renderControlBoxPane.apply(this, arguments);
this.initDragResize().setDimensions();
return result;
}
},
ChatRoomView: {
events: {
'mousedown .dragresize-top': 'onStartVerticalResize',
'mousedown .dragresize-left': 'onStartHorizontalResize',
'mousedown .dragresize-topleft': 'onStartDiagonalResize'
},
initialize () {
window.addEventListener('resize', _.debounce(this.setDimensions.bind(this), 100));
this.__super__.initialize.apply(this, arguments);
},
render () {
const result = this.__super__.render.apply(this, arguments);
renderDragResizeHandles(this.__super__._converse, this);
this.setWidth();
return result;
}
}
},
initialize () {
/* The initialize function gets called as soon as the plugin is
* loaded by converse.js's plugin machinery.
*/
const { _converse } = this;
_converse.api.settings.update({
allow_dragresize: true,
});
_converse.applyDragResistance = function (value, default_value) { _converse.applyDragResistance = function (value, default_value) {
/* This method applies some resistance around the /* This method applies some resistance around the
...@@ -363,6 +306,45 @@ converse.plugins.add('converse-dragresize', { ...@@ -363,6 +306,45 @@ converse.plugins.add('converse-dragresize', {
} }
return value; return value;
}; };
/************************ BEGIN Event Handlers ************************/
function registerGlobalEventHandlers () {
document.addEventListener('mousemove', function (ev) {
if (!_converse.resizing || !_converse.allow_dragresize) { return true; }
ev.preventDefault();
_converse.resizing.chatbox.resizeChatBox(ev);
});
document.addEventListener('mouseup', function (ev) {
if (!_converse.resizing || !_converse.allow_dragresize) { return true; }
ev.preventDefault();
const height = _converse.applyDragResistance(
_converse.resizing.chatbox.height,
_converse.resizing.chatbox.model.get('default_height')
);
const width = _converse.applyDragResistance(
_converse.resizing.chatbox.width,
_converse.resizing.chatbox.model.get('default_width')
);
if (_converse.connection.connected) {
_converse.resizing.chatbox.model.save({'height': height});
_converse.resizing.chatbox.model.save({'width': width});
} else {
_converse.resizing.chatbox.model.set({'height': height});
_converse.resizing.chatbox.model.set({'width': width});
}
_converse.resizing = null;
});
}
_converse.api.listen.on('registeredGlobalEventHandlers', registerGlobalEventHandlers);
_converse.api.listen.on('chatBoxInitialized', view => {
window.addEventListener('resize', _.debounce(() => view.setDimensions(), 100));
});
/************************ END Event Handlers ************************/
} }
}); });
...@@ -30,7 +30,7 @@ converse.plugins.add('converse-minimize', { ...@@ -30,7 +30,7 @@ converse.plugins.add('converse-minimize', {
* *
* NB: These plugins need to have already been loaded via require.js. * NB: These plugins need to have already been loaded via require.js.
*/ */
dependencies: ["converse-chatview", "converse-controlbox", "converse-muc", "converse-muc-views", "converse-headline"], dependencies: ["converse-chatview", "converse-controlbox", "converse-muc-views", "converse-headline", "converse-dragresize"],
enabled (_converse) { enabled (_converse) {
return _converse.view_mode === 'overlayed'; return _converse.view_mode === 'overlayed';
...@@ -57,20 +57,6 @@ converse.plugins.add('converse-minimize', { ...@@ -57,20 +57,6 @@ converse.plugins.add('converse-minimize', {
}); });
}, },
maximize () {
u.safeSave(this, {
'minimized': false,
'time_opened': (new Date()).getTime()
});
},
minimize () {
u.safeSave(this, {
'minimized': true,
'time_minimized': (new Date()).toISOString()
});
},
maybeShow (force) { maybeShow (force) {
if (!force && this.get('minimized')) { if (!force && this.get('minimized')) {
// Must return the chatbox // Must return the chatbox
...@@ -122,65 +108,17 @@ converse.plugins.add('converse-minimize', { ...@@ -122,65 +108,17 @@ converse.plugins.add('converse-minimize', {
if (!this.model.get('minimized')) { if (!this.model.get('minimized')) {
return this.__super__.setChatBoxWidth.apply(this, arguments); return this.__super__.setChatBoxWidth.apply(this, arguments);
} }
},
onMinimizedChanged (item) {
if (item.get('minimized')) {
this.minimize();
} else {
this.maximize();
}
},
maximize () {
// Restores a minimized chat box
const { _converse } = this.__super__;
this.insertIntoDOM();
if (!this.model.isScrolledUp()) {
this.model.clearUnreadMsgCounter();
}
this.show();
/**
* Triggered when a previously minimized chat gets maximized
* @event _converse#chatBoxMaximized
* @type { _converse.ChatBoxView }
* @example _converse.api.listen.on('chatBoxMaximized', view => { ... });
*/
_converse.api.trigger('chatBoxMaximized', this);
return this;
},
minimize (ev) {
const { _converse } = this.__super__;
if (ev && ev.preventDefault) { ev.preventDefault(); }
// save the scroll position to restore it on maximize
if (this.model.collection && this.model.collection.browserStorage) {
this.model.save({'scroll': this.content.scrollTop});
} else {
this.model.set({'scroll': this.content.scrollTop});
} }
this.setChatState(_converse.INACTIVE).model.minimize();
this.hide();
/**
* Triggered when a previously maximized chat gets Minimized
* @event _converse#chatBoxMinimized
* @type { _converse.ChatBoxView }
* @example _converse.api.listen.on('chatBoxMinimized', view => { ... });
*/
_converse.api.trigger('chatBoxMinimized', this);
},
}, },
ChatBoxHeading: { ChatBoxHeading: {
render () { render () {
const { _converse } = this.__super__, const { _converse } = this.__super__,
{ __ } = _converse; { __ } = _converse;
const result = this.__super__.render.apply(this, arguments); const result = this.__super__.render.apply(this, arguments);
const new_html = tpl_chatbox_minimize( const new_html = tpl_chatbox_minimize({
{info_minimize: __('Minimize this chat box')} 'info_minimize': __('Minimize this chat box')
); });
const el = this.el.querySelector('.toggle-chatbox-button'); const el = this.el.querySelector('.toggle-chatbox-button');
if (el) { if (el) {
el.outerHTML = new_html; el.outerHTML = new_html;
...@@ -227,11 +165,109 @@ converse.plugins.add('converse-minimize', { ...@@ -227,11 +165,109 @@ converse.plugins.add('converse-minimize', {
} }
return div.innerHTML; return div.innerHTML;
} }
}
}, },
ChatBoxViews: {
initialize () {
/* The initialize function gets called as soon as the plugin is
* loaded by Converse.js's plugin machinery.
*/
const { _converse } = this,
{ __ } = _converse;
// Add new HTML templates.
_converse.templates.chatbox_minimize = tpl_chatbox_minimize;
_converse.templates.toggle_chats = tpl_toggle_chats;
_converse.templates.trimmed_chat = tpl_trimmed_chat;
_converse.templates.chats_panel = tpl_chats_panel;
_converse.api.settings.update({
'no_trimming': false, // Set to true for tests
});
const minimizableChatBox = {
maximize () {
u.safeSave(this, {
'minimized': false,
'time_opened': (new Date()).getTime()
});
},
minimize () {
u.safeSave(this, {
'minimized': true,
'time_minimized': (new Date()).toISOString()
});
}
}
Object.assign(_converse.ChatBox.prototype, minimizableChatBox);
const minimizableChatBoxView = {
maximize () {
// Restores a minimized chat box
const { _converse } = this.__super__;
this.insertIntoDOM();
if (!this.model.isScrolledUp()) {
this.model.clearUnreadMsgCounter();
}
this.show();
/**
* Triggered when a previously minimized chat gets maximized
* @event _converse#chatBoxMaximized
* @type { _converse.ChatBoxView }
* @example _converse.api.listen.on('chatBoxMaximized', view => { ... });
*/
_converse.api.trigger('chatBoxMaximized', this);
return this;
},
minimize (ev) {
const { _converse } = this.__super__;
if (ev && ev.preventDefault) { ev.preventDefault(); }
// save the scroll position to restore it on maximize
if (this.model.collection && this.model.collection.browserStorage) {
this.model.save({'scroll': this.content.scrollTop});
} else {
this.model.set({'scroll': this.content.scrollTop});
}
this.setChatState(_converse.INACTIVE).model.minimize();
this.hide();
/**
* Triggered when a previously maximized chat gets Minimized
* @event _converse#chatBoxMinimized
* @type { _converse.ChatBoxView }
* @example _converse.api.listen.on('chatBoxMinimized', view => { ... });
*/
_converse.api.trigger('chatBoxMinimized', this);
},
onMinimizedChanged (item) {
if (item.get('minimized')) {
this.minimize();
} else {
this.maximize();
}
}
}
Object.assign(_converse.ChatBoxView.prototype, minimizableChatBoxView);
const chatTrimmer = {
getChatBoxWidth (view) { getChatBoxWidth (view) {
if (!view.model.get('minimized') && u.isVisible(view.el)) { if (view.model.get('id') === 'controlbox') {
const controlbox = this.get('controlbox');
// We return the width of the controlbox or its toggle,
// depending on which is visible.
if (!controlbox || !u.isVisible(controlbox.el)) {
return u.getOuterWidth(_converse.controlboxtoggle.el, true);
} else {
return u.getOuterWidth(controlbox.el, true);
}
} else if (!view.model.get('minimized') && u.isVisible(view.el)) {
return u.getOuterWidth(view.el, true); return u.getOuterWidth(view.el, true);
} }
return 0; return 0;
...@@ -315,25 +351,8 @@ converse.plugins.add('converse-minimize', { ...@@ -315,25 +351,8 @@ converse.plugins.add('converse-minimize', {
return model; return model;
} }
} }
}, Object.assign(_converse.ChatBoxViews.prototype, chatTrimmer);
initialize () {
/* The initialize function gets called as soon as the plugin is
* loaded by Converse.js's plugin machinery.
*/
const { _converse } = this,
{ __ } = _converse;
// Add new HTML templates.
_converse.templates.chatbox_minimize = tpl_chatbox_minimize;
_converse.templates.toggle_chats = tpl_toggle_chats;
_converse.templates.trimmed_chat = tpl_trimmed_chat;
_converse.templates.chats_panel = tpl_chats_panel;
_converse.api.settings.update({
no_trimming: false, // Set to true for phantomjs tests (where browser apparently has no width)
});
_converse.api.promises.add('minimizedChatsInitialized'); _converse.api.promises.add('minimizedChatsInitialized');
...@@ -525,6 +544,7 @@ converse.plugins.add('converse-minimize', { ...@@ -525,6 +544,7 @@ converse.plugins.add('converse-minimize', {
} }
}); });
/************************ BEGIN Event Handlers ************************/
Promise.all([ Promise.all([
_converse.api.waitUntil('connectionInitialized'), _converse.api.waitUntil('connectionInitialized'),
_converse.api.waitUntil('chatBoxViewsInitialized') _converse.api.waitUntil('chatBoxViewsInitialized')
...@@ -559,5 +579,6 @@ converse.plugins.add('converse-minimize', { ...@@ -559,5 +579,6 @@ converse.plugins.add('converse-minimize', {
_converse.chatboxviews.trimChats(chatbox); _converse.chatboxviews.trimChats(chatbox);
} }
}); });
/************************ END Event Handlers ************************/
} }
}); });
...@@ -62,41 +62,14 @@ converse.plugins.add('converse-muc-views', { ...@@ -62,41 +62,14 @@ converse.plugins.add('converse-muc-views', {
dependencies: ["converse-autocomplete", "converse-modal", "converse-controlbox", "converse-chatview"], dependencies: ["converse-autocomplete", "converse-modal", "converse-controlbox", "converse-chatview"],
overrides: { overrides: {
ControlBoxView: { ControlBoxView: {
renderRoomsPanel () {
const { _converse } = this.__super__;
if (this.roomspanel && u.isVisible(this.roomspanel.el)) {
return;
}
this.roomspanel = new _converse.RoomsPanel({
'model': new (_converse.RoomsPanelModel.extend({
'id': `converse.roomspanel${_converse.bare_jid}`, // Required by web storage
'browserStorage': new BrowserStorage[_converse.config.get('storage')](
`converse.roomspanel${_converse.bare_jid}`)
}))()
});
this.roomspanel.model.fetch();
this.el.querySelector('.controlbox-pane').insertAdjacentElement(
'beforeEnd', this.roomspanel.render().el);
/**
* Triggered once the section of the _converse.ControlBoxView
* which shows gropuchats has been rendered.
* @event _converse#roomsPanelRendered
* @example _converse.api.listen.on('roomsPanelRendered', () => { ... });
*/
_converse.api.trigger('roomsPanelRendered');
},
renderControlBoxPane () { renderControlBoxPane () {
const { _converse } = this.__super__; const { _converse } = this.__super__;
this.__super__.renderControlBoxPane.apply(this, arguments); this.__super__.renderControlBoxPane.apply(this, arguments);
if (_converse.allow_muc) { if (_converse.allow_muc) {
this.renderRoomsPanel(); this.renderRoomsPanel();
} }
}, }
} }
}, },
...@@ -123,6 +96,35 @@ converse.plugins.add('converse-muc-views', { ...@@ -123,6 +96,35 @@ converse.plugins.add('converse-muc-views', {
} }
}); });
Object.assign(_converse.ControlBoxView.prototype, {
renderRoomsPanel () {
if (this.roomspanel && u.isVisible(this.roomspanel.el)) {
return;
}
this.roomspanel = new _converse.RoomsPanel({
'model': new (_converse.RoomsPanelModel.extend({
'id': `converse.roomspanel${_converse.bare_jid}`, // Required by web storage
'browserStorage': new BrowserStorage[_converse.config.get('storage')](
`converse.roomspanel${_converse.bare_jid}`)
}))()
});
this.roomspanel.model.fetch();
this.el.querySelector('.controlbox-pane').insertAdjacentElement(
'beforeEnd', this.roomspanel.render().el);
/**
* Triggered once the section of the _converse.ControlBoxView
* which shows gropuchats has been rendered.
* @event _converse#roomsPanelRendered
* @example _converse.api.listen.on('roomsPanelRendered', () => { ... });
*/
_converse.api.trigger('roomsPanelRendered');
}
});
function ___ (str) { function ___ (str) {
/* This is part of a hack to get gettext to scan strings to be /* This is part of a hack to get gettext to scan strings to be
* translated. Strings we cannot send to the function above because * translated. Strings we cannot send to the function above because
...@@ -572,6 +574,7 @@ converse.plugins.add('converse-muc-views', { ...@@ -572,6 +574,7 @@ converse.plugins.add('converse-muc-views', {
* @example _converse.api.listen.on('chatRoomOpened', view => { ... }); * @example _converse.api.listen.on('chatRoomOpened', view => { ... });
*/ */
_converse.api.trigger('chatRoomOpened', this); _converse.api.trigger('chatRoomOpened', this);
_converse.api.trigger('chatBoxInitialized', this);
}, },
render () { render () {
......
...@@ -38,7 +38,7 @@ converse.plugins.add("converse-oauth", { ...@@ -38,7 +38,7 @@ converse.plugins.add("converse-oauth", {
/* For example, the private *_converse* object has a /* For example, the private *_converse* object has a
* method "onConnected". You can override that method as follows: * method "onConnected". You can override that method as follows:
*/ */
'LoginPanel': { LoginPanel: {
insertOAuthProviders () { insertOAuthProviders () {
const { _converse } = this.__super__; const { _converse } = this.__super__;
......
...@@ -172,6 +172,90 @@ converse.plugins.add('converse-omemo', { ...@@ -172,6 +172,90 @@ converse.plugins.add('converse-omemo', {
}, },
ChatBox: { ChatBox: {
async getMessageAttributesFromStanza (stanza, original_stanza) {
const { _converse } = this.__super__;
const encrypted = sizzle(`encrypted[xmlns="${Strophe.NS.OMEMO}"]`, original_stanza).pop(),
attrs = await this.__super__.getMessageAttributesFromStanza.apply(this, arguments);
if (!encrypted || !_converse.config.get('trusted')) {
return attrs;
} else {
return this.getEncryptionAttributesfromStanza(stanza, original_stanza, attrs);
}
},
async sendMessage (text, spoiler_hint) {
if (this.get('omemo_active') && text) {
const { _converse } = this.__super__;
const attrs = this.getOutgoingMessageAttributes(text, spoiler_hint);
attrs['is_encrypted'] = true;
attrs['plaintext'] = attrs.message;
try {
const devices = await _converse.getBundlesAndBuildSessions(this);
const stanza = await _converse.createOMEMOMessageStanza(this, this.messages.create(attrs), devices);
_converse.api.send(stanza);
} catch (e) {
this.handleMessageSendError(e);
return false;
}
return true;
} else {
return this.__super__.sendMessage.apply(this, arguments);
}
}
},
ChatBoxView: {
events: {
'click .toggle-omemo': 'toggleOMEMO'
},
initialize () {
this.__super__.initialize.apply(this, arguments);
this.model.on('change:omemo_active', this.renderOMEMOToolbarButton, this);
this.model.on('change:omemo_supported', this.onOMEMOSupportedDetermined, this);
},
showMessage (message) {
// We don't show a message if it's only keying material
if (!message.get('is_only_key')) {
return this.__super__.showMessage.apply(this, arguments);
}
}
},
ChatRoomView: {
events: {
'click .toggle-omemo': 'toggleOMEMO'
},
initialize () {
this.__super__.initialize.apply(this, arguments);
this.model.on('change:omemo_active', this.renderOMEMOToolbarButton, this);
this.model.on('change:omemo_supported', this.onOMEMOSupportedDetermined, this);
}
}
},
initialize () {
/* The initialize function gets called as soon as the plugin is
* loaded by Converse.js's plugin machinery.
*/
const { _converse } = this,
{ __ } = _converse;
_converse.api.promises.add(['OMEMOInitialized']);
_converse.NUM_PREKEYS = 100; // Set here so that tests can override
/**
* Mixin object that contains OMEMO-related methods for
* {@link _converse.ChatBox} or {@link _converse.ChatRoom} objects.
*
* @typedef {Object} OMEMOEnabledChatBox
*/
const OMEMOEnabledChatBox = {
async encryptMessage (plaintext) { async encryptMessage (plaintext) {
// The client MUST use fresh, randomly generated key/IV pairs // The client MUST use fresh, randomly generated key/IV pairs
...@@ -219,7 +303,6 @@ converse.plugins.add('converse-omemo', { ...@@ -219,7 +303,6 @@ converse.plugins.add('converse-omemo', {
}, },
reportDecryptionError (e) { reportDecryptionError (e) {
const { _converse } = this.__super__;
if (_converse.debug) { if (_converse.debug) {
const { __ } = _converse; const { __ } = _converse;
this.messages.create({ this.messages.create({
...@@ -231,8 +314,7 @@ converse.plugins.add('converse-omemo', { ...@@ -231,8 +314,7 @@ converse.plugins.add('converse-omemo', {
}, },
async handleDecryptedWhisperMessage (attrs, key_and_tag) { async handleDecryptedWhisperMessage (attrs, key_and_tag) {
const { _converse } = this.__super__, const encrypted = attrs.encrypted,
encrypted = attrs.encrypted,
devicelist = _converse.devicelists.getDeviceList(this.get('jid')); devicelist = _converse.devicelists.getDeviceList(this.get('jid'));
this.save('omemo_supported', true); this.save('omemo_supported', true);
...@@ -250,8 +332,7 @@ converse.plugins.add('converse-omemo', { ...@@ -250,8 +332,7 @@ converse.plugins.add('converse-omemo', {
}, },
decrypt (attrs) { decrypt (attrs) {
const { _converse } = this.__super__, const session_cipher = this.getSessionCipher(attrs.from, parseInt(attrs.encrypted.device_id, 10));
session_cipher = this.getSessionCipher(attrs.from, parseInt(attrs.encrypted.device_id, 10));
// https://xmpp.org/extensions/xep-0384.html#usecases-receiving // https://xmpp.org/extensions/xep-0384.html#usecases-receiving
if (attrs.encrypted.prekey === true) { if (attrs.encrypted.prekey === true) {
...@@ -284,8 +365,7 @@ converse.plugins.add('converse-omemo', { ...@@ -284,8 +365,7 @@ converse.plugins.add('converse-omemo', {
}, },
getEncryptionAttributesfromStanza (stanza, original_stanza, attrs) { getEncryptionAttributesfromStanza (stanza, original_stanza, attrs) {
const { _converse } = this.__super__, const encrypted = sizzle(`encrypted[xmlns="${Strophe.NS.OMEMO}"]`, original_stanza).pop(),
encrypted = sizzle(`encrypted[xmlns="${Strophe.NS.OMEMO}"]`, original_stanza).pop(),
header = encrypted.querySelector('header'), header = encrypted.querySelector('header'),
key = sizzle(`key[rid="${_converse.omemo_store.get('device_id')}"]`, encrypted).pop(); key = sizzle(`key[rid="${_converse.omemo_store.get('device_id')}"]`, encrypted).pop();
if (key) { if (key) {
...@@ -303,22 +383,8 @@ converse.plugins.add('converse-omemo', { ...@@ -303,22 +383,8 @@ converse.plugins.add('converse-omemo', {
} }
}, },
async getMessageAttributesFromStanza (stanza, original_stanza) {
const { _converse } = this.__super__,
encrypted = sizzle(`encrypted[xmlns="${Strophe.NS.OMEMO}"]`, original_stanza).pop(),
attrs = await this.__super__.getMessageAttributesFromStanza.apply(this, arguments);
if (!encrypted || !_converse.config.get('trusted')) {
return attrs;
} else {
return this.getEncryptionAttributesfromStanza(stanza, original_stanza, attrs);
}
},
getSessionCipher (jid, id) { getSessionCipher (jid, id) {
const { _converse } = this.__super__, const address = new libsignal.SignalProtocolAddress(jid, id);
address = new libsignal.SignalProtocolAddress(jid, id);
this.session_cipher = new window.libsignal.SessionCipher(_converse.omemo_store, address); this.session_cipher = new window.libsignal.SessionCipher(_converse.omemo_store, address);
return this.session_cipher; return this.session_cipher;
}, },
...@@ -330,8 +396,6 @@ converse.plugins.add('converse-omemo', { ...@@ -330,8 +396,6 @@ converse.plugins.add('converse-omemo', {
}, },
handleMessageSendError (e) { handleMessageSendError (e) {
const { _converse } = this.__super__,
{ __ } = _converse;
if (e.name === 'IQError') { if (e.name === 'IQError') {
this.save('omemo_supported', false); this.save('omemo_supported', false);
...@@ -355,46 +419,12 @@ converse.plugins.add('converse-omemo', { ...@@ -355,46 +419,12 @@ converse.plugins.add('converse-omemo', {
} else { } else {
throw e; throw e;
} }
},
async sendMessage (text, spoiler_hint) {
if (this.get('omemo_active') && text) {
const { _converse } = this.__super__;
const attrs = this.getOutgoingMessageAttributes(text, spoiler_hint);
attrs['is_encrypted'] = true;
attrs['plaintext'] = attrs.message;
try {
const devices = await _converse.getBundlesAndBuildSessions(this);
const stanza = await _converse.createOMEMOMessageStanza(this, this.messages.create(attrs), devices);
_converse.api.send(stanza);
} catch (e) {
this.handleMessageSendError(e);
return false;
}
return true;
} else {
return this.__super__.sendMessage.apply(this, arguments);
} }
} }
}, Object.assign(_converse.ChatBox.prototype, OMEMOEnabledChatBox);
ChatBoxView: {
events: {
'click .toggle-omemo': 'toggleOMEMO'
},
initialize () { const OMEMOEnabledChatView = {
this.__super__.initialize.apply(this, arguments);
this.model.on('change:omemo_active', this.renderOMEMOToolbarButton, this);
this.model.on('change:omemo_supported', this.onOMEMOSupportedDetermined, this);
},
showMessage (message) {
// We don't show a message if it's only keying material
if (!message.get('is_only_key')) {
return this.__super__.showMessage.apply(this, arguments);
}
},
onOMEMOSupportedDetermined () { onOMEMOSupportedDetermined () {
if (!this.model.get('omemo_supported') && this.model.get('omemo_active')) { if (!this.model.get('omemo_supported') && this.model.get('omemo_active')) {
...@@ -405,82 +435,47 @@ converse.plugins.add('converse-omemo', { ...@@ -405,82 +435,47 @@ converse.plugins.add('converse-omemo', {
}, },
renderOMEMOToolbarButton () { renderOMEMOToolbarButton () {
const { _converse } = this.__super__, if (this.model.get('type') !== _converse.CHATROOMS_TYPE ||
{ __ } = _converse, this.model.features.get('membersonly') &&
icon = this.el.querySelector('.toggle-omemo'), this.model.features.get('nonanonymous')) {
html = tpl_toolbar_omemo(Object.assign(this.model.toJSON(), {'__': __}));
const icon = this.el.querySelector('.toggle-omemo');
const html = tpl_toolbar_omemo(Object.assign(this.model.toJSON(), {'__': __}));
if (icon) { if (icon) {
icon.outerHTML = html; icon.outerHTML = html;
} else { } else {
this.el.querySelector('.chat-toolbar').insertAdjacentHTML('beforeend', html); this.el.querySelector('.chat-toolbar').insertAdjacentHTML('beforeend', html);
} }
}, } else {
const icon = this.el.querySelector('.toggle-omemo');
toggleOMEMO (ev) { if (icon) {
const { _converse } = this.__super__, { __ } = _converse; icon.parentElement.removeChild(icon);
if (!this.model.get('omemo_supported')) {
return _converse.api.alert.show(
Strophe.LogLevel.ERROR,
__('Error'),
[__("Cannot use end-to-end encryption because %1$s uses a client that doesn't support OMEMO.",
this.model.contact.getDisplayName()
)]
)
} }
ev.preventDefault();
this.model.save({'omemo_active': !this.model.get('omemo_active')});
} }
}, },
ChatRoomView: {
events: {
'click .toggle-omemo': 'toggleOMEMO'
},
initialize () {
this.__super__.initialize.apply(this, arguments);
this.model.on('change:omemo_active', this.renderOMEMOToolbarButton, this);
this.model.on('change:omemo_supported', this.onOMEMOSupportedDetermined, this);
},
toggleOMEMO (ev) { toggleOMEMO (ev) {
const { _converse } = this.__super__, { __ } = _converse;
if (!this.model.get('omemo_supported')) { if (!this.model.get('omemo_supported')) {
return _converse.api.alert.show( let messages;
Strophe.LogLevel.ERROR, if (this.model.get('type') === _converse.CHATROOMS_TYPE) {
__('Error'), messages = [__(
[__('Cannot use end-to-end encryption in this groupchat, '+ 'Cannot use end-to-end encryption in this groupchat, '+
'either the groupchat has some anonymity or not all participants support OMEMO.')] 'either the groupchat has some anonymity or not all participants support OMEMO.'
); )];
}
ev.preventDefault();
this.model.save({'omemo_active': !this.model.get('omemo_active')});
},
renderOMEMOToolbarButton () {
if (this.model.features.get('membersonly') && this.model.features.get('nonanonymous')) {
this.__super__.renderOMEMOToolbarButton.apply(arguments);
} else { } else {
const icon = this.el.querySelector('.toggle-omemo'); messages = [__(
if (icon) { "Cannot use end-to-end encryption because %1$s uses a client that doesn't support OMEMO.",
icon.parentElement.removeChild(icon); this.model.contact.getDisplayName()
)];
} }
return _converse.api.alert.show(Strophe.LogLevel.ERROR, __('Error'), messages);
} }
ev.preventDefault();
this.model.save({'omemo_active': !this.model.get('omemo_active')});
} }
} }
}, Object.assign(_converse.ChatBoxView.prototype, OMEMOEnabledChatView);
initialize () {
/* The initialize function gets called as soon as the plugin is
* loaded by Converse.js's plugin machinery.
*/
const { _converse } = this,
{ __ } = _converse;
_converse.api.promises.add(['OMEMOInitialized']);
_converse.NUM_PREKEYS = 100; // Set here so that tests can override
async function generateFingerprint (device) { async function generateFingerprint (device) {
if (_.get(device.get('bundle'), 'fingerprint')) { if (_.get(device.get('bundle'), 'fingerprint')) {
......
...@@ -45,17 +45,6 @@ converse.plugins.add('converse-register', { ...@@ -45,17 +45,6 @@ converse.plugins.add('converse-register', {
// New functions which don't exist yet can also be added. // New functions which don't exist yet can also be added.
LoginPanel: { LoginPanel: {
insertRegisterLink () {
const { _converse } = this.__super__;
if (_.isUndefined(this.registerlinkview)) {
this.registerlinkview = new _converse.RegisterLinkView({'model': this.model});
this.registerlinkview.render();
this.el.querySelector('.buttons').insertAdjacentElement('afterend', this.registerlinkview.el);
}
this.registerlinkview.render();
},
render (cfg) { render (cfg) {
const { _converse } = this.__super__; const { _converse } = this.__super__;
this.__super__.render.apply(this, arguments); this.__super__.render.apply(this, arguments);
...@@ -67,11 +56,50 @@ converse.plugins.add('converse-register', { ...@@ -67,11 +56,50 @@ converse.plugins.add('converse-register', {
}, },
ControlBoxView: { ControlBoxView: {
renderLoginPanel () {
/* Also render a registration panel, when rendering the
* login panel.
*/
this.__super__.renderLoginPanel.apply(this, arguments);
this.renderRegistrationPanel();
return this;
}
}
},
initialize () { initialize () {
this.__super__.initialize.apply(this, arguments); /* The initialize function gets called as soon as the plugin is
this.model.on('change:active-form', this.showLoginOrRegisterForm.bind(this)) * loaded by converse.js's plugin machinery.
}, */
const { _converse } = this,
{ __ } = _converse;
_converse.CONNECTION_STATUS[Strophe.Status.REGIFAIL] = 'REGIFAIL';
_converse.CONNECTION_STATUS[Strophe.Status.REGISTERED] = 'REGISTERED';
_converse.CONNECTION_STATUS[Strophe.Status.CONFLICT] = 'CONFLICT';
_converse.CONNECTION_STATUS[Strophe.Status.NOTACCEPTABLE] = 'NOTACCEPTABLE';
_converse.api.settings.update({
'allow_registration': true,
'domain_placeholder': __(" e.g. conversejs.org"), // Placeholder text shown in the domain input on the registration form
'providers_link': 'https://compliance.conversations.im/', // Link to XMPP providers shown on registration page
'registration_domain': ''
});
Object.assign(_converse.LoginPanel.prototype, {
insertRegisterLink () {
if (_.isUndefined(this.registerlinkview)) {
this.registerlinkview = new _converse.RegisterLinkView({'model': this.model});
this.registerlinkview.render();
this.el.querySelector('.buttons').insertAdjacentElement('afterend', this.registerlinkview.el);
}
this.registerlinkview.render();
}
});
Object.assign(_converse.ControlBoxView.prototype, {
showLoginOrRegisterForm () { showLoginOrRegisterForm () {
const { _converse } = this.__super__; const { _converse } = this.__super__;
...@@ -88,7 +116,6 @@ converse.plugins.add('converse-register', { ...@@ -88,7 +116,6 @@ converse.plugins.add('converse-register', {
}, },
renderRegistrationPanel () { renderRegistrationPanel () {
const { _converse } = this.__super__;
if (_converse.allow_registration) { if (_converse.allow_registration) {
this.registerpanel = new _converse.RegisterPanel({ this.registerpanel = new _converse.RegisterPanel({
'model': this.model 'model': this.model
...@@ -102,36 +129,7 @@ converse.plugins.add('converse-register', { ...@@ -102,36 +129,7 @@ converse.plugins.add('converse-register', {
this.showLoginOrRegisterForm(); this.showLoginOrRegisterForm();
} }
return this; return this;
},
renderLoginPanel () {
/* Also render a registration panel, when rendering the
* login panel.
*/
this.__super__.renderLoginPanel.apply(this, arguments);
this.renderRegistrationPanel();
return this;
}
} }
},
initialize () {
/* The initialize function gets called as soon as the plugin is
* loaded by converse.js's plugin machinery.
*/
const { _converse } = this,
{ __ } = _converse;
_converse.CONNECTION_STATUS[Strophe.Status.REGIFAIL] = 'REGIFAIL';
_converse.CONNECTION_STATUS[Strophe.Status.REGISTERED] = 'REGISTERED';
_converse.CONNECTION_STATUS[Strophe.Status.CONFLICT] = 'CONFLICT';
_converse.CONNECTION_STATUS[Strophe.Status.NOTACCEPTABLE] = 'NOTACCEPTABLE';
_converse.api.settings.update({
'allow_registration': true,
'domain_placeholder': __(" e.g. conversejs.org"), // Placeholder text shown in the domain input on the registration form
'providers_link': 'https://compliance.conversations.im/', // Link to XMPP providers shown on registration page
'registration_domain': ''
}); });
...@@ -679,6 +677,12 @@ converse.plugins.add('converse-register', { ...@@ -679,6 +677,12 @@ converse.plugins.add('converse-register', {
return false; return false;
} }
}); });
/************************ BEGIN Event Handlers ************************/
_converse.api.listen.on('controlboxInitialized', view => {
view.model.on('change:active-form', view.showLoginOrRegisterForm, view);
});
/************************ END Event Handlers ************************/
} }
}); });
...@@ -29,27 +29,6 @@ converse.plugins.add('converse-rosterview', { ...@@ -29,27 +29,6 @@ converse.plugins.add('converse-rosterview', {
dependencies: ["converse-roster", "converse-modal"], dependencies: ["converse-roster", "converse-modal"],
overrides: {
// Overrides mentioned here will be picked up by converse.js's
// plugin architecture they will replace existing methods on the
// relevant objects or classes.
//
// New functions which don't exist yet can also be added.
afterReconnected () {
this.__super__.afterReconnected.apply(this, arguments);
},
RosterGroups: {
comparator () {
// RosterGroupsComparator only gets set later (once i18n is
// set up), so we need to wrap it in this nameless function.
const { _converse } = this.__super__;
return _converse.RosterGroupsComparator.apply(this, arguments);
}
}
},
initialize () { initialize () {
/* The initialize function gets called as soon as the plugin is /* The initialize function gets called as soon as the plugin is
* loaded by converse.js's plugin machinery. * loaded by converse.js's plugin machinery.
...@@ -78,36 +57,6 @@ converse.plugins.add('converse-rosterview', { ...@@ -78,36 +57,6 @@ converse.plugins.add('converse-rosterview', {
'away': __('This contact is away') 'away': __('This contact is away')
}; };
const LABEL_GROUPS = __('Groups'); const LABEL_GROUPS = __('Groups');
const HEADER_CURRENT_CONTACTS = __('My contacts');
const HEADER_PENDING_CONTACTS = __('Pending contacts');
const HEADER_REQUESTING_CONTACTS = __('Contact requests');
const HEADER_UNGROUPED = __('Ungrouped');
const HEADER_WEIGHTS = {};
HEADER_WEIGHTS[HEADER_REQUESTING_CONTACTS] = 0;
HEADER_WEIGHTS[HEADER_CURRENT_CONTACTS] = 1;
HEADER_WEIGHTS[HEADER_UNGROUPED] = 2;
HEADER_WEIGHTS[HEADER_PENDING_CONTACTS] = 3;
_converse.RosterGroupsComparator = function (a, b) {
/* Groups are sorted alphabetically, ignoring case.
* However, Ungrouped, Requesting Contacts and Pending Contacts
* appear last and in that order.
*/
a = a.get('name');
b = b.get('name');
const special_groups = Object.keys(HEADER_WEIGHTS);
const a_is_special = _.includes(special_groups, a);
const b_is_special = _.includes(special_groups, b);
if (!a_is_special && !b_is_special ) {
return a.toLowerCase() < b.toLowerCase() ? -1 : (a.toLowerCase() > b.toLowerCase() ? 1 : 0);
} else if (a_is_special && b_is_special) {
return HEADER_WEIGHTS[a] < HEADER_WEIGHTS[b] ? -1 : (HEADER_WEIGHTS[a] > HEADER_WEIGHTS[b] ? 1 : 0);
} else if (!a_is_special && b_is_special) {
return (b === HEADER_REQUESTING_CONTACTS) ? 1 : -1;
} else if (a_is_special && !b_is_special) {
return (a === HEADER_REQUESTING_CONTACTS) ? -1 : 1;
}
};
_converse.AddContactModal = _converse.BootstrapModal.extend({ _converse.AddContactModal = _converse.BootstrapModal.extend({
...@@ -678,7 +627,7 @@ converse.plugins.add('converse-rosterview', { ...@@ -678,7 +627,7 @@ converse.plugins.add('converse-rosterview', {
let matches; let matches;
q = q.toLowerCase(); q = q.toLowerCase();
if (type === 'state') { if (type === 'state') {
if (this.model.get('name') === HEADER_REQUESTING_CONTACTS) { if (this.model.get('name') === _converse.HEADER_REQUESTING_CONTACTS) {
// When filtering by chat state, we still want to // When filtering by chat state, we still want to
// show requesting contacts, even though they don't // show requesting contacts, even though they don't
// have the state in question. // have the state in question.
...@@ -747,13 +696,13 @@ converse.plugins.add('converse-rosterview', { ...@@ -747,13 +696,13 @@ converse.plugins.add('converse-rosterview', {
}, },
onContactSubscriptionChange (contact) { onContactSubscriptionChange (contact) {
if ((this.model.get('name') === HEADER_PENDING_CONTACTS) && contact.get('subscription') !== 'from') { if ((this.model.get('name') === _converse.HEADER_PENDING_CONTACTS) && contact.get('subscription') !== 'from') {
this.removeContact(contact); this.removeContact(contact);
} }
}, },
onContactRequestChange (contact) { onContactRequestChange (contact) {
if ((this.model.get('name') === HEADER_REQUESTING_CONTACTS) && !contact.get('requesting')) { if ((this.model.get('name') === _converse.HEADER_REQUESTING_CONTACTS) && !contact.get('requesting')) {
this.removeContact(contact); this.removeContact(contact);
} }
}, },
...@@ -926,16 +875,16 @@ converse.plugins.add('converse-rosterview', { ...@@ -926,16 +875,16 @@ converse.plugins.add('converse-rosterview', {
this.update(); this.update();
if (_.has(contact.changed, 'subscription')) { if (_.has(contact.changed, 'subscription')) {
if (contact.changed.subscription === 'from') { if (contact.changed.subscription === 'from') {
this.addContactToGroup(contact, HEADER_PENDING_CONTACTS); this.addContactToGroup(contact, _converse.HEADER_PENDING_CONTACTS);
} else if (_.includes(['both', 'to'], contact.get('subscription'))) { } else if (_.includes(['both', 'to'], contact.get('subscription'))) {
this.addExistingContact(contact); this.addExistingContact(contact);
} }
} }
if (_.has(contact.changed, 'ask') && contact.changed.ask === 'subscribe') { if (_.has(contact.changed, 'ask') && contact.changed.ask === 'subscribe') {
this.addContactToGroup(contact, HEADER_PENDING_CONTACTS); this.addContactToGroup(contact, _converse.HEADER_PENDING_CONTACTS);
} }
if (_.has(contact.changed, 'subscription') && contact.changed.requesting === 'true') { if (_.has(contact.changed, 'subscription') && contact.changed.requesting === 'true') {
this.addContactToGroup(contact, HEADER_REQUESTING_CONTACTS); this.addContactToGroup(contact, _converse.HEADER_REQUESTING_CONTACTS);
} }
this.updateFilter(); this.updateFilter();
}, },
...@@ -961,10 +910,10 @@ converse.plugins.add('converse-rosterview', { ...@@ -961,10 +910,10 @@ converse.plugins.add('converse-rosterview', {
if (_converse.roster_groups) { if (_converse.roster_groups) {
groups = contact.get('groups'); groups = contact.get('groups');
if (groups.length === 0) { if (groups.length === 0) {
groups = [HEADER_UNGROUPED]; groups = [_converse.HEADER_UNGROUPED];
} }
} else { } else {
groups = [HEADER_CURRENT_CONTACTS]; groups = [_converse.HEADER_CURRENT_CONTACTS];
} }
_.each(groups, _.bind(this.addContactToGroup, this, contact, _, options)); _.each(groups, _.bind(this.addContactToGroup, this, contact, _, options));
}, },
...@@ -982,9 +931,9 @@ converse.plugins.add('converse-rosterview', { ...@@ -982,9 +931,9 @@ converse.plugins.add('converse-rosterview', {
return; return;
} }
if ((contact.get('ask') === 'subscribe') || (contact.get('subscription') === 'from')) { if ((contact.get('ask') === 'subscribe') || (contact.get('subscription') === 'from')) {
this.addContactToGroup(contact, HEADER_PENDING_CONTACTS, options); this.addContactToGroup(contact, _converse.HEADER_PENDING_CONTACTS, options);
} else if (contact.get('requesting') === true) { } else if (contact.get('requesting') === true) {
this.addContactToGroup(contact, HEADER_REQUESTING_CONTACTS, options); this.addContactToGroup(contact, _converse.HEADER_REQUESTING_CONTACTS, options);
} }
} }
return this; return this;
......
...@@ -40,13 +40,11 @@ converse.plugins.add('converse-bookmarks', { ...@@ -40,13 +40,11 @@ converse.plugins.add('converse-bookmarks', {
// New functions which don't exist yet can also be added. // New functions which don't exist yet can also be added.
ChatRoom: { ChatRoom: {
getAndPersistNickname(nick) { getAndPersistNickname(nick) {
const { _converse } = this.__super__; const { _converse } = this.__super__;
nick = nick || _converse.getNicknameFromBookmark(this.get('jid')); nick = nick || _converse.getNicknameFromBookmark(this.get('jid'));
return this.__super__.getAndPersistNickname.call(this, nick); return this.__super__.getAndPersistNickname.call(this, nick);
}, }
} }
}, },
......
...@@ -251,7 +251,7 @@ _converse.log = function (message, level, style='') { ...@@ -251,7 +251,7 @@ _converse.log = function (message, level, style='') {
message = message.outerHTML; message = message.outerHTML;
} }
const prefix = style ? '%c' : ''; const prefix = style ? '%c' : '';
const logger = _.assign({ const logger = Object.assign({
'debug': _.get(console, 'log') ? console.log.bind(console) : _.noop, 'debug': _.get(console, 'log') ? console.log.bind(console) : _.noop,
'error': _.get(console, 'log') ? console.log.bind(console) : _.noop, 'error': _.get(console, 'log') ? console.log.bind(console) : _.noop,
'info': _.get(console, 'log') ? console.log.bind(console) : _.noop, 'info': _.get(console, 'log') ? console.log.bind(console) : _.noop,
......
...@@ -29,9 +29,40 @@ converse.plugins.add('converse-mam', { ...@@ -29,9 +29,40 @@ converse.plugins.add('converse-mam', {
// Overrides mentioned here will be picked up by converse.js's // Overrides mentioned here will be picked up by converse.js's
// plugin architecture they will replace existing methods on the // plugin architecture they will replace existing methods on the
// relevant objects or classes. // relevant objects or classes.
//
// New functions which don't exist yet can also be added.
ChatBox: { ChatBox: {
async getDuplicateMessage (stanza) {
const message = await this.__super__.getDuplicateMessage.apply(this, arguments);
if (!message) {
return this.findDuplicateFromArchiveID(stanza);
}
return message;
},
getUpdatedMessageAttributes (message, stanza) {
const attrs = this.__super__.getUpdatedMessageAttributes.apply(this, arguments);
if (message && !message.get('is_archived')) {
return Object.assign(attrs, {
'is_archived': this.isArchived(stanza)
}, this.getStanzaIDs(stanza))
}
return attrs;
}
}
},
initialize () {
/* The initialize function gets called as soon as the plugin is
* loaded by Converse.js's plugin machinery.
*/
const { _converse } = this;
_converse.api.settings.update({
archived_messages_page_size: '50',
message_archiving: undefined, // Supported values are 'always', 'never', 'roster' (https://xmpp.org/extensions/xep-0313.html#prefs)
message_archiving_timeout: 20000, // Time (in milliseconds) to wait before aborting MAM request
});
const MAMEnabledChat = {
fetchNewestMessages () { fetchNewestMessages () {
/* Fetches messages that might have been archived *after* /* Fetches messages that might have been archived *after*
...@@ -40,7 +71,6 @@ converse.plugins.add('converse-mam', { ...@@ -40,7 +71,6 @@ converse.plugins.add('converse-mam', {
if (this.disable_mam) { if (this.disable_mam) {
return; return;
} }
const { _converse } = this.__super__;
const most_recent_msg = u.getMostRecentMessage(this); const most_recent_msg = u.getMostRecentMessage(this);
if (_.isNil(most_recent_msg)) { if (_.isNil(most_recent_msg)) {
...@@ -59,7 +89,6 @@ converse.plugins.add('converse-mam', { ...@@ -59,7 +89,6 @@ converse.plugins.add('converse-mam', {
if (this.disable_mam) { if (this.disable_mam) {
return; return;
} }
const { _converse } = this.__super__;
const is_groupchat = this.get('type') === CHATROOMS_TYPE; const is_groupchat = this.get('type') === CHATROOMS_TYPE;
const mam_jid = is_groupchat ? this.get('jid') : _converse.bare_jid; const mam_jid = is_groupchat ? this.get('jid') : _converse.bare_jid;
if (!(await _converse.api.disco.supports(Strophe.NS.MAM, mam_jid))) { if (!(await _converse.api.disco.supports(Strophe.NS.MAM, mam_jid))) {
...@@ -92,7 +121,6 @@ converse.plugins.add('converse-mam', { ...@@ -92,7 +121,6 @@ converse.plugins.add('converse-mam', {
}, },
async findDuplicateFromArchiveID (stanza) { async findDuplicateFromArchiveID (stanza) {
const { _converse } = this.__super__;
const result = sizzle(`result[xmlns="${Strophe.NS.MAM}"]`, stanza).pop(); const result = sizzle(`result[xmlns="${Strophe.NS.MAM}"]`, stanza).pop();
if (!result) { if (!result) {
return null; return null;
...@@ -107,32 +135,11 @@ converse.plugins.add('converse-mam', { ...@@ -107,32 +135,11 @@ converse.plugins.add('converse-mam', {
return this.messages.findWhere(query); return this.messages.findWhere(query);
}, },
async getDuplicateMessage (stanza) {
const message = await this.__super__.getDuplicateMessage.apply(this, arguments);
if (!message) {
return this.findDuplicateFromArchiveID(stanza);
}
return message;
},
getUpdatedMessageAttributes (message, stanza) {
const attrs = this.__super__.getUpdatedMessageAttributes.apply(this, arguments);
if (message && !message.get('is_archived')) {
return Object.assign(attrs, {
'is_archived': this.isArchived(stanza)
}, this.getStanzaIDs(stanza))
}
return attrs;
} }
}, Object.assign(_converse.ChatBox.prototype, MAMEnabledChat);
ChatRoom: {
initialize () {
this.__super__.initialize.apply(this, arguments);
this.on('change:mam_enabled', this.fetchArchivedMessagesIfNecessary, this);
this.on('change:connection_status', this.fetchArchivedMessagesIfNecessary, this);
},
Object.assign(_converse.ChatRoom.prototype, {
fetchArchivedMessagesIfNecessary () { fetchArchivedMessagesIfNecessary () {
if (this.get('connection_status') !== converse.ROOMSTATUS.ENTERED || if (this.get('connection_status') !== converse.ROOMSTATUS.ENTERED ||
!this.get('mam_enabled') || !this.get('mam_enabled') ||
...@@ -142,22 +149,9 @@ converse.plugins.add('converse-mam', { ...@@ -142,22 +149,9 @@ converse.plugins.add('converse-mam', {
this.fetchArchivedMessages(); this.fetchArchivedMessages();
this.save({'mam_initialized': true}); this.save({'mam_initialized': true});
} }
},
},
initialize () {
/* The initialize function gets called as soon as the plugin is
* loaded by Converse.js's plugin machinery.
*/
const { _converse } = this;
_converse.api.settings.update({
archived_messages_page_size: '50',
message_archiving: undefined, // Supported values are 'always', 'never', 'roster' (https://xmpp.org/extensions/xep-0313.html#prefs)
message_archiving_timeout: 20000, // Time (in milliseconds) to wait before aborting MAM request
}); });
_converse.onMAMError = function (iq) { _converse.onMAMError = function (iq) {
if (iq.querySelectorAll('feature-not-implemented').length) { if (iq.querySelectorAll('feature-not-implemented').length) {
_converse.log( _converse.log(
...@@ -220,6 +214,11 @@ converse.plugins.add('converse-mam', { ...@@ -220,6 +214,11 @@ converse.plugins.add('converse-mam', {
_converse.api.listen.on('afterMessagesFetched', chat => chat.fetchNewestMessages()); _converse.api.listen.on('afterMessagesFetched', chat => chat.fetchNewestMessages());
_converse.api.listen.on('chatReconnected', chat => chat.fetchNewestMessages()); _converse.api.listen.on('chatReconnected', chat => chat.fetchNewestMessages());
_converse.api.listen.on('addClientFeatures', () => _converse.api.disco.own.features.add(Strophe.NS.MAM)); _converse.api.listen.on('addClientFeatures', () => _converse.api.disco.own.features.add(Strophe.NS.MAM));
_converse.api.listen.on('chatRoomOpened', (room) => {
room.on('change:mam_enabled', room.fetchArchivedMessagesIfNecessary, room);
room.on('change:connection_status', room.fetchArchivedMessagesIfNecessary, room);
});
/************************ END Event Handlers **************************/ /************************ END Event Handlers **************************/
......
...@@ -36,6 +36,17 @@ converse.plugins.add('converse-roster', { ...@@ -36,6 +36,17 @@ converse.plugins.add('converse-roster', {
'rosterInitialized', 'rosterInitialized',
]); ]);
_converse.HEADER_CURRENT_CONTACTS = __('My contacts');
_converse.HEADER_PENDING_CONTACTS = __('Pending contacts');
_converse.HEADER_REQUESTING_CONTACTS = __('Contact requests');
_converse.HEADER_UNGROUPED = __('Ungrouped');
const HEADER_WEIGHTS = {};
HEADER_WEIGHTS[_converse.HEADER_REQUESTING_CONTACTS] = 0;
HEADER_WEIGHTS[_converse.HEADER_CURRENT_CONTACTS] = 1;
HEADER_WEIGHTS[_converse.HEADER_UNGROUPED] = 2;
HEADER_WEIGHTS[_converse.HEADER_PENDING_CONTACTS] = 3;
_converse.registerPresenceHandler = function () { _converse.registerPresenceHandler = function () {
_converse.unregisterPresenceHandler(); _converse.unregisterPresenceHandler();
...@@ -381,6 +392,10 @@ converse.plugins.add('converse-roster', { ...@@ -381,6 +392,10 @@ converse.plugins.add('converse-roster', {
model: _converse.RosterContact, model: _converse.RosterContact,
comparator (contact1, contact2) { comparator (contact1, contact2) {
/* Groups are sorted alphabetically, ignoring case.
* However, Ungrouped, Requesting Contacts and Pending Contacts
* appear last and in that order.
*/
const status1 = contact1.presence.get('show') || 'offline'; const status1 = contact1.presence.get('show') || 'offline';
const status2 = contact2.presence.get('show') || 'offline'; const status2 = contact2.presence.get('show') || 'offline';
if (_converse.STATUS_WEIGHTS[status1] === _converse.STATUS_WEIGHTS[status2]) { if (_converse.STATUS_WEIGHTS[status1] === _converse.STATUS_WEIGHTS[status2]) {
...@@ -854,6 +869,23 @@ converse.plugins.add('converse-roster', { ...@@ -854,6 +869,23 @@ converse.plugins.add('converse-roster', {
_converse.RosterGroups = Backbone.Collection.extend({ _converse.RosterGroups = Backbone.Collection.extend({
model: _converse.RosterGroup, model: _converse.RosterGroup,
comparator (a, b) {
a = a.get('name');
b = b.get('name');
const special_groups = Object.keys(HEADER_WEIGHTS);
const a_is_special = _.includes(special_groups, a);
const b_is_special = _.includes(special_groups, b);
if (!a_is_special && !b_is_special ) {
return a.toLowerCase() < b.toLowerCase() ? -1 : (a.toLowerCase() > b.toLowerCase() ? 1 : 0);
} else if (a_is_special && b_is_special) {
return HEADER_WEIGHTS[a] < HEADER_WEIGHTS[b] ? -1 : (HEADER_WEIGHTS[a] > HEADER_WEIGHTS[b] ? 1 : 0);
} else if (!a_is_special && b_is_special) {
return (b === _converse.HEADER_REQUESTING_CONTACTS) ? 1 : -1;
} else if (a_is_special && !b_is_special) {
return (a === _converse.HEADER_REQUESTING_CONTACTS) ? -1 : 1;
}
},
fetchRosterGroups () { fetchRosterGroups () {
/* Fetches all the roster groups from sessionStorage. /* Fetches all the roster groups from sessionStorage.
* *
......
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