Merge branch 'fix/local-user-notes-count' into 'develop'
fix: recalculate notesCount of local user when notes are deleted. Co-authored-by: Linca <lhcfllinca@gmail.com> Co-authored-by: Lhcfl <Lhcfl@outlook.com> See merge request firefish/firefish!10701
This commit is contained in:
commit
df8078d9a4
1 changed files with 66 additions and 16 deletions
|
@ -16,11 +16,24 @@ import {
|
||||||
import { countSameRenotes } from "@/misc/count-same-renotes.js";
|
import { countSameRenotes } from "@/misc/count-same-renotes.js";
|
||||||
import { registerOrFetchInstanceDoc } from "@/services/register-or-fetch-instance-doc.js";
|
import { registerOrFetchInstanceDoc } from "@/services/register-or-fetch-instance-doc.js";
|
||||||
import { deliverToRelays } from "@/services/relay.js";
|
import { deliverToRelays } from "@/services/relay.js";
|
||||||
|
import type { IActivity } from "@/remote/activitypub/type.js";
|
||||||
|
|
||||||
|
async function recalculateNotesCountOfLocalUser(user: {
|
||||||
|
id: User["id"];
|
||||||
|
host: User["host"];
|
||||||
|
}) {
|
||||||
|
if (Users.isLocalUser(user)) {
|
||||||
|
await Notes.countBy({ userId: user.id }).then((newCount: number) =>
|
||||||
|
Users.update(user.id, { updatedAt: new Date(), notesCount: newCount }),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 投稿を削除します。
|
* 投稿を削除します。
|
||||||
* @param user 投稿者
|
* @param user 投稿者
|
||||||
* @param note 投稿
|
* @param note 投稿
|
||||||
|
* @param deleteFromDb false if called by making private
|
||||||
*/
|
*/
|
||||||
export default async function (
|
export default async function (
|
||||||
user: { id: User["id"]; uri: User["uri"]; host: User["host"] },
|
user: { id: User["id"]; uri: User["uri"]; host: User["host"] },
|
||||||
|
@ -44,6 +57,16 @@ export default async function (
|
||||||
await Notes.decrement({ id: note.replyId }, "repliesCount", 1);
|
await Notes.decrement({ id: note.replyId }, "repliesCount", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cascadingNotes = await findCascadingNotes(note);
|
||||||
|
const affectedLocalUsers: Record<
|
||||||
|
User["id"],
|
||||||
|
{ id: User["id"]; uri: User["uri"]; host: null }
|
||||||
|
> = {};
|
||||||
|
if (Users.isLocalUser(user)) {
|
||||||
|
affectedLocalUsers[user.id] = user;
|
||||||
|
}
|
||||||
|
const instanceNotesCountDecreasement: Record<string, number> = {};
|
||||||
|
|
||||||
if (!quiet) {
|
if (!quiet) {
|
||||||
// Only broadcast "deleted" to local if the note is deleted from db
|
// Only broadcast "deleted" to local if the note is deleted from db
|
||||||
if (deleteFromDb) {
|
if (deleteFromDb) {
|
||||||
|
@ -87,12 +110,23 @@ export default async function (
|
||||||
}
|
}
|
||||||
|
|
||||||
// also deliever delete activity to cascaded notes
|
// also deliever delete activity to cascaded notes
|
||||||
const cascadingNotes = (await findCascadingNotes(note)).filter(
|
|
||||||
(note) => !note.localOnly,
|
|
||||||
); // filter out local-only notes
|
|
||||||
for (const cascadingNote of cascadingNotes) {
|
for (const cascadingNote of cascadingNotes) {
|
||||||
|
if (deleteFromDb) {
|
||||||
|
// For other notes, publishNoteStream is also required.
|
||||||
|
publishNoteStream(cascadingNote.id, "deleted", {
|
||||||
|
deletedAt: deletedAt,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!cascadingNote.user) continue;
|
if (!cascadingNote.user) continue;
|
||||||
if (!Users.isLocalUser(cascadingNote.user)) continue;
|
if (!Users.isLocalUser(cascadingNote.user)) {
|
||||||
|
if (!Users.isRemoteUser(cascadingNote.user)) continue;
|
||||||
|
instanceNotesCountDecreasement[cascadingNote.user.host] ??= 0;
|
||||||
|
instanceNotesCountDecreasement[cascadingNote.user.host]++;
|
||||||
|
continue; // filter out remote users
|
||||||
|
}
|
||||||
|
affectedLocalUsers[cascadingNote.user.id] ??= cascadingNote.user;
|
||||||
|
if (cascadingNote.localOnly) continue; // filter out local-only notes
|
||||||
const content = renderActivity(
|
const content = renderActivity(
|
||||||
renderDelete(
|
renderDelete(
|
||||||
renderTombstone(`${config.url}/notes/${cascadingNote.id}`),
|
renderTombstone(`${config.url}/notes/${cascadingNote.id}`),
|
||||||
|
@ -104,8 +138,14 @@ export default async function (
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
if (Users.isRemoteUser(user)) {
|
if (Users.isRemoteUser(user)) {
|
||||||
registerOrFetchInstanceDoc(user.host).then((i) => {
|
instanceNotesCountDecreasement[user.host] ??= 0;
|
||||||
Instances.decrement({ id: i.id }, "notesCount", 1);
|
instanceNotesCountDecreasement[user.host]++;
|
||||||
|
}
|
||||||
|
for (const [host, count] of Object.entries(
|
||||||
|
instanceNotesCountDecreasement,
|
||||||
|
)) {
|
||||||
|
registerOrFetchInstanceDoc(host).then((i) => {
|
||||||
|
Instances.decrement({ id: i.id }, "notesCount", count);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,13 +155,20 @@ export default async function (
|
||||||
id: note.id,
|
id: note.id,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Handle cascading deletion (it's not as simple as notesCount -= 1)
|
||||||
|
await Promise.all(
|
||||||
|
Object.values(affectedLocalUsers).map((user) =>
|
||||||
|
recalculateNotesCountOfLocalUser(user),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findCascadingNotes(note: Note) {
|
async function findCascadingNotes(note: Note) {
|
||||||
const cascadingNotes: Note[] = [];
|
const cascadingNotes: Note[] = [];
|
||||||
|
|
||||||
const recursive = async (noteId: string) => {
|
const findRepliesAndQuotes = async (noteId: string) => {
|
||||||
const query = Notes.createQueryBuilder("note")
|
const query = Notes.createQueryBuilder("note")
|
||||||
.where("note.replyId = :noteId", { noteId })
|
.where("note.replyId = :noteId", { noteId })
|
||||||
.orWhere(
|
.orWhere(
|
||||||
|
@ -132,15 +179,18 @@ async function findCascadingNotes(note: Note) {
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.leftJoinAndSelect("note.user", "user");
|
.leftJoinAndSelect("note.user", "user");
|
||||||
const replies = await query.getMany();
|
const repliesAndQuotes = await query.getMany();
|
||||||
for (const reply of replies) {
|
|
||||||
cascadingNotes.push(reply);
|
|
||||||
await recursive(reply.id);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
await recursive(note.id);
|
|
||||||
|
|
||||||
return cascadingNotes.filter((note) => note.userHost === null); // filter out non-local users
|
await Promise.all(
|
||||||
|
repliesAndQuotes.map((n: Note) => {
|
||||||
|
cascadingNotes.push(n);
|
||||||
|
return findRepliesAndQuotes(n.id);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
await findRepliesAndQuotes(note.id);
|
||||||
|
|
||||||
|
return cascadingNotes;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getMentionedRemoteUsers(note: Note) {
|
async function getMentionedRemoteUsers(note: Note) {
|
||||||
|
@ -171,7 +221,7 @@ async function getMentionedRemoteUsers(note: Note) {
|
||||||
async function deliverToConcerned(
|
async function deliverToConcerned(
|
||||||
user: { id: ILocalUser["id"]; host: null },
|
user: { id: ILocalUser["id"]; host: null },
|
||||||
note: Note,
|
note: Note,
|
||||||
content: any,
|
content: IActivity | null,
|
||||||
) {
|
) {
|
||||||
deliverToFollowers(user, content);
|
deliverToFollowers(user, content);
|
||||||
deliverToRelays(user, content);
|
deliverToRelays(user, content);
|
||||||
|
|
Loading…
Reference in a new issue