diff --git a/src/activitypub/notes.js b/src/activitypub/notes.js index d057b52e11..5e396efa62 100644 --- a/src/activitypub/notes.js +++ b/src/activitypub/notes.js @@ -292,8 +292,13 @@ Notes.syncUserInboxes = async function (tid, uid) { const keys = Array.from(uids).map(uid => `uid:${uid}:inbox`); const score = await db.sortedSetScore(`cid:${cid}:tids`, tid); + const removeKeys = (await db.getSetMembers(`tid:${tid}:recipients`)) + .filter(uid => !uids.has(parseInt(uid, 10))) + .map((uid => `uid:${uid}:inbox`)); + // winston.verbose(`[activitypub/syncUserInboxes] Syncing tid ${tid} with ${uids.size} inboxes`); await Promise.all([ + db.sortedSetsRemove(removeKeys, tid), db.sortedSetsAdd(keys, keys.map(() => score || Date.now()), tid), db.setAdd(`tid:${tid}:recipients`, Array.from(uids)), ]); @@ -371,10 +376,14 @@ Notes.delete = async (pids) => { const exists = await posts.exists(pids); pids = pids.filter((_, idx) => exists[idx]); + let tids = await posts.getPostsFields(pids, ['tid']); + tids = new Set(tids.map(obj => obj.tid)); + const recipientSets = pids.map(id => `post:${id}:recipients`); const announcerSets = pids.map(id => `pid:${id}:announces`); await db.deleteAll([...recipientSets, ...announcerSets]); + await Promise.all(Array.from(tids).map(async tid => Notes.syncUserInboxes(tid))); }; Notes.prune = async () => { diff --git a/test/activitypub/notes.js b/test/activitypub/notes.js index 90b95a9aa9..482b706d14 100644 --- a/test/activitypub/notes.js +++ b/test/activitypub/notes.js @@ -69,6 +69,14 @@ describe('Notes', () => { assert.strictEqual(inboxed, true); }); + + it('should remove a topic from a user\'s inbox if that user is no longer a recipient in any contained posts', async () => { + await activitypub.notes.syncUserInboxes(topicData.tid, uid); + await activitypub.notes.syncUserInboxes(topicData.tid); + const inboxed = await db.isSortedSetMember(`uid:${uid}:inbox`, topicData.tid); + + assert.strictEqual(inboxed, false); + }); }); describe('Deletion', () => {