diff --git a/biome.json b/biome.json index 21b711f457..487165266a 100644 --- a/biome.json +++ b/biome.json @@ -14,7 +14,7 @@ }, "overrides": [ { - "include": ["*.vue"], + "include": ["*.vue", "packages/client/*.ts"], "linter": { "rules": { "style": { diff --git a/docs/api-change.md b/docs/api-change.md index dcd4329a27..89cf8a9d38 100644 --- a/docs/api-change.md +++ b/docs/api-change.md @@ -5,6 +5,13 @@ Breaking changes are indicated by the :warning: icon. ## Unreleased - Added `antennaLimit` field to the response of `meta` and `admin/meta`, and the request of `admin/update-meta` (optional). +- Added `filter` optional parameter to `notes/renotes` endpoint to filter the types of renotes. It can take the following values: + - `all` (default) + - `renote` + - `quote` +- :warning: Removed the following optional parameters in `notes/reactions`, as they were never taken into account due to a bug: + - `sinceId` + - `untilId` ## v20240413 diff --git a/packages/backend/src/models/repositories/note.ts b/packages/backend/src/models/repositories/note.ts index c877048709..7fa26373b8 100644 --- a/packages/backend/src/models/repositories/note.ts +++ b/packages/backend/src/models/repositories/note.ts @@ -1,4 +1,4 @@ -import { In } from "typeorm"; +import { In, IsNull, Not } from "typeorm"; import * as mfm from "mfm-js"; import { Note } from "@/models/entities/note.js"; import type { User } from "@/models/entities/user.js"; @@ -10,6 +10,7 @@ import { Followings, Polls, Channels, + Notes, } from "../index.js"; import type { Packed } from "@/misc/schema.js"; import { countReactions, decodeReaction, nyaify } from "backend-rs"; @@ -101,7 +102,7 @@ export const NoteRepository = db.getRepository(Note).extend({ return true; } else { // 指定されているかどうか - return note.visibleUserIds.some((id: any) => meId === id); + return note.visibleUserIds.some((id) => meId === id); } } @@ -211,8 +212,25 @@ export const NoteRepository = db.getRepository(Note).extend({ localOnly: note.localOnly || undefined, visibleUserIds: note.visibility === "specified" ? note.visibleUserIds : undefined, + // FIXME: Deleting a post does not decrease these two numbers, causing the number to be wrong renoteCount: note.renoteCount, repliesCount: note.repliesCount, + // TODO: add it to database and use note.quoteCount + quoteCount: Notes.count({ + where: { + renoteId: note.id, + text: Not(IsNull()), + }, + }), + myRenoteCount: me + ? Notes.count({ + where: { + renoteId: note.id, + text: IsNull(), + userId: me.id, + }, + }) + : undefined, reactions: countReactions(note.reactions), reactionEmojis: reactionEmoji, emojis: noteEmoji, diff --git a/packages/backend/src/models/schema/note.ts b/packages/backend/src/models/schema/note.ts index fff872b69f..6064919960 100644 --- a/packages/backend/src/models/schema/note.ts +++ b/packages/backend/src/models/schema/note.ts @@ -208,5 +208,15 @@ export const packedNoteSchema = { optional: true, nullable: true, }, + myRenoteCount: { + type: "number", + optional: true, + nullable: false, + }, + quoteCount: { + type: "number", + optional: false, + nullable: false, + }, }, } as const; diff --git a/packages/backend/src/server/api/common/make-pagination-query.ts b/packages/backend/src/server/api/common/make-pagination-query.ts index a2c3275693..83827b3df1 100644 --- a/packages/backend/src/server/api/common/make-pagination-query.ts +++ b/packages/backend/src/server/api/common/make-pagination-query.ts @@ -1,6 +1,6 @@ -import type { SelectQueryBuilder } from "typeorm"; +import type { ObjectLiteral, SelectQueryBuilder } from "typeorm"; -export function makePaginationQuery( +export function makePaginationQuery( q: SelectQueryBuilder, sinceId?: string, untilId?: string, diff --git a/packages/backend/src/server/api/endpoints/notes/reactions.ts b/packages/backend/src/server/api/endpoints/notes/reactions.ts index 386a3a08df..d6ff8888af 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions.ts @@ -42,8 +42,6 @@ export const paramDef = { type: { type: "string", nullable: true }, limit: { type: "integer", minimum: 1, maximum: 100, default: 10 }, offset: { type: "integer", default: 0 }, - sinceId: { type: "string", format: "misskey:id" }, - untilId: { type: "string", format: "misskey:id" }, }, required: ["noteId"], } as const; diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts index 683004ebe8..16304dd269 100644 --- a/packages/backend/src/server/api/endpoints/notes/renotes.ts +++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts @@ -42,6 +42,12 @@ export const paramDef = { limit: { type: "integer", minimum: 1, maximum: 100, default: 10 }, sinceId: { type: "string", format: "misskey:id" }, untilId: { type: "string", format: "misskey:id" }, + filter: { + type: "string", + enum: ["all", "renote", "quote"], + nullable: true, + default: null, + }, }, required: ["noteId"], } as const; @@ -53,7 +59,7 @@ export default define(meta, paramDef, async (ps, user) => { throw err; }); - let query = makePaginationQuery( + const query = makePaginationQuery( Notes.createQueryBuilder("note"), ps.sinceId, ps.untilId, @@ -61,6 +67,16 @@ export default define(meta, paramDef, async (ps, user) => { .andWhere("note.renoteId = :renoteId", { renoteId: note.id }) .innerJoinAndSelect("note.user", "user"); + // "all" doesn't filter out anything, it's just there for + // those who prefer to set the parameter explicitly + + if (ps.filter === "renote") { + query.andWhere("note.text IS NULL"); + } + if (ps.filter === "quote") { + query.andWhere("note.text IS NOT NULL"); + } + if (ps.userId) { query.andWhere("user.id = :userId", { userId: ps.userId }); } diff --git a/packages/client/src/components/MkNoteDetailed.vue b/packages/client/src/components/MkNoteDetailed.vue index b6c0d784ba..2c242645b4 100644 --- a/packages/client/src/components/MkNoteDetailed.vue +++ b/packages/client/src/components/MkNoteDetailed.vue @@ -33,7 +33,7 @@ @contextmenu.stop="onContextmenu" > - + - - - + + + - - + + + - - - - + :pagination="renotePagination" + > + +