mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-01 19:46:01 +01:00
Category watch state (#7109)
* feat: wip, category watch change * feat: pass data to client * feat: allow changing state * fix: account page categories * fix: show in unread if topic is followed or category is watched * feat: add default watch state to acp * feat: save user category watch state * feat: update unread recent pages * fix: remove dupe code * fix: flip conditions * fix: handle empty arrays * fix: ignore/watch on others profile * feat: upgrade script for category states if there are any users ignoring categories set their state in new zset and delete cid:<cid>:ignorers * fix: upgrade * fix: tests * fix: redis count * fix: more tests
This commit is contained in:
committed by
GitHub
parent
2104877c76
commit
eb7ae54f81
@@ -116,5 +116,6 @@
|
|||||||
"eventLoopLagThreshold": 100,
|
"eventLoopLagThreshold": 100,
|
||||||
"eventLoopInterval": 500,
|
"eventLoopInterval": 500,
|
||||||
"onlineCutoff": 30,
|
"onlineCutoff": 30,
|
||||||
"timeagoCutoff": 30
|
"timeagoCutoff": 30,
|
||||||
|
"categoryWatchState": "watching"
|
||||||
}
|
}
|
||||||
@@ -70,5 +70,9 @@
|
|||||||
"email-post-notif": "Send an email when replies are made to topics I am subscribed to",
|
"email-post-notif": "Send an email when replies are made to topics I am subscribed to",
|
||||||
"follow-created-topics": "Follow topics you create",
|
"follow-created-topics": "Follow topics you create",
|
||||||
"follow-replied-topics": "Follow topics that you reply to",
|
"follow-replied-topics": "Follow topics that you reply to",
|
||||||
"default-notification-settings": "Default notification settings"
|
"default-notification-settings": "Default notification settings",
|
||||||
|
"categoryWatchState": "Default category watch state",
|
||||||
|
"categoryWatchState.watching": "Watching",
|
||||||
|
"categoryWatchState.notwatching": "Not Watching",
|
||||||
|
"categoryWatchState.ignoring": "Ignoring"
|
||||||
}
|
}
|
||||||
@@ -10,16 +10,18 @@
|
|||||||
"no_replies": "No one has replied",
|
"no_replies": "No one has replied",
|
||||||
"no_new_posts": "No new posts.",
|
"no_new_posts": "No new posts.",
|
||||||
|
|
||||||
"share_this_category": "Share this category",
|
|
||||||
"watch": "Watch",
|
"watch": "Watch",
|
||||||
"ignore": "Ignore",
|
"ignore": "Ignore",
|
||||||
"watching": "Watching",
|
"watching": "Watching",
|
||||||
|
"not-watching": "Not Watching",
|
||||||
"ignoring": "Ignoring",
|
"ignoring": "Ignoring",
|
||||||
"watching.description": "Show topics in unread",
|
"watching.description": "Show topics in unread and recent",
|
||||||
"ignoring.description": "Do not show topics in unread",
|
"not-watching.description": "Do not show topics in unread, show in recent",
|
||||||
|
"ignoring.description": "Do not show topics in unread and recent",
|
||||||
|
|
||||||
"watch.message": "You are now watching updates from this category and all subcategories",
|
"watching.message": "You are now watching updates from this category and all subcategories",
|
||||||
"ignore.message": "You are now ignoring updates from this category and all subcategories",
|
"notwatching.message": "You are not watching updates from this category and all subcategories",
|
||||||
|
"ignoring.message": "You are now ignoring updates from this category and all subcategories",
|
||||||
|
|
||||||
"watched-categories": "Watched categories"
|
"watched-categories": "Watched categories"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
"watched_categories": "Watched categories",
|
"watched_categories": "Watched categories",
|
||||||
"watched": "Watched",
|
"watched": "Watched",
|
||||||
"ignored": "Ignored",
|
"ignored": "Ignored",
|
||||||
|
"default-category-watch-state": "Default category watch state",
|
||||||
"followers": "Followers",
|
"followers": "Followers",
|
||||||
"following": "Following",
|
"following": "Following",
|
||||||
"blocks": "Blocks",
|
"blocks": "Blocks",
|
||||||
|
|||||||
@@ -14,25 +14,28 @@ define('forum/account/categories', ['forum/account/header'], function (header) {
|
|||||||
|
|
||||||
function handleIgnoreWatch(cid) {
|
function handleIgnoreWatch(cid) {
|
||||||
var category = $('[data-cid="' + cid + '"]');
|
var category = $('[data-cid="' + cid + '"]');
|
||||||
category.find('[component="category/watching"], [component="category/ignoring"]').on('click', function () {
|
category.find('[component="category/watching"], [component="category/ignoring"], [component="category/notwatching"]').on('click', function () {
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
var command = $this.attr('component') === 'category/watching' ? 'watch' : 'ignore';
|
var state = $this.attr('data-state');
|
||||||
|
|
||||||
socket.emit('categories.' + command, { cid: cid, uid: ajaxify.data.uid }, function (err, modified_cids) {
|
socket.emit('categories.setWatchState', { cid: cid, state: state, uid: ajaxify.data.uid }, function (err, modified_cids) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return app.alertError(err.message);
|
return app.alertError(err.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
modified_cids.forEach(function (cid) {
|
modified_cids.forEach(function (cid) {
|
||||||
var category = $('[data-cid="' + cid + '"]');
|
var category = $('[data-cid="' + cid + '"]');
|
||||||
category.find('[component="category/watching/menu"]').toggleClass('hidden', command !== 'watch');
|
category.find('[component="category/watching/menu"]').toggleClass('hidden', state !== 'watching');
|
||||||
category.find('[component="category/watching/check"]').toggleClass('fa-check', command === 'watch');
|
category.find('[component="category/watching/check"]').toggleClass('fa-check', state === 'watching');
|
||||||
|
|
||||||
category.find('[component="category/ignoring/menu"]').toggleClass('hidden', command !== 'ignore');
|
category.find('[component="category/notwatching/menu"]').toggleClass('hidden', state !== 'notwatching');
|
||||||
category.find('[component="category/ignoring/check"]').toggleClass('fa-check', command === 'ignore');
|
category.find('[component="category/notwatching/check"]').toggleClass('fa-check', state === 'notwatching');
|
||||||
|
|
||||||
|
category.find('[component="category/ignoring/menu"]').toggleClass('hidden', state !== 'ignoring');
|
||||||
|
category.find('[component="category/ignoring/check"]').toggleClass('fa-check', state === 'ignoring');
|
||||||
});
|
});
|
||||||
|
|
||||||
app.alertSuccess('[[category:' + command + '.message]]');
|
app.alertSuccess('[[category:' + state + '.message]]');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,22 +62,25 @@ define('forum/category', [
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleIgnoreWatch(cid) {
|
function handleIgnoreWatch(cid) {
|
||||||
$('[component="category/watching"], [component="category/ignoring"]').on('click', function () {
|
$('[component="category/watching"], [component="category/ignoring"], [component="category/notwatching"]').on('click', function () {
|
||||||
var $this = $(this);
|
var $this = $(this);
|
||||||
var command = $this.attr('component') === 'category/watching' ? 'watch' : 'ignore';
|
var state = $this.attr('data-state');
|
||||||
|
|
||||||
socket.emit('categories.' + command, cid, function (err) {
|
socket.emit('categories.setWatchState', { cid: cid, state: state }, function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return app.alertError(err.message);
|
return app.alertError(err.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
$('[component="category/watching/menu"]').toggleClass('hidden', command !== 'watch');
|
$('[component="category/watching/menu"]').toggleClass('hidden', state !== 'watching');
|
||||||
$('[component="category/watching/check"]').toggleClass('fa-check', command === 'watch');
|
$('[component="category/watching/check"]').toggleClass('fa-check', state === 'watching');
|
||||||
|
|
||||||
$('[component="category/ignoring/menu"]').toggleClass('hidden', command !== 'ignore');
|
$('[component="category/notwatching/menu"]').toggleClass('hidden', state !== 'notwatching');
|
||||||
$('[component="category/ignoring/check"]').toggleClass('fa-check', command === 'ignore');
|
$('[component="category/notwatching/check"]').toggleClass('fa-check', state === 'notwatching');
|
||||||
|
|
||||||
app.alertSuccess('[[category:' + command + '.message]]');
|
$('[component="category/ignoring/menu"]').toggleClass('hidden', state !== 'ignoring');
|
||||||
|
$('[component="category/ignoring/check"]').toggleClass('fa-check', state === 'ignoring');
|
||||||
|
|
||||||
|
app.alertSuccess('[[category:' + state + '.message]]');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ module.exports = function (Categories) {
|
|||||||
'cid:' + cid + ':tids:posts',
|
'cid:' + cid + ':tids:posts',
|
||||||
'cid:' + cid + ':pids',
|
'cid:' + cid + ':pids',
|
||||||
'cid:' + cid + ':read_by_uid',
|
'cid:' + cid + ':read_by_uid',
|
||||||
'cid:' + cid + ':ignorers',
|
'cid:' + cid + ':uid:watch:state',
|
||||||
'cid:' + cid + ':children',
|
'cid:' + cid + ':children',
|
||||||
'cid:' + cid + ':tag:whitelist',
|
'cid:' + cid + ':tag:whitelist',
|
||||||
'category:' + cid,
|
'category:' + cid,
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ require('./unread')(Categories);
|
|||||||
require('./activeusers')(Categories);
|
require('./activeusers')(Categories);
|
||||||
require('./recentreplies')(Categories);
|
require('./recentreplies')(Categories);
|
||||||
require('./update')(Categories);
|
require('./update')(Categories);
|
||||||
|
require('./watch')(Categories);
|
||||||
|
|
||||||
Categories.exists = function (cid, callback) {
|
Categories.exists = function (cid, callback) {
|
||||||
db.exists('category:' + cid, callback);
|
db.exists('category:' + cid, callback);
|
||||||
@@ -45,8 +46,8 @@ Categories.getCategoryById = function (data, callback) {
|
|||||||
topicCount: function (next) {
|
topicCount: function (next) {
|
||||||
Categories.getTopicCount(data, next);
|
Categories.getTopicCount(data, next);
|
||||||
},
|
},
|
||||||
isIgnored: function (next) {
|
watchState: function (next) {
|
||||||
Categories.isIgnored([data.cid], data.uid, next);
|
Categories.getWatchState([data.cid], data.uid, next);
|
||||||
},
|
},
|
||||||
parent: function (next) {
|
parent: function (next) {
|
||||||
if (category.parentCid) {
|
if (category.parentCid) {
|
||||||
@@ -64,7 +65,9 @@ Categories.getCategoryById = function (data, callback) {
|
|||||||
category.topics = results.topics.topics;
|
category.topics = results.topics.topics;
|
||||||
category.nextStart = results.topics.nextStart;
|
category.nextStart = results.topics.nextStart;
|
||||||
category.topic_count = results.topicCount;
|
category.topic_count = results.topicCount;
|
||||||
category.isIgnored = results.isIgnored[0];
|
category.isWatched = results.watchState[0] === Categories.watchStates.watching;
|
||||||
|
category.isNotWatched = results.watchState[0] === Categories.watchStates.notwatching;
|
||||||
|
category.isIgnored = results.watchState[0] === Categories.watchStates.ignoring;
|
||||||
category.parent = results.parent;
|
category.parent = results.parent;
|
||||||
|
|
||||||
calculateTopicPostCount(category);
|
calculateTopicPostCount(category);
|
||||||
@@ -76,14 +79,6 @@ Categories.getCategoryById = function (data, callback) {
|
|||||||
], callback);
|
], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
Categories.isIgnored = function (cids, uid, callback) {
|
|
||||||
if (parseInt(uid, 10) <= 0) {
|
|
||||||
return setImmediate(callback, null, cids.map(() => false));
|
|
||||||
}
|
|
||||||
const keys = cids.map(cid => 'cid:' + cid + ':ignorers');
|
|
||||||
db.isMemberOfSortedSets(keys, uid, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
Categories.getAllCidsFromSet = function (key, callback) {
|
Categories.getAllCidsFromSet = function (key, callback) {
|
||||||
const cids = cache.get(key);
|
const cids = cache.get(key);
|
||||||
if (cids) {
|
if (cids) {
|
||||||
@@ -443,20 +438,4 @@ Categories.buildForSelectCategories = function (categories, callback) {
|
|||||||
callback(null, categoriesData);
|
callback(null, categoriesData);
|
||||||
};
|
};
|
||||||
|
|
||||||
Categories.getIgnorers = function (cid, start, stop, callback) {
|
|
||||||
db.getSortedSetRevRange('cid:' + cid + ':ignorers', start, stop, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
Categories.filterIgnoringUids = function (cid, uids, callback) {
|
|
||||||
async.waterfall([
|
|
||||||
function (next) {
|
|
||||||
db.isSortedSetMembers('cid:' + cid + ':ignorers', uids, next);
|
|
||||||
},
|
|
||||||
function (isIgnoring, next) {
|
|
||||||
const readingUids = uids.filter((uid, index) => uid && !isIgnoring[index]);
|
|
||||||
next(null, readingUids);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
Categories.async = require('../promisify')(Categories);
|
Categories.async = require('../promisify')(Categories);
|
||||||
|
|||||||
80
src/categories/watch.js
Normal file
80
src/categories/watch.js
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const async = require('async');
|
||||||
|
|
||||||
|
const db = require('../database');
|
||||||
|
const user = require('../user');
|
||||||
|
|
||||||
|
module.exports = function (Categories) {
|
||||||
|
Categories.watchStates = {
|
||||||
|
ignoring: 1,
|
||||||
|
notwatching: 2,
|
||||||
|
watching: 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
Categories.isIgnored = function (cids, uid, callback) {
|
||||||
|
if (!(parseInt(uid, 10) > 0)) {
|
||||||
|
return setImmediate(callback, null, cids.map(() => false));
|
||||||
|
}
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
Categories.getWatchState(cids, uid, next);
|
||||||
|
},
|
||||||
|
function (states, next) {
|
||||||
|
next(null, states.map(state => state === Categories.watchStates.ignoring));
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
Categories.getWatchState = function (cids, uid, callback) {
|
||||||
|
if (!(parseInt(uid, 10) > 0)) {
|
||||||
|
return setImmediate(callback, null, cids.map(() => Categories.watchStates.notwatching));
|
||||||
|
}
|
||||||
|
if (!Array.isArray(cids) || !cids.length) {
|
||||||
|
return setImmediate(callback, null, []);
|
||||||
|
}
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
const keys = cids.map(cid => 'cid:' + cid + ':uid:watch:state');
|
||||||
|
async.parallel({
|
||||||
|
userSettings: async.apply(user.getSettings, uid),
|
||||||
|
states: async.apply(db.sortedSetsScore, keys, uid),
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
function (results, next) {
|
||||||
|
next(null, results.states.map(state => state || Categories.watchStates[results.userSettings.categoryWatchState]));
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
Categories.getIgnorers = function (cid, start, stop, callback) {
|
||||||
|
const count = (stop === -1) ? -1 : (stop - start + 1);
|
||||||
|
db.getSortedSetRevRangeByScore('cid:' + cid + ':uid:watch:state', start, count, Categories.watchStates.ignoring, Categories.watchStates.ignoring, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
Categories.filterIgnoringUids = function (cid, uids, callback) {
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
Categories.getUidsWatchStates(cid, uids, next);
|
||||||
|
},
|
||||||
|
function (states, next) {
|
||||||
|
const readingUids = uids.filter((uid, index) => uid && states[index] !== Categories.watchStates.ignoring);
|
||||||
|
next(null, readingUids);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
Categories.getUidsWatchStates = function (cid, uids, callback) {
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
async.parallel({
|
||||||
|
userSettings: async.apply(user.getMultipleUserSettings, uids),
|
||||||
|
states: async.apply(db.sortedSetScores, 'cid:' + cid + ':uid:watch:state', uids),
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
function (results, next) {
|
||||||
|
next(null, results.states.map((state, index) => state || Categories.watchStates[results.userSettings[index].categoryWatchState]));
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -21,8 +21,8 @@ categoriesController.get = function (req, res, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async.parallel({
|
async.parallel({
|
||||||
ignored: function (next) {
|
states: function (next) {
|
||||||
user.getIgnoredCategories(userData.uid, next);
|
user.getCategoryWatchState(userData.uid, next);
|
||||||
},
|
},
|
||||||
categories: function (next) {
|
categories: function (next) {
|
||||||
categories.buildForSelect(userData.uid, 'find', next);
|
categories.buildForSelect(userData.uid, 'find', next);
|
||||||
@@ -32,7 +32,9 @@ categoriesController.get = function (req, res, callback) {
|
|||||||
function (results) {
|
function (results) {
|
||||||
results.categories.forEach(function (category) {
|
results.categories.forEach(function (category) {
|
||||||
if (category) {
|
if (category) {
|
||||||
category.isIgnored = results.ignored.includes(String(category.cid));
|
category.isIgnored = results.states[category.cid] === categories.watchStates.ignoring;
|
||||||
|
category.isWatched = results.states[category.cid] === categories.watchStates.watching;
|
||||||
|
category.isNotWatched = results.states[category.cid] === categories.watchStates.notwatching;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
userData.categories = results.categories;
|
userData.categories = results.categories;
|
||||||
|
|||||||
@@ -161,6 +161,8 @@ settingsController.get = function (req, res, callback) {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
userData.categoryWatchState = { [userData.settings.categoryWatchState]: true };
|
||||||
|
|
||||||
userData.disableCustomUserSkins = meta.config.disableCustomUserSkins;
|
userData.disableCustomUserSkins = meta.config.disableCustomUserSkins;
|
||||||
|
|
||||||
userData.allowUserHomePage = meta.config.allowUserHomePage;
|
userData.allowUserHomePage = meta.config.allowUserHomePage;
|
||||||
|
|||||||
@@ -239,6 +239,20 @@ helpers.getCategories = function (set, uid, privilege, selectedCid, callback) {
|
|||||||
], callback);
|
], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
helpers.getCategoriesByStates = function (uid, selectedCid, states, callback) {
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
user.getCategoriesByStates(uid, states, next);
|
||||||
|
},
|
||||||
|
function (cids, next) {
|
||||||
|
privileges.categories.filterCids('read', cids, uid, next);
|
||||||
|
},
|
||||||
|
function (cids, next) {
|
||||||
|
getCategoryData(cids, uid, selectedCid, next);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
helpers.getWatchedCategories = function (uid, selectedCid, callback) {
|
helpers.getWatchedCategories = function (uid, selectedCid, callback) {
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function (next) {
|
function (next) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ var async = require('async');
|
|||||||
var nconf = require('nconf');
|
var nconf = require('nconf');
|
||||||
|
|
||||||
var user = require('../user');
|
var user = require('../user');
|
||||||
|
var categories = require('../categories');
|
||||||
var topics = require('../topics');
|
var topics = require('../topics');
|
||||||
var meta = require('../meta');
|
var meta = require('../meta');
|
||||||
var helpers = require('./helpers');
|
var helpers = require('./helpers');
|
||||||
@@ -47,8 +48,8 @@ recentController.getData = function (req, url, sort, callback) {
|
|||||||
settings: function (next) {
|
settings: function (next) {
|
||||||
user.getSettings(req.uid, next);
|
user.getSettings(req.uid, next);
|
||||||
},
|
},
|
||||||
watchedCategories: function (next) {
|
categories: function (next) {
|
||||||
helpers.getWatchedCategories(req.uid, cid, next);
|
helpers.getCategoriesByStates(req.uid, cid, [categories.watchStates.watching, categories.watchStates.notwatching], next);
|
||||||
},
|
},
|
||||||
rssToken: function (next) {
|
rssToken: function (next) {
|
||||||
user.auth.getFeedToken(req.uid, next);
|
user.auth.getFeedToken(req.uid, next);
|
||||||
@@ -58,7 +59,7 @@ recentController.getData = function (req, url, sort, callback) {
|
|||||||
function (results, next) {
|
function (results, next) {
|
||||||
rssToken = results.rssToken;
|
rssToken = results.rssToken;
|
||||||
settings = results.settings;
|
settings = results.settings;
|
||||||
categoryData = results.watchedCategories;
|
categoryData = results.categories;
|
||||||
|
|
||||||
var start = Math.max(0, (page - 1) * settings.topicsPerPage);
|
var start = Math.max(0, (page - 1) * settings.topicsPerPage);
|
||||||
stop = start + settings.topicsPerPage - 1;
|
stop = start + settings.topicsPerPage - 1;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ var querystring = require('querystring');
|
|||||||
var meta = require('../meta');
|
var meta = require('../meta');
|
||||||
var pagination = require('../pagination');
|
var pagination = require('../pagination');
|
||||||
var user = require('../user');
|
var user = require('../user');
|
||||||
|
var categories = require('../categories');
|
||||||
var topics = require('../topics');
|
var topics = require('../topics');
|
||||||
var plugins = require('../plugins');
|
var plugins = require('../plugins');
|
||||||
var helpers = require('./helpers');
|
var helpers = require('./helpers');
|
||||||
@@ -35,7 +36,7 @@ unreadController.get = function (req, res, next) {
|
|||||||
if (plugins.hasListeners('filter:unread.categories')) {
|
if (plugins.hasListeners('filter:unread.categories')) {
|
||||||
plugins.fireHook('filter:unread.categories', { uid: req.uid, cid: cid }, next);
|
plugins.fireHook('filter:unread.categories', { uid: req.uid, cid: cid }, next);
|
||||||
} else {
|
} else {
|
||||||
helpers.getWatchedCategories(req.uid, cid, next);
|
helpers.getCategoriesByStates(req.uid, cid, [categories.watchStates.watching], next);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
settings: function (next) {
|
settings: function (next) {
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ module.exports = function (db, module) {
|
|||||||
query.score.$lte = max;
|
query.score.$lte = max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (max === min) {
|
||||||
|
query.score = max;
|
||||||
|
}
|
||||||
|
|
||||||
const fields = { _id: 0, _key: 0 };
|
const fields = { _id: 0, _key: 0 };
|
||||||
if (!withScores) {
|
if (!withScores) {
|
||||||
fields.score = 0;
|
fields.score = 0;
|
||||||
@@ -115,10 +119,12 @@ module.exports = function (db, module) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function getSortedSetRangeByScore(key, start, count, min, max, sort, withScores, callback) {
|
function getSortedSetRangeByScore(key, start, count, min, max, sort, withScores, callback) {
|
||||||
if (parseInt(count, 10) === -1) {
|
if (parseInt(count, 10) === 0) {
|
||||||
count = 0;
|
return setImmediate(callback, null, []);
|
||||||
}
|
}
|
||||||
var stop = start + count - 1;
|
const stop = (parseInt(count, 10) === -1) ? -1 : (start + count - 1);
|
||||||
|
|
||||||
|
console.log(key, start, stop);
|
||||||
getSortedSetRange(key, start, stop, min, max, sort, withScores, callback);
|
getSortedSetRange(key, start, stop, min, max, sort, withScores, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,7 +267,7 @@ module.exports = function (db, module) {
|
|||||||
|
|
||||||
module.sortedSetsScore = function (keys, value, callback) {
|
module.sortedSetsScore = function (keys, value, callback) {
|
||||||
if (!Array.isArray(keys) || !keys.length) {
|
if (!Array.isArray(keys) || !keys.length) {
|
||||||
return callback();
|
return callback(null, []);
|
||||||
}
|
}
|
||||||
value = helpers.valueToString(value);
|
value = helpers.valueToString(value);
|
||||||
db.collection('objects').find({ _key: { $in: keys }, value: value }, { projection: { _id: 0, value: 0 } }).toArray(function (err, result) {
|
db.collection('objects').find({ _key: { $in: keys }, value: value }, { projection: { _id: 0, value: 0 } }).toArray(function (err, result) {
|
||||||
|
|||||||
@@ -348,7 +348,7 @@ SELECT z."score" s
|
|||||||
|
|
||||||
module.sortedSetsScore = function (keys, value, callback) {
|
module.sortedSetsScore = function (keys, value, callback) {
|
||||||
if (!Array.isArray(keys) || !keys.length) {
|
if (!Array.isArray(keys) || !keys.length) {
|
||||||
return callback();
|
return callback(null, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
value = helpers.valueToString(value);
|
value = helpers.valueToString(value);
|
||||||
|
|||||||
@@ -170,6 +170,9 @@ module.exports = function (redisClient, module) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
module.sortedSetsScore = function (keys, value, callback) {
|
module.sortedSetsScore = function (keys, value, callback) {
|
||||||
|
if (!Array.isArray(keys) || !keys.length) {
|
||||||
|
return callback(null, []);
|
||||||
|
}
|
||||||
helpers.execKeysValue(redisClient, 'batch', 'zscore', keys, value, function (err, scores) {
|
helpers.execKeysValue(redisClient, 'batch', 'zscore', keys, value, function (err, scores) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
|
|||||||
@@ -330,7 +330,7 @@ function getSearchCids(data, callback) {
|
|||||||
async.parallel({
|
async.parallel({
|
||||||
watchedCids: function (next) {
|
watchedCids: function (next) {
|
||||||
if (data.categories.includes('watched')) {
|
if (data.categories.includes('watched')) {
|
||||||
user.getWatchedCategories(data.uid, next);
|
user.getCategoriesByStates(data.uid, [categories.watchStates.watching], next);
|
||||||
} else {
|
} else {
|
||||||
setImmediate(next, null, []);
|
setImmediate(next, null, []);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,20 +155,28 @@ SocketCategories.getSelectCategories = function (socket, data, callback) {
|
|||||||
], callback);
|
], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
SocketCategories.watch = function (socket, cid, callback) {
|
SocketCategories.setWatchState = function (socket, data, callback) {
|
||||||
ignoreOrWatch(user.watchCategory, socket, cid, callback);
|
if (!data || !data.cid || !data.state) {
|
||||||
|
return callback(new Error('[[error:invalid-data]]'));
|
||||||
|
}
|
||||||
|
ignoreOrWatch(function (uid, cid, next) {
|
||||||
|
user.setCategoryWatchState(uid, cid, categories.watchStates[data.state], next);
|
||||||
|
}, socket, data, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
SocketCategories.ignore = function (socket, cid, callback) {
|
SocketCategories.watch = function (socket, data, callback) {
|
||||||
ignoreOrWatch(user.ignoreCategory, socket, cid, callback);
|
ignoreOrWatch(user.watchCategory, socket, data, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
function ignoreOrWatch(fn, socket, cid, callback) {
|
SocketCategories.ignore = function (socket, data, callback) {
|
||||||
|
ignoreOrWatch(user.ignoreCategory, socket, data, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
function ignoreOrWatch(fn, socket, data, callback) {
|
||||||
var targetUid = socket.uid;
|
var targetUid = socket.uid;
|
||||||
var cids = [parseInt(cid, 10)];
|
var cids = [parseInt(data.cid, 10)];
|
||||||
if (typeof cid === 'object') {
|
if (data.hasOwnProperty('uid')) {
|
||||||
targetUid = cid.uid;
|
targetUid = data.uid;
|
||||||
cids = [parseInt(cid.cid, 10)];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ var websockets = require('./index');
|
|||||||
var user = require('../user');
|
var user = require('../user');
|
||||||
var posts = require('../posts');
|
var posts = require('../posts');
|
||||||
var topics = require('../topics');
|
var topics = require('../topics');
|
||||||
|
var categories = require('../categories');
|
||||||
var privileges = require('../privileges');
|
var privileges = require('../privileges');
|
||||||
var notifications = require('../notifications');
|
var notifications = require('../notifications');
|
||||||
var plugins = require('../plugins');
|
var plugins = require('../plugins');
|
||||||
@@ -63,15 +64,15 @@ function filterTidCidIgnorers(uids, tid, cid, callback) {
|
|||||||
topicIgnored: function (next) {
|
topicIgnored: function (next) {
|
||||||
db.isSetMembers('tid:' + tid + ':ignorers', uids, next);
|
db.isSetMembers('tid:' + tid + ':ignorers', uids, next);
|
||||||
},
|
},
|
||||||
categoryIgnored: function (next) {
|
categoryWatchStates: function (next) {
|
||||||
db.sortedSetScores('cid:' + cid + ':ignorers', uids, next);
|
categories.getUidsWatchStates(cid, uids, next);
|
||||||
},
|
},
|
||||||
}, next);
|
}, next);
|
||||||
},
|
},
|
||||||
function (results, next) {
|
function (results, next) {
|
||||||
uids = uids.filter(function (uid, index) {
|
uids = uids.filter(function (uid, index) {
|
||||||
return results.topicFollowed[index] ||
|
return results.topicFollowed[index] ||
|
||||||
(!results.topicFollowed[index] && !results.topicIgnored[index] && !results.categoryIgnored[index]);
|
(!results.topicIgnored[index] && results.categoryWatchStates[index] !== categories.watchStates.ignoring);
|
||||||
});
|
});
|
||||||
next(null, uids);
|
next(null, uids);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -194,8 +194,8 @@ module.exports = function (Topics) {
|
|||||||
isTopicsFollowed: function (next) {
|
isTopicsFollowed: function (next) {
|
||||||
db.sortedSetScores('uid:' + uid + ':followed_tids', tids, next);
|
db.sortedSetScores('uid:' + uid + ':followed_tids', tids, next);
|
||||||
},
|
},
|
||||||
ignoredCids: function (next) {
|
categoryWatchState: function (next) {
|
||||||
categories.isIgnored(cids, uid, next);
|
categories.getWatchState(cids, uid, next);
|
||||||
},
|
},
|
||||||
readableCids: function (next) {
|
readableCids: function (next) {
|
||||||
privileges.categories.filterCids('read', cids, uid, next);
|
privileges.categories.filterCids('read', cids, uid, next);
|
||||||
@@ -205,7 +205,7 @@ module.exports = function (Topics) {
|
|||||||
function (results, next) {
|
function (results, next) {
|
||||||
cid = cid && cid.map(String);
|
cid = cid && cid.map(String);
|
||||||
results.readableCids = results.readableCids.map(String);
|
results.readableCids = results.readableCids.map(String);
|
||||||
const isCidIgnored = _.zipObject(cids, results.ignoredCids);
|
const userCidState = _.zipObject(cids, results.categoryWatchState);
|
||||||
|
|
||||||
topicData.forEach(function (topic, index) {
|
topicData.forEach(function (topic, index) {
|
||||||
function cidMatch(topicCid) {
|
function cidMatch(topicCid) {
|
||||||
@@ -214,7 +214,7 @@ module.exports = function (Topics) {
|
|||||||
|
|
||||||
if (topic && topic.cid && cidMatch(topic.cid) && !blockedUids.includes(parseInt(topic.uid, 10))) {
|
if (topic && topic.cid && cidMatch(topic.cid) && !blockedUids.includes(parseInt(topic.uid, 10))) {
|
||||||
topic.tid = parseInt(topic.tid, 10);
|
topic.tid = parseInt(topic.tid, 10);
|
||||||
if ((results.isTopicsFollowed[index] || !isCidIgnored[topic.cid])) {
|
if ((results.isTopicsFollowed[index] || userCidState[topic.cid] === categories.watchStates.watching)) {
|
||||||
tidsByFilter[''].push(topic.tid);
|
tidsByFilter[''].push(topic.tid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
46
src/upgrades/1.12.0/category_watch_state.js
Normal file
46
src/upgrades/1.12.0/category_watch_state.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var async = require('async');
|
||||||
|
|
||||||
|
var db = require('../../database');
|
||||||
|
var batch = require('../../batch');
|
||||||
|
var categories = require('../../categories');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
name: 'Update category watch data',
|
||||||
|
timestamp: Date.UTC(2018, 11, 13),
|
||||||
|
method: function (callback) {
|
||||||
|
const progress = this.progress;
|
||||||
|
let keys;
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
db.getSortedSetRange('categories:cids', 0, -1, next);
|
||||||
|
},
|
||||||
|
function (cids, next) {
|
||||||
|
keys = cids.map(cid => 'cid:' + cid + ':ignorers');
|
||||||
|
batch.processSortedSet('users:joindate', function (uids, next) {
|
||||||
|
progress.incr(uids.length);
|
||||||
|
|
||||||
|
async.eachSeries(cids, function (cid, next) {
|
||||||
|
db.isSortedSetMembers('cid:' + cid + ':ignorers', uids, function (err, isMembers) {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
uids = uids.filter((uid, index) => isMembers[index]);
|
||||||
|
if (!uids.length) {
|
||||||
|
return setImmediate(next);
|
||||||
|
}
|
||||||
|
const states = uids.map(() => categories.watchStates.ignoring);
|
||||||
|
db.sortedSetAdd('cid:' + cid + ':uid:watch:state', states, uids, next);
|
||||||
|
});
|
||||||
|
}, next);
|
||||||
|
}, {
|
||||||
|
progress: progress,
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
function (next) {
|
||||||
|
db.deleteAll(keys, next);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,15 +1,39 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var async = require('async');
|
const async = require('async');
|
||||||
|
const _ = require('lodash');
|
||||||
|
|
||||||
var db = require('../database');
|
const db = require('../database');
|
||||||
var categories = require('../categories');
|
const categories = require('../categories');
|
||||||
|
|
||||||
module.exports = function (User) {
|
module.exports = function (User) {
|
||||||
User.getIgnoredCategories = function (uid, callback) {
|
User.setCategoryWatchState = function (uid, cid, state, callback) {
|
||||||
if (parseInt(uid, 10) <= 0) {
|
if (!(parseInt(uid, 10) > 0)) {
|
||||||
return setImmediate(callback, null, []);
|
return setImmediate(callback);
|
||||||
}
|
}
|
||||||
|
const isStateValid = Object.keys(categories.watchStates).some(key => categories.watchStates[key] === parseInt(state, 10));
|
||||||
|
if (!isStateValid) {
|
||||||
|
return setImmediate(callback, new Error('[[error:invalid-watch-state]]'));
|
||||||
|
}
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
categories.exists(cid, next);
|
||||||
|
},
|
||||||
|
function (exists, next) {
|
||||||
|
if (!exists) {
|
||||||
|
return next(new Error('[[error:no-category]]'));
|
||||||
|
}
|
||||||
|
|
||||||
|
db.sortedSetAdd('cid:' + cid + ':uid:watch:state', state, uid, next);
|
||||||
|
},
|
||||||
|
], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
User.getCategoryWatchState = function (uid, callback) {
|
||||||
|
if (parseInt(uid, 10) <= 0) {
|
||||||
|
return setImmediate(callback, null, {});
|
||||||
|
}
|
||||||
|
|
||||||
let cids;
|
let cids;
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function (next) {
|
function (next) {
|
||||||
@@ -17,69 +41,49 @@ module.exports = function (User) {
|
|||||||
},
|
},
|
||||||
function (_cids, next) {
|
function (_cids, next) {
|
||||||
cids = _cids;
|
cids = _cids;
|
||||||
db.isMemberOfSortedSets(cids.map(cid => 'cid:' + cid + ':ignorers'), uid, next);
|
categories.getWatchState(cids, uid, next);
|
||||||
},
|
},
|
||||||
function (isMembers, next) {
|
function (states, next) {
|
||||||
next(null, cids.filter((cid, index) => isMembers[index]));
|
next(null, _.zipObject(cids, states));
|
||||||
},
|
},
|
||||||
], callback);
|
], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
User.getIgnoredCategories = function (uid, callback) {
|
||||||
|
if (parseInt(uid, 10) <= 0) {
|
||||||
|
return setImmediate(callback, null, []);
|
||||||
|
}
|
||||||
|
User.getCategoriesByStates(uid, [categories.watchStates.ignoring], callback);
|
||||||
|
};
|
||||||
|
|
||||||
User.getWatchedCategories = function (uid, callback) {
|
User.getWatchedCategories = function (uid, callback) {
|
||||||
|
if (parseInt(uid, 10) <= 0) {
|
||||||
|
return setImmediate(callback, null, []);
|
||||||
|
}
|
||||||
|
User.getCategoriesByStates(uid, [categories.watchStates.watching], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
User.getCategoriesByStates = function (uid, states, callback) {
|
||||||
|
if (!(parseInt(uid, 10) > 0)) {
|
||||||
|
return categories.getAllCidsFromSet('categories:cid', callback);
|
||||||
|
}
|
||||||
|
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function (next) {
|
function (next) {
|
||||||
async.parallel({
|
User.getCategoryWatchState(uid, next);
|
||||||
ignored: function (next) {
|
|
||||||
User.getIgnoredCategories(uid, next);
|
|
||||||
},
|
|
||||||
all: function (next) {
|
|
||||||
categories.getAllCidsFromSet('categories:cid', next);
|
|
||||||
},
|
|
||||||
}, next);
|
|
||||||
},
|
},
|
||||||
function (results, next) {
|
function (userState, next) {
|
||||||
const ignored = new Set(results.ignored);
|
const cids = Object.keys(userState);
|
||||||
const watched = results.all.filter(cid => cid && !ignored.has(String(cid)));
|
next(null, cids.filter(cid => states.includes(userState[cid])));
|
||||||
next(null, watched);
|
|
||||||
},
|
},
|
||||||
], callback);
|
], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
User.ignoreCategory = function (uid, cid, callback) {
|
User.ignoreCategory = function (uid, cid, callback) {
|
||||||
if (uid <= 0) {
|
User.setCategoryWatchState(uid, cid, categories.watchStates.ignoring, callback);
|
||||||
return setImmediate(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
async.waterfall([
|
|
||||||
function (next) {
|
|
||||||
categories.exists(cid, next);
|
|
||||||
},
|
|
||||||
function (exists, next) {
|
|
||||||
if (!exists) {
|
|
||||||
return next(new Error('[[error:no-category]]'));
|
|
||||||
}
|
|
||||||
|
|
||||||
db.sortedSetAdd('cid:' + cid + ':ignorers', Date.now(), uid, next);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
User.watchCategory = function (uid, cid, callback) {
|
User.watchCategory = function (uid, cid, callback) {
|
||||||
if (uid <= 0) {
|
User.setCategoryWatchState(uid, cid, categories.watchStates.watching, callback);
|
||||||
return callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
async.waterfall([
|
|
||||||
function (next) {
|
|
||||||
categories.exists(cid, next);
|
|
||||||
},
|
|
||||||
function (exists, next) {
|
|
||||||
if (!exists) {
|
|
||||||
return next(new Error('[[error:no-category]]'));
|
|
||||||
}
|
|
||||||
|
|
||||||
db.sortedSetRemove('cid:' + cid + ':ignorers', uid, next);
|
|
||||||
},
|
|
||||||
], callback);
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ module.exports = function (User) {
|
|||||||
settings.topicSearchEnabled = parseInt(getSetting(settings, 'topicSearchEnabled', 0), 10) === 1;
|
settings.topicSearchEnabled = parseInt(getSetting(settings, 'topicSearchEnabled', 0), 10) === 1;
|
||||||
settings.bootswatchSkin = settings.bootswatchSkin || '';
|
settings.bootswatchSkin = settings.bootswatchSkin || '';
|
||||||
settings.scrollToMyPost = parseInt(getSetting(settings, 'scrollToMyPost', 1), 10) === 1;
|
settings.scrollToMyPost = parseInt(getSetting(settings, 'scrollToMyPost', 1), 10) === 1;
|
||||||
|
settings.categoryWatchState = getSetting(settings, 'categoryWatchState', 'notwatching');
|
||||||
|
|
||||||
notifications.getAllNotificationTypes(next);
|
notifications.getAllNotificationTypes(next);
|
||||||
},
|
},
|
||||||
@@ -137,6 +138,7 @@ module.exports = function (User) {
|
|||||||
outgoingChatSound: data.outgoingChatSound,
|
outgoingChatSound: data.outgoingChatSound,
|
||||||
upvoteNotifFreq: data.upvoteNotifFreq,
|
upvoteNotifFreq: data.upvoteNotifFreq,
|
||||||
bootswatchSkin: data.bootswatchSkin,
|
bootswatchSkin: data.bootswatchSkin,
|
||||||
|
categoryWatchState: data.categoryWatchState,
|
||||||
};
|
};
|
||||||
|
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
|
|||||||
@@ -307,6 +307,15 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>[[admin/settings/user:categoryWatchState]]</label>
|
||||||
|
<select class="form-control" data-field="categoryWatchState">
|
||||||
|
<option value="watching">[[admin/settings/user:categoryWatchState.watching]]</option>
|
||||||
|
<option value="notwatching">[[admin/settings/user:categoryWatchState.notwatching]]</option>
|
||||||
|
<option value="ignoring">[[admin/settings/user:categoryWatchState.ignoring]]</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<label>[[admin/settings/user:default-notification-settings]]</label>
|
<label>[[admin/settings/user:default-notification-settings]]</label>
|
||||||
|
|
||||||
<!-- BEGIN notificationSettings -->
|
<!-- BEGIN notificationSettings -->
|
||||||
|
|||||||
@@ -284,18 +284,22 @@ describe('Categories', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should ignore category', function (done) {
|
it('should ignore category', function (done) {
|
||||||
socketCategories.ignore({ uid: posterUid }, categoryObj.cid, function (err) {
|
socketCategories.ignore({ uid: posterUid }, { cid: categoryObj.cid }, function (err) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
Categories.isIgnored([categoryObj.cid], posterUid, function (err, isIgnored) {
|
Categories.isIgnored([categoryObj.cid], posterUid, function (err, isIgnored) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
assert.equal(isIgnored[0], true);
|
assert.equal(isIgnored[0], true);
|
||||||
done();
|
Categories.getIgnorers(categoryObj.cid, 0, -1, function (err, ignorers) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.deepEqual(ignorers, [posterUid]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should watch category', function (done) {
|
it('should watch category', function (done) {
|
||||||
socketCategories.watch({ uid: posterUid }, categoryObj.cid, function (err) {
|
socketCategories.watch({ uid: posterUid }, { cid: categoryObj.cid }, function (err) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
Categories.isIgnored([categoryObj.cid], posterUid, function (err, isIgnored) {
|
Categories.isIgnored([categoryObj.cid], posterUid, function (err, isIgnored) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
@@ -305,6 +309,13 @@ describe('Categories', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should error if watch state does not exist', function (done) {
|
||||||
|
socketCategories.setWatchState({ uid: posterUid }, { cid: categoryObj.cid, state: 'invalid-state' }, function (err) {
|
||||||
|
assert.equal(err.message, '[[error:invalid-watch-state]]');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should check if user is moderator', function (done) {
|
it('should check if user is moderator', function (done) {
|
||||||
socketCategories.isModerator({ uid: posterUid }, {}, function (err, isModerator) {
|
socketCategories.isModerator({ uid: posterUid }, {}, function (err, isModerator) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
|
|||||||
@@ -228,6 +228,22 @@ describe('Sorted Set methods', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return empty array if count is 0', function (done) {
|
||||||
|
db.getSortedSetRevRangeByScore('sortedSetTest1', 0, 0, '+inf', '-inf', function (err, values) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.deepEqual(values, []);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return elements from 1 to end', function (done) {
|
||||||
|
db.getSortedSetRevRangeByScore('sortedSetTest1', 1, -1, '+inf', '-inf', function (err, values) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.deepEqual(values, ['value2', 'value1']);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should return elements from 3 to last', function (done) {
|
it('should return elements from 3 to last', function (done) {
|
||||||
db.sortedSetAdd('partialZset', [1, 2, 3, 4, 5], ['value1', 'value2', 'value3', 'value4', 'value5'], function (err) {
|
db.sortedSetAdd('partialZset', [1, 2, 3, 4, 5], ['value1', 'value2', 'value3', 'value4', 'value5'], function (err) {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
@@ -523,6 +539,15 @@ describe('Sorted Set methods', function () {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return empty array if keys is empty array', function (done) {
|
||||||
|
db.sortedSetsScore([], 'value1', function (err, scores) {
|
||||||
|
assert.equal(err, null);
|
||||||
|
assert.equal(arguments.length, 2);
|
||||||
|
assert.deepEqual(scores, []);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('sortedSetScores()', function () {
|
describe('sortedSetScores()', function () {
|
||||||
|
|||||||
@@ -1377,6 +1377,38 @@ describe('Topic\'s', function () {
|
|||||||
], done);
|
], done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not return topics in category you ignored/not watching', function (done) {
|
||||||
|
var ignoredCid;
|
||||||
|
var tid;
|
||||||
|
async.waterfall([
|
||||||
|
function (next) {
|
||||||
|
categories.create({
|
||||||
|
name: 'ignored category',
|
||||||
|
description: 'ignored category',
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
function (category, next) {
|
||||||
|
ignoredCid = category.cid;
|
||||||
|
privileges.categories.rescind(['read'], category.cid, 'registered-users', next);
|
||||||
|
},
|
||||||
|
function (next) {
|
||||||
|
topics.post({ uid: adminUid, title: 'topic in private category', content: 'registered-users cant see this', cid: ignoredCid }, next);
|
||||||
|
},
|
||||||
|
function (data, next) {
|
||||||
|
tid = data.topicData.tid;
|
||||||
|
User.ignoreCategory(uid, ignoredCid, next);
|
||||||
|
},
|
||||||
|
function (next) {
|
||||||
|
topics.getUnreadTids({ uid: uid }, next);
|
||||||
|
},
|
||||||
|
function (unreadTids, next) {
|
||||||
|
unreadTids = unreadTids.map(String);
|
||||||
|
assert(!unreadTids.includes(String(tid)));
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
], done);
|
||||||
|
});
|
||||||
|
|
||||||
it('should not return topic as unread if new post is from blocked user', function (done) {
|
it('should not return topic as unread if new post is from blocked user', function (done) {
|
||||||
var blockedUid;
|
var blockedUid;
|
||||||
var topic;
|
var topic;
|
||||||
|
|||||||
Reference in New Issue
Block a user