summaryrefslogtreecommitdiff
path: root/packages/backend/src/server/api
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2023-03-16 17:24:49 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2023-03-16 17:24:49 +0900
commitb644567735443ae203f78dbdbe1963c252ceb1ad (patch)
tree23a08ea860a7e856e419082cb0101bab45d54a34 /packages/backend/src/server/api
parentenhance: カスタム絵文字にライセンス情報を付与できるよ... (diff)
downloadsharkey-b644567735443ae203f78dbdbe1963c252ceb1ad.tar.gz
sharkey-b644567735443ae203f78dbdbe1963c252ceb1ad.tar.bz2
sharkey-b644567735443ae203f78dbdbe1963c252ceb1ad.zip
feat: clip favorite
Resolve #10337
Diffstat (limited to 'packages/backend/src/server/api')
-rw-r--r--packages/backend/src/server/api/EndpointsModule.ts12
-rw-r--r--packages/backend/src/server/api/endpoints.ts6
-rw-r--r--packages/backend/src/server/api/endpoints/clips/add-note.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/clips/create.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/clips/favorite.ts76
-rw-r--r--packages/backend/src/server/api/endpoints/clips/list.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/clips/my-favorites.ts52
-rw-r--r--packages/backend/src/server/api/endpoints/clips/show.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/clips/unfavorite.ts65
-rw-r--r--packages/backend/src/server/api/endpoints/clips/update.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/notes/clips.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/users/clips.ts2
12 files changed, 222 insertions, 7 deletions
diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts
index 2724649590..76fb8f636b 100644
--- a/packages/backend/src/server/api/EndpointsModule.ts
+++ b/packages/backend/src/server/api/EndpointsModule.ts
@@ -114,6 +114,9 @@ import * as ep___clips_list from './endpoints/clips/list.js';
import * as ep___clips_notes from './endpoints/clips/notes.js';
import * as ep___clips_show from './endpoints/clips/show.js';
import * as ep___clips_update from './endpoints/clips/update.js';
+import * as ep___clips_favorite from './endpoints/clips/favorite.js';
+import * as ep___clips_unfavorite from './endpoints/clips/unfavorite.js';
+import * as ep___clips_myFavorites from './endpoints/clips/my-favorites.js';
import * as ep___drive from './endpoints/drive.js';
import * as ep___drive_files from './endpoints/drive/files.js';
import * as ep___drive_files_attachedNotes from './endpoints/drive/files/attached-notes.js';
@@ -438,6 +441,9 @@ const $clips_list: Provider = { provide: 'ep:clips/list', useClass: ep___clips_l
const $clips_notes: Provider = { provide: 'ep:clips/notes', useClass: ep___clips_notes.default };
const $clips_show: Provider = { provide: 'ep:clips/show', useClass: ep___clips_show.default };
const $clips_update: Provider = { provide: 'ep:clips/update', useClass: ep___clips_update.default };
+const $clips_favorite: Provider = { provide: 'ep:clips/favorite', useClass: ep___clips_favorite.default };
+const $clips_unfavorite: Provider = { provide: 'ep:clips/unfavorite', useClass: ep___clips_unfavorite.default };
+const $clips_myFavorites: Provider = { provide: 'ep:clips/my-favorites', useClass: ep___clips_myFavorites.default };
const $drive: Provider = { provide: 'ep:drive', useClass: ep___drive.default };
const $drive_files: Provider = { provide: 'ep:drive/files', useClass: ep___drive_files.default };
const $drive_files_attachedNotes: Provider = { provide: 'ep:drive/files/attached-notes', useClass: ep___drive_files_attachedNotes.default };
@@ -766,6 +772,9 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
$clips_notes,
$clips_show,
$clips_update,
+ $clips_favorite,
+ $clips_unfavorite,
+ $clips_myFavorites,
$drive,
$drive_files,
$drive_files_attachedNotes,
@@ -1088,6 +1097,9 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
$clips_notes,
$clips_show,
$clips_update,
+ $clips_favorite,
+ $clips_unfavorite,
+ $clips_myFavorites,
$drive,
$drive_files,
$drive_files_attachedNotes,
diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts
index 58f4fcc8a8..e928b0c2b1 100644
--- a/packages/backend/src/server/api/endpoints.ts
+++ b/packages/backend/src/server/api/endpoints.ts
@@ -114,6 +114,9 @@ import * as ep___clips_list from './endpoints/clips/list.js';
import * as ep___clips_notes from './endpoints/clips/notes.js';
import * as ep___clips_show from './endpoints/clips/show.js';
import * as ep___clips_update from './endpoints/clips/update.js';
+import * as ep___clips_favorite from './endpoints/clips/favorite.js';
+import * as ep___clips_unfavorite from './endpoints/clips/unfavorite.js';
+import * as ep___clips_myFavorites from './endpoints/clips/my-favorites.js';
import * as ep___drive from './endpoints/drive.js';
import * as ep___drive_files from './endpoints/drive/files.js';
import * as ep___drive_files_attachedNotes from './endpoints/drive/files/attached-notes.js';
@@ -436,6 +439,9 @@ const eps = [
['clips/notes', ep___clips_notes],
['clips/show', ep___clips_show],
['clips/update', ep___clips_update],
+ ['clips/favorite', ep___clips_favorite],
+ ['clips/unfavorite', ep___clips_unfavorite],
+ ['clips/my-favorites', ep___clips_myFavorites],
['drive', ep___drive],
['drive/files', ep___drive_files],
['drive/files/attached-notes', ep___drive_files_attachedNotes],
diff --git a/packages/backend/src/server/api/endpoints/clips/add-note.ts b/packages/backend/src/server/api/endpoints/clips/add-note.ts
index f3f9c3477f..b9d8dce47a 100644
--- a/packages/backend/src/server/api/endpoints/clips/add-note.ts
+++ b/packages/backend/src/server/api/endpoints/clips/add-note.ts
@@ -106,6 +106,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
noteId: note.id,
clipId: clip.id,
});
+
+ await this.clipsRepository.update(clip.id, {
+ lastClippedAt: new Date(),
+ });
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/clips/create.ts b/packages/backend/src/server/api/endpoints/clips/create.ts
index c095de702c..a770dc986d 100644
--- a/packages/backend/src/server/api/endpoints/clips/create.ts
+++ b/packages/backend/src/server/api/endpoints/clips/create.ts
@@ -67,7 +67,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
description: ps.description,
}).then(x => this.clipsRepository.findOneByOrFail(x.identifiers[0]));
- return await this.clipEntityService.pack(clip);
+ return await this.clipEntityService.pack(clip, me);
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/clips/favorite.ts b/packages/backend/src/server/api/endpoints/clips/favorite.ts
new file mode 100644
index 0000000000..6addf743a2
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/clips/favorite.ts
@@ -0,0 +1,76 @@
+import { Inject, Injectable } from '@nestjs/common';
+import type { ClipsRepository, ClipFavoritesRepository } from '@/models/index.js';
+import { IdService } from '@/core/IdService.js';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { DI } from '@/di-symbols.js';
+import { ApiError } from '../../error.js';
+
+export const meta = {
+ tags: ['clip'],
+
+ requireCredential: true,
+
+ kind: 'write:clip-favorite',
+
+ errors: {
+ noSuchClip: {
+ message: 'No such clip.',
+ code: 'NO_SUCH_CLIP',
+ id: '4c2aaeae-80d8-4250-9606-26cb1fdb77a5',
+ },
+
+ alreadyFavorited: {
+ message: 'The clip has already been favorited.',
+ code: 'ALREADY_FAVORITED',
+ id: '92658936-c625-4273-8326-2d790129256e',
+ },
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ clipId: { type: 'string', format: 'misskey:id' },
+ },
+ required: ['clipId'],
+} as const;
+
+// eslint-disable-next-line import/no-default-export
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> {
+ constructor(
+ @Inject(DI.clipsRepository)
+ private clipsRepository: ClipsRepository,
+
+ @Inject(DI.clipFavoritesRepository)
+ private clipFavoritesRepository: ClipFavoritesRepository,
+
+ private idService: IdService,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ const clip = await this.clipsRepository.findOneBy({ id: ps.clipId });
+ if (clip == null) {
+ throw new ApiError(meta.errors.noSuchClip);
+ }
+ if ((clip.userId !== me.id) && !clip.isPublic) {
+ throw new ApiError(meta.errors.noSuchClip);
+ }
+
+ const exist = await this.clipFavoritesRepository.findOneBy({
+ clipId: clip.id,
+ userId: me.id,
+ });
+
+ if (exist != null) {
+ throw new ApiError(meta.errors.alreadyFavorited);
+ }
+
+ await this.clipFavoritesRepository.insert({
+ id: this.idService.genId(),
+ createdAt: new Date(),
+ clipId: clip.id,
+ userId: me.id,
+ });
+ });
+ }
+}
diff --git a/packages/backend/src/server/api/endpoints/clips/list.ts b/packages/backend/src/server/api/endpoints/clips/list.ts
index 63ca069364..3b8deab709 100644
--- a/packages/backend/src/server/api/endpoints/clips/list.ts
+++ b/packages/backend/src/server/api/endpoints/clips/list.ts
@@ -42,7 +42,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
userId: me.id,
});
- return await Promise.all(clips.map(x => this.clipEntityService.pack(x)));
+ return await this.clipEntityService.packMany(clips, me);
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/clips/my-favorites.ts b/packages/backend/src/server/api/endpoints/clips/my-favorites.ts
new file mode 100644
index 0000000000..fc727e93bd
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/clips/my-favorites.ts
@@ -0,0 +1,52 @@
+import { Inject, Injectable } from '@nestjs/common';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import type { ClipFavoritesRepository } from '@/models/index.js';
+import { DI } from '@/di-symbols.js';
+import { ClipEntityService } from '@/core/entities/ClipEntityService.js';
+
+export const meta = {
+ tags: ['account', 'clip'],
+
+ requireCredential: true,
+
+ kind: 'read:clip-favorite',
+
+ res: {
+ type: 'array',
+ optional: false, nullable: false,
+ items: {
+ type: 'object',
+ optional: false, nullable: false,
+ ref: 'Clip',
+ },
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ },
+ required: [],
+} as const;
+
+// eslint-disable-next-line import/no-default-export
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> {
+ constructor(
+ @Inject(DI.clipFavoritesRepository)
+ private clipFavoritesRepository: ClipFavoritesRepository,
+
+ private clipEntityService: ClipEntityService,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ const query = this.clipFavoritesRepository.createQueryBuilder('favorite')
+ .andWhere('favorite.userId = :meId', { meId: me.id })
+ .leftJoinAndSelect('favorite.clip', 'clip');
+
+ const favorites = await query
+ .getMany();
+
+ return this.clipEntityService.packMany(favorites.map(x => x.clip!), me);
+ });
+ }
+}
diff --git a/packages/backend/src/server/api/endpoints/clips/show.ts b/packages/backend/src/server/api/endpoints/clips/show.ts
index e6d3f4f1f8..99d630a9b5 100644
--- a/packages/backend/src/server/api/endpoints/clips/show.ts
+++ b/packages/backend/src/server/api/endpoints/clips/show.ts
@@ -58,7 +58,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
throw new ApiError(meta.errors.noSuchClip);
}
- return await this.clipEntityService.pack(clip);
+ return await this.clipEntityService.pack(clip, me);
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/clips/unfavorite.ts b/packages/backend/src/server/api/endpoints/clips/unfavorite.ts
new file mode 100644
index 0000000000..244843d50f
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/clips/unfavorite.ts
@@ -0,0 +1,65 @@
+import { Inject, Injectable } from '@nestjs/common';
+import type { ClipsRepository, ClipFavoritesRepository } from '@/models/index.js';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { DI } from '@/di-symbols.js';
+import { ApiError } from '../../error.js';
+
+export const meta = {
+ tags: ['clip'],
+
+ requireCredential: true,
+
+ kind: 'write:clip-favorite',
+
+ errors: {
+ noSuchClip: {
+ message: 'No such clip.',
+ code: 'NO_SUCH_CLIP',
+ id: '2603966e-b865-426c-94a7-af4a01241dc1',
+ },
+
+ notFavorited: {
+ message: 'You have not favorited the clip.',
+ code: 'NOT_FAVORITED',
+ id: '90c3a9e8-b321-4dae-bf57-2bf79bbcc187',
+ },
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ clipId: { type: 'string', format: 'misskey:id' },
+ },
+ required: ['clipId'],
+} as const;
+
+// eslint-disable-next-line import/no-default-export
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> {
+ constructor(
+ @Inject(DI.clipsRepository)
+ private clipsRepository: ClipsRepository,
+
+ @Inject(DI.clipFavoritesRepository)
+ private clipFavoritesRepository: ClipFavoritesRepository,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ const clip = await this.clipsRepository.findOneBy({ id: ps.clipId });
+ if (clip == null) {
+ throw new ApiError(meta.errors.noSuchClip);
+ }
+
+ const exist = await this.clipFavoritesRepository.findOneBy({
+ clipId: clip.id,
+ userId: me.id,
+ });
+
+ if (exist == null) {
+ throw new ApiError(meta.errors.notFavorited);
+ }
+
+ await this.clipFavoritesRepository.delete(exist.id);
+ });
+ }
+}
diff --git a/packages/backend/src/server/api/endpoints/clips/update.ts b/packages/backend/src/server/api/endpoints/clips/update.ts
index 597b67c442..a103c3f7d3 100644
--- a/packages/backend/src/server/api/endpoints/clips/update.ts
+++ b/packages/backend/src/server/api/endpoints/clips/update.ts
@@ -64,7 +64,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
isPublic: ps.isPublic,
});
- return await this.clipEntityService.pack(clip.id);
+ return await this.clipEntityService.pack(clip.id, me);
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/notes/clips.ts b/packages/backend/src/server/api/endpoints/notes/clips.ts
index d5caec6e1d..0a5542f497 100644
--- a/packages/backend/src/server/api/endpoints/notes/clips.ts
+++ b/packages/backend/src/server/api/endpoints/notes/clips.ts
@@ -4,8 +4,8 @@ import type { ClipNotesRepository, ClipsRepository } from '@/models/index.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { ClipEntityService } from '@/core/entities/ClipEntityService.js';
import { DI } from '@/di-symbols.js';
-import { ApiError } from '../../error.js';
import { GetterService } from '@/server/api/GetterService.js';
+import { ApiError } from '../../error.js';
export const meta = {
tags: ['clips', 'notes'],
@@ -67,7 +67,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
isPublic: true,
});
- return await Promise.all(clips.map(x => this.clipEntityService.pack(x)));
+ return await this.clipEntityService.packMany(clips, me);
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/users/clips.ts b/packages/backend/src/server/api/endpoints/users/clips.ts
index e3fd0920c9..c5aa93baaf 100644
--- a/packages/backend/src/server/api/endpoints/users/clips.ts
+++ b/packages/backend/src/server/api/endpoints/users/clips.ts
@@ -51,7 +51,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
.take(ps.limit)
.getMany();
- return await this.clipEntityService.packMany(clips);
+ return await this.clipEntityService.packMany(clips, me);
});
}
}