mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-08 06:55:46 +01:00
Async refactor in place (#7736)
* feat: allow both callback&and await
* feat: ignore async key
* feat: callbackify and promisify in same file
* Revert "feat: callbackify and promisify in same file"
This reverts commit cea206a9b8.
* feat: no need to store .callbackify
* feat: change getTopics to async
* feat: remove .async
* fix: byScore
* feat: rewrite topics/index and social with async/await
* fix: rewrite topics/data.js
fix issue with async.waterfall, only pass result if its not undefined
* feat: add callbackify to redis/psql
* feat: psql use await
* fix: redis 🌋
* feat: less returns
* feat: more await rewrite
* fix: redis tests
* feat: convert sortedSetAdd
rewrite psql transaction to async/await
* feat: 🐶
* feat: test
* feat: log client and query
* feat: log bind
* feat: more logs
* feat: more logs
* feat: check perform
* feat: dont callbackify transaction
* feat: remove logs
* fix: main functions
* feat: more logs
* fix: increment
* fix: rename
* feat: remove cls
* fix: remove console.log
* feat: add deprecation message to .async usage
* feat: update more dbal methods
* fix: redis :voodoo:
* feat: fix redis zrem, convert setObject
* feat: upgrade getObject methods
* fix: psql getObjectField
* fix: redis tests
* feat: getObjectKeys
* feat: getObjectValues
* feat: isObjectField
* fix: add missing return
* feat: delObjectField
* feat: incrObjectField
* fix: add missing await
* feat: remove exposed helpers
* feat: list methods
* feat: flush/empty
* feat: delete
* fix: redis delete all
* feat: get/set
* feat: incr/rename
* feat: type
* feat: expire
* feat: setAdd
* feat: setRemove
* feat: isSetMember
* feat: getSetMembers
* feat: setCount, setRemoveRandom
* feat: zcard,zcount
* feat: sortedSetRank
* feat: isSortedSetMember
* feat: zincrby
* feat: sortedSetLex
* feat: processSortedSet
* fix: add mising await
* feat: debug psql
* fix: psql test
* fix: test
* fix: another test
* fix: test fix
* fix: psql tests
* feat: remove logs
* feat: user arrow func
use builtin async promises
* feat: topic bookmarks
* feat: topic.delete
* feat: topic.restore
* feat: topics.purge
* feat: merge
* feat: suggested
* feat: topics/user.js
* feat: topics modules
* feat: topics/follow
* fix: deprecation msg
* feat: fork
* feat: topics/posts
* feat: sorted/recent
* feat: topic/teaser
* feat: topics/tools
* feat: topics/unread
* feat: add back node versions
disable deprecation notice
wrap async controllers in try/catch
* feat: use db directly
* feat: promisify in place
* fix: redis/psql
* feat: deprecation message
logs for psql
* feat: more logs
* feat: more logs
* feat: logs again
* feat: more logs
* fix: call release
* feat: restore travis, remove logs
* fix: loops
* feat: remove .async. usage
This commit is contained in:
committed by
GitHub
parent
43ce5f8af3
commit
805dcd7ca2
@@ -15,66 +15,47 @@ var utils = require('../utils');
|
||||
var plugins = require('../plugins');
|
||||
|
||||
module.exports = function (Topics) {
|
||||
Topics.getTotalUnread = function (uid, filter, callback) {
|
||||
if (!callback) {
|
||||
callback = filter;
|
||||
filter = '';
|
||||
}
|
||||
Topics.getUnreadTids({ cid: 0, uid: uid, count: true }, function (err, counts) {
|
||||
callback(err, counts && counts[filter]);
|
||||
});
|
||||
Topics.getTotalUnread = async function (uid, filter) {
|
||||
filter = filter || '';
|
||||
const counts = await Topics.getUnreadTids({ cid: 0, uid: uid, count: true });
|
||||
return counts && counts[filter];
|
||||
};
|
||||
|
||||
Topics.getUnreadTopics = function (params, callback) {
|
||||
Topics.getUnreadTopics = async function (params) {
|
||||
var unreadTopics = {
|
||||
showSelect: true,
|
||||
nextStart: 0,
|
||||
topics: [],
|
||||
};
|
||||
let tids = await Topics.getUnreadTids(params);
|
||||
unreadTopics.topicCount = tids.length;
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
Topics.getUnreadTids(params, next);
|
||||
},
|
||||
function (tids, next) {
|
||||
unreadTopics.topicCount = tids.length;
|
||||
if (!tids.length) {
|
||||
return unreadTopics;
|
||||
}
|
||||
|
||||
if (!tids.length) {
|
||||
return next(null, []);
|
||||
}
|
||||
tids = tids.slice(params.start, params.stop !== -1 ? params.stop + 1 : undefined);
|
||||
|
||||
tids = tids.slice(params.start, params.stop !== -1 ? params.stop + 1 : undefined);
|
||||
|
||||
Topics.getTopicsByTids(tids, params.uid, next);
|
||||
},
|
||||
function (topicData, next) {
|
||||
if (!topicData.length) {
|
||||
return next(null, unreadTopics);
|
||||
}
|
||||
Topics.calculateTopicIndices(topicData, params.start);
|
||||
unreadTopics.topics = topicData;
|
||||
unreadTopics.nextStart = params.stop + 1;
|
||||
next(null, unreadTopics);
|
||||
},
|
||||
], callback);
|
||||
const topicData = await Topics.getTopicsByTids(tids, params.uid);
|
||||
if (!topicData.length) {
|
||||
return unreadTopics;
|
||||
}
|
||||
Topics.calculateTopicIndices(topicData, params.start);
|
||||
unreadTopics.topics = topicData;
|
||||
unreadTopics.nextStart = params.stop + 1;
|
||||
return unreadTopics;
|
||||
};
|
||||
|
||||
Topics.unreadCutoff = function () {
|
||||
return Date.now() - (meta.config.unreadCutoff * 86400000);
|
||||
};
|
||||
|
||||
Topics.getUnreadTids = function (params, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
Topics.getUnreadData(params, next);
|
||||
},
|
||||
function (results, next) {
|
||||
next(null, params.count ? results.counts : results.tids);
|
||||
},
|
||||
], callback);
|
||||
Topics.getUnreadTids = async function (params) {
|
||||
const results = await Topics.getUnreadData(params);
|
||||
return params.count ? results.counts : results.tids;
|
||||
};
|
||||
|
||||
Topics.getUnreadData = function (params, callback) {
|
||||
Topics.getUnreadData = async function (params) {
|
||||
const uid = parseInt(params.uid, 10);
|
||||
const counts = {
|
||||
'': 0,
|
||||
@@ -94,7 +75,7 @@ module.exports = function (Topics) {
|
||||
};
|
||||
|
||||
if (uid <= 0) {
|
||||
return setImmediate(callback, null, noUnreadData);
|
||||
return noUnreadData;
|
||||
}
|
||||
|
||||
params.filter = params.filter || '';
|
||||
@@ -104,45 +85,35 @@ module.exports = function (Topics) {
|
||||
if (params.cid && !Array.isArray(params.cid)) {
|
||||
params.cid = [params.cid];
|
||||
}
|
||||
const [ignoredTids, recentTids, userScores, tids_unread] = await Promise.all([
|
||||
user.getIgnoredTids(uid, 0, -1),
|
||||
db.getSortedSetRevRangeByScoreWithScores('topics:recent', 0, -1, '+inf', cutoff),
|
||||
db.getSortedSetRevRangeByScoreWithScores('uid:' + uid + ':tids_read', 0, -1, '+inf', cutoff),
|
||||
db.getSortedSetRevRangeWithScores('uid:' + uid + ':tids_unread', 0, -1),
|
||||
]);
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.parallel({
|
||||
ignoredTids: function (next) {
|
||||
user.getIgnoredTids(uid, 0, -1, next);
|
||||
},
|
||||
recentTids: function (next) {
|
||||
db.getSortedSetRevRangeByScoreWithScores('topics:recent', 0, -1, '+inf', cutoff, next);
|
||||
},
|
||||
userScores: function (next) {
|
||||
db.getSortedSetRevRangeByScoreWithScores('uid:' + uid + ':tids_read', 0, -1, '+inf', cutoff, next);
|
||||
},
|
||||
tids_unread: function (next) {
|
||||
db.getSortedSetRevRangeWithScores('uid:' + uid + ':tids_unread', 0, -1, next);
|
||||
},
|
||||
}, next);
|
||||
},
|
||||
function (results, next) {
|
||||
if (results.recentTids && !results.recentTids.length && !results.tids_unread.length) {
|
||||
return callback(null, noUnreadData);
|
||||
}
|
||||
if (recentTids && !recentTids.length && !tids_unread.length) {
|
||||
return noUnreadData;
|
||||
}
|
||||
|
||||
filterTopics(params, results, next);
|
||||
},
|
||||
function (data, next) {
|
||||
plugins.fireHook('filter:topics.getUnreadTids', {
|
||||
uid: uid,
|
||||
tids: data.tids,
|
||||
counts: data.counts,
|
||||
tidsByFilter: data.tidsByFilter,
|
||||
cid: params.cid,
|
||||
filter: params.filter,
|
||||
}, next);
|
||||
},
|
||||
], callback);
|
||||
const data = await filterTopics(params, {
|
||||
ignoredTids: ignoredTids,
|
||||
recentTids: recentTids,
|
||||
userScores: userScores,
|
||||
tids_unread: tids_unread,
|
||||
});
|
||||
const result = await plugins.fireHook('filter:topics.getUnreadTids', {
|
||||
uid: uid,
|
||||
tids: data.tids,
|
||||
counts: data.counts,
|
||||
tidsByFilter: data.tidsByFilter,
|
||||
cid: params.cid,
|
||||
filter: params.filter,
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
function filterTopics(params, results, callback) {
|
||||
async function filterTopics(params, results) {
|
||||
const counts = {
|
||||
'': 0,
|
||||
new: 0,
|
||||
@@ -180,385 +151,253 @@ module.exports = function (Topics) {
|
||||
var uid = params.uid;
|
||||
var cids;
|
||||
var topicData;
|
||||
var blockedUids;
|
||||
|
||||
tids = tids.slice(0, 200);
|
||||
|
||||
if (!tids.length) {
|
||||
return callback(null, { counts: counts, tids: tids, tidsByFilter: tidsByFilter });
|
||||
return { counts: counts, tids: tids, tidsByFilter: tidsByFilter };
|
||||
}
|
||||
const blockedUids = await user.blocks.list(uid);
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
user.blocks.list(uid, next);
|
||||
},
|
||||
function (_blockedUids, next) {
|
||||
blockedUids = _blockedUids;
|
||||
filterTidsThatHaveBlockedPosts({
|
||||
uid: uid,
|
||||
tids: tids,
|
||||
blockedUids: blockedUids,
|
||||
recentTids: results.recentTids,
|
||||
}, next);
|
||||
},
|
||||
function (_tids, next) {
|
||||
tids = _tids;
|
||||
Topics.getTopicsFields(tids, ['tid', 'cid', 'uid', 'postcount'], next);
|
||||
},
|
||||
function (_topicData, next) {
|
||||
topicData = _topicData;
|
||||
cids = _.uniq(topicData.map(topic => topic.cid)).filter(Boolean);
|
||||
tids = await filterTidsThatHaveBlockedPosts({
|
||||
uid: uid,
|
||||
tids: tids,
|
||||
blockedUids: blockedUids,
|
||||
recentTids: results.recentTids,
|
||||
});
|
||||
|
||||
async.parallel({
|
||||
isTopicsFollowed: function (next) {
|
||||
db.sortedSetScores('uid:' + uid + ':followed_tids', tids, next);
|
||||
},
|
||||
categoryWatchState: function (next) {
|
||||
categories.getWatchState(cids, uid, next);
|
||||
},
|
||||
readableCids: function (next) {
|
||||
privileges.categories.filterCids('read', cids, uid, next);
|
||||
},
|
||||
}, next);
|
||||
},
|
||||
function (results, next) {
|
||||
cid = cid && cid.map(String);
|
||||
results.readableCids = results.readableCids.map(String);
|
||||
const userCidState = _.zipObject(cids, results.categoryWatchState);
|
||||
topicData = await Topics.getTopicsFields(tids, ['tid', 'cid', 'uid', 'postcount']);
|
||||
cids = _.uniq(topicData.map(topic => topic.cid)).filter(Boolean);
|
||||
|
||||
topicData.forEach(function (topic, index) {
|
||||
function cidMatch(topicCid) {
|
||||
return (!cid || (cid.length && cid.includes(String(topicCid)))) && results.readableCids.includes(String(topicCid));
|
||||
}
|
||||
const [isTopicsFollowed, categoryWatchState, readCids] = await Promise.all([
|
||||
db.sortedSetScores('uid:' + uid + ':followed_tids', tids),
|
||||
categories.getWatchState(cids, uid),
|
||||
privileges.categories.filterCids('read', cids, uid),
|
||||
]);
|
||||
cid = cid && cid.map(String);
|
||||
const readableCids = readCids.map(String);
|
||||
const userCidState = _.zipObject(cids, categoryWatchState);
|
||||
|
||||
if (topic && topic.cid && cidMatch(topic.cid) && !blockedUids.includes(parseInt(topic.uid, 10))) {
|
||||
topic.tid = parseInt(topic.tid, 10);
|
||||
if ((results.isTopicsFollowed[index] || userCidState[topic.cid] === categories.watchStates.watching)) {
|
||||
tidsByFilter[''].push(topic.tid);
|
||||
}
|
||||
topicData.forEach(function (topic, index) {
|
||||
function cidMatch(topicCid) {
|
||||
return (!cid || (cid.length && cid.includes(String(topicCid)))) && readableCids.includes(String(topicCid));
|
||||
}
|
||||
|
||||
if (results.isTopicsFollowed[index]) {
|
||||
tidsByFilter.watched.push(topic.tid);
|
||||
}
|
||||
if (topic && topic.cid && cidMatch(topic.cid) && !blockedUids.includes(parseInt(topic.uid, 10))) {
|
||||
topic.tid = parseInt(topic.tid, 10);
|
||||
if ((isTopicsFollowed[index] || userCidState[topic.cid] === categories.watchStates.watching)) {
|
||||
tidsByFilter[''].push(topic.tid);
|
||||
}
|
||||
|
||||
if (topic.postcount <= 1) {
|
||||
tidsByFilter.unreplied.push(topic.tid);
|
||||
}
|
||||
if (isTopicsFollowed[index]) {
|
||||
tidsByFilter.watched.push(topic.tid);
|
||||
}
|
||||
|
||||
if (!userRead[topic.tid]) {
|
||||
tidsByFilter.new.push(topic.tid);
|
||||
}
|
||||
}
|
||||
});
|
||||
counts[''] = tidsByFilter[''].length;
|
||||
counts.watched = tidsByFilter.watched.length;
|
||||
counts.unreplied = tidsByFilter.unreplied.length;
|
||||
counts.new = tidsByFilter.new.length;
|
||||
if (topic.postcount <= 1) {
|
||||
tidsByFilter.unreplied.push(topic.tid);
|
||||
}
|
||||
|
||||
next(null, {
|
||||
counts: counts,
|
||||
tids: tidsByFilter[params.filter],
|
||||
tidsByFilter: tidsByFilter,
|
||||
});
|
||||
},
|
||||
], callback);
|
||||
if (!userRead[topic.tid]) {
|
||||
tidsByFilter.new.push(topic.tid);
|
||||
}
|
||||
}
|
||||
});
|
||||
counts[''] = tidsByFilter[''].length;
|
||||
counts.watched = tidsByFilter.watched.length;
|
||||
counts.unreplied = tidsByFilter.unreplied.length;
|
||||
counts.new = tidsByFilter.new.length;
|
||||
|
||||
return {
|
||||
counts: counts,
|
||||
tids: tidsByFilter[params.filter],
|
||||
tidsByFilter: tidsByFilter,
|
||||
};
|
||||
}
|
||||
|
||||
function filterTidsThatHaveBlockedPosts(params, callback) {
|
||||
async function filterTidsThatHaveBlockedPosts(params) {
|
||||
if (!params.blockedUids.length) {
|
||||
return setImmediate(callback, null, params.tids);
|
||||
return params.tids;
|
||||
}
|
||||
const topicScores = _.mapValues(_.keyBy(params.recentTids, 'value'), 'score');
|
||||
|
||||
db.sortedSetScores('uid:' + params.uid + ':tids_read', params.tids, function (err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
const userScores = _.zipObject(params.tids, results);
|
||||
const results = await db.sortedSetScores('uid:' + params.uid + ':tids_read', params.tids);
|
||||
|
||||
async.filter(params.tids, function (tid, next) {
|
||||
doesTidHaveUnblockedUnreadPosts(tid, {
|
||||
blockedUids: params.blockedUids,
|
||||
topicTimestamp: topicScores[tid],
|
||||
userLastReadTimestamp: userScores[tid],
|
||||
}, next);
|
||||
}, callback);
|
||||
const userScores = _.zipObject(params.tids, results);
|
||||
|
||||
return await async.filter(params.tids, async function (tid) {
|
||||
return await doesTidHaveUnblockedUnreadPosts(tid, {
|
||||
blockedUids: params.blockedUids,
|
||||
topicTimestamp: topicScores[tid],
|
||||
userLastReadTimestamp: userScores[tid],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function doesTidHaveUnblockedUnreadPosts(tid, params, callback) {
|
||||
async function doesTidHaveUnblockedUnreadPosts(tid, params) {
|
||||
var userLastReadTimestamp = params.userLastReadTimestamp;
|
||||
if (!userLastReadTimestamp) {
|
||||
return setImmediate(callback, null, true);
|
||||
return true;
|
||||
}
|
||||
var start = 0;
|
||||
var count = 3;
|
||||
var done = false;
|
||||
var hasUnblockedUnread = params.topicTimestamp > userLastReadTimestamp;
|
||||
if (!params.blockedUids.length) {
|
||||
return setImmediate(callback, null, hasUnblockedUnread);
|
||||
return hasUnblockedUnread;
|
||||
}
|
||||
async.whilst(function (next) {
|
||||
next(null, !done);
|
||||
}, function (_next) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getSortedSetRangeByScore('tid:' + tid + ':posts', start, count, userLastReadTimestamp, '+inf', next);
|
||||
},
|
||||
function (pidsSinceLastVisit, next) {
|
||||
if (!pidsSinceLastVisit.length) {
|
||||
done = true;
|
||||
return _next();
|
||||
}
|
||||
while (!done) {
|
||||
/* eslint-disable no-await-in-loop */
|
||||
const pidsSinceLastVisit = await db.getSortedSetRangeByScore('tid:' + tid + ':posts', start, count, userLastReadTimestamp, '+inf');
|
||||
if (!pidsSinceLastVisit.length) {
|
||||
return hasUnblockedUnread;
|
||||
}
|
||||
let postData = await posts.getPostsFields(pidsSinceLastVisit, ['pid', 'uid']);
|
||||
postData = postData.filter(function (post) {
|
||||
return !params.blockedUids.includes(parseInt(post.uid, 10));
|
||||
});
|
||||
|
||||
posts.getPostsFields(pidsSinceLastVisit, ['pid', 'uid'], next);
|
||||
},
|
||||
function (postData, next) {
|
||||
postData = postData.filter(function (post) {
|
||||
return !params.blockedUids.includes(parseInt(post.uid, 10));
|
||||
});
|
||||
|
||||
done = postData.length > 0;
|
||||
hasUnblockedUnread = postData.length > 0;
|
||||
start += count;
|
||||
next();
|
||||
},
|
||||
], _next);
|
||||
}, function (err) {
|
||||
callback(err, hasUnblockedUnread);
|
||||
});
|
||||
done = postData.length > 0;
|
||||
hasUnblockedUnread = postData.length > 0;
|
||||
start += count;
|
||||
}
|
||||
return hasUnblockedUnread;
|
||||
}
|
||||
|
||||
Topics.pushUnreadCount = function (uid, callback) {
|
||||
callback = callback || function () {};
|
||||
|
||||
Topics.pushUnreadCount = async function (uid) {
|
||||
if (!uid || parseInt(uid, 10) <= 0) {
|
||||
return setImmediate(callback);
|
||||
return;
|
||||
}
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
Topics.getUnreadTids({ uid: uid, count: true }, next);
|
||||
},
|
||||
function (results, next) {
|
||||
require('../socket.io').in('uid_' + uid).emit('event:unread.updateCount', {
|
||||
unreadTopicCount: results[''],
|
||||
unreadNewTopicCount: results.new,
|
||||
unreadWatchedTopicCount: results.watched,
|
||||
unreadUnrepliedTopicCount: results.unreplied,
|
||||
});
|
||||
setImmediate(next);
|
||||
},
|
||||
], callback);
|
||||
const results = await Topics.getUnreadTids({ uid: uid, count: true });
|
||||
require('../socket.io').in('uid_' + uid).emit('event:unread.updateCount', {
|
||||
unreadTopicCount: results[''],
|
||||
unreadNewTopicCount: results.new,
|
||||
unreadWatchedTopicCount: results.watched,
|
||||
unreadUnrepliedTopicCount: results.unreplied,
|
||||
});
|
||||
};
|
||||
|
||||
Topics.markAsUnreadForAll = function (tid, callback) {
|
||||
Topics.markCategoryUnreadForAll(tid, callback);
|
||||
Topics.markAsUnreadForAll = async function (tid) {
|
||||
await Topics.markCategoryUnreadForAll(tid);
|
||||
};
|
||||
|
||||
Topics.markAsRead = function (tids, uid, callback) {
|
||||
callback = callback || function () {};
|
||||
Topics.markAsRead = async function (tids, uid) {
|
||||
if (!Array.isArray(tids) || !tids.length) {
|
||||
return setImmediate(callback, null, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
tids = _.uniq(tids).filter(function (tid) {
|
||||
return tid && utils.isNumber(tid);
|
||||
tids = _.uniq(tids).filter(tid => tid && utils.isNumber(tid));
|
||||
|
||||
if (!tids.length) {
|
||||
return false;
|
||||
}
|
||||
const [topicScores, userScores] = await Promise.all([
|
||||
db.sortedSetScores('topics:recent', tids),
|
||||
db.sortedSetScores('uid:' + uid + ':tids_read', tids),
|
||||
]);
|
||||
|
||||
tids = tids.filter(function (tid, index) {
|
||||
return topicScores[index] && (!userScores[index] || userScores[index] < topicScores[index]);
|
||||
});
|
||||
|
||||
if (!tids.length) {
|
||||
return setImmediate(callback, null, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.parallel({
|
||||
topicScores: async.apply(db.sortedSetScores, 'topics:recent', tids),
|
||||
userScores: async.apply(db.sortedSetScores, 'uid:' + uid + ':tids_read', tids),
|
||||
}, next);
|
||||
},
|
||||
function (results, next) {
|
||||
tids = tids.filter(function (tid, index) {
|
||||
return results.topicScores[index] && (!results.userScores[index] || results.userScores[index] < results.topicScores[index]);
|
||||
});
|
||||
var now = Date.now();
|
||||
var scores = tids.map(() => now);
|
||||
const [topicData] = await Promise.all([
|
||||
Topics.getTopicsFields(tids, ['cid']),
|
||||
db.sortedSetAdd('uid:' + uid + ':tids_read', scores, tids),
|
||||
db.sortedSetRemove('uid:' + uid + ':tids_unread', tids),
|
||||
]);
|
||||
|
||||
if (!tids.length) {
|
||||
return callback(null, false);
|
||||
}
|
||||
var cids = _.uniq(topicData.map(t => t && t.cid).filter(Boolean));
|
||||
await categories.markAsRead(cids, uid);
|
||||
|
||||
var now = Date.now();
|
||||
var scores = tids.map(function () {
|
||||
return now;
|
||||
});
|
||||
|
||||
async.parallel({
|
||||
markRead: async.apply(db.sortedSetAdd, 'uid:' + uid + ':tids_read', scores, tids),
|
||||
markUnread: async.apply(db.sortedSetRemove, 'uid:' + uid + ':tids_unread', tids),
|
||||
topicData: async.apply(Topics.getTopicsFields, tids, ['cid']),
|
||||
}, next);
|
||||
},
|
||||
function (results, next) {
|
||||
var cids = results.topicData.map(function (topic) {
|
||||
return topic && topic.cid;
|
||||
}).filter(Boolean);
|
||||
|
||||
cids = _.uniq(cids);
|
||||
|
||||
categories.markAsRead(cids, uid, next);
|
||||
},
|
||||
function (next) {
|
||||
plugins.fireHook('action:topics.markAsRead', { uid: uid, tids: tids });
|
||||
next(null, true);
|
||||
},
|
||||
], callback);
|
||||
plugins.fireHook('action:topics.markAsRead', { uid: uid, tids: tids });
|
||||
return true;
|
||||
};
|
||||
|
||||
Topics.markAllRead = function (uid, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.getSortedSetRevRangeByScore('topics:recent', 0, -1, '+inf', Topics.unreadCutoff(), next);
|
||||
},
|
||||
function (tids, next) {
|
||||
Topics.markTopicNotificationsRead(tids, uid);
|
||||
Topics.markAsRead(tids, uid, next);
|
||||
},
|
||||
function (markedRead, next) {
|
||||
db.delete('uid:' + uid + ':tids_unread', next);
|
||||
},
|
||||
], callback);
|
||||
Topics.markAllRead = async function (uid) {
|
||||
const tids = await db.getSortedSetRevRangeByScore('topics:recent', 0, -1, '+inf', Topics.unreadCutoff());
|
||||
Topics.markTopicNotificationsRead(tids, uid);
|
||||
await Topics.markAsRead(tids, uid);
|
||||
await db.delete('uid:' + uid + ':tids_unread');
|
||||
};
|
||||
|
||||
Topics.markTopicNotificationsRead = function (tids, uid, callback) {
|
||||
callback = callback || function () {};
|
||||
Topics.markTopicNotificationsRead = async function (tids, uid) {
|
||||
if (!Array.isArray(tids) || !tids.length) {
|
||||
return callback();
|
||||
return;
|
||||
}
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
user.notifications.getUnreadByField(uid, 'tid', tids, next);
|
||||
},
|
||||
function (nids, next) {
|
||||
notifications.markReadMultiple(nids, uid, next);
|
||||
},
|
||||
function (next) {
|
||||
user.notifications.pushCount(uid);
|
||||
next();
|
||||
},
|
||||
], callback);
|
||||
const nids = await user.notifications.getUnreadByField(uid, 'tid', tids);
|
||||
await notifications.markReadMultiple(nids, uid);
|
||||
user.notifications.pushCount(uid);
|
||||
};
|
||||
|
||||
Topics.markCategoryUnreadForAll = function (tid, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
Topics.getTopicField(tid, 'cid', next);
|
||||
},
|
||||
function (cid, next) {
|
||||
categories.markAsUnreadForAll(cid, next);
|
||||
},
|
||||
], callback);
|
||||
Topics.markCategoryUnreadForAll = async function (tid) {
|
||||
const cid = await Topics.getTopicField(tid, 'cid');
|
||||
await categories.markAsUnreadForAll(cid);
|
||||
};
|
||||
|
||||
Topics.hasReadTopics = function (tids, uid, callback) {
|
||||
Topics.hasReadTopics = async function (tids, uid) {
|
||||
if (!(parseInt(uid, 10) > 0)) {
|
||||
return setImmediate(callback, null, tids.map(() => false));
|
||||
return tids.map(() => false);
|
||||
}
|
||||
const [topicScores, userScores, tids_unread, blockedUids] = await Promise.all([
|
||||
db.sortedSetScores('topics:recent', tids),
|
||||
db.sortedSetScores('uid:' + uid + ':tids_read', tids),
|
||||
db.sortedSetScores('uid:' + uid + ':tids_unread', tids),
|
||||
user.blocks.list(uid),
|
||||
]);
|
||||
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
async.parallel({
|
||||
topicScores: function (next) {
|
||||
db.sortedSetScores('topics:recent', tids, next);
|
||||
},
|
||||
userScores: function (next) {
|
||||
db.sortedSetScores('uid:' + uid + ':tids_read', tids, next);
|
||||
},
|
||||
tids_unread: function (next) {
|
||||
db.sortedSetScores('uid:' + uid + ':tids_unread', tids, next);
|
||||
},
|
||||
blockedUids: function (next) {
|
||||
user.blocks.list(uid, next);
|
||||
},
|
||||
}, next);
|
||||
},
|
||||
function (results, next) {
|
||||
var cutoff = Topics.unreadCutoff();
|
||||
var result = tids.map(function (tid, index) {
|
||||
var read = !results.tids_unread[index] &&
|
||||
(results.topicScores[index] < cutoff ||
|
||||
!!(results.userScores[index] && results.userScores[index] >= results.topicScores[index]));
|
||||
return { tid: tid, read: read, index: index };
|
||||
});
|
||||
var cutoff = Topics.unreadCutoff();
|
||||
var result = tids.map(function (tid, index) {
|
||||
var read = !tids_unread[index] &&
|
||||
(topicScores[index] < cutoff ||
|
||||
!!(userScores[index] && userScores[index] >= topicScores[index]));
|
||||
return { tid: tid, read: read, index: index };
|
||||
});
|
||||
|
||||
async.map(result, function (data, next) {
|
||||
if (data.read) {
|
||||
return next(null, true);
|
||||
}
|
||||
doesTidHaveUnblockedUnreadPosts(data.tid, {
|
||||
topicTimestamp: results.topicScores[data.index],
|
||||
userLastReadTimestamp: results.userScores[data.index],
|
||||
blockedUids: results.blockedUids,
|
||||
}, function (err, hasUnblockedUnread) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
if (!hasUnblockedUnread) {
|
||||
data.read = true;
|
||||
}
|
||||
next(null, data.read);
|
||||
});
|
||||
}, next);
|
||||
},
|
||||
], callback);
|
||||
};
|
||||
|
||||
Topics.hasReadTopic = function (tid, uid, callback) {
|
||||
Topics.hasReadTopics([tid], uid, function (err, hasRead) {
|
||||
callback(err, Array.isArray(hasRead) && hasRead.length ? hasRead[0] : false);
|
||||
return await async.map(result, async function (data) {
|
||||
if (data.read) {
|
||||
return true;
|
||||
}
|
||||
const hasUnblockedUnread = await doesTidHaveUnblockedUnreadPosts(data.tid, {
|
||||
topicTimestamp: topicScores[data.index],
|
||||
userLastReadTimestamp: userScores[data.index],
|
||||
blockedUids: blockedUids,
|
||||
});
|
||||
if (!hasUnblockedUnread) {
|
||||
data.read = true;
|
||||
}
|
||||
return data.read;
|
||||
});
|
||||
};
|
||||
|
||||
Topics.markUnread = function (tid, uid, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
Topics.exists(tid, next);
|
||||
},
|
||||
function (exists, next) {
|
||||
if (!exists) {
|
||||
return next(new Error('[[error:no-topic]]'));
|
||||
}
|
||||
db.sortedSetRemove('uid:' + uid + ':tids_read', tid, next);
|
||||
},
|
||||
function (next) {
|
||||
db.sortedSetAdd('uid:' + uid + ':tids_unread', Date.now(), tid, next);
|
||||
},
|
||||
], callback);
|
||||
Topics.hasReadTopic = async function (tid, uid) {
|
||||
const hasRead = await Topics.hasReadTopics([tid], uid);
|
||||
return Array.isArray(hasRead) && hasRead.length ? hasRead[0] : false;
|
||||
};
|
||||
|
||||
Topics.filterNewTids = function (tids, uid, callback) {
|
||||
if (parseInt(uid, 10) <= 0) {
|
||||
return setImmediate(callback, null, []);
|
||||
Topics.markUnread = async function (tid, uid) {
|
||||
const exists = await Topics.exists(tid);
|
||||
if (!exists) {
|
||||
throw new Error('[[error:no-topic]]');
|
||||
}
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.sortedSetScores('uid:' + uid + ':tids_read', tids, next);
|
||||
},
|
||||
function (scores, next) {
|
||||
tids = tids.filter((tid, index) => tid && !scores[index]);
|
||||
next(null, tids);
|
||||
},
|
||||
], callback);
|
||||
await db.sortedSetRemove('uid:' + uid + ':tids_read', tid);
|
||||
await db.sortedSetAdd('uid:' + uid + ':tids_unread', Date.now(), tid);
|
||||
};
|
||||
|
||||
Topics.filterUnrepliedTids = function (tids, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
db.sortedSetScores('topics:posts', tids, next);
|
||||
},
|
||||
function (scores, next) {
|
||||
tids = tids.filter((tid, index) => tid && scores[index] <= 1);
|
||||
next(null, tids);
|
||||
},
|
||||
], callback);
|
||||
Topics.filterNewTids = async function (tids, uid) {
|
||||
if (parseInt(uid, 10) <= 0) {
|
||||
return [];
|
||||
}
|
||||
const scores = await db.sortedSetScores('uid:' + uid + ':tids_read', tids);
|
||||
return tids.filter((tid, index) => tid && !scores[index]);
|
||||
};
|
||||
|
||||
Topics.filterUnrepliedTids = async function (tids) {
|
||||
const scores = await db.sortedSetScores('topics:posts', tids);
|
||||
return tids.filter((tid, index) => tid && scores[index] <= 1);
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user