You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nodebb/src/topics/follow.js

178 lines
4.8 KiB
JavaScript

'use strict';
const db = require('../database');
const notifications = require('../notifications');
const privileges = require('../privileges');
const plugins = require('../plugins');
const utils = require('../utils');
module.exports = function (Topics) {
Topics.toggleFollow = async function (tid, uid) {
const exists = await Topics.exists(tid);
if (!exists) {
throw new Error('[[error:no-topic]]');
}
const isFollowing = await Topics.isFollowing([tid], uid);
if (isFollowing[0]) {
await Topics.unfollow(tid, uid);
} else {
await Topics.follow(tid, uid);
}
return !isFollowing[0];
};
Topics.follow = async function (tid, uid) {
await setWatching(follow, unignore, 'action:topic.follow', tid, uid);
};
Topics.unfollow = async function (tid, uid) {
await setWatching(unfollow, unignore, 'action:topic.unfollow', tid, uid);
};
Topics.ignore = async function (tid, uid) {
await setWatching(ignore, unfollow, 'action:topic.ignore', tid, uid);
};
async function setWatching(method1, method2, hook, tid, uid) {
if (parseInt(uid, 10) <= 0) {
return;
}
const exists = await Topics.exists(tid);
if (!exists) {
throw new Error('[[error:no-topic]]');
}
await method1(tid, uid);
await method2(tid, uid);
plugins.hooks.fire(hook, { uid: uid, tid: tid });
}
async function follow(tid, uid) {
await addToSets(`tid:${tid}:followers`, `uid:${uid}:followed_tids`, tid, uid);
}
async function unfollow(tid, uid) {
await removeFromSets(`tid:${tid}:followers`, `uid:${uid}:followed_tids`, tid, uid);
}
async function ignore(tid, uid) {
await addToSets(`tid:${tid}:ignorers`, `uid:${uid}:ignored_tids`, tid, uid);
}
async function unignore(tid, uid) {
await removeFromSets(`tid:${tid}:ignorers`, `uid:${uid}:ignored_tids`, tid, uid);
}
async function addToSets(set1, set2, tid, uid) {
await db.setAdd(set1, uid);
await db.sortedSetAdd(set2, Date.now(), tid);
}
async function removeFromSets(set1, set2, tid, uid) {
await db.setRemove(set1, uid);
await db.sortedSetRemove(set2, tid);
}
Topics.isFollowing = async function (tids, uid) {
return await isIgnoringOrFollowing('followers', tids, uid);
};
Topics.isIgnoring = async function (tids, uid) {
return await isIgnoringOrFollowing('ignorers', tids, uid);
};
Topics.getFollowData = async function (tids, uid) {
if (!Array.isArray(tids)) {
return;
}
if (parseInt(uid, 10) <= 0) {
return tids.map(() => ({ following: false, ignoring: false }));
}
const keys = [];
tids.forEach(tid => keys.push(`tid:${tid}:followers`, `tid:${tid}:ignorers`));
const data = await db.isMemberOfSets(keys, uid);
const followData = [];
for (let i = 0; i < data.length; i += 2) {
followData.push({
following: data[i],
ignoring: data[i + 1],
});
}
return followData;
};
async function isIgnoringOrFollowing(set, tids, uid) {
if (!Array.isArray(tids)) {
return;
}
if (parseInt(uid, 10) <= 0) {
return tids.map(() => false);
}
const keys = tids.map(tid => `tid:${tid}:${set}`);
return await db.isMemberOfSets(keys, uid);
}
Topics.getFollowers = async function (tid) {
return await db.getSetMembers(`tid:${tid}:followers`);
};
Topics.getIgnorers = async function (tid) {
return await db.getSetMembers(`tid:${tid}:ignorers`);
};
Topics.filterIgnoringUids = async function (tid, uids) {
const isIgnoring = await db.isSetMembers(`tid:${tid}:ignorers`, uids);
const readingUids = uids.filter((uid, index) => uid && !isIgnoring[index]);
return readingUids;
};
Topics.filterWatchedTids = async function (tids, uid) {
if (parseInt(uid, 10) <= 0) {
return [];
}
const scores = await db.sortedSetScores(`uid:${uid}:followed_tids`, tids);
return tids.filter((tid, index) => tid && !!scores[index]);
};
Topics.filterNotIgnoredTids = async function (tids, uid) {
if (parseInt(uid, 10) <= 0) {
return tids;
}
const scores = await db.sortedSetScores(`uid:${uid}:ignored_tids`, tids);
return tids.filter((tid, index) => tid && !scores[index]);
};
Topics.notifyFollowers = async function (postData, exceptUid, notifData) {
notifData = notifData || {};
let followers = await Topics.getFollowers(postData.topic.tid);
const index = followers.indexOf(String(exceptUid));
if (index !== -1) {
followers.splice(index, 1);
}
followers = await privileges.topics.filterUids('topics:read', postData.topic.tid, followers);
if (!followers.length) {
return;
}
let { title } = postData.topic;
if (title) {
title = utils.decodeHTMLEntities(title);
}
const notification = await notifications.create({
subject: title,
bodyLong: postData.content,
pid: postData.pid,
path: `/post/${postData.pid}`,
tid: postData.topic.tid,
from: exceptUid,
topicTitle: title,
...notifData,
});
notifications.push(notification, followers);
};
};