diff --git a/css/converse.css b/css/converse.css index 049c46e4a..80ae4345a 100644 --- a/css/converse.css +++ b/css/converse.css @@ -1977,13 +1977,14 @@ font-size: 85%; } #conversejs #controlbox #converse-register .instructions:hover { color: #777; } - #conversejs #controlbox .conn-feedback p { - color: #578EA9; - padding-bottom: 0.5em; } - #conversejs #controlbox .conn-feedback p.feedback-subject.error { - font-weight: bold; } - #conversejs #controlbox .conn-feedback p.error { + #conversejs #controlbox .conn-feedback { + color: #578EA9; } + #conversejs #controlbox .conn-feedback.error { color: #A53214; } + #conversejs #controlbox .conn-feedback p { + padding-bottom: 0.5em; } + #conversejs #controlbox .conn-feedback p.feedback-subject.error { + font-weight: bold; } #conversejs #controlbox .brand-heading-container .brand-heading { text-align: left; font-size: 150%; } diff --git a/css/inverse.css b/css/inverse.css index b648bb2ba..9ca8c0d5c 100644 --- a/css/inverse.css +++ b/css/inverse.css @@ -2065,13 +2065,14 @@ body { font-size: 85%; } #conversejs #controlbox #converse-register .instructions:hover { color: #777; } - #conversejs #controlbox .conn-feedback p { - color: #578EA9; - padding-bottom: 0.5em; } - #conversejs #controlbox .conn-feedback p.feedback-subject.error { - font-weight: bold; } - #conversejs #controlbox .conn-feedback p.error { + #conversejs #controlbox .conn-feedback { + color: #578EA9; } + #conversejs #controlbox .conn-feedback.error { color: #A53214; } + #conversejs #controlbox .conn-feedback p { + padding-bottom: 0.5em; } + #conversejs #controlbox .conn-feedback p.feedback-subject.error { + font-weight: bold; } #conversejs #controlbox .brand-heading-container .brand-heading { text-align: left; font-size: 150%; } @@ -2387,8 +2388,6 @@ body { width: 200px; float: left; margin: 0; } - #conversejs #controlbox .conn-feedback { - padding-top: 2em; } #conversejs #controlbox .toggle-register-login { line-height: 30px; } #conversejs #controlbox .brand-heading-container { diff --git a/sass/_controlbox.scss b/sass/_controlbox.scss index 6348c1ab8..1ae77c76d 100644 --- a/sass/_controlbox.scss +++ b/sass/_controlbox.scss @@ -115,15 +115,15 @@ } .conn-feedback { + color: $controlbox-head-color; + &.error { + color: $error-color; + } p { - color: $controlbox-head-color; padding-bottom: 0.5em; &.feedback-subject.error { font-weight: bold; } - &.error { - color: $error-color; - } } } diff --git a/sass/inverse/_controlbox.scss b/sass/inverse/_controlbox.scss index 144f19ecd..2f1a94ea7 100644 --- a/sass/inverse/_controlbox.scss +++ b/sass/inverse/_controlbox.scss @@ -5,10 +5,6 @@ float: left; margin: 0; - .conn-feedback { - padding-top: 2em; - } - .toggle-register-login { line-height: $line-height-huge; } diff --git a/src/converse-controlbox.js b/src/converse-controlbox.js index 3e6539a3a..4abba3373 100644 --- a/src/converse-controlbox.js +++ b/src/converse-controlbox.js @@ -58,6 +58,43 @@ const CHATBOX_TYPE = 'chatbox'; const { Strophe, Backbone, Promise, utils, _, moment } = converse.env; + const CONNECTION_STATUS_CSS_CLASS = { + 'Error': 'error', + 'Connecting': 'info', + 'Connection failure': 'error', + 'Authenticating': 'info', + 'Authentication failure': 'error', + 'Connected': 'info', + 'Disconnected': 'error', + 'Disconnecting': 'warn', + 'Attached': 'info', + 'Redirect': 'info', + 'Reconnecting': 'warn' + }; + + const PRETTY_CONNECTION_STATUS = { + 0: 'Error', + 1: 'Connecting', + 2: 'Connection failure', + 3: 'Authenticating', + 4: 'Authentication failure', + 5: 'Connected', + 6: 'Disconnected', + 7: 'Disconnecting', + 8: 'Attached', + 9: 'Redirect', + 10: 'Reconnecting' + }; + + const REPORTABLE_STATUSES = [ + 0, // ERROR' + 1, // CONNECTING + 2, // CONNFAIL + 3, // AUTHENTICATING + 4, // AUTHFAIL + 7, // DISCONNECTING + 10 // RECONNECTING + ]; converse.plugins.add('converse-controlbox', { @@ -299,7 +336,9 @@ renderLoginPanel () { this.el.classList.add("logged-out"); if (_.isNil(this.loginpanel)) { - this.loginpanel = new _converse.LoginPanel({'model': this}); + this.loginpanel = new _converse.LoginPanel({ + 'model': new _converse.LoginPanelModel() + }); const panes = this.el.querySelector('.controlbox-panes'); panes.innerHTML = ''; panes.appendChild(this.loginpanel.render().el); @@ -413,31 +452,51 @@ } }); + _converse.LoginPanelModel = Backbone.Model.extend({ + defaults: { + 'errors': [], + } + }); _converse.LoginPanel = Backbone.View.extend({ tagName: 'div', id: "converse-login-panel", className: 'controlbox-pane fade-in', events: { - 'submit form#converse-login': 'authenticate' + 'submit form#converse-login': 'authenticate', + 'blur input': 'validate' }, initialize (cfg) { - _converse.connfeedback.on('change', this.renderConnectionFeedback, this); + this.model.on('change', this.render, this); + this.listenTo(_converse.connfeedback, 'change', this.render); }, render () { - const html = tpl_login_panel({ - '__': __, - 'ANONYMOUS': _converse.ANONYMOUS, - 'EXTERNAL': _converse.EXTERNAL, - 'LOGIN': _converse.LOGIN, - 'PREBIND': _converse.PREBIND, - 'auto_login': _converse.auto_login, - 'authentication': _converse.authentication, - 'label_anon_login': __('Click here to log in anonymously'), - 'placeholder_username': (_converse.locked_domain || _converse.default_domain) && __('Username') || __('user@domain'), - }); + const connection_status = _converse.connfeedback.get('connection_status'); + let feedback_class, pretty_status; + if (_.includes(REPORTABLE_STATUSES, connection_status)) { + pretty_status = PRETTY_CONNECTION_STATUS[connection_status]; + feedback_class = CONNECTION_STATUS_CSS_CLASS[pretty_status]; + } + const html = tpl_login_panel( + _.extend(this.model.toJSON(), { + '__': __, + '_converse': _converse, + 'ANONYMOUS': _converse.ANONYMOUS, + 'EXTERNAL': _converse.EXTERNAL, + 'LOGIN': _converse.LOGIN, + 'PREBIND': _converse.PREBIND, + 'auto_login': _converse.auto_login, + 'authentication': _converse.authentication, + 'connection_status': connection_status, + 'conn_feedback_class': feedback_class, + 'conn_feedback_subject': pretty_status, + 'conn_feedback_message': _converse.connfeedback.get('message'), + 'placeholder_username': (_converse.locked_domain || _converse.default_domain) && + __('Username') || __('user@domain'), + }) + ); const form = this.el.querySelector('form'); if (_.isNull(form)) { this.el.innerHTML = html; @@ -445,75 +504,47 @@ const patches = vdom.diff(vdom_parser(form), vdom_parser(html)); vdom.patch(form, patches); } - this.renderConnectionFeedback(); return this; }, - renderConnectionFeedback () { - const feedback_html = tpl_login_feedback({ - 'conn_feedback_class': _converse.connfeedback.get('klass'), - 'conn_feedback_subject': _converse.connfeedback.get('subject'), - 'conn_feedback_message': _converse.connfeedback.get('message'), - }); - const feedback_el = this.el.querySelector('.conn-feedback'); - if (_.isNull(feedback_el)) { - this.el.insertAdjacentHTML('afterbegin', feedback_html); - } else { - feedback_el.outerHTML = feedback_html; - } - }, - - showSpinner (only_submit_button=false) { + validate () { const form = this.el.querySelector('form'); - if (only_submit_button) { - const button = form.querySelector('input[type=submit]'); - button.classList.add('hidden'); - button.insertAdjacentHTML('afterend', ''); - } else { - form.innerHTML = tpl_spinner(); + const jid = form.querySelector('input[name=jid]').value; + const password = _.get(form.querySelector('input[name=password]'), 'value'); + const errors = []; + if (!jid || ( + !_converse.locked_domain && + !_converse.default_domain && + _.filter(jid.split('@')).length < 2)) { + errors.push(errors, 'invalid_jid'); } - return this; + if (!password && _converse.authentication !== _converse.EXTERNAL) { + errors.push(errors, 'password_required'); + } + this.model.set('errors', errors); + return errors.length == 0; }, authenticate (ev) { /* Authenticate the user based on a form submission event. */ if (ev && ev.preventDefault) { ev.preventDefault(); } - const $form = $(ev.target); if (_converse.authentication === _converse.ANONYMOUS) { - this.showSpinner().connect(_converse.jid, null); + this.connect(_converse.jid, null); return; } - const $jid_input = $form.find('input[name=jid]'); - const $jid_error_msg = $form.find('.invalid-jid-msg'); - const $pw_input = $form.find('input[name=password]'); - const password = $pw_input.val(); - - let jid = $jid_input.val(), - errors = false; - - if (!jid || ( - !_converse.locked_domain && - !_converse.default_domain && - _.filter(jid.split('@')).length < 2)) { - errors = true; - $jid_input.addClass('error'); - $jid_error_msg.removeClass('hidden'); - } else { - $jid_error_msg.addClass('hidden'); + if (!this.validate()) { + return; } + let jid = ev.target.querySelector('input[name=jid]').value; + const password = _.get(ev.target.querySelector('input[name=password]'), 'value'); - if (!password && _converse.authentication !== _converse.EXTERNAL) { - errors = true; - $pw_input.addClass('error'); - } - if (errors) { return; } if (_converse.locked_domain) { jid = Strophe.escapeNode(jid) + '@' + _converse.locked_domain; } else if (_converse.default_domain && !_.includes(jid, '@')) { jid = jid + '@' + _converse.default_domain; } - this.showSpinner(true).connect(jid, password); + this.connect(jid, password); return false; }, diff --git a/src/converse-core.js b/src/converse-core.js index 8478f18c1..5bef6f90e 100755 --- a/src/converse-core.js +++ b/src/converse-core.js @@ -100,7 +100,7 @@ _converse.OPENED = 'opened'; _converse.PREBIND = "prebind"; - _converse.PRETTY_CONNECTION_STATUS = { + _converse.CONNECTION_STATUS = { 0: 'ERROR', 1: 'CONNECTING', 2: 'CONNFAIL', @@ -110,7 +110,8 @@ 6: 'DISCONNECTED', 7: 'DISCONNECTING', 8: 'ATTACHED', - 9: 'REDIRECT' + 9: 'REDIRECT', + 10: 'RECONNECTING' }; _converse.DEFAULT_IMAGE_TYPE = 'image/png'; @@ -407,10 +408,9 @@ _converse.everySecondTrigger = window.setInterval(_converse.onEverySecond, 1000); }; - this.giveFeedback = function (subject, klass, message) { + this.setConnectionStatus = function (connection_status, message) { _converse.connfeedback.set({ - 'subject': subject, - 'klass': klass, + 'connection_status': connection_status, 'message': message }); }; @@ -431,9 +431,8 @@ this.reconnect = _.debounce(function () { _converse.log('RECONNECTING'); _converse.log('The connection has dropped, attempting to reconnect.'); - _converse.giveFeedback( - __("Reconnecting"), - 'warn', + _converse.setConnectionStatus( + Strophe.Status.RECONNECTING, __('The connection has dropped, attempting to reconnect.') ); _converse.connection.reconnecting = true; @@ -495,9 +494,9 @@ * through various states while establishing or tearing down a * connection. */ - _converse.log(`Status changed to: ${_converse.PRETTY_CONNECTION_STATUS[status]}`); + _converse.log(`Status changed to: ${_converse.CONNECTION_STATUS[status]}`); if (status === Strophe.Status.CONNECTED || status === Strophe.Status.ATTACHED) { - _converse.giveFeedback(); + _converse.setConnectionStatus(status); // By default we always want to send out an initial presence stanza. _converse.send_initial_presence = true; _converse.setDisconnectionCause(); @@ -517,20 +516,19 @@ _converse.setDisconnectionCause(status, message); _converse.onDisconnected(); } else if (status === Strophe.Status.ERROR) { - _converse.giveFeedback( - __('Connection error'), - 'error', + _converse.setConnectionStatus( + status, __('An error occurred while connecting to the chat server.') ); } else if (status === Strophe.Status.CONNECTING) { - _converse.giveFeedback(__('Connecting…')); + _converse.setConnectionStatus(status); } else if (status === Strophe.Status.AUTHENTICATING) { - _converse.giveFeedback(__('Authenticating…')); + _converse.setConnectionStatus(status); } else if (status === Strophe.Status.AUTHFAIL) { if (!message) { message = __('Your Jabber ID and/or password is incorrect. Please try again.'); } - _converse.giveFeedback(__('Authentication failed'), 'error', message); + _converse.setConnectionStatus(status, message); _converse.setDisconnectionCause(status, message, true); _converse.onDisconnected(); } else if (status === Strophe.Status.CONNFAIL) { @@ -541,11 +539,7 @@ } 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', - feedback - ); + _converse.setConnectionStatus(status, feedback); _converse.setDisconnectionCause(status, message); } else if (status === Strophe.Status.DISCONNECTING) { _converse.setDisconnectionCause(status, message); @@ -571,7 +565,7 @@ } }; - this.initStatus = () => + this.initStatus = () => new Promise((resolve, reject) => { const promise = new utils.getWrappedPromise(); this.xmppstatus = new this.XMPPStatus(); @@ -1521,15 +1515,14 @@ this.ConnectionFeedback = Backbone.Model.extend({ - initialize () { - this.on('change', this.emitConnectionFeedbackChange); + defaults: { + 'connection_status': undefined, + 'message': '' }, - emitConnectionFeedbackChange () { - _converse.emit('connfeedback', { - 'klass': _converse.connfeedback.get('klass'), - 'message': _converse.connfeedback.get('message'), - 'subject': _converse.connfeedback.get('subject') + initialize () { + this.on('change', () => { + _converse.emit('connfeedback', _converse.connfeedback); }); } }); diff --git a/src/converse-register.js b/src/converse-register.js index a141820cc..e57055426 100644 --- a/src/converse-register.js +++ b/src/converse-register.js @@ -148,10 +148,10 @@ const { _converse } = this, { __ } = _converse; - _converse.PRETTY_CONNECTION_STATUS[Strophe.Status.REGIFAIL] = 'REGIFAIL'; - _converse.PRETTY_CONNECTION_STATUS[Strophe.Status.REGISTERED] = 'REGISTERED'; - _converse.PRETTY_CONNECTION_STATUS[Strophe.Status.CONFLICT] = 'CONFLICT'; - _converse.PRETTY_CONNECTION_STATUS[Strophe.Status.NOTACCEPTABLE] = 'NOTACCEPTABLE'; + _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, @@ -433,7 +433,7 @@ ], status_code)) { _converse.log( - `Problem during registration: Strophe.Status is ${_converse.PRETTY_CONNECTION_STATUS[status_code]}`, + `Problem during registration: Strophe.Status is ${_converse.CONNECTION_STATUS[status_code]}`, Strophe.LogLevel.ERROR ); this.abortRegistration(); diff --git a/src/templates/login_feedback.html b/src/templates/login_feedback.html deleted file mode 100644 index 3343b5c15..000000000 --- a/src/templates/login_feedback.html +++ /dev/null @@ -1,8 +0,0 @@ - diff --git a/src/templates/login_panel.html b/src/templates/login_panel.html index f77a5be47..b7f9eccf3 100644 --- a/src/templates/login_panel.html +++ b/src/templates/login_panel.html @@ -1,22 +1,38 @@
- {[ if (auto_login) { ]} -