Merge pull request #3834 from NodeBB/room-optimize

Room optimize
v1.18.x
Barış Soner Uşaklı 9 years ago
commit 9cb9531b8e

@ -22,7 +22,7 @@ define('admin/general/dashboard', ['semver'], function(semver) {
Admin.init = function() {
app.enterRoom('admin');
socket.emit('meta.rooms.getAll', Admin.updateRoomUsage);
socket.emit('admin.rooms.getAll', Admin.updateRoomUsage);
isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
@ -439,7 +439,7 @@ define('admin/general/dashboard', ['semver'], function(semver) {
intervals.rooms = setInterval(function() {
if (app.isFocused && app.isConnected) {
socket.emit('meta.rooms.getAll', Admin.updateRoomUsage);
socket.emit('admin.rooms.getAll', Admin.updateRoomUsage);
}
}, realtime ? DEFAULTS.realtimeInterval : DEFAULTS.roomInterval);

@ -130,11 +130,7 @@ app.cacheBuster = null;
app.enterRoom = function (room, callback) {
callback = callback || function() {};
if (socket) {
if (app.currentRoom === room) {
return;
}
if (socket && app.user.uid && app.currentRoom !== room) {
socket.emit('meta.rooms.enter', {
enter: room,
username: app.user.username,
@ -148,6 +144,7 @@ app.cacheBuster = null;
return app.alertError(err.message);
}
app.currentRoom = room;
callback();
});
}
};

@ -8,12 +8,11 @@ define('forum/topic', [
'forum/topic/threadTools',
'forum/topic/postTools',
'forum/topic/events',
'forum/topic/browsing',
'forum/topic/posts',
'navigator',
'sort',
'components'
], function(infinitescroll, threadTools, postTools, events, browsing, posts, navigator, sort, components) {
], function(infinitescroll, threadTools, postTools, events, posts, navigator, sort, components) {
var Topic = {},
currentUrl = '';
@ -70,16 +69,6 @@ define('forum/topic', [
$(window).on('scroll', updateTopicTitle);
if (app.user.uid) {
socket.emit('topics.enter', tid, function(err, data) {
if (err) {
return app.alertError(err.message);
}
browsing.onUpdateUsersInRoom(data);
});
}
handleTopicSearch();
$(window).trigger('action:topic.loaded');

@ -1,111 +0,0 @@
'use strict';
/* globals define, app, config, socket, ajaxify */
define('forum/topic/browsing', function() {
var Browsing = {};
Browsing.onUpdateUsersInRoom = function(data) {
if (data && data.room.indexOf('topic_' + ajaxify.data.tid) !== -1) {
$('[component="topic/browsing/list"]').parent().toggleClass('hidden', !data.users.length);
for (var i=0; i<data.users.length; ++i) {
addUserIcon(data.users[i]);
}
updateUserCount(data.hidden);
}
};
Browsing.onUserEnter = function(data) {
var browsingList = $('[component="topic/browsing/list"]');
var user = browsingList.find('a[data-uid="' + data.uid + '"]');
if (!user.length && browsingList.first().children().length < 10) {
addUserIcon(data);
} else if (user.length) {
user.attr('data-count', parseInt(user.attr('data-count'), 10) + 1);
} else {
increaseUserCount(1);
}
Browsing.onUserStatusChange(data);
};
Browsing.onUserLeave = function(uid) {
if (app.user.uid === parseInt(uid, 10)) {
return;
}
var browsingList = $('[component="topic/browsing/list"]');
var user = browsingList.find('a[data-uid="' + uid + '"]');
if (user.length) {
var count = Math.max(0, parseInt(user.attr('data-count'), 10) - 1);
user.attr('data-count', count);
if (count <= 0) {
user.parent().remove();
if (!browsingList.children(':not(.hidden)').length) {
browsingList.parent().addClass('hidden');
}
}
} else {
increaseUserCount(-1);
}
};
Browsing.onUserStatusChange = function(data) {
app.updateUserStatus($('[data-uid="' + data.uid + '"] [component="user/status"]'), data.status);
updateBrowsingUsers(data);
};
function updateBrowsingUsers(data) {
var browsingList = $('[component="topic/browsing/list"]');
var user = browsingList.find('a[data-uid="'+ data.uid + '"]');
if (user.length) {
user.parent().toggleClass('hidden', data.status === 'offline');
browsingList.parent().toggleClass('hidden', !browsingList.children(':not(.hidden)').length);
}
}
function addUserIcon(user) {
if (!user.userslug) {
return;
}
var browsingList = $('[component="topic/browsing/list"]');
var userEl = createUserIcon(user.uid, user.picture, user.userslug, user.username, user['icon:bgColor'], user['icon:text']);
var isSelf = parseInt(user.uid, 10) === parseInt(app.user.uid, 10);
if (isSelf) {
browsingList.prepend(userEl);
} else {
browsingList.append(userEl);
}
browsingList.find('a[data-uid]').tooltip({
placement: 'top'
});
}
function createUserIcon(uid, picture, userslug, username, iconBg, iconText) {
if (!$('[component="topic/browsing/list"]').find('[data-uid="' + uid + '"]').length) {
var imgOrIcon = picture ?
'<img src="'+ picture +'" />' :
'<div class="user-icon" style="background-color: ' + iconBg + ';">' + iconText + '</div>';
return $('<div class="inline-block"><a title="' + username + '" data-uid="' + uid + '" data-count="1" href="' + config.relative_path + '/user/' + userslug + '">' + imgOrIcon + '</a></div>');
}
}
function updateUserCount(count) {
count = parseInt(count, 10);
if (!count || count < 0) {
count = 0;
}
$('[component="topic/browsing/count"]').text(count).parent().toggleClass('hidden', count === 0);
}
function increaseUserCount(incr) {
updateUserCount(parseInt($('[component="topic/browsing/count"]').first().text(), 10) + incr);
}
return Browsing;
});

@ -4,19 +4,16 @@
/* globals config, app, ajaxify, define, socket, templates, translator, utils */
define('forum/topic/events', [
'forum/topic/browsing',
'forum/topic/postTools',
'forum/topic/threadTools',
'forum/topic/posts',
'components'
], function(browsing, postTools, threadTools, posts, components) {
], function(postTools, threadTools, posts, components) {
var Events = {};
var events = {
'event:user_enter': browsing.onUserEnter,
'event:user_leave': browsing.onUserLeave,
'event:user_status_change': browsing.onUserStatusChange,
'event:user_status_change': onUserStatusChange,
'event:voted': updatePostVotesAndUserReputation,
'event:favourited': updateFavouriteCount,
@ -69,6 +66,10 @@ define('forum/topic/events', [
}
};
function onUserStatusChange(data) {
app.updateUserStatus($('[data-uid="' + data.uid + '"] [component="user/status"]'), data.status);
}
function updatePostVotesAndUserReputation(data) {
var votes = components.get('post/vote-count', data.post.pid),
reputationElements = $('.reputation[data-uid="' + data.post.uid + '"]');

@ -10,9 +10,6 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra
var chatsToggleEl = components.get('chat/dropdown'),
chatsListEl = components.get('chat/list');
// Sync open chats between all user socket sessions
module.sync();
chatsToggleEl.on('click', function() {
if (chatsToggleEl.parent().hasClass('open')) {
return;
@ -93,42 +90,6 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra
var modal = module.getModal(data.uid);
app.updateUserStatus(modal.find('[component="user/status"]'), data.status);
});
socket.on('query:chats.sync', function(data, callback) {
var chats = Array.prototype.map.call(taskbar.get('chat'), function(chatObj) {
return {
username: chatObj.options.title,
uid: chatObj.options.touid,
new: chatObj.element.hasClass('new')
};
});
callback(null, chats);
});
socket.on('event:chats.open', function(data) {
data.silent = true;
module.createModal(data);
});
socket.on('event:chats.close', function(uid) {
module.close(module.getModal(uid), true);
});
socket.on('event:chats.toggleNew', function(data) {
var uuid = module.getModal(data.uid).attr('UUID');
module.toggleNew(uuid, data.state, true);
});
$(window).on('action:taskbar.toggleNew', function(ev, uuid) {
var modal = $('.chat-modal[uuid="' + uuid + '"]'),
touid = modal.attr('touid');
socket.emit('modules.chats.toggleNew', {
uid: touid,
state: false
});
});
};
module.loadChatsDropdown = function(chatsListEl) {
@ -318,10 +279,6 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra
state: ''
});
if (!data.silent) {
socket.emit('modules.chats.open', data);
}
$(window).trigger('action:chat.loaded', chatModal);
if (typeof callback === 'function') {
@ -343,10 +300,6 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra
taskbar.discard('chat', chatModal.attr('UUID'));
Chats.notifyTyping(chatModal.attr('touid'), false);
if (!silent) {
socket.emit('modules.chats.close', chatModal.attr('touid'));
}
if (chatModal.attr('data-mobile')) {
module.disableMobileBehaviour(chatModal);
}
@ -423,27 +376,7 @@ define('chat', ['components', 'taskbar', 'string', 'sounds', 'forum/chats', 'tra
socket.emit('modules.chats.canMessage', toUid, callback);
};
module.sync = function() {
socket.emit('modules.chats.sync', function(err, users) {
if (err) {
return app.alertError(err.message);
}
users.forEach(function(user) {
if (!module.modalExists(user.uid)) {
module.createModal({
username: user.username,
touid: user.uid,
silent: true
}, function(modal) {
if (user.new) {
module.toggleNew(modal.attr('UUID'), true, true);
}
});
}
});
});
};
return module;
});

@ -1,8 +1,7 @@
'use strict';
var cronJob = require('cron').CronJob,
db = require('./database');
var cronJob = require('cron').CronJob;
var db = require('./database');
(function(Analytics) {
@ -61,6 +60,8 @@ var cronJob = require('cron').CronJob,
db.incrObjectFieldBy('global', 'uniqueIPCount', uniqueIPCount);
uniqueIPCount = 0;
}
};
Analytics.getUnwrittenPageviews = function() {

@ -58,7 +58,7 @@ helpers.getUserDataByUserSlug = function(userslug, callerUID, callback) {
var self = parseInt(callerUID, 10) === parseInt(userData.uid, 10);
userData.joindate = utils.toISOString(userData.joindate);
userData.lastonline = userData.lastonline ? utils.toISOString(userData.lastonline) : userData.joindate;
userData.lastonlineISO = utils.toISOString(userData.lastonline || userData.joindate);
userData.age = userData.birthday ? Math.floor((new Date().getTime() - new Date(userData.birthday).getTime()) / 31536000000) : '';
if (!(isAdmin || self || (userData.email && userSettings.showemail))) {
@ -86,7 +86,7 @@ helpers.getUserDataByUserSlug = function(userslug, callerUID, callback) {
userData['email:confirmed'] = !!parseInt(userData['email:confirmed'], 10);
userData.profile_links = results.profile_links;
userData.sso = results.sso.associations;
userData.status = require('../../socket.io').isUserOnline(userData.uid) ? (userData.status || 'online') : 'offline';
userData.status = user.getStatus(userData);
userData.banned = parseInt(userData.banned, 10) === 1;
userData.website = validator.escape(userData.website);
userData.websiteLink = !userData.website.startsWith('http') ? 'http://' + userData.website : userData.website;

@ -125,6 +125,7 @@ topicsController.get = function(req, res, callback) {
});
},
function (topicData, next) {
var breadcrumbs = [
{
text: topicData.category.name,
@ -265,6 +266,18 @@ topicsController.get = function(req, res, callback) {
topics.increaseViewCount(tid);
if (req.uid) {
topics.markAsRead([tid], req.uid, function(err, markedRead) {
if (err) {
return callback(err);
}
if (markedRead) {
topics.pushUnreadCount(req.uid);
topics.markTopicNotificationsRead(tid, req.uid);
}
});
}
plugins.fireHook('filter:topic.build', {req: req, res: res, templateData: data}, function(err, data) {
if (err) {
return callback(err);

@ -149,84 +149,6 @@ usersController.getUsersForSearch = function(req, res, next) {
});
};
usersController.getMap = function(req, res, next) {
var socketIO = require('../socket.io');
var rooms = require('../socket.io/rooms');
var roomNames = ['user_list', 'categories', 'unread_topics', 'recent_topics', 'popular_topics', 'tags'];
var links = {
user_list: '/users',
categories: '/categories',
unread_topics: '/unread',
recent_topics: '/recent',
popular_topics: '/popular',
tags: '/tags'
};
var keys = Object.keys(rooms.roomClients());
keys = keys.filter(function(key) {
return key.startsWith('topic_') || key.startsWith('category_');
});
roomNames = roomNames.concat(keys);
async.map(roomNames, function(roomName, next) {
socketIO.getUsersInRoom(0, roomName, 0, 39, function(err, data) {
if (err) {
return next(err);
}
if (roomName.startsWith('category_')) {
var cid = roomName.split('_')[1];
categories.getCategoryFields(cid, ['slug', 'name'], function(err, categoryData) {
if (err) {
return next(err);
}
data.room = validator.escape(categoryData.name);
data.link = '/category/' + categoryData.slug;
data.core = false;
next(null, data);
});
} else if (roomName.startsWith('topic_')) {
var tid = roomName.split('_')[1];
topics.getTopicFields(tid, ['slug', 'title'], function(err, topicData) {
if (err) {
return next(err);
}
data.room = validator.escape(topicData.title);
data.link = '/topic/' + topicData.slug;
data.core = false;
next(null, data);
});
} else {
data.core = true;
next(null, data);
}
});
}, function(err, data) {
if (err) {
return next(err);
}
data.sort(function(a, b) {
return b.users.length - a.users.length;
});
data.forEach(function(room) {
if (!room.link) {
room.link = links[room.room];
}
});
res.render('usersMap', {
rooms: data,
'reputation:disabled': parseInt(meta.config['reputation:disabled'], 10) === 1,
title: '[[pages:users/map]]',
breadcrumbs: helpers.buildBreadcrumbs([{text: '[[global:users]]', url: '/users'}, {text: '[[global:map]]'}])
});
});
};
function render(req, res, data, next) {
plugins.fireHook('filter:users.build', {req: req, res: res, templateData: data}, function(err, data) {
if (err) {

@ -321,7 +321,7 @@ module.exports = function(db, module) {
return callback();
}
value = helpers.valueToString(value);
db.collection('objects').findOne({_key:key, value: value}, {fields:{_id: 0, score: 1}}, function(err, result) {
db.collection('objects').findOne({_key: key, value: value}, {fields:{_id: 0, score: 1}}, function(err, result) {
callback(err, result ? result.score : null);
});
};

@ -266,7 +266,7 @@ var db = require('./database'),
db.isSortedSetMembers('uid:' + uid + ':chats:unread', uids, next);
},
users: function(next) {
user.getUsersFields(uids, ['uid', 'username', 'picture', 'status'] , next);
user.getUsersFields(uids, ['uid', 'username', 'picture', 'status', 'lastonline'] , next);
},
teasers: function(next) {
async.map(uids, function(fromuid, next) {
@ -288,11 +288,11 @@ var db = require('./database'),
return callback(err);
}
results.users.forEach(function(user, index) {
if (user && parseInt(user.uid, 10)) {
user.unread = results.unread[index];
user.status = sockets.isUserOnline(user.uid) ? user.status : 'offline';
user.teaser = results.teasers[index];
results.users.forEach(function(userData, index) {
if (userData && parseInt(userData.uid, 10)) {
userData.unread = results.unread[index];
userData.status = user.getStatus(userData);
userData.teaser = results.teasers[index];
}
});
@ -416,8 +416,9 @@ var db = require('./database'),
};
function sendNotifications(fromuid, touid, messageObj, callback) {
if (sockets.isUserOnline(touid)) {
return callback();
user.isOnline(touid, function(err, isOnline) {
if (err || isOnline) {
return callback(err);
}
notifications.create({
@ -446,6 +447,7 @@ var db = require('./database'),
});
}
});
});
}
}(exports));

@ -52,7 +52,6 @@ module.exports = function(Meta) {
'public/src/client/recent.js',
'public/src/client/unread.js',
'public/src/client/topic.js',
'public/src/client/topic/browsing.js',
'public/src/client/topic/events.js',
'public/src/client/topic/flag.js',
'public/src/client/topic/fork.js',

@ -20,10 +20,7 @@ module.exports = function(Posts) {
user.getMultipleUserSettings(uids, next);
},
userData: function(next) {
user.getUsersFields(uids, ['uid', 'username', 'fullname', 'userslug', 'reputation', 'postcount', 'picture', 'signature', 'banned', 'status'], next);
},
online: function(next) {
require('../socket.io').isUsersOnline(uids, next);
user.getUsersFields(uids, ['uid', 'username', 'fullname', 'userslug', 'reputation', 'postcount', 'picture', 'signature', 'banned', 'status', 'lastonline'], next);
}
}, function(err, results) {
if (err) {
@ -47,7 +44,7 @@ module.exports = function(Posts) {
userData.selectedGroup = userData.groups[index];
}
});
userData.status = user.getStatus(userData.status, results.online[i]);
userData.status = user.getStatus(userData);
});
async.map(userData, function(userData, next) {

@ -62,7 +62,6 @@ function userRoutes(app, middleware, controllers) {
setupPageRoute(app, '/users/sort-posts', middleware, middlewares, controllers.users.getUsersSortedByPosts);
setupPageRoute(app, '/users/sort-reputation', middleware, middlewares, controllers.users.getUsersSortedByReputation);
setupPageRoute(app, '/users/search', middleware, middlewares, controllers.users.getUsersForSearch);
setupPageRoute(app, '/users/map', middleware, middlewares, controllers.users.getMap);
}

@ -28,6 +28,7 @@ var async = require('async'),
tags: require('./admin/tags'),
rewards: require('./admin/rewards'),
navigation: require('./admin/navigation'),
rooms: require('./admin/rooms'),
themes: {},
plugins: {},
widgets: {},

@ -0,0 +1,73 @@
'use strict';
var validator = require('validator');
var topics = require('../../topics');
var SocketRooms = {};
SocketRooms.getAll = function(socket, data, callback) {
var websockets = require('../index');
var io = websockets.server;
if (!io) {
return;
}
var roomClients = io.sockets.adapter.rooms;
var socketData = {
onlineGuestCount: websockets.getOnlineAnonCount(),
onlineRegisteredCount: websockets.getOnlineUserCount(),
socketCount: websockets.getSocketCount(),
users: {
categories: roomClients.categories ? Object.keys(roomClients.categories).length : 0,
recent: roomClients.recent_topics ? Object.keys(roomClients.recent_topics).length : 0,
unread: roomClients.unread_topics ? Object.keys(roomClients.unread_topics).length: 0,
popular: roomClients.popular_topics ? Object.keys(roomClients.popular_topics).length: 0,
topics: 0,
category: 0
},
topics: {}
};
var topTenTopics = [],
tid;
for (var room in roomClients) {
if (roomClients.hasOwnProperty(room)) {
tid = room.match(/^topic_(\d+)/);
if (tid) {
var length = Object.keys(roomClients[room]).length;
socketData.users.topics += length;
topTenTopics.push({tid: tid[1], count: length});
} else if (room.match(/^category/)) {
socketData.users.category += Object.keys(roomClients[room]).length;
}
}
}
topTenTopics = topTenTopics.sort(function(a, b) {
return b.count - a.count;
}).slice(0, 10);
var topTenTids = topTenTopics.map(function(topic) {
return topic.tid;
});
topics.getTopicsFields(topTenTids, ['title'], function(err, titles) {
if (err) {
return callback(err);
}
topTenTopics.forEach(function(topic, index) {
socketData.topics[topic.tid] = {
value: topic.count || 0,
title: validator.escape(titles[index].title)
};
});
callback(null, socketData);
});
};
module.exports = SocketRooms;

@ -137,11 +137,6 @@ SocketCategories.getTopicCount = function(socket, cid, callback) {
categories.getCategoryField(cid, 'topic_count', callback);
};
SocketCategories.getUsersInCategory = function(socket, cid, callback) {
var uids = websockets.getUidsInRoom('category_' + cid);
user.getUsersFields(uids, ['uid', 'userslug', 'username', 'picture'], callback);
};
SocketCategories.getCategoriesByPrivilege = function(socket, privilege, callback) {
categories.getCategoriesByPrivilege('categories:cid', socket.uid, privilege, callback);
};

@ -11,7 +11,6 @@ var SocketIO = require('socket.io'),
user = require('../user'),
logger = require('../logger'),
ratelimit = require('../middleware/ratelimit'),
rooms = require('./rooms'),
Sockets = {},
Namespaces = {};
@ -46,12 +45,6 @@ function onConnection(socket) {
onConnect(socket);
// see https://github.com/Automattic/socket.io/issues/1814 and
// http://stackoverflow.com/questions/25830415/get-the-list-of-rooms-the-client-is-currently-in-on-disconnect-event
socket.onclose = function(reason) {
Object.getPrototypeOf(this).onclose.call(this, {reason: reason, rooms: socket.rooms.slice()});
};
socket.on('disconnect', function(data) {
onDisconnect(socket, data);
});
@ -63,8 +56,8 @@ function onConnection(socket) {
function onConnect(socket) {
if (socket.uid) {
rooms.enter(socket, 'uid_' + socket.uid);
rooms.enter(socket, 'online_users');
socket.join('uid_' + socket.uid);
socket.join('online_users');
user.getUserFields(socket.uid, ['status'], function(err, userData) {
if (err || !userData) {
@ -76,25 +69,17 @@ function onConnect(socket) {
}
});
} else {
rooms.enter(socket, 'online_guests');
socket.join('online_guests');
}
}
function onDisconnect(socket, data) {
function onDisconnect(socket) {
if (socket.uid) {
var socketCount = Sockets.getUserSocketCount(socket.uid);
if (socketCount <= 1) {
socket.broadcast.emit('event:user_status_change', {uid: socket.uid, status: 'offline'});
}
// see https://github.com/Automattic/socket.io/issues/1814
data.rooms.forEach(function(roomName) {
if (roomName.startsWith('topic')) {
io.in(roomName).emit('event:user_leave', socket.uid);
}
});
}
rooms.leaveAll(socket, data.rooms);
}
function onMessage(socket, payload) {
@ -211,25 +196,32 @@ Sockets.in = function(room) {
};
Sockets.getSocketCount = function() {
return rooms.socketCount();
if (!io) {
return 0;
}
return io.sockets.sockets.length;
};
Sockets.getUserSocketCount = function(uid) {
return rooms.clients('uid_' + uid).length;
if (!io) {
return 0;
}
return io.sockets.adapter.rooms['uid_' + uid] ? Object.keys(io.sockets.adapter.rooms['uid_' + uid]).length : 0;
};
Sockets.getOnlineUserCount = function() {
var count = 0;
Object.keys(rooms.roomClients()).forEach(function(roomName) {
if (roomName.startsWith('uid_')) {
++ count;
if (!io) {
return 0;
}
});
return count;
return io.sockets.adapter.rooms.online_users ? Object.keys(io.sockets.adapter.rooms.online_users).length : 0;
};
Sockets.getOnlineAnonCount = function () {
return rooms.clients('online_guests').length;
if (!io) {
return 0;
}
return io.sockets.adapter.rooms.online_guests ? Object.keys(io.sockets.adapter.rooms.online_guests).length : 0;
};
Sockets.reqFromSocket = function(socket) {
@ -249,76 +241,30 @@ Sockets.reqFromSocket = function(socket) {
};
Sockets.isUserOnline = function(uid) {
return !!rooms.clients('uid_' + uid).length;
winston.warn('[deprecated] Sockets.isUserOnline')
return false;
};
Sockets.isUsersOnline = function(uids, callback) {
callback(null, uids.map(Sockets.isUserOnline));
winston.warn('[deprecated] Sockets.isUsersOnline')
callback(null, uids.map(function() { return false; }));
};
Sockets.getUsersInRoom = function (uid, roomName, start, stop, callback) {
if (!roomName) {
return;
}
var uids = Sockets.getUidsInRoom(roomName);
var total = uids.length;
if (stop !== -1) {
uids = uids.slice(start, stop);
}
if (uid && uids.indexOf(uid.toString()) === -1) {
uids = [uid].concat(uids);
}
if (!uids.length) {
return callback(null, {users: [], total: 0 , room: roomName});
}
user.getUsersFields(uids, ['uid', 'username', 'userslug', 'picture', 'status'], function(err, users) {
if (err) {
return callback(err);
}
users = users.filter(function(user) {
return user && user.status !== 'offline';
});
winston.warn('[deprecated] Sockets.getUsersInRoom')
callback(null, {
users: users,
users: [],
room: roomName,
total: users.length ? total : 0,
hidden: Math.max(0, total - uids.length)
});
total: 0,
hidden: 0
});
return;
};
Sockets.getUidsInRoom = function(roomName, callback) {
winston.warn('[deprecated] Sockets.getUidsInRoom')
callback = callback || function() {};
var uids = [];
var socketids = rooms.clients(roomName);
if (!Array.isArray(socketids) || !socketids.length) {
callback(null, []);
return [];
}
for(var i=0; i<socketids.length; ++i) {
var socketRooms = rooms.clientRooms(socketids[i]);
if (Array.isArray(socketRooms)) {
socketRooms.forEach(function(roomName) {
if (roomName.startsWith('uid_')) {
var uid = roomName.split('_')[1];
if (uids.indexOf(uid) === -1) {
uids.push(uid);
}
}
});
}
}
callback(null, uids);
return uids;
};

@ -1,12 +1,9 @@
'use strict';
var validator = require('validator'),
meta = require('../meta'),
var meta = require('../meta'),
user = require('../user'),
topics = require('../topics'),
emitter = require('../emitter'),
rooms = require('./rooms'),
websockets = require('./'),
@ -50,16 +47,8 @@ SocketMeta.rooms.enter = function(socket, data, callback) {
leaveCurrentRoom(socket);
if (data.enter) {
rooms.enter(socket, data.enter);
socket.join(data.enter);
socket.currentRoom = data.enter;
if (data.enter.indexOf('topic') !== -1) {
data.uid = socket.uid;
data.picture = validator.escape(data.picture);
data.username = validator.escape(data.username);
data.userslug = validator.escape(data.userslug);
websockets.in(data.enter).emit('event:user_enter', data);
}
}
callback();
};
@ -74,70 +63,11 @@ SocketMeta.rooms.leaveCurrent = function(socket, data, callback) {
function leaveCurrentRoom(socket) {
if (socket.currentRoom) {
rooms.leave(socket, socket.currentRoom);
if (socket.currentRoom.indexOf('topic') !== -1) {
websockets.in(socket.currentRoom).emit('event:user_leave', socket.uid);
}
socket.leave(socket.currentRoom);
socket.currentRoom = '';
}
}
SocketMeta.rooms.getAll = function(socket, data, callback) {
var roomClients = rooms.roomClients();
var socketData = {
onlineGuestCount: websockets.getOnlineAnonCount(),
onlineRegisteredCount: websockets.getOnlineUserCount(),
socketCount: websockets.getSocketCount(),
users: {
categories: roomClients.categories ? roomClients.categories.length : 0,
recent: roomClients.recent_topics ? roomClients.recent_topics.length : 0,
unread: roomClients.unread_topics ? roomClients.unread_topics.length: 0,
popular: roomClients.popular_topics ? roomClients.popular_topics.length: 0,
topics: 0,
category: 0
},
topics: {}
};
var topTenTopics = [],
tid;
for (var room in roomClients) {
if (roomClients.hasOwnProperty(room)) {
tid = room.match(/^topic_(\d+)/);
if (tid) {
var length = roomClients[room].length;
socketData.users.topics += length;
topTenTopics.push({tid: tid[1], count: length});
} else if (room.match(/^category/)) {
socketData.users.category += roomClients[room].length;
}
}
}
topTenTopics = topTenTopics.sort(function(a, b) {
return b.count - a.count;
}).slice(0, 10);
var topTenTids = topTenTopics.map(function(topic) {
return topic.tid;
});
topics.getTopicsFields(topTenTids, ['title'], function(err, titles) {
if (err) {
return callback(err);
}
topTenTopics.forEach(function(topic, index) {
socketData.topics[topic.tid] = {
value: topic.count || 0,
title: validator.escape(titles[index].title)
};
});
callback(null, socketData);
});
};
module.exports = SocketMeta;

@ -7,7 +7,6 @@ var meta = require('../meta'),
async = require('async'),
server = require('./'),
rooms = require('./rooms'),
SocketModules = {
chats: {},
@ -105,37 +104,6 @@ SocketModules.chats.getRecentChats = function(socket, data, callback) {
Messaging.getRecentChats(socket.uid, start, stop, callback);
};
SocketModules.chats.sync = function(socket, data, callback) {
var chats = [],
uids = [],
socketIds = rooms.clients('uid_' + socket.uid);
rooms.broadcast(socket, 'uid_' + socket.uid, 'query:chats.sync', {}, function(err, sessionData) {
sessionData.forEach(function(data) {
data.forEach(function(chat) {
if (uids.indexOf(chat.uid) === -1) {
chats.push(chat);
uids.push(chat.uid);
}
});
});
callback(err, chats);
});
};
SocketModules.chats.open = function(socket, data, callback) {
rooms.broadcast(socket, 'uid_' + socket.uid, 'event:chats.open', data);
};
SocketModules.chats.close = function(socket, data, callback) {
rooms.broadcast(socket, 'uid_' + socket.uid, 'event:chats.close', data);
};
SocketModules.chats.toggleNew = function(socket, data, callback) {
rooms.broadcast(socket, 'uid_' + socket.uid, 'event:chats.toggleNew', data);
};
/* Sounds */
SocketModules.sounds.getSounds = function(socket, data, callback) {

@ -63,15 +63,14 @@ module.exports = function(SocketPosts) {
async.parallel({
admins: async.apply(groups.getMembers, 'administrators', 0, -1),
moderators: async.apply(groups.getMembers, 'cid:' + result.topic.cid + ':privileges:mods', 0, -1),
uidsInTopic: async.apply(websockets.getUidsInRoom, 'topic_' + result.topic.tid)
moderators: async.apply(groups.getMembers, 'cid:' + result.topic.cid + ':privileges:mods', 0, -1)
}, function(err, results) {
if (err) {
return winston.error(err);
}
var uids = results.uidsInTopic.filter(function(uid) {
return (results.admins.indexOf(uid) !== -1 || results.moderators.indexOf(uid) !== -1) && parseInt(uid, 10) !== socket.uid;
var uids = results.admins.concat(results.moderators).filter(function(uid, index, array) {
return uid && array.indexOf(uid) === index;
});
uids.forEach(function(uid) {

@ -1,129 +0,0 @@
'use strict';
// Temp solution until
// https://github.com/NodeBB/NodeBB/issues/2486
// and
// https://github.com/Automattic/socket.io/issues/1945
// are closed.
// Once they are closed switch to .clients() and async calls
var pubsub = require('../pubsub'),
async = require('async');
var rooms = {};
var clientRooms = {};
var roomClients = {};
rooms.enter = function(socket, room) {
socket.join(room);
pubsub.publish('socket:join', {id: socket.id, room: room});
};
rooms.leave = function(socket, room) {
socket.leave(room);
pubsub.publish('socket:leave', {id: socket.id, room: room});
};
rooms.leaveAll = function(socket, roomsToLeave) {
roomsToLeave.forEach(function(room) {
rooms.leave(socket, room);
});
};
rooms.broadcast = function(socket, room, msg, data, callback) {
var io = require('./'),
socketIds = rooms.clients(room);
callback = callback || function() {};
// Filter out socketIds that aren't actually connected
socketIds = socketIds.filter(function(id) {
return io.server.sockets.connected.hasOwnProperty(id);
});
async.map(socketIds, function(id, next) {
var timeout,
timeoutPassed = false;
if (socket.id === id) {
return setImmediate(next, null, []);
}
timeout = setTimeout(function() {
timeoutPassed = true;
next(null, []);
}, 500);
io.server.sockets.connected[id].emit(msg, data || {}, function(err, returnData) {
clearTimeout(timeout);
if (!timeoutPassed) {
next(null, returnData);
}
});
}, callback);
};
pubsub.on('socket:join', onSocketJoin);
pubsub.on('socket:leave', onSocketLeave);
function onSocketJoin(data) {
clientRooms[data.id] = clientRooms[data.id] || [];
if (clientRooms[data.id].indexOf(data.room) === -1) {
clientRooms[data.id].push(data.room);
}
roomClients[data.room] = roomClients[data.room] || [];
if (roomClients[data.room].indexOf(data.id) === -1) {
roomClients[data.room].push(data.id);
}
}
function onSocketLeave(data) {
var index;
if (Array.isArray(clientRooms[data.id])) {
index = clientRooms[data.id].indexOf(data.room);
if (index !== -1) {
clientRooms[data.id].splice(index, 1);
if (!clientRooms[data.id].length) {
delete clientRooms[data.id];
}
}
}
if (Array.isArray(roomClients[data.room])) {
index = roomClients[data.room].indexOf(data.id);
if (index !== -1) {
roomClients[data.room].splice(index, 1);
if (!roomClients[data.room].length) {
delete roomClients[data.room];
}
}
}
}
rooms.clients = function(room) {
return Array.isArray(roomClients[room]) ? roomClients[room] : [];
};
rooms.clientRooms = function(id) {
return Array.isArray(clientRooms[id]) ? clientRooms[id] : [];
};
rooms.socketCount = function() {
return Object.keys(clientRooms || {}).length;
};
rooms.roomClients = function() {
return roomClients;
};
module.exports = rooms;

@ -74,22 +74,6 @@ SocketTopics.post = function(socket, data, callback) {
});
};
SocketTopics.enter = function(socket, tid, callback) {
if (!parseInt(tid, 10) || !socket.uid) {
return;
}
async.parallel({
markAsRead: function(next) {
SocketTopics.markAsRead(socket, [tid], next);
},
users: function(next) {
websockets.getUsersInRoom(socket.uid, 'topic_' + tid, 0, 9, next);
}
}, function(err, result) {
callback(err, result ? result.users : null);
});
};
SocketTopics.postcount = function(socket, tid, callback) {
topics.getTopicField(tid, 'postcount', callback);
};

@ -8,15 +8,12 @@ module.exports = function(SocketUser) {
if (!socket.uid) {
return callback('[[error:invalid-uid]]');
}
var online = websockets.isUserOnline(uid);
if (!online) {
return callback(null, 'offline');
}
user.getUserField(uid, 'status', function(err, status) {
user.getUserFields(uid, ['lastonline', 'status'], function(err, userData) {
if (err) {
return callback(err);
}
status = status || 'online';
var status = user.getStatus(userData);
callback(null, status);
});
};

@ -272,13 +272,13 @@ module.exports = function(Topics) {
var tid = postData.tid;
var uid = postData.uid;
async.waterfall([
function(next) {
function (next) {
Topics.markAsUnreadForAll(tid, next);
},
function(next) {
function (next) {
Topics.markAsRead([tid], uid, next);
},
function(next) {
function (markedRead, next) {
async.parallel({
userInfo: function(next) {
posts.getUserInfoForPosts([postData.uid], uid, next);
@ -294,7 +294,7 @@ module.exports = function(Topics) {
}
}, next);
},
function(results, next) {
function (results, next) {
postData.user = results.userInfo[0];
postData.topic = results.topicInfo;

@ -161,18 +161,14 @@ module.exports = function(Topics) {
return callback();
}
async.waterfall([
function (next) {
async.parallel({
topicScores: function(next) {
db.sortedSetScores('topics:recent', tids, next);
topicScores: async.apply(db.sortedSetScores, 'topics:recent', tids),
userScores: async.apply(db.sortedSetScores, 'uid:' + uid + ':tids_read', tids)
}, next);
},
userScores: function(next) {
db.sortedSetScores('uid:' + uid + ':tids_read', tids, next);
}
}, function(err, results) {
if (err) {
return callback(err);
}
function (results, next) {
tids = tids.filter(function(tid, index) {
return results.topicScores[index] && (!results.userScores[index] || results.userScores[index] < results.topicScores[index]);
});
@ -187,26 +183,23 @@ module.exports = function(Topics) {
});
async.parallel({
markRead: function(next) {
db.sortedSetAdd('uid:' + uid + ':tids_read', scores, tids, next);
markRead: async.apply(db.sortedSetAdd, 'uid:' + uid + ':tids_read', scores, tids),
topicData: async.apply( Topics.getTopicsFields, tids, ['cid'])
}, next);
},
topicData: function(next) {
Topics.getTopicsFields(tids, ['cid'], next);
}
}, function(err, results) {
if (err) {
return callback(err);
}
function (results, next) {
var cids = results.topicData.map(function(topic) {
return topic && topic.cid;
}).filter(function(topic, index, array) {
return topic && array.indexOf(topic) === index;
});
categories.markAsRead(cids, uid, callback);
});
});
categories.markAsRead(cids, uid, next);
},
function (next) {
next(null, true);
}
], callback);
};
Topics.markTopicNotificationsRead = function(tid, uid) {

@ -1,12 +1,9 @@
'use strict';
var async = require('async'),
nconf = require('nconf'),
gravatar = require('gravatar'),
plugins = require('./plugins'),
db = require('./database'),
meta = require('./meta'),
topics = require('./topics'),
privileges = require('./privileges'),
utils = require('../public/src/utils');
@ -91,7 +88,7 @@ var async = require('async'),
};
User.getUsers = function(uids, uid, callback) {
var fields = ['uid', 'username', 'userslug', 'picture', 'status', 'banned', 'joindate', 'postcount', 'reputation', 'email:confirmed'];
var fields = ['uid', 'username', 'userslug', 'picture', 'status', 'banned', 'joindate', 'postcount', 'reputation', 'email:confirmed', 'lastonline'];
plugins.fireHook('filter:users.addFields', {fields: fields}, function(err, data) {
if (err) {
return callback(err);
@ -105,9 +102,6 @@ var async = require('async'),
},
isAdmin: function(next) {
User.isAdministrator(uids, next);
},
isOnline: function(next) {
require('./socket.io').isUsersOnline(uids, next);
}
}, function(err, results) {
if (err) {
@ -118,7 +112,7 @@ var async = require('async'),
if (!user) {
return;
}
user.status = User.getStatus(user.status, results.isOnline[index]);
user.status = User.getStatus(user);
user.joindateISO = utils.toISOString(user.joindate);
user.administrator = results.isAdmin[index];
user.banned = parseInt(user.banned, 10) === 1;
@ -135,8 +129,19 @@ var async = require('async'),
});
};
User.getStatus = function(status, isOnline) {
return isOnline ? (status || 'online') : 'offline';
User.getStatus = function(userData) {
var isOnline = Date.now() - parseInt(userData.lastonline, 10) < 300000;
return isOnline ? (userData.status || 'online') : 'offline';
};
User.isOnline = function(uid, callback) {
db.sortedSetScore('users:online', uid, function(err, lastonline) {
if (err) {
return callback(err);
}
var isOnline = Date.now() - parseInt(lastonline, 10) < 300000;
callback(null, isOnline);
});
};
User.exists = function(uid, callback) {

@ -98,29 +98,16 @@ module.exports = function(User) {
function filterAndSortUids(uids, data, callback) {
var sortBy = data.sortBy || 'joindate';
var fields = ['uid', 'status', sortBy];
var fields = ['uid', 'status', 'lastonline', sortBy];
async.parallel({
userData: function(next) {
User.getUsersFields(uids, fields, next);
},
isOnline: function(next) {
if (data.onlineOnly) {
require('../socket.io').isUsersOnline(uids, next);
} else {
next();
}
}
}, function(err, results) {
User.getUsersFields(uids, fields, function(err, userData) {
if (err) {
return callback(err);
}
var userData = results.userData;
if (data.onlineOnly) {
userData = userData.filter(function(user, index) {
return user && user.status !== 'offline' && results.isOnline[index];
userData = userData.filter(function(user) {
return user && user.status !== 'offline' && (Date.now() - parseInt(user.lastonline, 10) < 300000);
});
}

Loading…
Cancel
Save