From 8b92972e9102fca873c12888e15930268fd22190 Mon Sep 17 00:00:00 2001 From: GrapeApple0 <84321396+GrapeApple0@users.noreply.github.com> Date: Fri, 3 Nov 2023 12:50:48 +0000 Subject: [PATCH] =?UTF-8?q?=E5=BC=95=E7=94=A8=E4=B8=80=E8=A6=A7=E3=82=92?= =?UTF-8?q?=E8=AA=AD=E3=81=BF=E8=BE=BC=E3=82=80=E3=81=A8=E3=81=8D=E3=81=AB?= =?UTF-8?q?notes/quotes=E3=82=92=E4=BD=BF=E3=81=86=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/src/server/api/EndpointsModule.ts | 4 + packages/backend/src/server/api/endpoints.ts | 2 + .../src/server/api/endpoints/notes/quotes.ts | 80 +++++++++++++++++++ .../src/components/MkNoteDetailed.vue | 33 ++++---- packages/misskey-js/etc/misskey-js.api.md | 9 +++ packages/misskey-js/src/api.types.ts | 1 + 6 files changed, 111 insertions(+), 18 deletions(-) create mode 100644 packages/backend/src/server/api/endpoints/notes/quotes.ts diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 23067a9b26..0a394728fb 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -273,6 +273,7 @@ import * as ep___notes_localTimeline from './endpoints/notes/local-timeline.js'; import * as ep___notes_mentions from './endpoints/notes/mentions.js'; import * as ep___notes_polls_recommendation from './endpoints/notes/polls/recommendation.js'; import * as ep___notes_polls_vote from './endpoints/notes/polls/vote.js'; +import * as ep___notes_quotes from './endpoints/notes/quotes.js'; import * as ep___notes_reactions from './endpoints/notes/reactions.js'; import * as ep___notes_reactions_create from './endpoints/notes/reactions/create.js'; import * as ep___notes_reactions_delete from './endpoints/notes/reactions/delete.js'; @@ -631,6 +632,7 @@ const $notes_localTimeline: Provider = { provide: 'ep:notes/local-timeline', use const $notes_mentions: Provider = { provide: 'ep:notes/mentions', useClass: ep___notes_mentions.default }; const $notes_polls_recommendation: Provider = { provide: 'ep:notes/polls/recommendation', useClass: ep___notes_polls_recommendation.default }; const $notes_polls_vote: Provider = { provide: 'ep:notes/polls/vote', useClass: ep___notes_polls_vote.default }; +const $notes_quotes: Provider = { provide: 'ep:notes/quotes', useClass: ep___notes_quotes.default }; const $notes_reactions: Provider = { provide: 'ep:notes/reactions', useClass: ep___notes_reactions.default }; const $notes_reactions_create: Provider = { provide: 'ep:notes/reactions/create', useClass: ep___notes_reactions_create.default }; const $notes_reactions_delete: Provider = { provide: 'ep:notes/reactions/delete', useClass: ep___notes_reactions_delete.default }; @@ -993,6 +995,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $notes_mentions, $notes_polls_recommendation, $notes_polls_vote, + $notes_quotes, $notes_reactions, $notes_reactions_create, $notes_reactions_delete, @@ -1349,6 +1352,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $notes_mentions, $notes_polls_recommendation, $notes_polls_vote, + $notes_quotes, $notes_reactions, $notes_reactions_create, $notes_reactions_delete, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index af798fd166..4bee1c183c 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -273,6 +273,7 @@ import * as ep___notes_localTimeline from './endpoints/notes/local-timeline.js'; import * as ep___notes_mentions from './endpoints/notes/mentions.js'; import * as ep___notes_polls_recommendation from './endpoints/notes/polls/recommendation.js'; import * as ep___notes_polls_vote from './endpoints/notes/polls/vote.js'; +import * as ep___notes_quotes from './endpoints/notes/quotes.js'; import * as ep___notes_reactions from './endpoints/notes/reactions.js'; import * as ep___notes_reactions_create from './endpoints/notes/reactions/create.js'; import * as ep___notes_reactions_delete from './endpoints/notes/reactions/delete.js'; @@ -629,6 +630,7 @@ const eps = [ ['notes/mentions', ep___notes_mentions], ['notes/polls/recommendation', ep___notes_polls_recommendation], ['notes/polls/vote', ep___notes_polls_vote], + ['notes/quotes', ep___notes_quotes], ['notes/reactions', ep___notes_reactions], ['notes/reactions/create', ep___notes_reactions_create], ['notes/reactions/delete', ep___notes_reactions_delete], diff --git a/packages/backend/src/server/api/endpoints/notes/quotes.ts b/packages/backend/src/server/api/endpoints/notes/quotes.ts new file mode 100644 index 0000000000..2cf8b6b922 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/notes/quotes.ts @@ -0,0 +1,80 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Brackets } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; +import type { NotesRepository } from '@/models/_.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { QueryService } from '@/core/QueryService.js'; +import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import { DI } from '@/di-symbols.js'; + +export const meta = { + tags: ['notes'], + + requireCredential: false, + + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + ref: 'Note', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + }, + required: ['noteId'], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + + private noteEntityService: NoteEntityService, + private queryService: QueryService, + ) { + super(meta, paramDef, async (ps, me) => { + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) + .andWhere(new Brackets(qb => { + qb + .where('note.renoteId = :noteId', { noteId: ps.noteId }) + .andWhere(new Brackets(qb => { + qb + .where('note.text IS NOT NULL') + .orWhere('note.fileIds != \'{}\'') + .orWhere('note.hasPoll = TRUE') + .orWhere('note.replyId IS NULL'); + })); + })) + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); + + this.queryService.generateVisibilityQuery(query, me); + if (me) { + this.queryService.generateMutedUserQuery(query, me); + this.queryService.generateBlockedUserQuery(query, me); + } + + const notes = await query.limit(ps.limit).getMany(); + + return await this.noteEntityService.packMany(notes, me); + }); + } +} diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index e9ca2066de..02881643cd 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -133,7 +133,7 @@ SPDX-License-Identifier: AGPL-3.0-only
- +
@@ -154,11 +154,12 @@ SPDX-License-Identifier: AGPL-3.0-only
-
-
- {{ i18n.ts.showMore }} -
- +
+ + +
@@ -304,6 +305,14 @@ const reactionsPagination = $computed(() => ({ }, })); +const quotesPagination = $computed(() => ({ + endpoint: 'notes/quotes', + limit: 10, + params: { + noteId: appearNote.id, + }, +})); + useNoteCapture({ rootEl: el, note: $$(appearNote), @@ -458,18 +467,6 @@ function loadReplies() { }); } -const quotesLoaded = ref(false); - -function loadQuotes() { - quotesLoaded.value = true; - os.api('notes/children', { - noteId: appearNote.id, - limit: 30, - }).then(res => { - quotes.value = res.filter(item => item.renoteId != null); - }); -} - const conversationLoaded = ref(false); function loadConversation() { diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 87922ba791..cad6232c47 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -1825,6 +1825,15 @@ export type Endpoints = { }; res: null; }; + 'notes/quotes': { + req: { + noteId: Note['id']; + limit?: number; + sinceId?: Note['id']; + untilId?: Note['id']; + }; + res: Note[]; + } 'notes/reactions': { req: { noteId: Note['id']; diff --git a/packages/misskey-js/src/api.types.ts b/packages/misskey-js/src/api.types.ts index 54b175fcf1..3857fb2cee 100644 --- a/packages/misskey-js/src/api.types.ts +++ b/packages/misskey-js/src/api.types.ts @@ -518,6 +518,7 @@ export type Endpoints = { 'notes/mentions': { req: { following?: boolean; limit?: number; sinceId?: Note['id']; untilId?: Note['id']; }; res: Note[]; }; 'notes/polls/recommendation': { req: TODO; res: TODO; }; 'notes/polls/vote': { req: { noteId: Note['id']; choice: number; }; res: null; }; + 'notes/quotes': { req: { noteId: Note['id']; limit?: number; sinceId?: Note['id']; untilId?: Note['id']; }; res: Note[]; }; 'notes/reactions': { req: { noteId: Note['id']; type?: string | null; limit?: number; }; res: NoteReaction[]; }; 'notes/reactions/create': { req: { noteId: Note['id']; reaction: string; }; res: null; }; 'notes/reactions/delete': { req: { noteId: Note['id']; }; res: null; };