summaryrefslogtreecommitdiff
path: root/packages/backend/src/server/api
diff options
context:
space:
mode:
authordakkar <dakkar@thenautilus.net>2024-10-07 17:15:33 +0000
committerdakkar <dakkar@thenautilus.net>2024-10-07 17:15:33 +0000
commit32a97a5a057941d88868b94b01b320e61b8d0e15 (patch)
treea12a4a05b616459bfad4f385759898a70c01261d /packages/backend/src/server/api
parentmerge: Add controls to delete all files or sever all relations with a remote ... (diff)
parentupdate autogen types (diff)
downloadsharkey-32a97a5a057941d88868b94b01b320e61b8d0e15.tar.gz
sharkey-32a97a5a057941d88868b94b01b320e61b8d0e15.tar.bz2
sharkey-32a97a5a057941d88868b94b01b320e61b8d0e15.zip
merge: Add feed of latest posts by followed users (!640)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/640 Approved-by: dakkar <dakkar@thenautilus.net> Approved-by: Marie <github@yuugi.dev>
Diffstat (limited to 'packages/backend/src/server/api')
-rw-r--r--packages/backend/src/server/api/EndpointsModule.ts4
-rw-r--r--packages/backend/src/server/api/endpoints.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/notes/following.ts95
3 files changed, 101 insertions, 0 deletions
diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts
index 4a08410ceb..c90a23d7b5 100644
--- a/packages/backend/src/server/api/EndpointsModule.ts
+++ b/packages/backend/src/server/api/EndpointsModule.ts
@@ -290,6 +290,7 @@ import * as ep___notes_delete from './endpoints/notes/delete.js';
import * as ep___notes_favorites_create from './endpoints/notes/favorites/create.js';
import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete.js';
import * as ep___notes_featured from './endpoints/notes/featured.js';
+import * as ep___notes_following from './endpoints/notes/following.js';
import * as ep___notes_globalTimeline from './endpoints/notes/global-timeline.js';
import * as ep___notes_bubbleTimeline from './endpoints/notes/bubble-timeline.js';
import * as ep___notes_hybridTimeline from './endpoints/notes/hybrid-timeline.js';
@@ -686,6 +687,7 @@ const $notes_delete: Provider = { provide: 'ep:notes/delete', useClass: ep___not
const $notes_favorites_create: Provider = { provide: 'ep:notes/favorites/create', useClass: ep___notes_favorites_create.default };
const $notes_favorites_delete: Provider = { provide: 'ep:notes/favorites/delete', useClass: ep___notes_favorites_delete.default };
const $notes_featured: Provider = { provide: 'ep:notes/featured', useClass: ep___notes_featured.default };
+const $notes_following: Provider = { provide: 'ep:notes/following', useClass: ep___notes_following.default };
const $notes_globalTimeline: Provider = { provide: 'ep:notes/global-timeline', useClass: ep___notes_globalTimeline.default };
const $notes_bubbleTimeline: Provider = { provide: 'ep:notes/bubble-timeline', useClass: ep___notes_bubbleTimeline.default };
const $notes_hybridTimeline: Provider = { provide: 'ep:notes/hybrid-timeline', useClass: ep___notes_hybridTimeline.default };
@@ -1086,6 +1088,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
$notes_favorites_create,
$notes_favorites_delete,
$notes_featured,
+ $notes_following,
$notes_globalTimeline,
$notes_bubbleTimeline,
$notes_hybridTimeline,
@@ -1480,6 +1483,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
$notes_favorites_create,
$notes_favorites_delete,
$notes_featured,
+ $notes_following,
$notes_globalTimeline,
$notes_bubbleTimeline,
$notes_hybridTimeline,
diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts
index e2fcd1a9d0..e93e57f907 100644
--- a/packages/backend/src/server/api/endpoints.ts
+++ b/packages/backend/src/server/api/endpoints.ts
@@ -296,6 +296,7 @@ import * as ep___notes_delete from './endpoints/notes/delete.js';
import * as ep___notes_favorites_create from './endpoints/notes/favorites/create.js';
import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete.js';
import * as ep___notes_featured from './endpoints/notes/featured.js';
+import * as ep___notes_following from './endpoints/notes/following.js';
import * as ep___notes_globalTimeline from './endpoints/notes/global-timeline.js';
import * as ep___notes_bubbleTimeline from './endpoints/notes/bubble-timeline.js';
import * as ep___notes_hybridTimeline from './endpoints/notes/hybrid-timeline.js';
@@ -690,6 +691,7 @@ const eps = [
['notes/favorites/create', ep___notes_favorites_create],
['notes/favorites/delete', ep___notes_favorites_delete],
['notes/featured', ep___notes_featured],
+ ['notes/following', ep___notes_following],
['notes/global-timeline', ep___notes_globalTimeline],
['notes/bubble-timeline', ep___notes_bubbleTimeline],
['notes/hybrid-timeline', ep___notes_hybridTimeline],
diff --git a/packages/backend/src/server/api/endpoints/notes/following.ts b/packages/backend/src/server/api/endpoints/notes/following.ts
new file mode 100644
index 0000000000..436160f250
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/notes/following.ts
@@ -0,0 +1,95 @@
+/*
+ * SPDX-FileCopyrightText: hazelnoot and other Sharkey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import { LatestNote, MiFollowing } from '@/models/_.js';
+import type { NotesRepository } from '@/models/_.js';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
+import { DI } from '@/di-symbols.js';
+import { QueryService } from '@/core/QueryService.js';
+
+export const meta = {
+ tags: ['notes'],
+
+ requireCredential: true,
+ kind: 'read:account',
+ allowGet: true,
+
+ res: {
+ type: 'array',
+ optional: false, nullable: false,
+ items: {
+ type: 'object',
+ optional: false, nullable: false,
+ ref: 'Note',
+ },
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ mutualsOnly: { type: 'boolean', default: false },
+ limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
+ sinceId: { type: 'string', format: 'misskey:id' },
+ untilId: { type: 'string', format: 'misskey:id' },
+ sinceDate: { type: 'integer' },
+ untilDate: { type: 'integer' },
+ },
+ required: [],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // 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) => {
+ let query = this.notesRepository
+ .createQueryBuilder('note')
+ .setParameter('me', me.id)
+
+ // Limit to latest notes
+ .innerJoin(LatestNote, 'latest', 'note.id = latest.note_id')
+
+ // Avoid N+1 queries from the "pack" method
+ .innerJoinAndSelect('note.user', 'user')
+ .leftJoinAndSelect('note.reply', 'reply')
+ .leftJoinAndSelect('note.renote', 'renote')
+ .leftJoinAndSelect('reply.user', 'replyUser')
+ .leftJoinAndSelect('renote.user', 'renoteUser')
+ .leftJoinAndSelect('note.channel', 'channel')
+
+ // Limit to followers
+ .innerJoin(MiFollowing, 'following', 'latest.user_id = following."followeeId"')
+ .andWhere('following."followerId" = :me');
+
+ // Limit to mutuals, if requested
+ if (ps.mutualsOnly) {
+ query = query
+ .innerJoin(MiFollowing, 'mutuals', 'latest.user_id = mutuals."followerId" AND mutuals."followeeId" = :me');
+ }
+
+ // Respect blocks and mutes
+ this.queryService.generateBlockedUserQuery(query, me);
+ this.queryService.generateMutedUserQuery(query, me);
+
+ // Support pagination
+ query = this.queryService
+ .makePaginationQuery(query, ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
+ .orderBy('note.id', 'DESC')
+ .take(ps.limit);
+
+ // Query and return the next page
+ const notes = await query.getMany();
+ return await this.noteEntityService.packMany(notes, me);
+ });
+ }
+}