// content-vote.js // Copyright (C) 2022 DTP Technologies, LLC // License: Apache-2.0 'use strict'; const mongoose = require('mongoose'); const ContentVote = mongoose.model('ContentVote'); const ioEmitter = require('socket.io-emitter'); const { SiteService } = require('../../lib/site-lib'); class ContentVoteService extends SiteService { constructor (dtp) { super(dtp, module.exports); } async start ( ) { await super.start(); this.emitter = ioEmitter(this.dtp.redis); } async recordVote (user, resourceType, resource, vote) { const NOW = new Date(); const ResourceModel = mongoose.model(resourceType); const updateOp = { $inc: { } }; let message; const contentVote = await ContentVote.findOne({ user: user._id, resource: resource._id, }); this.log.debug('processing content vote', { resource: resource._id, user: user._id, vote, contentVote }); if (!contentVote) { /* * It's a new vote from a new user */ await ContentVote.create({ created: NOW, resourceType, resource: resource._id, user: user._id, vote }); if (vote === 'up') { updateOp.$inc['resourceStats.upvoteCount'] = 1; message = 'Comment upvote recorded'; } else { updateOp.$inc['resourceStats.downvoteCount'] = 1; message = 'Comment downvote recorded'; } } else { /* * If vote not changed, do no further work. */ if (contentVote.vote === vote) { const updatedResource = await ResourceModel.findById(resource._id).select('resourceStats'); return { message: "Comment vote unchanged", resourceStats: updatedResource.resourceStats }; } /* * Update the user's existing vote */ await ContentVote.updateOne( { _id: contentVote._id }, { $set: { vote } } ); /* * Adjust resource's stats based on the changed vote */ if (vote === 'up') { updateOp.$inc['resourceStats.upvoteCount'] = 1; updateOp.$inc['resourceStats.downvoteCount'] = -1; message = 'Comment vote changed to upvote'; } else { updateOp.$inc['resourceStats.upvoteCount'] = -1; updateOp.$inc['resourceStats.downvoteCount'] = 1; message = 'Comment vote changed to downvote'; } } this.log.info('updating resource stats', { resourceType, resource, updateOp }); await ResourceModel.updateOne({ _id: resource._id }, updateOp); const updatedResource = await ResourceModel.findById(resource._id).select('resourceStats'); return { message, resourceStats: updatedResource.resourceStats }; } async removeForResource (resource) { this.log.info('removing all votes for resource', { resourceId: resource._id }); await ContentVote.deleteMany({ resource: resource._id }); } async removeForUser (user) { await ContentVote .find({ user: user._id }) .cursor() .eachAsync(async (vote) => { const updateOp = { $inc: { } }; if (vote.vote === 'up') { updateOp.$inc['resourceStats.upvoteCount'] = -1; } else { updateOp.$inc['resourceStats.downvoteCount'] = -1; } const resourceModel = mongoose.model(vote.resourceType); await resourceModel.updateOne({ _id: vote.resource }, updateOp); }); await ContentVote.deleteMany({ user: user._id }); } } module.exports = { logId: 'svc:content-vote', index: 'contentVote', className: 'ContentVoteService', create: (dtp) => { return new ContentVoteService(dtp); }, };