From 39d6af135f43c2521bd7688fcb1c46bcce546b73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=8B=E3=81=A3=E3=81=93=E3=81=8B=E3=82=8A?= <67428053+kakkokari-gtyih@users.noreply.github.com> Date: Thu, 29 Feb 2024 20:03:30 +0900 Subject: [PATCH] =?UTF-8?q?enhance:=20=E9=80=9A=E7=9F=A5=E3=81=AE=E5=B1=A5?= =?UTF-8?q?=E6=AD=B4=E3=82=92=E3=83=AA=E3=82=BB=E3=83=83=E3=83=88=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=20(#13335)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * enhance: 通知の履歴をリセットできるように * Update Changelog * 通知欄も連動して更新するように * revert some changes * Update CHANGELOG.md * Remove unused part * fix --- CHANGELOG.md | 1 + locales/index.d.ts | 4 ++ locales/ja-JP.yml | 1 + .../backend/src/core/GlobalEventService.ts | 1 + .../backend/src/core/NotificationService.ts | 9 ++++ .../backend/src/server/api/EndpointsModule.ts | 5 ++ packages/backend/src/server/api/endpoints.ts | 2 + .../api/endpoints/notifications/flush.ts | 33 ++++++++++++ .../src/components/MkNotifications.vue | 5 +- .../src/pages/settings/notifications.vue | 12 +++++ packages/misskey-js/etc/misskey-js.api.md | 1 + .../misskey-js/src/autogen/apiClientJSDoc.ts | 11 ++++ packages/misskey-js/src/autogen/endpoint.ts | 1 + packages/misskey-js/src/autogen/types.ts | 53 +++++++++++++++++++ packages/misskey-js/src/streaming.types.ts | 1 + 15 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 packages/backend/src/server/api/endpoints/notifications/flush.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 5160228696..ae611875dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - Enhance: サーバーごとにモデレーションノートを残せるように - Enhance: コンディショナルロールの条件に「マニュアルロールへのアサイン」を追加 - Enhance: 通知の受信設定に「フォロー中またはフォロワー」を追加 +- Enhance: 通知の履歴をリセットできるように ### Client - Enhance: ノート作成画面のファイル添付メニューの区切り線の位置を調整 diff --git a/locales/index.d.ts b/locales/index.d.ts index 3edc9d235e..0883749a33 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -8913,6 +8913,10 @@ export interface Locale extends ILocale { * {n}人にフォローされました */ "followedBySomeUsers": ParameterizedString<"n">; + /** + * 通知の履歴をリセットする + */ + "flushNotification": string; "_types": { /** * すべて diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 66ddf6a46d..dc91b9f210 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2356,6 +2356,7 @@ _notification: reactedBySomeUsers: "{n}人がリアクションしました" renotedBySomeUsers: "{n}人がリノートしました" followedBySomeUsers: "{n}人にフォローされました" + flushNotification: "通知の履歴をリセットする" _types: all: "すべて" diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index 7c1b34da05..90efd63f3a 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -69,6 +69,7 @@ export interface MainEventTypes { file: Packed<'DriveFile'>; }; readAllNotifications: undefined; + notificationFlushed: undefined; 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 af5755f88b..68ad92f396 100644 --- a/packages/backend/src/core/NotificationService.ts +++ b/packages/backend/src/core/NotificationService.ts @@ -214,6 +214,15 @@ export class NotificationService implements OnApplicationShutdown { */ } + @bindThis + public async flushAllNotifications(userId: MiUser['id']) { + await Promise.all([ + this.redisClient.del(`notificationTimeline:${userId}`), + this.redisClient.del(`latestReadNotification:${userId}`), + ]); + this.globalEventService.publishMainStream(userId, 'notificationFlushed'); + } + @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 8a003725cd..88d3999eb0 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_flush from './endpoints/notifications/flush.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_flush: Provider = { provide: 'ep:notifications/flush', useClass: ep___notifications_flush.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_flush, $notifications_markAllAsRead, $notifications_testNotification, $pagePush, @@ -1408,7 +1411,9 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ $notes_unrenote, $notes_userListTimeline, $notifications_create, + $notifications_flush, $notifications_markAllAsRead, + $notifications_testNotification, $pagePush, $pages_create, $pages_delete, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index e1c8be727e..f7e64a7356 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_flush from './endpoints/notifications/flush.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/flush', ep___notifications_flush], ['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/flush.ts b/packages/backend/src/server/api/endpoints/notifications/flush.ts new file mode 100644 index 0000000000..47c0642fd1 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/notifications/flush.ts @@ -0,0 +1,33 @@ +/* + * 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'; + +export const meta = { + tags: ['notifications', 'account'], + + requireCredential: true, + + kind: 'write:notifications', +} as const; + +export const paramDef = { + type: 'object', + properties: {}, + required: [], +} 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) => { + this.notificationService.flushAllNotifications(me.id); + }); + } +} diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue index a9f019dd9c..389987338d 100644 --- a/packages/frontend/src/components/MkNotifications.vue +++ b/packages/frontend/src/components/MkNotifications.vue @@ -35,6 +35,7 @@ import { notificationTypes } from '@/const.js'; import { infoImageUrl } from '@/instance.js'; import { defaultStore } from '@/store.js'; import MkPullToRefresh from '@/components/MkPullToRefresh.vue'; +import * as Misskey from 'misskey-js'; const props = defineProps<{ excludeTypes?: typeof notificationTypes[number][]; @@ -75,17 +76,19 @@ function reload() { }); } -let connection; +let connection: Misskey.ChannelConnection; onMounted(() => { connection = useStream().useChannel('main'); connection.on('notification', onNotification); + connection.on('notificationFlushed', reload); }); onActivated(() => { pagingComponent.value?.reload(); connection = useStream().useChannel('main'); connection.on('notification', onNotification); + connection.on('notificationFlushed', reload); }); onUnmounted(() => { diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue index bbcef65283..70db6a5109 100644 --- a/packages/frontend/src/pages/settings/notifications.vue +++ b/packages/frontend/src/pages/settings/notifications.vue @@ -35,6 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts._notification.sendTestNotification }} + {{ i18n.ts._notification.flushNotification }}
@@ -114,6 +115,17 @@ function testNotification(): void { misskeyApi('notifications/test-notification'); } +async function flushNotification() { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.ts.resetAreYouSure, + }); + + if (canceled) return; + + os.apiWithDialog('notifications/flush'); +} + const headerActions = computed(() => []); const headerTabs = computed(() => []); diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 0e990ffd5a..2237d278f4 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -530,6 +530,7 @@ export type Channels = { unreadNotification: (payload: Notification_2) => void; unreadMention: (payload: Note['id']) => void; readAllUnreadMentions: () => void; + notificationFlushed: () => void; unreadSpecifiedNote: (payload: Note['id']) => void; readAllUnreadSpecifiedNotes: () => void; readAllAntennas: () => void; diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts index d27413810c..5309350100 100644 --- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts +++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts @@ -3195,6 +3195,17 @@ declare module '../api.js' { credential?: string | null, ): Promise>; + /** + * No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:notifications* + */ + request( + endpoint: E, + params: P, + credential?: string | null, + ): Promise>; + /** * No description provided. * diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts index 656ac28246..b0982e1e55 100644 --- a/packages/misskey-js/src/autogen/endpoint.ts +++ b/packages/misskey-js/src/autogen/endpoint.ts @@ -841,6 +841,7 @@ export type Endpoints = { 'notes/unrenote': { req: NotesUnrenoteRequest; res: EmptyResponse }; 'notes/user-list-timeline': { req: NotesUserListTimelineRequest; res: NotesUserListTimelineResponse }; 'notifications/create': { req: NotificationsCreateRequest; res: EmptyResponse }; + 'notifications/flush': { req: EmptyRequest; res: EmptyResponse }; 'notifications/mark-all-as-read': { req: EmptyRequest; res: EmptyResponse }; 'notifications/test-notification': { req: EmptyRequest; res: EmptyResponse }; 'page-push': { req: PagePushRequest; res: EmptyResponse }; diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 9a2ff7487f..a89e18ea76 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -2770,6 +2770,15 @@ export type paths = { */ post: operations['notifications/create']; }; + '/notifications/flush': { + /** + * notifications/flush + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:notifications* + */ + post: operations['notifications/flush']; + }; '/notifications/mark-all-as-read': { /** * notifications/mark-all-as-read @@ -22056,6 +22065,50 @@ export type operations = { }; }; }; + /** + * notifications/flush + * @description No description provided. + * + * **Credential required**: *Yes* / **Permission**: *write:notifications* + */ + 'notifications/flush': { + responses: { + /** @description OK (without any results) */ + 204: { + content: never; + }; + /** @description Client error */ + 400: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Authentication error */ + 401: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Forbidden error */ + 403: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description I'm Ai */ + 418: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + /** @description Internal server error */ + 500: { + content: { + 'application/json': components['schemas']['Error']; + }; + }; + }; + }; /** * notifications/mark-all-as-read * @description No description provided. diff --git a/packages/misskey-js/src/streaming.types.ts b/packages/misskey-js/src/streaming.types.ts index 0ba5715d68..9a86e03d69 100644 --- a/packages/misskey-js/src/streaming.types.ts +++ b/packages/misskey-js/src/streaming.types.ts @@ -40,6 +40,7 @@ export type Channels = { unreadNotification: (payload: Notification) => void; unreadMention: (payload: Note['id']) => void; readAllUnreadMentions: () => void; + notificationFlushed: () => void; unreadSpecifiedNote: (payload: Note['id']) => void; readAllUnreadSpecifiedNotes: () => void; readAllAntennas: () => void;