nodebb-plugin-total-vote-count
Advanced tools
Comparing version 1.3.0 to 1.3.1
# Changelog | ||
## [1.3.1](https://github.com/oplik0/nodebb-plugin-total-vote-count/compare/v1.3.0...v1.3.1) (2024-03-24) | ||
### Bug Fixes | ||
* dont calculate totalVoteCount on every page load ([#21](https://github.com/oplik0/nodebb-plugin-total-vote-count/issues/21)) ([ef969d2](https://github.com/oplik0/nodebb-plugin-total-vote-count/commit/ef969d2c5205798fd4b224a74fc9fa0d6f414ff0)) | ||
## [1.3.0](https://github.com/oplik0/nodebb-plugin-total-vote-count/compare/v1.2.1...v1.3.0) (2024-03-20) | ||
@@ -4,0 +11,0 @@ |
131
library.js
'use strict'; | ||
const winston = require.main.require('winston'); | ||
const _ = require.main.require('lodash'); | ||
const db = require.main.require('./src/database'); | ||
const topics = require.main.require('./src/topics'); | ||
const batch = require.main.require('./src/batch'); | ||
const socketAdmin = require.main.require('./src/socket.io/admin'); | ||
const plugin = module.exports; | ||
plugin.init = async function (params) { | ||
const { router } = params; | ||
const routeHelpers = require.main.require('./src/routes/helpers'); | ||
routeHelpers.setupAdminPageRoute(router, '/admin/plugins/total-vote-count', [], (req, res) => { | ||
res.render('admin/plugins/total-vote-count', { | ||
title: 'Total Vote Count', | ||
}); | ||
}); | ||
}; | ||
plugin.addAdminNavigation = async function (header) { | ||
header.plugins.push({ | ||
route: '/plugins/total-vote-count', | ||
icon: 'fa-book', | ||
name: 'Total vote count', | ||
}); | ||
return header; | ||
}; | ||
plugin.getTopics = async (hookData) => { | ||
let voteData; | ||
try { | ||
voteData = await db.getSortedSetsMembersWithScores( | ||
hookData.topics.map(t => `tid:${t.tid}:posts:votes`) | ||
); | ||
} catch (e) { | ||
winston.error(`Error getting vote data ${e}`); | ||
return hookData; | ||
} | ||
for (const [index, topic] of Object.entries(hookData.topics)) { | ||
if (topic) { | ||
for (const { score } of voteData[index]) { | ||
topic.votes += score; | ||
} | ||
hookData.topics.forEach((t) => { | ||
if (t && t.hasOwnProperty('totalVoteCount')) { | ||
t.votes = parseInt(t.totalVoteCount, 10); | ||
} | ||
} | ||
}); | ||
return hookData; | ||
@@ -32,18 +44,87 @@ }; | ||
const { tid } = hookData.post; | ||
const topicData = await topics.getTopicFields(tid, ['cid', 'upvotes', 'downvotes', 'pinned']); | ||
const voteData = await db.getSortedSetMembersWithScores(`tid:${tid}:posts:votes`); | ||
await recalculateTotalTopicVotes([tid]); | ||
}; | ||
let { votes } = topicData; | ||
for (const { score } of voteData) { | ||
votes += score; | ||
plugin.actionPostMove = async (hookData) => { | ||
await recalculateTotalTopicVotes([hookData.tid, hookData.post.tid]); | ||
}; | ||
plugin.actionPostDelete = async (hookData) => { | ||
await recalculateTotalTopicVotes([hookData.post.tid]); | ||
}; | ||
plugin.actionPostRestore = async (hookData) => { | ||
await recalculateTotalTopicVotes([hookData.post.tid]); | ||
}; | ||
plugin.actionPostsPurge = async (hookData) => { | ||
const tids = _.uniq(hookData.posts.map(p => p && p.tid)); | ||
await recalculateTotalTopicVotes(tids); | ||
}; | ||
async function recalculateTotalTopicVotes(tids) { | ||
if (!tids.length) { | ||
return; | ||
} | ||
const topicData = await topics.getTopicsFields(tids, [ | ||
'tid', 'cid', 'upvotes', 'downvotes', 'pinned', | ||
]); | ||
const voteData = await db.getSortedSetsMembersWithScores( | ||
tids.map(tid => `tid:${tid}:posts:votes`) | ||
); | ||
for (const [index, topic] of Object.entries(topicData)) { | ||
if (topic) { | ||
topic.totalVoteCount = topic.votes; | ||
for (const { score } of voteData[index]) { | ||
topic.totalVoteCount += score; | ||
} | ||
} | ||
} | ||
const promises = [ | ||
db.sortedSetAdd('topics:votes', votes, tid), | ||
db.sortedSetAddBulk(topicData.map(t => (['topic:votes', t.totalVoteCount, t.tid]))), | ||
db.setObjectBulk(topicData.map(t => ([`topic:${t.tid}`, { totalVoteCount: t.totalVoteCount }]))), | ||
]; | ||
if (!topicData.pinned) { | ||
promises.push(db.sortedSetAdd(`cid:${topicData.cid}:tids:votes`, votes, tid)); | ||
const nonPinned = topicData.filter(t => t && !t.pinned); | ||
if (nonPinned.length) { | ||
promises.push(db.sortedSetAddBulk(nonPinned.map(t => ([`cid:${t.cid}:tids:votes`, t.totalVoteCount, t.tid])))); | ||
} | ||
await Promise.all(promises); | ||
} | ||
socketAdmin.plugins.totalVotes = Object.create(null); | ||
socketAdmin.plugins.totalVotes.calculate = async (/* socket */) => { | ||
await batch.processSortedSet('topics:tid', recalculateTotalTopicVotes, { | ||
batch: 500, | ||
}); | ||
}; | ||
socketAdmin.plugins.totalVotes.revert = async (/* socket */) => { | ||
await batch.processSortedSet('topics:tid', async (tids) => { | ||
let topicData = await db.getObjectsFields( | ||
tids.map(tid => `topic:${tid}`), | ||
['tid', 'cid', 'upvotes', 'downvotes', 'pinned'] | ||
); | ||
topicData = topicData.filter(t => t && t.cid); | ||
topicData.forEach((t) => { | ||
t.votes = parseInt(t.upvotes || 0, 10) - parseInt(t.downvotes || 0, 10); | ||
}); | ||
const promises = [ | ||
db.sortedSetAddBulk(topicData.map(t => ([`topic:votes`, t.votes, t.tid]))), | ||
db.deleteObjectFields(tids.map(tid => `topic:${tid}`), ['totalVoteCount']), | ||
]; | ||
const nonPinned = topicData.filter(t => t && !t.pinned); | ||
if (nonPinned.length) { | ||
promises.push( | ||
db.sortedSetAddBulk(nonPinned.map(t => ([`cid:${t.cid}:tids:votes`, t.votes, t.tid]))) | ||
); | ||
} | ||
await Promise.all(promises); | ||
}, { | ||
batch: 500, | ||
}); | ||
}; | ||
{ | ||
"name": "nodebb-plugin-total-vote-count", | ||
"version": "1.3.0", | ||
"version": "1.3.1", | ||
"description": "A starter kit for quickly creating NodeBB plugins", | ||
@@ -5,0 +5,0 @@ "main": "library.js", |
@@ -6,5 +6,14 @@ { | ||
"hooks": [ | ||
{ "hook": "static:app.load", "method": "init" }, | ||
{ "hook": "filter:admin.header.build", "method": "addAdminNavigation" }, | ||
{ "hook": "filter:topics.get", "method": "getTopics" }, | ||
{ "hook": "action:post.updatePostVoteCount", "method": "updatePostVoteCount" } | ||
] | ||
{ "hook": "action:post.updatePostVoteCount", "method": "updatePostVoteCount" }, | ||
{ "hook": "action:post.move", "method": "actionPostMove" }, | ||
{ "hook": "action:post.delete", "method": "actionPostDelete" }, | ||
{ "hook": "action:post.restore", "method": "actionPostRestore" }, | ||
{ "hook": "action:posts.purge", "method": "actionPostsPurge" } | ||
], | ||
"modules": { | ||
"../admin/plugins/total-vote-count.js": "./public/admin.js" | ||
} | ||
} |
@@ -22,2 +22,3 @@ /** | ||
const assert = require('assert'); | ||
const util = require('util'); | ||
@@ -29,2 +30,4 @@ const topics = require.main.require('./src/topics'); | ||
const sleep = util.promisify(setTimeout); | ||
describe('nodebb-plugin-total-vote-count', () => { | ||
@@ -72,2 +75,3 @@ let authorUid; | ||
await posts.upvote(postData.pid, commenterUid); | ||
await sleep(1000); | ||
let [topic] = await topics.getTopicsByTids([topicData.tid]); | ||
@@ -77,2 +81,3 @@ assert.strictEqual(topic.votes, 1); | ||
await posts.downvote(postData.pid, commenterUid); | ||
await sleep(1000); | ||
[topic] = await topics.getTopicsByTids([topicData.tid]); | ||
@@ -85,2 +90,3 @@ assert.strictEqual(topic.votes, -1); | ||
await posts.upvote(responseData.pid, authorUid); | ||
await sleep(1000); | ||
let [topic] = await topics.getTopicsByTids([topicData.tid]); | ||
@@ -90,2 +96,3 @@ assert.strictEqual(topic.votes, 1); | ||
await posts.downvote(responseData.pid, authorUid); | ||
await sleep(1000); | ||
[topic] = await topics.getTopicsByTids([topicData.tid]); | ||
@@ -98,2 +105,3 @@ assert.strictEqual(topic.votes, -1); | ||
await posts.upvote(responseData.pid, authorUid); | ||
await sleep(1000); | ||
const [topic] = await topics.getTopicsByTids([topicData.tid]); | ||
@@ -106,2 +114,3 @@ assert.strictEqual(topic.votes, 2); | ||
await posts.downvote(responseData.pid, authorUid); | ||
await sleep(1000); | ||
const [topic] = await topics.getTopicsByTids([topicData.tid]); | ||
@@ -108,0 +117,0 @@ assert.strictEqual(topic.votes, 0); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
203344
25
5449
1