diff --git a/CHANGES.md b/CHANGES.md index 18edb5202..ac5336da5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,7 @@ # Changelog ## 5.0.0 (Unreleased) +- Support for XEP-0410 to check whether we're still present in a room - Initial support for the [CredentialsContainer](https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer) web API - Allow for synchronous events. When a synchronous event is fired, Converse will wait for all promises returned by the event's handlers to finish before continuing. diff --git a/package-lock.json b/package-lock.json index 8ab03e163..edf8799c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13716,8 +13716,8 @@ } }, "strophe.js": { - "version": "github:strophe/strophejs#bc8a6b62203a17ada6bd78945c993c6f7dccd1de", - "from": "github:strophe/strophejs#bc8a6b62203a17ada6bd78945c993c6f7dccd1de" + "version": "github:strophe/strophejs#c675bcfcf44527ba1cf844a1aaa68fe7003c6140", + "from": "github:strophe/strophejs#c675bcfcf44527ba1cf844a1aaa68fe7003c6140" }, "style-loader": { "version": "0.23.1", diff --git a/src/headless/converse-chatboxes.js b/src/headless/converse-chatboxes.js index e721bb856..b3f3477e2 100644 --- a/src/headless/converse-chatboxes.js +++ b/src/headless/converse-chatboxes.js @@ -401,6 +401,11 @@ converse.plugins.add('converse-chatboxes', { } }, + shouldShowErrorMessage () { + // Gets overridden in ChatRoom + return true; + }, + /** * If the passed in `message` stanza contains an * [XEP-0308](https://xmpp.org/extensions/xep-0308.html#usecase) @@ -987,11 +992,10 @@ converse.plugins.add('converse-chatboxes', { // We also ignore duplicate error messages. return; } - } else { - // An error message without id likely means that we - // sent a message without id (which shouldn't happen). - _converse.log('Received an error message without id attribute!', Strophe.LogLevel.ERROR); - _converse.log(message, Strophe.LogLevel.ERROR); + } + const should_show = await chatbox.shouldShowErrorMessage(message); + if (!should_show) { + return; } const attrs = await chatbox.getMessageAttributesFromStanza(message, message); chatbox.messages.create(attrs); diff --git a/src/headless/converse-core.js b/src/headless/converse-core.js index 2b95697f9..e22cca815 100644 --- a/src/headless/converse-core.js +++ b/src/headless/converse-core.js @@ -1350,7 +1350,7 @@ _converse.initialize = async function (settings, callback) { } } else if (reconnecting) { this.autoLogin(); - } else if (window.PasswordCredential) { + } else if (!isTestEnv() && window.PasswordCredential) { const creds = await navigator.credentials.get({'password': true}); if (creds && creds.type == 'password' && u.isValidJID(creds.id)) { setUserJID(creds.id); @@ -1514,7 +1514,7 @@ _converse.api = { * @returns {string} The current user's full JID (Jabber ID) * @example _converse.api.user.jid()) */ - 'jid' () { + jid () { return _converse.connection.jid; }, @@ -1577,19 +1577,20 @@ _converse.api = { */ _converse.api.trigger('logout'); }, + /** * Set and get the user's chat status, also called their *availability*. * * @namespace _converse.api.user.status * @memberOf _converse.api.user */ - 'status': { + status: { /** Return the current user's availability status. * * @method _converse.api.user.status.get * @example _converse.api.user.status.get(); */ - 'get' () { + get () { return _converse.xmppstatus.get('status'); }, /** @@ -1602,7 +1603,7 @@ _converse.api = { * @example this._converse.api.user.status.set('dnd'); * @example this._converse.api.user.status.set('dnd', 'In a meeting'); */ - 'set' (value, message) { + set (value, message) { const data = {'status': value}; if (!_.includes(Object.keys(_converse.STATUS_WEIGHTS), value)) { throw new Error('Invalid availability value. See https://xmpp.org/rfcs/rfc3921.html#rfc.section.2.2.2.1'); @@ -1626,7 +1627,7 @@ _converse.api = { * @returns {string} The status message * @example const message = _converse.api.user.status.message.get() */ - 'get' () { + get () { return _converse.xmppstatus.get('status_message'); }, /** @@ -1634,7 +1635,7 @@ _converse.api = { * @param {string} status The status message * @example _converse.api.user.status.message.set('In a meeting'); */ - 'set' (status) { + set (status) { _converse.xmppstatus.save({'status_message': status}); } } diff --git a/src/headless/converse-muc.js b/src/headless/converse-muc.js index 4a5c49cf8..274da9f05 100644 --- a/src/headless/converse-muc.js +++ b/src/headless/converse-muc.js @@ -1224,9 +1224,66 @@ converse.plugins.add('converse-muc', { return attrs; }, + /** + * Send a MUC-0410 MUC Self-Ping stanza to room to determine + * whether we're still joined. + * @async + * @private + * @method _converse.ChatRoom#isJoined + * @returns {Promise} + */ + async isJoined () { + const ping = $iq({ + 'to': `${this.get('jid')}/${this.get('nick')}`, + 'type': "get" + }).c("ping", {'xmlns': Strophe.NS.PING}); + let result; + try { + result = await _converse.api.sendIQ(ping); + } catch (e) { + const sel = `error[type="cancel"] not-acceptable[xmlns="${Strophe.NS.STANZAS}"]`; + if (_.isElement(e) && sizzle(sel, e).length) { + return false; + } + } + return true; + }, + + /** + * Check whether we're still joined and re-join if not + * @async + * @private + * @method _converse.ChatRoom#rejoinIfNecessary + */ + async rejoinIfNecessary () { + const is_joined = await this.isJoined(); + if (!is_joined) { + this.save('connection_status', converse.ROOMSTATUS.DISCONNECTED); + this.enterRoom(); + return true; + } + }, + + /** + * @async + * @private + * @method _converse.ChatRoom#shouldShowErrorMessage + * @returns {Promise} + */ + async shouldShowErrorMessage (stanza) { + if (sizzle(`not-acceptable[xmlns="${Strophe.NS.STANZAS}"]`, stanza).length) { + if (await this.rejoinIfNecessary()) { + return false; + } + } + return true; + }, + getErrorMessage (stanza) { if (sizzle(`forbidden[xmlns="${Strophe.NS.STANZAS}"]`, stanza).length) { return __("Your message was not delivered because you're not allowed to send messages in this groupchat."); + } else if (sizzle(`not-acceptable[xmlns="${Strophe.NS.STANZAS}"]`, stanza).length) { + return __("Your message was not delivered because you're not present in the groupchat."); } else { return _converse.ChatBox.prototype.getErrorMessage.apply(this, arguments); } diff --git a/src/headless/package.json b/src/headless/package.json index f70ed8160..e0bb2785b 100644 --- a/src/headless/package.json +++ b/src/headless/package.json @@ -29,7 +29,7 @@ "jed": "1.1.1", "lodash": "^4.17.11", "pluggable.js": "2.0.1", - "strophe.js": "strophe/strophejs#bc8a6b62203a17ada6bd78945c993c6f7dccd1de", + "strophe.js": "strophe/strophejs#c675bcfcf44527ba1cf844a1aaa68fe7003c6140", "twemoji": "^11.0.1", "urijs": "^1.19.1" }