Commit 7ab59ad6 authored by JC Brand's avatar JC Brand

Refactor MAM and clear private chats upon reconnection

- Add `onReconnected` method for chatboxes to clear messages
- Move MAM models to headless build.
- New event `onChatReconnected`
parent 271c79ea
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
"split", "trim", "forEach", "toUpperCase", "includes", "values" "split", "trim", "forEach", "toUpperCase", "includes", "values"
] ]
}], }],
"lodash/import-scope": "off",
"lodash/prefer-invoke-map": "off", "lodash/prefer-invoke-map": "off",
"lodash/prefer-startswith": "off", "lodash/prefer-startswith": "off",
"lodash/prefer-constant": "off", "lodash/prefer-constant": "off",
......
...@@ -14,6 +14,8 @@ ...@@ -14,6 +14,8 @@
- New API method [\_converse.api.disco.features.get](https://conversejs.org/docs/html/api/-_converse.api.disco.features.html#.get) - New API method [\_converse.api.disco.features.get](https://conversejs.org/docs/html/api/-_converse.api.disco.features.html#.get)
- New config setting [muc_show_join_leave_status](https://conversejs.org/docs/html/configuration.html#muc-show-join-leave-status) - New config setting [muc_show_join_leave_status](https://conversejs.org/docs/html/configuration.html#muc-show-join-leave-status)
- New event: `chatBoxBlurred`. - New event: `chatBoxBlurred`.
- New event: [chatBoxBlurred](https://conversejs.org/docs/html/api/-_converse.html#event:chatBoxBlurred)
- New event: [chatReconnected](https://conversejs.org/docs/html/api/-_converse.html#event:chatReconnected)
- Properly handle message correction being received before the corrected message - Properly handle message correction being received before the corrected message
- #1296: `embedded` view mode shows `chatbox-navback` arrow in header - #1296: `embedded` view mode shows `chatbox-navback` arrow in header
- #1465: When highlighting a roster contact, they're incorrectly shown as online - #1465: When highlighting a roster contact, they're incorrectly shown as online
......
...@@ -10,15 +10,15 @@ ...@@ -10,15 +10,15 @@
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
<link type="text/css" rel="stylesheet" href="/docs/source/_static/api.css"> <link type="text/css" rel="stylesheet" href="/docs/source/_static/api.css">
<link type="text/css" rel="stylesheet" href="/css/converse.min.css"> <link type="text/css" rel="stylesheet" href="/dist/converse.min.css">
<link rel="shortcut icon" href="/css/images/favicon.ico"/> <link rel="shortcut icon" href="/images/favicon.ico"/>
</head> </head>
<body> <body>
<div id="main"> <div id="main">
<h1 class="brand-heading fade-in"> <h1 class="brand-heading fade-in">
<svg <svg
style="height: 8rem" style="height: 8rem"
xmlns:svg="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
{% extends "!layout.html" %} {% extends "!layout.html" %}
{# Custom CSS overrides #} {# Custom CSS overrides #}
{% set css_files = css_files + ['_static/style.css', "../../css/converse.min.css"] %} {% set css_files = css_files + ['_static/style.css', "../../dist/converse.min.css"] %}
{% set script_files = script_files + ["../../dist/converse.min.js", "../../analytics.js"] %} {% set script_files = script_files + ["../../dist/converse.min.js", "../../analytics.js"] %}
{# Add some extra stuff before and use existing with 'super()' call. #} {# Add some extra stuff before and use existing with 'super()' call. #}
......
...@@ -18,7 +18,7 @@ which hosts its JavaScript and CSS files. ...@@ -18,7 +18,7 @@ which hosts its JavaScript and CSS files.
The latest versions of these files are available at these URLs: The latest versions of these files are available at these URLs:
* https://cdn.conversejs.org/dist/converse.min.js * https://cdn.conversejs.org/dist/converse.min.js
* https://cdn.conversejs.org/css/converse.min.css * https://cdn.conversejs.org/dist/converse.min.css
If you are integrating Converse into an existing website or app, then we recommend If you are integrating Converse into an existing website or app, then we recommend
that you load a specific version of Converse. Otherwise your website or app that you load a specific version of Converse. Otherwise your website or app
...@@ -26,15 +26,15 @@ might break when a new backwards-incompatible version of Converse is released. ...@@ -26,15 +26,15 @@ might break when a new backwards-incompatible version of Converse is released.
To load a specific version of Converse you can put the version in the URL: To load a specific version of Converse you can put the version in the URL:
* https://cdn.conversejs.org/4.2.0/dist/converse.min.js * https://cdn.conversejs.org/5.0.0/dist/converse.min.js
* https://cdn.conversejs.org/4.2.0/css/converse.min.css * https://cdn.conversejs.org/5.0.0/dist/converse.min.css
You can include these two URLs inside the *<head>* element of your website You can include these two URLs inside the *<head>* element of your website
via the *script* and *link* tags: via the *script* and *link* tags:
.. code-block:: html .. code-block:: html
<link rel="stylesheet" type="text/css" media="screen" href="https://cdn.conversejs.org/4.2.0/css/converse.min.css"> <link rel="stylesheet" type="text/css" media="screen" href="https://cdn.conversejs.org/4.2.0/dist/converse.min.css">
<script src="https://cdn.conversejs.org/4.2.0/dist/converse.min.js" charset="utf-8"></script> <script src="https://cdn.conversejs.org/4.2.0/dist/converse.min.js" charset="utf-8"></script>
......
...@@ -199,7 +199,9 @@ converse.plugins.add('converse-controlbox', { ...@@ -199,7 +199,9 @@ converse.plugins.add('converse-controlbox', {
'type': _converse.CONTROLBOX_TYPE, 'type': _converse.CONTROLBOX_TYPE,
'url': '' 'url': ''
} }
} },
onReconnection: _.noop
}); });
......
...@@ -7,16 +7,12 @@ ...@@ -7,16 +7,12 @@
// Views for XEP-0313 Message Archive Management // Views for XEP-0313 Message Archive Management
import converse from "@converse/headless/converse-core"; import converse from "@converse/headless/converse-core";
import { debounce } from 'lodash'
const CHATROOMS_TYPE = 'chatroom';
const { Strophe, _ } = converse.env;
const u = converse.env.utils;
converse.plugins.add('converse-mam-views', { converse.plugins.add('converse-mam-views', {
dependencies: ['converse-disco', 'converse-mam', 'converse-chatview', 'converse-muc-views'], dependencies: ['converse-mam', 'converse-chatview', 'converse-muc-views'],
overrides: { overrides: {
// Overrides mentioned here will be picked up by converse.js's // Overrides mentioned here will be picked up by converse.js's
...@@ -25,73 +21,11 @@ converse.plugins.add('converse-mam-views', { ...@@ -25,73 +21,11 @@ converse.plugins.add('converse-mam-views', {
// //
// New functions which don't exist yet can also be added. // New functions which don't exist yet can also be added.
ChatBox: {
fetchNewestMessages () {
/* Fetches messages that might have been archived *after*
* the last archived message in our local cache.
*/
if (this.disable_mam) {
return;
}
const { _converse } = this.__super__;
const most_recent_msg = u.getMostRecentMessage(this);
if (_.isNil(most_recent_msg)) {
this.fetchArchivedMessages();
} else {
const stanza_id = most_recent_msg.get(`stanza_id ${this.get('jid')}`);
if (stanza_id) {
this.fetchArchivedMessages({'after': stanza_id});
} else {
this.fetchArchivedMessages({'start': most_recent_msg.get('time')});
}
}
},
async fetchArchivedMessages (options) {
if (this.disable_mam) {
return;
}
const { _converse } = this.__super__;
const is_groupchat = this.get('type') === CHATROOMS_TYPE;
const mam_jid = is_groupchat ? this.get('jid') : _converse.bare_jid;
if (!(await _converse.api.disco.supports(Strophe.NS.MAM, mam_jid))) {
return;
}
let message_handler;
if (is_groupchat) {
message_handler = this.onMessage.bind(this);
} else {
message_handler = _converse.chatboxes.onMessage.bind(_converse.chatboxes)
}
let result = {};
try {
result = await _converse.api.archive.query(
Object.assign({
'groupchat': is_groupchat,
'before': '', // Page backwards from the most recent message
'max': _converse.archived_messages_page_size,
'with': this.get('jid'),
}, options));
} catch (e) {
_converse.log(
"Error or timeout while trying to fetch "+
"archived messages", Strophe.LogLevel.ERROR);
_converse.log(e, Strophe.LogLevel.ERROR);
}
if (result.messages) {
result.messages.forEach(message_handler);
}
}
},
ChatBoxView: { ChatBoxView: {
render () { render () {
const result = this.__super__.render.apply(this, arguments); const result = this.__super__.render.apply(this, arguments);
if (!this.disable_mam) { if (!this.disable_mam) {
this.content.addEventListener('scroll', _.debounce(this.onScroll.bind(this), 100)); this.content.addEventListener('scroll', debounce(this.onScroll.bind(this), 100));
} }
return result; return result;
}, },
...@@ -115,52 +49,14 @@ converse.plugins.add('converse-mam-views', { ...@@ -115,52 +49,14 @@ converse.plugins.add('converse-mam-views', {
} }
}, },
ChatRoom: {
initialize () {
this.__super__.initialize.apply(this, arguments);
this.on('change:mam_enabled', this.fetchArchivedMessagesIfNecessary, this);
this.on('change:connection_status', this.fetchArchivedMessagesIfNecessary, this);
},
fetchArchivedMessagesIfNecessary () {
if (this.get('connection_status') !== converse.ROOMSTATUS.ENTERED ||
!this.get('mam_enabled') ||
this.get('mam_initialized')) {
return;
}
this.fetchArchivedMessages();
this.save({'mam_initialized': true});
}
},
ChatRoomView: { ChatRoomView: {
renderChatArea () { renderChatArea () {
const result = this.__super__.renderChatArea.apply(this, arguments); const result = this.__super__.renderChatArea.apply(this, arguments);
if (!this.disable_mam) { if (!this.disable_mam) {
this.content.addEventListener('scroll', _.debounce(this.onScroll.bind(this), 100)); this.content.addEventListener('scroll', debounce(this.onScroll.bind(this), 100));
} }
return result; return result;
} }
} }
},
initialize () {
/* The initialize function gets called as soon as the plugin is
* loaded by Converse.js's plugin machinery.
*/
const { _converse } = this;
/************************ BEGIN Event Handlers ************************/
_converse.api.listen.on('afterMessagesFetched', chatbox => chatbox.fetchNewestMessages());
_converse.api.listen.on('reconnected', () => {
const private_chats = _converse.chatboxviews.filter(
view => _.at(view, 'model.attributes.type')[0] === 'chatbox'
);
_.each(private_chats, view => view.model.fetchNewestMessages())
});
/************************ END Event Handlers ************************/
} }
}); });
...@@ -352,6 +352,17 @@ converse.plugins.add('converse-chatboxes', { ...@@ -352,6 +352,17 @@ converse.plugins.add('converse-chatboxes', {
} }
}, },
onReconnection () {
this.clearMessages();
/**
* Triggered whenever a `_converse.ChatBox` instance has reconnected after an outage
* @event _converse#onChatReconnected
* @type {_converse.ChatBox | _converse.ChatRoom}
* @example _converse.api.listen.on('onChatReconnected', chatbox => { ... });
*/
_converse.api.trigger('chatReconnected', this);
},
validate (attrs, options) { validate (attrs, options) {
const { _converse } = this.__super__; const { _converse } = this.__super__;
if (!attrs.jid) { if (!attrs.jid) {
...@@ -1150,6 +1161,8 @@ converse.plugins.add('converse-chatboxes', { ...@@ -1150,6 +1161,8 @@ converse.plugins.add('converse-chatboxes', {
_converse.api.trigger('chatBoxesInitialized'); _converse.api.trigger('chatBoxesInitialized');
}); });
_converse.api.listen.on('presencesInitialized', () => _converse.chatboxes.onConnected()); _converse.api.listen.on('presencesInitialized', () => _converse.chatboxes.onConnected());
_converse.api.listen.on('reconnected', () => _converse.chatboxes.forEach(m => m.onReconnection()));
/************************ END Event Handlers ************************/ /************************ END Event Handlers ************************/
......
...@@ -23,7 +23,7 @@ const MAM_ATTRIBUTES = ['with', 'start', 'end']; ...@@ -23,7 +23,7 @@ const MAM_ATTRIBUTES = ['with', 'start', 'end'];
converse.plugins.add('converse-mam', { converse.plugins.add('converse-mam', {
dependencies: ['converse-muc'], dependencies: ['converse-disco', 'converse-muc'],
overrides: { overrides: {
// Overrides mentioned here will be picked up by converse.js's // Overrides mentioned here will be picked up by converse.js's
...@@ -33,6 +33,64 @@ converse.plugins.add('converse-mam', { ...@@ -33,6 +33,64 @@ converse.plugins.add('converse-mam', {
// New functions which don't exist yet can also be added. // New functions which don't exist yet can also be added.
ChatBox: { ChatBox: {
fetchNewestMessages () {
/* Fetches messages that might have been archived *after*
* the last archived message in our local cache.
*/
if (this.disable_mam) {
return;
}
const { _converse } = this.__super__;
const most_recent_msg = u.getMostRecentMessage(this);
if (_.isNil(most_recent_msg)) {
this.fetchArchivedMessages();
} else {
const stanza_id = most_recent_msg.get(`stanza_id ${this.get('jid')}`);
if (stanza_id) {
this.fetchArchivedMessages({'after': stanza_id});
} else {
this.fetchArchivedMessages({'start': most_recent_msg.get('time')});
}
}
},
async fetchArchivedMessages (options) {
if (this.disable_mam) {
return;
}
const { _converse } = this.__super__;
const is_groupchat = this.get('type') === CHATROOMS_TYPE;
const mam_jid = is_groupchat ? this.get('jid') : _converse.bare_jid;
if (!(await _converse.api.disco.supports(Strophe.NS.MAM, mam_jid))) {
return;
}
let message_handler;
if (is_groupchat) {
message_handler = this.onMessage.bind(this);
} else {
message_handler = _converse.chatboxes.onMessage.bind(_converse.chatboxes)
}
let result = {};
try {
result = await _converse.api.archive.query(
Object.assign({
'groupchat': is_groupchat,
'before': '', // Page backwards from the most recent message
'max': _converse.archived_messages_page_size,
'with': this.get('jid'),
}, options));
} catch (e) {
_converse.log(
"Error or timeout while trying to fetch "+
"archived messages", Strophe.LogLevel.ERROR);
_converse.log(e, Strophe.LogLevel.ERROR);
}
if (result.messages) {
result.messages.forEach(message_handler);
}
},
async findDuplicateFromArchiveID (stanza) { async findDuplicateFromArchiveID (stanza) {
const { _converse } = this.__super__; const { _converse } = this.__super__;
const result = sizzle(`result[xmlns="${Strophe.NS.MAM}"]`, stanza).pop(); const result = sizzle(`result[xmlns="${Strophe.NS.MAM}"]`, stanza).pop();
...@@ -66,7 +124,26 @@ converse.plugins.add('converse-mam', { ...@@ -66,7 +124,26 @@ converse.plugins.add('converse-mam', {
} }
return attrs; return attrs;
} }
} },
ChatRoom: {
initialize () {
this.__super__.initialize.apply(this, arguments);
this.on('change:mam_enabled', this.fetchArchivedMessagesIfNecessary, this);
this.on('change:connection_status', this.fetchArchivedMessagesIfNecessary, this);
},
fetchArchivedMessagesIfNecessary () {
if (this.get('connection_status') !== converse.ROOMSTATUS.ENTERED ||
!this.get('mam_enabled') ||
this.get('mam_initialized')) {
return;
}
this.fetchArchivedMessages();
this.save({'mam_initialized': true});
}
},
}, },
initialize () { initialize () {
...@@ -139,6 +216,8 @@ converse.plugins.add('converse-mam', { ...@@ -139,6 +216,8 @@ converse.plugins.add('converse-mam', {
} }
}); });
_converse.api.listen.on('afterMessagesFetched', 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));
/************************ END Event Handlers ************************/ /************************ END Event Handlers ************************/
......
...@@ -240,7 +240,6 @@ converse.plugins.add('converse-muc', { ...@@ -240,7 +240,6 @@ converse.plugins.add('converse-muc', {
} }
}, },
async onConnectionStatusChanged () { async onConnectionStatusChanged () {
if (this.get('connection_status') === converse.ROOMSTATUS.ENTERED) { if (this.get('connection_status') === converse.ROOMSTATUS.ENTERED) {
this.occupants.fetchMembers(); this.occupants.fetchMembers();
...@@ -254,6 +253,14 @@ converse.plugins.add('converse-muc', { ...@@ -254,6 +253,14 @@ converse.plugins.add('converse-muc', {
} }
}, },
onReconnection () {
this.save('connection_status', converse.ROOMSTATUS.DISCONNECTED);
this.clearMessages();
this.registerHandlers();
this.fetchMessages();
this.join();
},
initFeatures () { initFeatures () {
const storage = _converse.config.get('storage'); const storage = _converse.config.get('storage');
const id = `converse.muc-features-${_converse.bare_jid}-${this.get('jid')}`; const id = `converse.muc-features-${_converse.bare_jid}-${this.get('jid')}`;
...@@ -1559,22 +1566,6 @@ converse.plugins.add('converse-muc', { ...@@ -1559,22 +1566,6 @@ converse.plugins.add('converse-muc', {
} }
}); });
}); });
function reconnectToChatRooms () {
/* Upon a reconnection event from converse, join again
* all the open groupchats.
*/
_converse.chatboxes
.filter(m => (m.get('type') === _converse.CHATROOMS_TYPE))
.forEach(room => {
room.save('connection_status', converse.ROOMSTATUS.DISCONNECTED);
room.clearMessages();
room.registerHandlers();
room.fetchMessages();
room.join();
});
}
_converse.api.listen.on('reconnected', reconnectToChatRooms);
/************************ END Event Handlers ************************/ /************************ END Event Handlers ************************/
......
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