From 0e028ce1561d73fabe779e1bf3a591ec7ab67484 Mon Sep 17 00:00:00 2001 From: taichan Date: Mon, 19 Feb 2024 17:08:04 +0900 Subject: [PATCH 1/2] =?UTF-8?q?enhance:=20=E9=80=9A=E7=9F=A5=E3=81=AE?= =?UTF-8?q?=E5=80=8B=E5=88=A5=E5=89=8A=E9=99=A4=20(=E4=B8=BB=E3=81=AB?= =?UTF-8?q?=E3=82=B5=E3=83=BC=E3=83=90=E3=83=BC=E3=82=B5=E3=82=A4=E3=83=89?= =?UTF-8?q?=E3=81=AE=E5=A4=89=E6=9B=B4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/src/core/GlobalEventService.ts | 2 + .../backend/src/core/NotificationService.ts | 21 +++++++ .../backend/src/server/api/EndpointsModule.ts | 3 + packages/backend/src/server/api/endpoints.ts | 2 + .../api/endpoints/notifications/delete.ts | 47 ++++++++++++++ .../src/components/MkNotifications.vue | 7 +++ packages/misskey-js/etc/misskey-js.api.md | 4 ++ .../misskey-js/src/autogen/apiClientJSDoc.ts | 11 ++++ packages/misskey-js/src/autogen/endpoint.ts | 2 + packages/misskey-js/src/autogen/entities.ts | 1 + packages/misskey-js/src/autogen/types.ts | 61 +++++++++++++++++++ 11 files changed, 161 insertions(+) create mode 100644 packages/backend/src/server/api/endpoints/notifications/delete.ts diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index 01dd133eadc4..e4687804cce4 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -10,6 +10,7 @@ import type { MiChannel } from '@/models/Channel.js'; import type { MiUser } from '@/models/User.js'; import type { MiUserProfile } from '@/models/UserProfile.js'; import type { MiNote } from '@/models/Note.js'; +import type { MiNotification } from '@/models/Notification.js'; import type { MiAntenna } from '@/models/Antenna.js'; import type { MiDriveFile } from '@/models/DriveFile.js'; import type { MiDriveFolder } from '@/models/DriveFolder.js'; @@ -69,6 +70,7 @@ export interface MainEventTypes { file: Packed<'DriveFile'>; }; readAllNotifications: undefined; + notificationDeleted: MiNotification['id']; unreadNotification: Packed<'Notification'>; unreadMention: MiNote['id']; readAllUnreadMentions: undefined; diff --git a/packages/backend/src/core/NotificationService.ts b/packages/backend/src/core/NotificationService.ts index ee16193579fe..bf56bd35cd28 100644 --- a/packages/backend/src/core/NotificationService.ts +++ b/packages/backend/src/core/NotificationService.ts @@ -7,6 +7,7 @@ import { setTimeout } from 'node:timers/promises'; import * as Redis from 'ioredis'; import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; import { In } from 'typeorm'; +import { th } from 'date-fns/locale'; import { DI } from '@/di-symbols.js'; import type { UsersRepository } from '@/models/_.js'; import type { MiUser } from '@/models/User.js'; @@ -204,6 +205,26 @@ export class NotificationService implements OnApplicationShutdown { */ } + async #getNotification(userId: MiUser['id'], notificationId: MiNotification['id']) { + // 通知のidから読み取れる時間とタイムラグが有るため、ラグを考慮する必要がある + const notificationRes = await this.redisClient.xrange( + `notificationTimeline:${userId}`, + `${this.idService.parse(notificationId).date.getTime() - 1000 }-0`, + `${this.idService.parse(notificationId).date.getTime() + 1000 }-9999`, + 'COUNT', 50); + return notificationRes.find(x => JSON.parse(x[1][1]).id === notificationId); + } + + @bindThis + public async deleteNotification(userId: MiUser['id'], notificationId: MiNotification['id']) : Promise { + const targetResId = (await this.#getNotification(userId, notificationId))?.[0]; + if (!targetResId) return; + + await this.redisClient.xdel(`notificationTimeline:${userId}`, targetResId); + this.globalEventService.publishMainStream(userId, 'notificationDeleted', notificationId); + return notificationId; + } + @bindThis public dispose(): void { this.#shutdownController.abort(); diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 8a003725cd5b..d92e03505e38 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -293,6 +293,7 @@ import * as ep___notes_translate from './endpoints/notes/translate.js'; import * as ep___notes_unrenote from './endpoints/notes/unrenote.js'; import * as ep___notes_userListTimeline from './endpoints/notes/user-list-timeline.js'; import * as ep___notifications_create from './endpoints/notifications/create.js'; +import * as ep___notifications_delete from './endpoints/notifications/delete.js'; import * as ep___notifications_markAllAsRead from './endpoints/notifications/mark-all-as-read.js'; import * as ep___notifications_testNotification from './endpoints/notifications/test-notification.js'; import * as ep___pagePush from './endpoints/page-push.js'; @@ -664,6 +665,7 @@ const $notes_translate: Provider = { provide: 'ep:notes/translate', useClass: ep const $notes_unrenote: Provider = { provide: 'ep:notes/unrenote', useClass: ep___notes_unrenote.default }; const $notes_userListTimeline: Provider = { provide: 'ep:notes/user-list-timeline', useClass: ep___notes_userListTimeline.default }; const $notifications_create: Provider = { provide: 'ep:notifications/create', useClass: ep___notifications_create.default }; +const $notifications_delete: Provider = { provide: 'ep:notifications/delete', useClass: ep___notifications_delete.default }; const $notifications_markAllAsRead: Provider = { provide: 'ep:notifications/mark-all-as-read', useClass: ep___notifications_markAllAsRead.default }; const $notifications_testNotification: Provider = { provide: 'ep:notifications/test-notification', useClass: ep___notifications_testNotification.default }; const $pagePush: Provider = { provide: 'ep:page-push', useClass: ep___pagePush.default }; @@ -1039,6 +1041,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ $notes_unrenote, $notes_userListTimeline, $notifications_create, + $notifications_delete, $notifications_markAllAsRead, $notifications_testNotification, $pagePush, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index e1c8be727e47..2e379a6c8902 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -293,6 +293,7 @@ import * as ep___notes_translate from './endpoints/notes/translate.js'; import * as ep___notes_unrenote from './endpoints/notes/unrenote.js'; import * as ep___notes_userListTimeline from './endpoints/notes/user-list-timeline.js'; import * as ep___notifications_create from './endpoints/notifications/create.js'; +import * as ep___notifications_delete from './endpoints/notifications/delete.js'; import * as ep___notifications_markAllAsRead from './endpoints/notifications/mark-all-as-read.js'; import * as ep___notifications_testNotification from './endpoints/notifications/test-notification.js'; import * as ep___pagePush from './endpoints/page-push.js'; @@ -662,6 +663,7 @@ const eps = [ ['notes/unrenote', ep___notes_unrenote], ['notes/user-list-timeline', ep___notes_userListTimeline], ['notifications/create', ep___notifications_create], + ['notifications/delete', ep___notifications_delete], ['notifications/mark-all-as-read', ep___notifications_markAllAsRead], ['notifications/test-notification', ep___notifications_testNotification], ['page-push', ep___pagePush], diff --git a/packages/backend/src/server/api/endpoints/notifications/delete.ts b/packages/backend/src/server/api/endpoints/notifications/delete.ts new file mode 100644 index 000000000000..c64f4d7408b3 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/notifications/delete.ts @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { NotificationService } from '@/core/NotificationService.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['notifications', 'account'], + + requireCredential: true, + + kind: 'write:notifications', + + errors: { + 'noSuchNotification': { + message: 'No such notification.', + code: 'NO_SUCH_NOTIFICATION', + id: '2bcf7352-eff1-4470-b5fb-97f1b0097832', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + notificationId: { type: 'string', format: 'misskey:id' }, + }, + required: ['notificationId'], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + private notificationService: NotificationService, + ) { + super(meta, paramDef, async (ps, me) => { + const res = await this.notificationService.deleteNotification(me.id, ps.notificationId); + if (!res) { + throw new ApiError(meta.errors.noSuchNotification); + } + }); + } +} diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue index a9f019dd9cc6..59d26f30d089 100644 --- a/packages/frontend/src/components/MkNotifications.vue +++ b/packages/frontend/src/components/MkNotifications.vue @@ -25,6 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only