diff --git a/CHANGES.md b/CHANGES.md index a42ba47ad..e63480b4b 100755 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ # Changelog +## 3.2.2 (Unreleased) + +- Don't hang indefinitely and provide nicer error messages when a connection + can't be established. + ## 3.2.1 (2017-08-29) ### Bugfixes diff --git a/css/converse.css b/css/converse.css index e736d4309..96f91c210 100644 --- a/css/converse.css +++ b/css/converse.css @@ -1990,6 +1990,13 @@ white-space: normal; } #conversejs #controlbox #converse-register .save-submit, #conversejs #controlbox #converse-login .save-submit { color: #3AA569; } + #conversejs #controlbox #converse-register .conn-feedback p, #conversejs #controlbox #converse-login .conn-feedback p { + color: #578EA9; + padding-bottom: 0.5em; } + #conversejs #controlbox #converse-register .conn-feedback p.feedback-subject.error, #conversejs #controlbox #converse-login .conn-feedback p.feedback-subject.error { + font-weight: bold; } + #conversejs #controlbox #converse-register .conn-feedback p.error, #conversejs #controlbox #converse-login .conn-feedback p.error { + color: #A53214; } #conversejs #controlbox #converse-register input, #conversejs #controlbox #converse-login input { width: 100%; margin: 0.5em 0; } diff --git a/css/inverse.css b/css/inverse.css index cad25d8e5..eabad4506 100644 --- a/css/inverse.css +++ b/css/inverse.css @@ -2058,6 +2058,13 @@ body { white-space: normal; } #conversejs #controlbox #converse-register .save-submit, #conversejs #controlbox #converse-login .save-submit { color: #3AA569; } + #conversejs #controlbox #converse-register .conn-feedback p, #conversejs #controlbox #converse-login .conn-feedback p { + color: #578EA9; + padding-bottom: 0.5em; } + #conversejs #controlbox #converse-register .conn-feedback p.feedback-subject.error, #conversejs #controlbox #converse-login .conn-feedback p.feedback-subject.error { + font-weight: bold; } + #conversejs #controlbox #converse-register .conn-feedback p.error, #conversejs #controlbox #converse-login .conn-feedback p.error { + color: #A53214; } #conversejs #controlbox #converse-register input, #conversejs #controlbox #converse-login input { width: 100%; margin: 0.5em 0; } diff --git a/package-lock.json b/package-lock.json index 182501dd5..2f758d1c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3296,14 +3296,6 @@ } } }, - "string_decoder": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, "string-width": { "version": "1.0.2", "bundled": true, @@ -3314,6 +3306,14 @@ "strip-ansi": "3.0.1" } }, + "string_decoder": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "5.0.1" + } + }, "stringstream": { "version": "0.0.5", "bundled": true, @@ -5080,6 +5080,7 @@ "integrity": "sha1-+Osa0A3FilUUNjtBylNCgX8L1kY=", "dev": true, "requires": { + "JSONStream": "1.3.1", "abbrev": "1.1.0", "ansi-regex": "2.1.1", "ansicolors": "0.3.2", @@ -5110,7 +5111,6 @@ "inherits": "2.0.3", "ini": "1.3.4", "init-package-json": "1.10.1", - "JSONStream": "1.3.1", "lazy-property": "1.0.0", "lockfile": "1.0.3", "lodash._baseindexof": "3.1.0", @@ -5173,6 +5173,27 @@ "write-file-atomic": "1.3.3" }, "dependencies": { + "JSONStream": { + "version": "1.3.1", + "bundled": true, + "dev": true, + "requires": { + "jsonparse": "1.3.0", + "through": "2.3.8" + }, + "dependencies": { + "jsonparse": { + "version": "1.3.0", + "bundled": true, + "dev": true + }, + "through": { + "version": "2.3.8", + "bundled": true, + "dev": true + } + } + }, "abbrev": { "version": "1.1.0", "bundled": true, @@ -5514,27 +5535,6 @@ } } }, - "JSONStream": { - "version": "1.3.1", - "bundled": true, - "dev": true, - "requires": { - "jsonparse": "1.3.0", - "through": "2.3.8" - }, - "dependencies": { - "jsonparse": { - "version": "1.3.0", - "bundled": true, - "dev": true - }, - "through": { - "version": "2.3.8", - "bundled": true, - "dev": true - } - } - }, "lazy-property": { "version": "1.0.0", "bundled": true, @@ -9005,15 +9005,6 @@ "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", "dev": true }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, "string-length": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/string-length/-/string-length-1.0.1.tgz", @@ -9034,6 +9025,15 @@ "strip-ansi": "3.0.1" } }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, "stringstream": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", diff --git a/sass/_controlbox.scss b/sass/_controlbox.scss index d51abac7a..642732576 100644 --- a/sass/_controlbox.scss +++ b/sass/_controlbox.scss @@ -113,6 +113,18 @@ .save-submit { color: $save-button-color; } + .conn-feedback { + p { + color: $controlbox-head-color; + padding-bottom: 0.5em; + &.feedback-subject.error { + font-weight: bold; + } + &.error { + color: $error-color; + } + } + } input { width: 100%; margin: 0.5em 0; diff --git a/src/config.js b/src/config.js index 2135bb513..4abfa3f9a 100644 --- a/src/config.js +++ b/src/config.js @@ -43,7 +43,6 @@ require.config({ "strophe.vcard": "node_modules/strophejs-plugin-vcard/strophe.vcard", "text": "node_modules/text/text", "tpl": "node_modules/lodash-template-loader/loader", - "typeahead": "components/typeahead.js/index", "underscore": "src/underscore-shim", "utils": "src/utils", "xss": "node_modules/xss/dist/xss", @@ -108,8 +107,6 @@ require.config({ packages: [{ 'name': 'moment', - // This location is relative to baseUrl. Choose bower_components - // or node_modules, depending on how moment was installed. 'location': 'node_modules/moment', 'main': 'moment' }], diff --git a/src/converse-controlbox.js b/src/converse-controlbox.js index bd0a67e35..63b6f1a58 100644 --- a/src/converse-controlbox.js +++ b/src/converse-controlbox.js @@ -269,6 +269,17 @@ return this; }, + showLoginPanel () { + if (!_.isUndefined(this.loginpanel)) { + this.renderLoginPanel(); + } else { + this.loginpanel.$el.find('input#jid').focus(); + if (!this.loginpanel.$el.is(':visible')) { + this.loginpanel.$el.show(); + } + } + }, + renderLoginPanel () { this.loginpanel = new _converse.LoginPanel({ '$parent': this.$el.find('.controlbox-panes'), @@ -390,6 +401,9 @@ 'PREBIND': _converse.PREBIND, 'auto_login': _converse.auto_login, 'authentication': _converse.authentication, + 'conn_feedback_class': _converse.connfeedback.get('klass'), + 'conn_feedback_subject': _converse.connfeedback.get('subject'), + 'conn_feedback_message': _converse.connfeedback.get('message'), 'label_username': __('XMPP Username:'), 'label_password': __('Password:'), 'label_anon_login': __('Click here to log in anonymously'), @@ -399,6 +413,7 @@ }) )); this.$tabs = cfg.$parent.parent().find('#controlbox-tabs'); + _converse.connfeedback.on('change', this.showConnectionFeedback, this); }, render () { @@ -410,6 +425,25 @@ return this; }, + showConnectionFeedback () { + const klass = _converse.connfeedback.get('klass'); + function insert (text, el) { + el.textContent = text; + el.classList.remove('error'); + if (klass) { + el.classList.add(klass); + } + } + insert( + _converse.connfeedback.get('subject'), + this.el.querySelector('.conn-feedback .feedback-subject') + ) + insert( + _converse.connfeedback.get('message'), + this.el.querySelector('.conn-feedback .feedback-message') + ) + }, + authenticate (ev) { /* Authenticate the user based on a form submission event. */ @@ -743,6 +777,7 @@ this.updateOnlineCount(); const that = this; _converse.api.waitUntil('initialized').then(() => { + this.render(); _converse.roster.on("add", that.updateOnlineCount, that); _converse.roster.on('change', that.updateOnlineCount, that); _converse.roster.on("destroy", that.updateOnlineCount, that); @@ -756,7 +791,7 @@ // artifacts (i.e. on page load the toggle is shown only to then // seconds later be hidden in favor of the control box). this.el.innerHTML = tpl_controlbox_toggle({ - 'label_toggle': __('Toggle chat') + 'label_toggle': _converse.connection.connected ? __('Contacts') : __('Toggle chat') }) return this; }, @@ -823,7 +858,7 @@ const view = _converse.chatboxviews.get('controlbox'); view.model.set({connected:false}); view.$('#controlbox-tabs').empty(); - view.renderLoginPanel(); + view.showLoginPanel(); }; _converse.on('disconnected', disconnect); diff --git a/src/converse-core.js b/src/converse-core.js index d0ea5a96c..0c78ce958 100755 --- a/src/converse-core.js +++ b/src/converse-core.js @@ -407,19 +407,10 @@ }; this.giveFeedback = function (subject, klass, message) { - _.forEach(document.querySelectorAll('.conn-feedback'), (el) => { - el.classList.add('conn-feedback'); - el.textContent = subject; - if (klass) { - el.classList.add(klass); - } else { - el.classList.remove('error'); - } - }); - _converse.emit('feedback', { + _converse.connfeedback.set({ + 'subject': subject, 'klass': klass, - 'message': message, - 'subject': subject + 'message': message }); }; @@ -462,6 +453,8 @@ * Will either start a teardown process for converse.js or attempt * to reconnect. */ + const reason = _converse.disconnection_reason; + if (_converse.disconnection_cause === Strophe.Status.AUTHFAIL) { if (_converse.credentials_url && _converse.auto_reconnect) { /* In this case, we reconnect, because we might be receiving @@ -473,7 +466,9 @@ return _converse.disconnect(); } } else if (_converse.disconnection_cause === _converse.LOGOUT || - _converse.disconnection_reason === "host-unknown" || + (!_.isUndefined(reason) && reason === _.get(Strophe, 'ErrorCondition.NO_AUTH_MECH')) || + reason === "host-unknown" || + reason === "remote-connection-failed" || !_converse.auto_reconnect) { return _converse.disconnect(); } @@ -494,7 +489,7 @@ } }; - this.onConnectStatusChanged = function (status, condition) { + this.onConnectStatusChanged = function (status, message) { /* Callback method called by Strophe as the Strophe.Connection goes * through various states while establishing or tearing down a * connection. @@ -517,29 +512,38 @@ _converse.onConnected(); } } else if (status === Strophe.Status.DISCONNECTED) { - _converse.setDisconnectionCause(status, condition); + _converse.setDisconnectionCause(status, message); _converse.onDisconnected(); } else if (status === Strophe.Status.ERROR) { _converse.giveFeedback( - __('Connection error'), 'error', + __('Connection error'), + 'error', __('An error occurred while connecting to the chat server.') ); } else if (status === Strophe.Status.CONNECTING) { - _converse.giveFeedback(__('Connecting')); + _converse.giveFeedback(__('Connecting…')); } else if (status === Strophe.Status.AUTHENTICATING) { - _converse.giveFeedback(__('Authenticating')); + _converse.giveFeedback(__('Authenticating…')); } else if (status === Strophe.Status.AUTHFAIL) { - _converse.giveFeedback(__('Authentication Failed'), 'error'); - _converse.setDisconnectionCause(status, condition, true); + _converse.giveFeedback(__('Authentication failed: '+message), 'error'); + _converse.setDisconnectionCause(status, message, true); _converse.onDisconnected(); } else if (status === Strophe.Status.CONNFAIL) { + let feedback = message; + if (message === "host-unknown" || message == "remote-connection-failed") { + feedback = __("Sorry, we could not connect to the XMPP host with domain: ") + + `\"${Strophe.getDomainFromJid(_converse.connection.jid)}\"`; + } else if (!_.isUndefined(message) && message === _.get(Strophe, 'ErrorCondition.NO_AUTH_MECH')) { + feedback = __("The XMPP server did not offer a supported authentication mechanism"); + } _converse.giveFeedback( - __('Connection failed'), 'error', - __('An error occurred while connecting to the chat server: '+condition) + __('Connection failed'), + 'error', + feedback ); - _converse.setDisconnectionCause(status, condition); + _converse.setDisconnectionCause(status, message); } else if (status === Strophe.Status.DISCONNECTING) { - _converse.setDisconnectionCause(status, condition); + _converse.setDisconnectionCause(status, message); } }; @@ -749,7 +753,6 @@ _converse.roster.onConnected(); _converse.populateRoster(); _converse.registerPresenceHandler(); - _converse.giveFeedback(__('Contacts')); if (reconnecting) { _converse.xmppstatus.sendPresence(); } else { @@ -1511,6 +1514,22 @@ } }); + this.ConnectionFeedback = Backbone.Model.extend({ + + initialize () { + this.on('change', this.emitConnectionFeedbackChange); + }, + + emitConnectionFeedbackChange () { + _converse.emit('connfeedback', { + 'klass': _converse.connfeedback.get('klass'), + 'message': _converse.connfeedback.get('message'), + 'subject': _converse.connfeedback.get('subject') + }); + } + }); + this.connfeedback = new this.ConnectionFeedback(); + this.XMPPStatus = Backbone.Model.extend({ initialize () { this.set({ diff --git a/src/templates/controlbox_toggle.html b/src/templates/controlbox_toggle.html index 69f450e17..df8f2d997 100644 --- a/src/templates/controlbox_toggle.html +++ b/src/templates/controlbox_toggle.html @@ -1 +1 @@ -{{{label_toggle}}} +{{{label_toggle}}} diff --git a/src/templates/login_panel.html b/src/templates/login_panel.html index 1b3cbc09f..4be6b37d7 100644 --- a/src/templates/login_panel.html +++ b/src/templates/login_panel.html @@ -2,6 +2,10 @@ {[ if (auto_login) { ]} {[ } ]} +
+ + +
{[ if (!auto_login) { ]} {[ if (authentication == LOGIN || authentication == EXTERNAL) { ]} @@ -11,7 +15,6 @@ {[ } ]} - {[ } ]} {[ if (authentication == ANONYMOUS) { ]}