diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index b7d4fb04ec..73e55c1fe5 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -667,7 +667,7 @@ export class ApRendererService { * @param orderedItems attached objects (optional) */ @bindThis - public renderOrderedCollection(id: string, totalItems: number, first?: string, last?: string, orderedItems?: IObject[]) { + public renderOrderedCollection(id: string, totalItems?: number, first?: string, last?: string, orderedItems?: IObject[]) { const page: any = { id, type: 'OrderedCollection', diff --git a/packages/backend/src/core/activitypub/type.ts b/packages/backend/src/core/activitypub/type.ts index 57b0a86f86..c018e7181e 100644 --- a/packages/backend/src/core/activitypub/type.ts +++ b/packages/backend/src/core/activitypub/type.ts @@ -96,14 +96,14 @@ export interface IActivity extends IObject { export interface ICollection extends IObject { type: 'Collection'; - totalItems: number; + totalItems?: number; first?: IObject | string; items?: ApObject; } export interface IOrderedCollection extends IObject { type: 'OrderedCollection'; - totalItems: number; + totalItems?: number; first?: IObject | string; orderedItems?: ApObject; } diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index 65f54d064e..b53fba2661 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -386,25 +386,22 @@ export class ActivityPubServerService { const limit = 10; const partOf = `${this.config.url}/users/${userId}/liked`; - const query = this.noteReactionsRepository.createQueryBuilder('reaction') - .andWhere('reaction.userId = :userId', { userId: user.id }); if (page) { - const countPromise = query.getCount(); + const query = this.noteReactionsRepository.createQueryBuilder('reaction') + .andWhere('reaction.userId = :userId', { userId: user.id }); // カーソルが指定されている場合 if (cursor) { query.andWhere('reaction.id < :id', { id: cursor }); } - const [reactions, reactionsCount] = await Promise.all([ - query - .limit(limit + 1) - .orderBy('reaction.id', 'DESC') - .innerJoinAndSelect('reaction.note', 'note') - .getMany(), - countPromise, - ]); + const reactions = await query + .limit(limit + 1) + .orderBy('reaction.id', 'DESC') + .innerJoinAndSelect('reaction.note', 'note') + .distinctOn(['note.id']) + .getMany(); // 「次のページ」があるかどうか const inStock = reactions.length === limit + 1; @@ -416,7 +413,7 @@ export class ActivityPubServerService { page: 'true', cursor, })}`, - reactionsCount, reactedNotes, partOf, + undefined, reactedNotes, partOf, undefined, inStock ? `${partOf}?${url.query({ page: 'true', @@ -428,10 +425,9 @@ export class ActivityPubServerService { return (this.apRendererService.addContext(rendered)); } else { // index page - const reactionsCount = await query.getCount(); const rendered = this.apRendererService.renderOrderedCollection( partOf, - reactionsCount, + undefined, `${partOf}?page=true`, ); reply.header('Cache-Control', 'public, max-age=180');