From 3ffbf6296f44c6f8837f0b8533a3b60b64403bf9 Mon Sep 17 00:00:00 2001 From: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Mon, 27 May 2024 17:15:11 +0900 Subject: feat: 個別のお知らせにリンクで飛べるように (#13885) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(announcement): 個別のお知らせにリンクで飛べるように (MisskeyIO#639) (cherry picked from commit f6bf7f992a78e54d86a4701dbd1e4fda7ef4eb27) * fix Co-authored-by: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com> * fix Co-authored-by: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com> * 一覧ページではお知らせpanel全体を押せるように * お知らせバーは個別ページに飛ばすように * Update Changelog * spdx * attempt to fox test * remove unnecessary thong * `announcement` → `announcements/show` * リンクを押せる場所をタイトルと日付部分のみに変更 --------- Co-authored-by: まっちゃとーにゅ <17376330+u1-liquid@users.noreply.github.com> --- packages/backend/src/core/AnnouncementService.ts | 49 +++++++-------- packages/backend/src/core/CoreModule.ts | 6 ++ .../src/core/entities/AnnouncementEntityService.ts | 71 ++++++++++++++++++++++ 3 files changed, 99 insertions(+), 27 deletions(-) create mode 100644 packages/backend/src/core/entities/AnnouncementEntityService.ts (limited to 'packages/backend/src/core') diff --git a/packages/backend/src/core/AnnouncementService.ts b/packages/backend/src/core/AnnouncementService.ts index b298a70929..9b60df2cae 100644 --- a/packages/backend/src/core/AnnouncementService.ts +++ b/packages/backend/src/core/AnnouncementService.ts @@ -4,13 +4,14 @@ */ import { Inject, Injectable } from '@nestjs/common'; -import { Brackets } from 'typeorm'; +import { Brackets, EntityNotFoundError } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { MiUser } from '@/models/User.js'; import type { AnnouncementReadsRepository, AnnouncementsRepository, MiAnnouncement, MiAnnouncementRead, UsersRepository } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; import { Packed } from '@/misc/json-schema.js'; import { IdService } from '@/core/IdService.js'; +import { AnnouncementEntityService } from '@/core/entities/AnnouncementEntityService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; @@ -29,6 +30,7 @@ export class AnnouncementService { private idService: IdService, private globalEventService: GlobalEventService, private moderationLogService: ModerationLogService, + private announcementEntityService: AnnouncementEntityService, ) { } @@ -79,7 +81,7 @@ export class AnnouncementService { userId: values.userId, }).then(x => this.announcementsRepository.findOneByOrFail(x.identifiers[0])); - const packed = (await this.packMany([announcement]))[0]; + const packed = await this.announcementEntityService.pack(announcement); if (values.userId) { this.globalEventService.publishMainStream(values.userId, 'announcementCreated', { @@ -177,6 +179,24 @@ export class AnnouncementService { } } + @bindThis + public async getAnnouncement(announcementId: MiAnnouncement['id'], me: MiUser | null): Promise> { + const announcement = await this.announcementsRepository.findOneByOrFail({ id: announcementId }); + if (me) { + if (announcement.userId && announcement.userId !== me.id) { + throw new EntityNotFoundError(this.announcementsRepository.metadata.target, { id: announcementId }); + } + + const read = await this.announcementReadsRepository.findOneBy({ + announcementId: announcement.id, + userId: me.id, + }); + return this.announcementEntityService.pack({ ...announcement, isRead: read !== null }, me); + } else { + return this.announcementEntityService.pack(announcement, null); + } + } + @bindThis public async read(user: MiUser, announcementId: MiAnnouncement['id']): Promise { try { @@ -193,29 +213,4 @@ export class AnnouncementService { this.globalEventService.publishMainStream(user.id, 'readAllAnnouncements'); } } - - @bindThis - public async packMany( - announcements: MiAnnouncement[], - me?: { id: MiUser['id'] } | null | undefined, - options?: { - reads?: MiAnnouncementRead[]; - }, - ): Promise[]> { - const reads = me ? (options?.reads ?? await this.getReads(me.id)) : []; - return announcements.map(announcement => ({ - id: announcement.id, - createdAt: this.idService.parse(announcement.id).date.toISOString(), - updatedAt: announcement.updatedAt?.toISOString() ?? null, - text: announcement.text, - title: announcement.title, - imageUrl: announcement.imageUrl, - icon: announcement.icon, - display: announcement.display, - needConfirmationToRead: announcement.needConfirmationToRead, - silence: announcement.silence, - forYou: announcement.userId === me?.id, - isRead: reads.some(read => read.announcementId === announcement.id), - })); - } } diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index 5953155872..be80df6f1c 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -84,6 +84,7 @@ import ApRequestChart from './chart/charts/ap-request.js'; import { ChartManagementService } from './chart/ChartManagementService.js'; import { AbuseUserReportEntityService } from './entities/AbuseUserReportEntityService.js'; +import { AnnouncementEntityService } from './entities/AnnouncementEntityService.js'; import { AntennaEntityService } from './entities/AntennaEntityService.js'; import { AppEntityService } from './entities/AppEntityService.js'; import { AuthSessionEntityService } from './entities/AuthSessionEntityService.js'; @@ -223,6 +224,7 @@ const $ApRequestChart: Provider = { provide: 'ApRequestChart', useExisting: ApRe const $ChartManagementService: Provider = { provide: 'ChartManagementService', useExisting: ChartManagementService }; const $AbuseUserReportEntityService: Provider = { provide: 'AbuseUserReportEntityService', useExisting: AbuseUserReportEntityService }; +const $AnnouncementEntityService: Provider = { provide: 'AnnouncementEntityService', useExisting: AnnouncementEntityService }; const $AntennaEntityService: Provider = { provide: 'AntennaEntityService', useExisting: AntennaEntityService }; const $AppEntityService: Provider = { provide: 'AppEntityService', useExisting: AppEntityService }; const $AuthSessionEntityService: Provider = { provide: 'AuthSessionEntityService', useExisting: AuthSessionEntityService }; @@ -363,6 +365,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting ChartManagementService, AbuseUserReportEntityService, + AnnouncementEntityService, AntennaEntityService, AppEntityService, AuthSessionEntityService, @@ -499,6 +502,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $ChartManagementService, $AbuseUserReportEntityService, + $AnnouncementEntityService, $AntennaEntityService, $AppEntityService, $AuthSessionEntityService, @@ -635,6 +639,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting ChartManagementService, AbuseUserReportEntityService, + AnnouncementEntityService, AntennaEntityService, AppEntityService, AuthSessionEntityService, @@ -770,6 +775,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $ChartManagementService, $AbuseUserReportEntityService, + $AnnouncementEntityService, $AntennaEntityService, $AppEntityService, $AuthSessionEntityService, diff --git a/packages/backend/src/core/entities/AnnouncementEntityService.ts b/packages/backend/src/core/entities/AnnouncementEntityService.ts new file mode 100644 index 0000000000..90b04d0229 --- /dev/null +++ b/packages/backend/src/core/entities/AnnouncementEntityService.ts @@ -0,0 +1,71 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { DI } from '@/di-symbols.js'; +import type { AnnouncementsRepository, AnnouncementReadsRepository, MiAnnouncement, MiUser } from '@/models/_.js'; +import type { Packed } from '@/misc/json-schema.js'; +import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; + +@Injectable() +export class AnnouncementEntityService { + constructor( + @Inject(DI.announcementsRepository) + private announcementsRepository: AnnouncementsRepository, + + @Inject(DI.announcementReadsRepository) + private announcementReadsRepository: AnnouncementReadsRepository, + + private idService: IdService, + ) { + } + + @bindThis + public async pack( + src: MiAnnouncement['id'] | MiAnnouncement & { isRead?: boolean | null }, + me?: { id: MiUser['id'] } | null | undefined, + ): Promise> { + const announcement = typeof src === 'object' + ? src + : await this.announcementsRepository.findOneByOrFail({ + id: src, + }) as MiAnnouncement & { isRead?: boolean | null }; + + if (me && announcement.isRead === undefined) { + announcement.isRead = await this.announcementReadsRepository + .countBy({ + announcementId: announcement.id, + userId: me.id, + }) + .then((count: number) => count > 0); + } + + return { + id: announcement.id, + createdAt: this.idService.parse(announcement.id).date.toISOString(), + updatedAt: announcement.updatedAt?.toISOString() ?? null, + title: announcement.title, + text: announcement.text, + imageUrl: announcement.imageUrl, + icon: announcement.icon, + display: announcement.display, + forYou: announcement.userId === me?.id, + needConfirmationToRead: announcement.needConfirmationToRead, + silence: announcement.silence, + isRead: announcement.isRead !== null ? announcement.isRead : undefined, + }; + } + + @bindThis + public async packMany( + announcements: (MiAnnouncement['id'] | MiAnnouncement & { isRead?: boolean | null } | MiAnnouncement)[], + me?: { id: MiUser['id'] } | null | undefined, + ) : Promise[]> { + return (await Promise.allSettled(announcements.map(x => this.pack(x, me)))) + .filter(result => result.status === 'fulfilled') + .map(result => (result as PromiseFulfilledResult>).value); + } +} -- cgit v1.2.3-freya