diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2023-03-16 17:24:49 +0900 |
|---|---|---|
| committer | syuilo <Syuilotan@yahoo.co.jp> | 2023-03-16 17:24:49 +0900 |
| commit | b644567735443ae203f78dbdbe1963c252ceb1ad (patch) | |
| tree | 23a08ea860a7e856e419082cb0101bab45d54a34 /packages/backend/src/server/api/endpoints/clips | |
| parent | enhance: カスタム絵文字にライセンス情報を付与できるよ... (diff) | |
| download | sharkey-b644567735443ae203f78dbdbe1963c252ceb1ad.tar.gz sharkey-b644567735443ae203f78dbdbe1963c252ceb1ad.tar.bz2 sharkey-b644567735443ae203f78dbdbe1963c252ceb1ad.zip | |
feat: clip favorite
Resolve #10337
Diffstat (limited to 'packages/backend/src/server/api/endpoints/clips')
8 files changed, 201 insertions, 4 deletions
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); }); } } |