Do not require a full refresh on login/logout (#6841)

* no-refresh login as well, plus lots of fixes for missing config on login

* replace config with new set on logout as well

* passing new payload data into new action:app.loggedIn hook, and old action:app.loggedOut hook

* fixed issues with socket.io not properly representing uid on server

* some light refactoring and cleanup

* minor cleanup, fixed spa logout not working after login

* have reconnection handler for socket.io wait 2s to confirm disconnection before reporting -- stops flicker if reconnecting immediately

* Dynamically replace chat and slideout menu on updateHeader()

... instead of just the menu items.

* more efficient calls to Benchpress and translator /cc @pitaj

* fix: chats and notification handlers not working after login

* fix: accidentally calling cb multiple times
v1.18.x
Julian Lam 6 years ago committed by GitHub
parent 6fb1f97d88
commit 84433f29ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -56,9 +56,7 @@ app.cacheBuster = null;
app.newTopic();
});
require(['components'], function (components) {
components.get('user/logout').on('click', app.logout);
});
$('#header-menu .container').on('click', '[component="user/logout"]', app.logout);
Visibility.change(function (event, state) {
if (state === 'visible') {
@ -106,6 +104,45 @@ app.cacheBuster = null;
});
};
app.updateHeader = function (data, callback) {
/**
* data:
* header (obj)
* config (obj)
* next (string)
*/
require(['benchpress', 'translator', 'notifications', 'chat'], function (Benchpress, translator, Notifications, Chat) {
app.user = data.header.user;
data.header.config = data.config;
config = data.config;
Benchpress.setGlobal('config', config);
// Manually reconnect socket.io
socket.close();
socket.open();
// Re-render top bar menu
var toRender = {
menu: $('#header-menu .container'),
'chats-menu': $('#chats-menu'),
'slideout-menu': $('.slideout-menu'),
};
Promise.all(Object.keys(toRender).map(function (tpl) {
return Benchpress.render('partials/' + tpl, data.header).then(function (render) {
return translator.Translator.create().translate(render);
});
})).then(function (html) {
Object.values(toRender).forEach(function (element, idx) {
element.html(html[idx]);
});
Notifications.prepareDOM();
Chat.prepareDOM();
callback();
});
});
};
app.logout = function (e) {
if (e) {
e.preventDefault();
@ -124,13 +161,18 @@ app.cacheBuster = null;
headers: {
'x-csrf-token': config.csrf_token,
},
success: function () {
var payload = {
next: config.relative_path + '/',
};
$(window).trigger('action:app.loggedOut', payload);
window.location.href = payload.next;
success: function (data) {
app.updateHeader(data, function () {
// Overwrite in hook (below) to redirect elsewhere
data.next = data.next || undefined;
$(window).trigger('action:app.loggedOut', data);
if (data.next) {
ajaxify.go(data.next);
} else {
ajaxify.refresh();
}
});
},
});
};

@ -35,14 +35,14 @@ define('forum/login', [], function () {
headers: {
'x-csrf-token': config.csrf_token,
},
success: function (returnTo) {
var pathname = utils.urlToLocation(returnTo).pathname;
var params = utils.params({ url: returnTo });
success: function (data) {
var params = utils.params({ url: data.next });
params.loggedin = true;
var qs = decodeURIComponent($.param(params));
window.location.href = pathname + '?' + qs;
app.updateHeader(data, function () {
ajaxify.go(data.next);
$(window).trigger('action:app.loggedIn', data);
});
},
error: function (data) {
if (data.status === 403 && data.responseText === 'Forbidden') {

@ -24,7 +24,14 @@ app.isConnected = false;
function addHandlers() {
socket.on('connect', onConnect);
socket.on('reconnecting', onReconnecting);
socket.on('reconnecting', function () {
// Wait 2s before firing
setTimeout(function () {
if (socket.disconnected) {
onReconnecting();
}
}, 2000);
});
socket.on('disconnect', onDisconnect);

@ -14,10 +14,12 @@ var plugins = require('../plugins');
var utils = require('../utils');
var translator = require('../translator');
var helpers = require('./helpers');
var middleware = require('../middleware');
var privileges = require('../privileges');
var sockets = require('../socket.io');
var authenticationController = module.exports;
var apiController = require('./api');
authenticationController.register = function (req, res) {
var registrationType = meta.config.registrationType || 'normal';
@ -277,10 +279,16 @@ function continueLogin(req, res, next) {
return helpers.noScriptErrors(req, res, err.message, 403);
}
res.status(200).send(nconf.get('relative_path') + '/reset/' + code);
res.status(200).send({
next: nconf.get('relative_path') + '/reset/' + code,
});
});
} else {
authenticationController.doLogin(req, userData.uid, function (err) {
async.parallel({
doLogin: async.apply(authenticationController.doLogin, req, userData.uid),
header: async.apply(middleware.generateHeader, req, res, {}),
config: async.apply(apiController.loadConfig, req),
}, function (err, payload) {
if (err) {
return helpers.noScriptErrors(req, res, err.message, 403);
}
@ -296,7 +304,11 @@ function continueLogin(req, res, next) {
if (req.body.noscript === 'true') {
res.redirect(destination + '?loggedin');
} else {
res.status(200).send(destination);
res.status(200).send({
next: destination,
header: payload.header,
config: payload.config,
});
}
});
}
@ -320,6 +332,9 @@ authenticationController.doLogin = function (req, uid, callback) {
authenticationController.onSuccessfulLogin = function (req, uid, callback) {
var uuid = utils.generateUUID();
req.uid = uid;
req.loggedIn = true;
async.waterfall([
function (next) {
meta.blacklist.test(req.ip, next);
@ -451,7 +466,8 @@ authenticationController.logout = function (req, res, next) {
},
function (next) {
req.logout();
req.session.destroy(function (err) {
req.session.regenerate(function (err) {
req.uid = 0;
next(err);
});
},
@ -467,7 +483,19 @@ authenticationController.logout = function (req, res, next) {
if (req.body.noscript === 'true') {
res.redirect(nconf.get('relative_path') + '/');
} else {
res.status(200).send('');
async.parallel({
header: async.apply(middleware.generateHeader, req, res, {}),
config: async.apply(apiController.loadConfig, req),
}, function (err, payload) {
if (err) {
return res.status(500);
}
res.status(200).send({
header: payload.header,
config: payload.config,
});
});
}
},
], next);

@ -45,7 +45,7 @@ module.exports = function (middleware) {
], next);
};
middleware.renderHeader = function (req, res, data, callback) {
middleware.generateHeader = function (req, res, data, callback) {
var registrationType = meta.config.registrationType || 'normal';
res.locals.config = res.locals.config || {};
var templateValues = {
@ -209,8 +209,16 @@ module.exports = function (middleware) {
templateValues: templateValues,
}, next);
},
function (data, next) {
req.app.render('header', data.templateValues, next);
], function (err, data) {
callback(err, data.templateValues);
});
};
middleware.renderHeader = function (req, res, data, callback) {
async.waterfall([
async.apply(middleware.generateHeader, req, res, data),
function (templateValues, next) {
req.app.render('header', templateValues, next);
},
], callback);
};

Loading…
Cancel
Save