diff --git a/public/language/en-GB/notifications.json b/public/language/en-GB/notifications.json index 2782fdaff9..fa54f162ed 100644 --- a/public/language/en-GB/notifications.json +++ b/public/language/en-GB/notifications.json @@ -106,5 +106,10 @@ "notificationType-post-queue": "When a new post is queued", "notificationType-new-post-flag": "When a post is flagged", "notificationType-new-user-flag": "When a user is flagged", - "notificationType-new-reward": "When you earn a new reward" + "notificationType-new-reward": "When you earn a new reward", + + "activitypub.announce": "%1 shared your post in %2 to their followers.", + "activitypub.announce-dual": "%1 and %2 shared your post in %3 to their followers.", + "activitypub.announce-triple": "%1, %2 and %3 shared your post in %4 to their followers.", + "activitypub.announce-multiple": "%1, %2 and %3 others shared your post in %4 to their followers." } diff --git a/src/activitypub/inbox.js b/src/activitypub/inbox.js index 6ea6e12f41..feaaac4d30 100644 --- a/src/activitypub/inbox.js +++ b/src/activitypub/inbox.js @@ -9,9 +9,11 @@ const user = require('../user'); const posts = require('../posts'); const topics = require('../topics'); const categories = require('../categories'); +const notifications = require('../notifications'); const utils = require('../utils'); const activitypub = require('.'); +const socketHelpers = require('../socket.io/helpers'); const helpers = require('./helpers'); const inbox = module.exports; @@ -88,7 +90,8 @@ inbox.like = async (req) => { winston.info(`[activitypub/inbox/like] id ${id} via ${actor}`); - await posts.upvote(id, actor); + const result = await posts.upvote(id, actor); + socketHelpers.upvote(result, 'notifications:upvoted-your-post-in'); }; inbox.announce = async (req) => { @@ -112,6 +115,8 @@ inbox.announce = async (req) => { pid = id; tid = await posts.getPostField(id, 'tid'); + + socketHelpers.sendNotificationToPostOwner(pid, actor, 'announce', 'notifications:activitypub.announce'); } else { pid = object; pid = await activitypub.resolveId(0, pid); // in case wrong id is passed-in; unlikely, but still. @@ -180,7 +185,8 @@ inbox.follow = async (req) => { const followerRemoteCount = await db.sortedSetCard(`followersRemote:${id}`); await user.setUserField(id, 'followerRemoteCount', followerRemoteCount); - await activitypub.send('uid', id, req.body.actor, { + user.onFollow(req.body.actor, id); + activitypub.send('uid', id, req.body.actor, { type: 'Accept', object: { type: 'Follow', @@ -280,6 +286,7 @@ inbox.undo = async (req) => { await db.sortedSetRemove(`followersRemote:${id}`, actor); const followerRemoteCount = await db.sortedSetCard(`followerRemote:${id}`); await user.setUserField(id, 'followerRemoteCount', followerRemoteCount); + notifications.rescind(`follow:${id}:uid:${actor}`); break; } @@ -314,6 +321,7 @@ inbox.undo = async (req) => { } await posts.unvote(id, actor); + notifications.rescind(`upvote:post:${id}:uid:${actor}`); break; } @@ -334,6 +342,8 @@ inbox.undo = async (req) => { if (existing.length) { await topics.events.purge(tid, existing); } + + notifications.rescind(`announce:post:${id}:uid:${actor}`); } } }; diff --git a/src/api/users.js b/src/api/users.js index febcf290e6..d3bb57a3e8 100644 --- a/src/api/users.js +++ b/src/api/users.js @@ -172,27 +172,11 @@ usersAPI.changePassword = async function (caller, data) { usersAPI.follow = async function (caller, data) { await user.follow(caller.uid, data.uid); + await user.onFollow(caller.uid, data.uid); plugins.hooks.fire('action:user.follow', { fromUid: caller.uid, toUid: data.uid, }); - - const userData = await user.getUserFields(caller.uid, ['username', 'userslug']); - const { displayname } = userData; - - const notifObj = await notifications.create({ - type: 'follow', - bodyShort: `[[notifications:user-started-following-you, ${displayname}]]`, - nid: `follow:${data.uid}:uid:${caller.uid}`, - from: caller.uid, - path: `/uid/${data.uid}/followers`, - mergeId: 'notifications:user-started-following-you', - }); - if (!notifObj) { - return; - } - notifObj.user = userData; - await notifications.push(notifObj, [data.uid]); }; usersAPI.unfollow = async function (caller, data) { diff --git a/src/notifications.js b/src/notifications.js index 12a43af793..1c1d2f7afd 100644 --- a/src/notifications.js +++ b/src/notifications.js @@ -403,6 +403,7 @@ Notifications.merge = async function (notifications) { 'notifications:user-posted-in-public-room', 'new-register', 'post-queue', + 'notifications:activitypub.announce', ]; notifications = mergeIds.reduce((notifications, mergeId) => { @@ -467,7 +468,8 @@ Notifications.merge = async function (notifications) { case 'notifications:user-started-following-you': case 'notifications:user-posted-to': case 'notifications:user-flagged-post-in': - case 'notifications:user-flagged-user': { + case 'notifications:user-flagged-user': + case 'notifications:activitypub.announce': { const usernames = _.uniq(set.map(notifObj => notifObj && notifObj.user && notifObj.user.username)); const numUsers = usernames.length; diff --git a/src/socket.io/helpers.js b/src/socket.io/helpers.js index 36b3384d24..d6d0576204 100644 --- a/src/socket.io/helpers.js +++ b/src/socket.io/helpers.js @@ -76,7 +76,7 @@ SocketHelpers.sendNotificationToPostOwner = async function (pid, fromuid, comman if (!pid || !fromuid || !notification) { return; } - fromuid = parseInt(fromuid, 10); + fromuid = utils.isNumber(fromuid) ? parseInt(fromuid, 10) : fromuid; const postData = await posts.getPostFields(pid, ['tid', 'uid', 'content']); const [canRead, isIgnoring] = await Promise.all([ privileges.posts.can('topics:read', pid, postData.uid), diff --git a/src/user/follow.js b/src/user/follow.js index ec7e36f10f..3fad783476 100644 --- a/src/user/follow.js +++ b/src/user/follow.js @@ -1,6 +1,7 @@ 'use strict'; +const notifications = require('../notifications'); const plugins = require('../plugins'); const activitypub = require('../activitypub'); const db = require('../database'); @@ -96,4 +97,23 @@ module.exports = function (User) { const setPrefix = isRemote ? 'followingRemote' : 'following'; return await db.isSortedSetMember(`${setPrefix}:${uid}`, theirid); }; + + User.onFollow = async function (uid, targetUid) { + const userData = await User.getUserFields(uid, ['username', 'userslug']); + const { displayname } = userData; + + const notifObj = await notifications.create({ + type: 'follow', + bodyShort: `[[notifications:user-started-following-you, ${displayname}]]`, + nid: `follow:${targetUid}:uid:${uid}`, + from: uid, + path: `/uid/${targetUid}/followers`, + mergeId: 'notifications:user-started-following-you', + }); + if (!notifObj) { + return; + } + notifObj.user = userData; + await notifications.push(notifObj, [targetUid]); + }; };