diff --git a/packages/backend/migration/1659446758000-fix-thread-id.js b/packages/backend/migration/1659446758000-fix-thread-id.js new file mode 100644 index 000000000..edd5712a2 --- /dev/null +++ b/packages/backend/migration/1659446758000-fix-thread-id.js @@ -0,0 +1,30 @@ +export class fixThreadId1659446758000 { + name = 'fixThreadId1659446758000' + + async up(queryRunner) { + await Promise.all([ + queryRunner.query(`UPDATE "note" SET "threadId" = "id" WHERE "replyId" IS NULL`), + queryRunner.query(`WITH "threads" ("noteId", "thread") AS ( + SELECT "id" as "noteId", "parent" as "thread" FROM ( + WITH RECURSIVE "parents" ("id", "parent", "height") AS ( + SELECT "id", "replyId", 0 FROM "note" WHERE "replyId" IS NOT NULL AND "threadId" IS NULL + UNION ALL + SELECT "parents"."id", "note"."replyId", "parents"."height" + 1 + FROM "parents" + JOIN "note" ON "parents"."parent" = "note"."id" + WHERE "note"."replyId" IS NOT NULL + ) SELECT *, MAX("height") OVER (PARTITION BY "id") AS "maxheight" FROM "parents" + ) AS "x" + WHERE "height" = "maxheight" + ) UPDATE "note" SET "threadId" = "threads"."thread" FROM "threads" WHERE "id" = "threads"."noteId"`), + ]); + await queryRunner.query(`ALTER TABLE "note" ALTER COLUMN "threadId" SET NOT NULL`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "note" ALTER COLUMN "threaadId" DROP NOT NULL`); + // Cannot un-fix thread id's for ones that just were not migrated (2nd query above) + // but can remove the thread ids for the root notes of each thread. + await queryRunner.query(`UPDATE "note" SET "threadId" = NULL WHERE "replyId" IS NULL`); + } +} diff --git a/packages/backend/src/models/entities/note.ts b/packages/backend/src/models/entities/note.ts index 0e15921b7..0181ed772 100644 --- a/packages/backend/src/models/entities/note.ts +++ b/packages/backend/src/models/entities/note.ts @@ -51,7 +51,7 @@ export class Note { @Column('varchar', { length: 256, nullable: true, }) - public threadId: string | null; + public threadId: string; @Column('text', { nullable: true, diff --git a/packages/backend/src/server/api/common/generate-muted-note-thread-query.ts b/packages/backend/src/server/api/common/generate-muted-note-thread-query.ts index 681ec4eb2..153893032 100644 --- a/packages/backend/src/server/api/common/generate-muted-note-thread-query.ts +++ b/packages/backend/src/server/api/common/generate-muted-note-thread-query.ts @@ -7,11 +7,7 @@ export function generateMutedNoteThreadQuery(q: SelectQueryBuilder, me: { i .select('threadMuted.threadId') .where('threadMuted.userId = :userId', { userId: me.id }); - q.andWhere(`note.id NOT IN (${ mutedQuery.getQuery() })`); - q.andWhere(new Brackets(qb => { qb - .where('note.threadId IS NULL') - .orWhere(`note.threadId NOT IN (${ mutedQuery.getQuery() })`); - })); + q.andWhere(`note.threadId NOT IN (${ mutedQuery.getQuery() })`); q.setParameters(mutedQuery.getParameters()); } diff --git a/packages/backend/src/server/api/endpoints/notes/state.ts b/packages/backend/src/server/api/endpoints/notes/state.ts index 67579b2a6..87b56c349 100644 --- a/packages/backend/src/server/api/endpoints/notes/state.ts +++ b/packages/backend/src/server/api/endpoints/notes/state.ts @@ -57,7 +57,7 @@ export default define(meta, paramDef, async (ps, user) => { NoteThreadMutings.count({ where: { userId: user.id, - threadId: note.threadId || note.id, + threadId: note.threadId, }, take: 1, }), diff --git a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts index dddb55fcb..9da5f4d90 100644 --- a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts @@ -41,9 +41,7 @@ export default define(meta, paramDef, async (ps, user) => { const mutedNotes = await Notes.find({ where: [{ - id: note.threadId || note.id, - }, { - threadId: note.threadId || note.id, + threadId: note.threadId, }], }); @@ -52,7 +50,7 @@ export default define(meta, paramDef, async (ps, user) => { await NoteThreadMutings.insert({ id: genId(), createdAt: new Date(), - threadId: note.threadId || note.id, + threadId: note.threadId, userId: user.id, mutingNotificationTypes: ps.mutingNotificationTypes, }); diff --git a/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts index c1ea564eb..1a9b803e7 100644 --- a/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts @@ -29,7 +29,7 @@ export default define(meta, paramDef, async (ps, user) => { }); await NoteThreadMutings.delete({ - threadId: note.threadId || note.id, + threadId: note.threadId, userId: user.id, }); }); diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts index 2cae03241..41ef2a053 100644 --- a/packages/backend/src/services/note/create.ts +++ b/packages/backend/src/services/note/create.ts @@ -369,7 +369,7 @@ export default async (user: { id: User['id']; username: User['username']; host: if (data.reply.userHost === null) { const threadMuted = await NoteThreadMutings.findOneBy({ userId: data.reply.userId, - threadId: data.reply.threadId || data.reply.id, + threadId: data.reply.threadId, }); if (!threadMuted) { @@ -500,15 +500,16 @@ function incRenoteCount(renote: Note): void { async function insertNote(user: { id: User['id']; host: User['host']; }, data: Option, tags: string[], emojis: string[], mentionedUsers: MinimumUser[]): Promise { const createdAt = data.createdAt ?? new Date(); + const id = genId(createdAt); const insert = new Note({ - id: genId(createdAt), + id, createdAt, - fileIds: data.files?.map(file => file.id) ?? [], - replyId: data.reply?.id ?? null, - renoteId: data.renote?.id ?? null, - channelId: data.channel?.id ?? null, - threadId: data.reply?.threadId ?? data.reply?.id ?? null, + fileIds: data.files ? data.files.map(file => file.id) : [], + replyId: data.reply ? data.reply.id : null, + renoteId: data.renote ? data.renote.id : null, + channelId: data.channel ? data.channel.id : null, + threadId: data.reply?.threadId ?? id, name: data.name, text: data.text, hasPoll: data.poll != null, @@ -616,7 +617,7 @@ async function createMentionedEvents(mentionedUsers: MinimumUser[], note: Note, for (const u of mentionedUsers.filter(u => Users.isLocalUser(u))) { const threadMuted = await NoteThreadMutings.findOneBy({ userId: u.id, - threadId: note.threadId || note.id, + threadId: note.threadId, }); if (threadMuted) { diff --git a/packages/backend/src/services/note/unread.ts b/packages/backend/src/services/note/unread.ts index 829a024ee..12c83e8a5 100644 --- a/packages/backend/src/services/note/unread.ts +++ b/packages/backend/src/services/note/unread.ts @@ -20,7 +20,7 @@ export async function insertNoteUnread(userId: User['id'], note: Note, params: { // スレッドミュート const threadMute = await NoteThreadMutings.findOneBy({ userId, - threadId: note.threadId || note.id, + threadId: note.threadId, }); if (threadMute) return;