enhance(backend): DBのコラム設定としてcreatedAtの値を入れるように/お知らせ機能API修正
This commit is contained in:
@@ -12,9 +12,10 @@ import { MiAnnouncementRead } from '@/models/_.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { Packed } from '@/misc/json-schema.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
import { AnnouncementEntityService } from '@/core/entities/AnnouncementEntityService.js';
|
||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
|
||||
@Injectable()
|
||||
export class AnnouncementService {
|
||||
@@ -30,6 +31,7 @@ export class AnnouncementService {
|
||||
|
||||
private idService: IdService,
|
||||
private userEntityService: UserEntityService,
|
||||
private announcementEntityService: AnnouncementEntityService,
|
||||
private globalEventService: GlobalEventService,
|
||||
private moderationLogService: ModerationLogService,
|
||||
) {
|
||||
@@ -45,7 +47,7 @@ export class AnnouncementService {
|
||||
@bindThis
|
||||
public async getUnreadAnnouncements(user: MiUser): Promise<Packed<'Announcement'>[]> {
|
||||
const q = this.announcementsRepository.createQueryBuilder('announcement');
|
||||
q.leftJoin(
|
||||
q.leftJoinAndSelect(
|
||||
MiAnnouncementRead,
|
||||
'read',
|
||||
'read."announcementId" = announcement.id AND read."userId" = :userId',
|
||||
@@ -53,7 +55,8 @@ export class AnnouncementService {
|
||||
);
|
||||
|
||||
q
|
||||
.where('announcement.isActive = true')
|
||||
.where('read.id IS NULL')
|
||||
.andWhere('announcement.isActive = true')
|
||||
.andWhere('announcement.silence = false')
|
||||
.andWhere(new Brackets(qb => {
|
||||
qb.orWhere('announcement.userId = :userId', { userId: user.id });
|
||||
@@ -62,15 +65,14 @@ export class AnnouncementService {
|
||||
.andWhere(new Brackets(qb => {
|
||||
qb.orWhere('announcement.forExistingUsers = false');
|
||||
qb.orWhere('announcement.id > :userId', { userId: user.id });
|
||||
}))
|
||||
.andWhere('read.id IS NULL');
|
||||
}));
|
||||
|
||||
q.orderBy({
|
||||
'announcement."displayOrder"': 'DESC',
|
||||
'announcement.id': 'DESC',
|
||||
});
|
||||
|
||||
return this.packMany(
|
||||
return this.announcementEntityService.packMany(
|
||||
await q.getMany(),
|
||||
user,
|
||||
);
|
||||
@@ -94,7 +96,7 @@ export class AnnouncementService {
|
||||
userId: values.userId,
|
||||
}).then(x => this.announcementsRepository.findOneByOrFail(x.identifiers[0]));
|
||||
|
||||
const packed = (await this.packMany([announcement], null))[0];
|
||||
const packed = (await this.announcementEntityService.packMany([announcement], null))[0];
|
||||
|
||||
if (values.userId) {
|
||||
this.globalEventService.publishMainStream(values.userId, 'announcementCreated', {
|
||||
@@ -136,7 +138,7 @@ export class AnnouncementService {
|
||||
limit: number,
|
||||
offset: number,
|
||||
moderator: MiUser,
|
||||
): Promise<(MiAnnouncement & { userInfo: Packed<'UserLite'> | null, readCount: number })[]> {
|
||||
): Promise<(MiAnnouncement & { userInfo: Packed<'UserLite'> | null, reads: number })[]> {
|
||||
const query = this.announcementsRepository.createQueryBuilder('announcement');
|
||||
if (userId) {
|
||||
query.andWhere('announcement."userId" = :userId', { userId: userId });
|
||||
@@ -173,7 +175,7 @@ export class AnnouncementService {
|
||||
return announcements.map(announcement => ({
|
||||
...announcement,
|
||||
userInfo: packedUsers.find(u => u.id === announcement.userId) ?? null,
|
||||
readCount: reads.get(announcement) ?? 0,
|
||||
reads: reads.get(announcement) ?? 0,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -188,6 +190,7 @@ export class AnnouncementService {
|
||||
|
||||
await this.announcementsRepository.update(announcement.id, {
|
||||
updatedAt: new Date(),
|
||||
isActive: values.isActive,
|
||||
title: values.title,
|
||||
text: values.text,
|
||||
/* eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- 空の文字列の場合、nullを渡すようにするため */
|
||||
@@ -195,11 +198,10 @@ export class AnnouncementService {
|
||||
display: values.display,
|
||||
icon: values.icon,
|
||||
forExistingUsers: values.forExistingUsers,
|
||||
silence: values.silence,
|
||||
needConfirmationToRead: values.needConfirmationToRead,
|
||||
closeDuration: values.closeDuration,
|
||||
displayOrder: values.displayOrder,
|
||||
isActive: values.isActive,
|
||||
silence: values.silence,
|
||||
userId: values.userId,
|
||||
});
|
||||
|
||||
@@ -305,7 +307,7 @@ export class AnnouncementService {
|
||||
'announcement.id': 'DESC',
|
||||
});
|
||||
|
||||
return this.packMany(
|
||||
return this.announcementEntityService.packMany(
|
||||
await query
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
@@ -315,32 +317,29 @@ export class AnnouncementService {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async countUnreadAnnouncements(me: MiUser): Promise<number> {
|
||||
const query = this.announcementsRepository.createQueryBuilder('announcement');
|
||||
query.leftJoinAndSelect(
|
||||
public async countUnreadAnnouncements(user: MiUser): Promise<number> {
|
||||
const q = this.announcementsRepository.createQueryBuilder('announcement');
|
||||
q.leftJoinAndSelect(
|
||||
MiAnnouncementRead,
|
||||
'read',
|
||||
'read."announcementId" = announcement.id AND read."userId" = :userId',
|
||||
{ userId: me.id },
|
||||
{ userId: user.id },
|
||||
);
|
||||
query.andWhere('read.id IS NULL');
|
||||
query.andWhere('announcement."isActive" = true');
|
||||
|
||||
query
|
||||
.andWhere(
|
||||
new Brackets((qb) => {
|
||||
qb.orWhere('announcement."userId" = :userId', { userId: me.id });
|
||||
qb.orWhere('announcement."userId" IS NULL');
|
||||
}),
|
||||
)
|
||||
.andWhere(
|
||||
new Brackets((qb) => {
|
||||
qb.orWhere('announcement."forExistingUsers" = false');
|
||||
qb.orWhere('announcement.id > :userId', { userId: me.id });
|
||||
}),
|
||||
);
|
||||
q
|
||||
.where('read.id IS NULL')
|
||||
.andWhere('announcement.isActive = true')
|
||||
.andWhere('announcement.silence = false')
|
||||
.andWhere(new Brackets(qb => {
|
||||
qb.orWhere('announcement.userId = :userId', { userId: user.id });
|
||||
qb.orWhere('announcement.userId IS NULL');
|
||||
}))
|
||||
.andWhere(new Brackets(qb => {
|
||||
qb.orWhere('announcement.forExistingUsers = false');
|
||||
qb.orWhere('announcement.id > :userId', { userId: user.id });
|
||||
}));
|
||||
|
||||
return query.getCount();
|
||||
return q.getCount();
|
||||
}
|
||||
|
||||
@bindThis
|
||||
@@ -359,27 +358,4 @@ export class AnnouncementService {
|
||||
this.globalEventService.publishMainStream(user.id, 'readAllAnnouncements');
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async packMany(
|
||||
announcements: (MiAnnouncement & { isRead?: boolean | null })[],
|
||||
me: { id: MiUser['id'] } | null | undefined,
|
||||
): Promise<Packed<'Announcement'>[]> {
|
||||
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,
|
||||
closeDuration: announcement.closeDuration,
|
||||
displayOrder: announcement.displayOrder,
|
||||
silence: announcement.silence,
|
||||
forYou: announcement.userId === me?.id,
|
||||
isRead: announcement.isRead ?? undefined,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,12 +57,14 @@ export class AntennaService implements OnApplicationShutdown {
|
||||
case 'antennaCreated':
|
||||
this.antennas.push({
|
||||
...body,
|
||||
createdAt: new Date(body.createdAt),
|
||||
lastUsedAt: new Date(body.lastUsedAt),
|
||||
});
|
||||
break;
|
||||
case 'antennaUpdated':
|
||||
this.antennas[this.antennas.findIndex(a => a.id === body.id)] = {
|
||||
...body,
|
||||
createdAt: new Date(body.createdAt),
|
||||
lastUsedAt: new Date(body.lastUsedAt),
|
||||
};
|
||||
break;
|
||||
|
||||
@@ -80,6 +80,7 @@ import PerUserDriveChart from './chart/charts/per-user-drive.js';
|
||||
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';
|
||||
@@ -214,6 +215,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 };
|
||||
@@ -348,6 +350,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||
ApRequestChart,
|
||||
ChartManagementService,
|
||||
AbuseUserReportEntityService,
|
||||
AnnouncementEntityService,
|
||||
AntennaEntityService,
|
||||
AppEntityService,
|
||||
AuthSessionEntityService,
|
||||
@@ -477,6 +480,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||
$ApRequestChart,
|
||||
$ChartManagementService,
|
||||
$AbuseUserReportEntityService,
|
||||
$AnnouncementEntityService,
|
||||
$AntennaEntityService,
|
||||
$AppEntityService,
|
||||
$AuthSessionEntityService,
|
||||
@@ -606,6 +610,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||
ApRequestChart,
|
||||
ChartManagementService,
|
||||
AbuseUserReportEntityService,
|
||||
AnnouncementEntityService,
|
||||
AntennaEntityService,
|
||||
AppEntityService,
|
||||
AuthSessionEntityService,
|
||||
@@ -734,6 +739,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
||||
$ApRequestChart,
|
||||
$ChartManagementService,
|
||||
$AbuseUserReportEntityService,
|
||||
$AnnouncementEntityService,
|
||||
$AntennaEntityService,
|
||||
$AppEntityService,
|
||||
$AuthSessionEntityService,
|
||||
|
||||
@@ -382,6 +382,7 @@ export class NoteCreateService implements OnApplicationShutdown {
|
||||
private async insertNote(user: { id: MiUser['id']; host: MiUser['host']; }, data: Option, tags: string[], emojis: string[], mentionedUsers: MinimumUser[]) {
|
||||
const insert = new MiNote({
|
||||
id: this.idService.gen(data.createdAt?.getTime()),
|
||||
createdAt: data.createdAt!,
|
||||
fileIds: data.files ? data.files.map(file => file.id) : [],
|
||||
replyId: data.reply ? data.reply.id : null,
|
||||
renoteId: data.renote ? data.renote.id : null,
|
||||
|
||||
@@ -158,6 +158,7 @@ export class ReactionService {
|
||||
|
||||
const record: MiNoteReaction = {
|
||||
id: this.idService.gen(),
|
||||
createdAt: new Date(),
|
||||
noteId: note.id,
|
||||
userId: user.id,
|
||||
reaction,
|
||||
|
||||
@@ -131,6 +131,7 @@ export class RoleService implements OnApplicationShutdown {
|
||||
if (cached) {
|
||||
cached.push({
|
||||
...body,
|
||||
createdAt: new Date(body.createdAt),
|
||||
updatedAt: new Date(body.updatedAt),
|
||||
lastUsedAt: new Date(body.lastUsedAt),
|
||||
});
|
||||
@@ -144,6 +145,7 @@ export class RoleService implements OnApplicationShutdown {
|
||||
if (i > -1) {
|
||||
cached[i] = {
|
||||
...body,
|
||||
createdAt: new Date(body.createdAt),
|
||||
updatedAt: new Date(body.updatedAt),
|
||||
lastUsedAt: new Date(body.lastUsedAt),
|
||||
};
|
||||
@@ -163,6 +165,7 @@ export class RoleService implements OnApplicationShutdown {
|
||||
if (cached) {
|
||||
cached.push({
|
||||
...body,
|
||||
createdAt: new Date(body.createdAt),
|
||||
expiresAt: body.expiresAt ? new Date(body.expiresAt) : null,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ export class WebhookService implements OnApplicationShutdown {
|
||||
if (body.active) {
|
||||
this.webhooks.push({
|
||||
...body,
|
||||
createdAt: new Date(body.createdAt),
|
||||
latestSentAt: body.latestSentAt ? new Date(body.latestSentAt) : null,
|
||||
});
|
||||
}
|
||||
@@ -61,11 +62,13 @@ export class WebhookService implements OnApplicationShutdown {
|
||||
if (i > -1) {
|
||||
this.webhooks[i] = {
|
||||
...body,
|
||||
createdAt: new Date(body.createdAt),
|
||||
latestSentAt: body.latestSentAt ? new Date(body.latestSentAt) : null,
|
||||
};
|
||||
} else {
|
||||
this.webhooks.push({
|
||||
...body,
|
||||
createdAt: new Date(body.createdAt),
|
||||
latestSentAt: body.latestSentAt ? new Date(body.latestSentAt) : null,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||
* 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<Packed<'Announcement'>> {
|
||||
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,
|
||||
closeDuration: announcement.closeDuration,
|
||||
displayOrder: announcement.displayOrder,
|
||||
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<Packed<'Announcement'>[]> {
|
||||
return (await Promise.allSettled(announcements.map(x => this.pack(x, me))))
|
||||
.filter(result => result.status === 'fulfilled')
|
||||
.map(result => (result as PromiseFulfilledResult<Packed<'Announcement'>>).value);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user