summaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorかっこかり <67428053+kakkokari-gtyih@users.noreply.github.com>2023-09-11 14:31:50 +0900
committerGitHub <noreply@github.com>2023-09-11 14:31:50 +0900
commitcd6428715e6780d51e8f6edf93fe7e32bd8f937b (patch)
tree3a1ac5a3f7d01f4fa1fbcb29af7c5bd9df0d6581 /packages
parentfix (diff)
downloadmisskey-cd6428715e6780d51e8f6edf93fe7e32bd8f937b.tar.gz
misskey-cd6428715e6780d51e8f6edf93fe7e32bd8f937b.tar.bz2
misskey-cd6428715e6780d51e8f6edf93fe7e32bd8f937b.zip
feat: テスト通知を送信できるようにする (#11810)
* (add) Notification test * Update Changelog * (add) backend, frontend impl * globalEventの名前を明確にする * Run API Extractor
Diffstat (limited to 'packages')
-rw-r--r--packages/backend/src/models/entities/Notification.ts1
-rw-r--r--packages/backend/src/server/api/EndpointsModule.ts3
-rw-r--r--packages/backend/src/server/api/endpoints.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/notifications/test-notification.ts33
-rw-r--r--packages/backend/src/types.ts2
-rw-r--r--packages/frontend/src/components/MkNotification.vue4
-rw-r--r--packages/frontend/src/pages/settings/general.vue3
-rw-r--r--packages/frontend/src/pages/settings/notifications.vue6
-rw-r--r--packages/frontend/src/scripts/test-notification.ts34
-rw-r--r--packages/frontend/src/ui/_common_/common.vue8
-rw-r--r--packages/misskey-js/etc/misskey-js.api.md8
-rw-r--r--packages/misskey-js/src/api.types.ts1
-rw-r--r--packages/misskey-js/src/entities.ts2
13 files changed, 103 insertions, 4 deletions
diff --git a/packages/backend/src/models/entities/Notification.ts b/packages/backend/src/models/entities/Notification.ts
index 94c7084cf0..fb7f67dfd8 100644
--- a/packages/backend/src/models/entities/Notification.ts
+++ b/packages/backend/src/models/entities/Notification.ts
@@ -33,6 +33,7 @@ export type MiNotification = {
* followRequestAccepted - 自分の送ったフォローリクエストが承認された
* achievementEarned - 実績を獲得
* app - アプリ通知
+ * test - テスト通知(サーバー側)
*/
type: typeof notificationTypes[number];
diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts
index 799ba4498d..7b9fa6c3b0 100644
--- a/packages/backend/src/server/api/EndpointsModule.ts
+++ b/packages/backend/src/server/api/EndpointsModule.ts
@@ -283,6 +283,7 @@ 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_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';
import * as ep___pages_create from './endpoints/pages/create.js';
import * as ep___pages_delete from './endpoints/pages/delete.js';
@@ -629,6 +630,7 @@ const $notes_unrenote: Provider = { provide: 'ep:notes/unrenote', useClass: ep__
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_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 };
const $pages_create: Provider = { provide: 'ep:pages/create', useClass: ep___pages_create.default };
const $pages_delete: Provider = { provide: 'ep:pages/delete', useClass: ep___pages_delete.default };
@@ -979,6 +981,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
$notes_userListTimeline,
$notifications_create,
$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 3924b43d16..a9cb7c341a 100644
--- a/packages/backend/src/server/api/endpoints.ts
+++ b/packages/backend/src/server/api/endpoints.ts
@@ -283,6 +283,7 @@ 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_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';
import * as ep___pages_create from './endpoints/pages/create.js';
import * as ep___pages_delete from './endpoints/pages/delete.js';
@@ -627,6 +628,7 @@ const eps = [
['notes/user-list-timeline', ep___notes_userListTimeline],
['notifications/create', ep___notifications_create],
['notifications/mark-all-as-read', ep___notifications_markAllAsRead],
+ ['notifications/test-notification', ep___notifications_testNotification],
['page-push', ep___pagePush],
['pages/create', ep___pages_create],
['pages/delete', ep___pages_delete],
diff --git a/packages/backend/src/server/api/endpoints/notifications/test-notification.ts b/packages/backend/src/server/api/endpoints/notifications/test-notification.ts
new file mode 100644
index 0000000000..04a68a8054
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/notifications/test-notification.ts
@@ -0,0 +1,33 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * 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'],
+
+ requireCredential: true,
+
+ kind: 'write:notifications',
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {},
+ required: [],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+ constructor(
+ private notificationService: NotificationService,
+ ) {
+ super(meta, paramDef, async (ps, user) => {
+ this.notificationService.createNotification(user.id, 'test', {});
+ });
+ }
+}
diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts
index a3a8e77cd0..024ba01e3b 100644
--- a/packages/backend/src/types.ts
+++ b/packages/backend/src/types.ts
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app'] as const;
+export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app', 'test'] as const;
export const obsoleteNotificationTypes = ['pollVote', 'groupInvited'] as const;
export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const;
diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue
index d46e8de553..ea2b6c1d4b 100644
--- a/packages/frontend/src/components/MkNotification.vue
+++ b/packages/frontend/src/components/MkNotification.vue
@@ -8,6 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.head">
<MkAvatar v-if="notification.type === 'pollEnded'" :class="$style.icon" :user="notification.note.user" link preview/>
<MkAvatar v-else-if="notification.type === 'achievementEarned'" :class="$style.icon" :user="$i" link preview/>
+ <img v-else-if="notification.type === 'test'" :class="$style.icon" :src="infoImageUrl"/>
<MkAvatar v-else-if="notification.user" :class="$style.icon" :user="notification.user" link preview/>
<img v-else-if="notification.icon" :class="$style.icon" :src="notification.icon" alt=""/>
<div
@@ -47,6 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<header :class="$style.header">
<span v-if="notification.type === 'pollEnded'">{{ i18n.ts._notification.pollEnded }}</span>
<span v-else-if="notification.type === 'achievementEarned'">{{ i18n.ts._notification.achievementEarned }}</span>
+ <span v-else-if="notification.type === 'test'">{{ i18n.ts._notification.testNotification }}</span>
<MkA v-else-if="notification.user" v-user-preview="notification.user.id" :class="$style.headerName" :to="userPage(notification.user)"><MkUserName :user="notification.user"/></MkA>
<span v-else>{{ notification.header }}</span>
<MkTime v-if="withTime" :time="notification.createdAt" :class="$style.headerTime"/>
@@ -91,6 +93,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton :class="$style.followRequestCommandButton" rounded danger @click="rejectFollowRequest()"><i class="ti ti-x"/> {{ i18n.ts.reject }}</MkButton>
</div>
</template>
+ <span v-else-if="notification.type === 'test'" :class="$style.text">{{ i18n.ts._notification.notificationWillBeDisplayedLikeThis }}</span>
<span v-else-if="notification.type === 'app'" :class="$style.text">
<Mfm :text="notification.body" :nowrap="false"/>
</span>
@@ -113,6 +116,7 @@ import { i18n } from '@/i18n';
import * as os from '@/os';
import { useTooltip } from '@/scripts/use-tooltip';
import { $i } from '@/account';
+import { infoImageUrl } from '@/instance';
const props = withDefaults(defineProps<{
notification: Misskey.entities.Notification;
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index 85a3a2e2e3..31d5dd93ec 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -95,6 +95,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<option value="vertical"><i class="ti ti-carousel-vertical"></i> {{ i18n.ts.vertical }}</option>
<option value="horizontal"><i class="ti ti-carousel-horizontal"></i> {{ i18n.ts.horizontal }}</option>
</MkRadios>
+
+ <MkButton @click="testNotification('client')">{{ i18n.ts._notification.checkNotificationBehavior }}</MkButton>
</div>
</FormSection>
@@ -190,6 +192,7 @@ import { unisonReload } from '@/scripts/unison-reload';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
import { miLocalStorage } from '@/local-storage';
+import { testNotification } from '@/scripts/test-notification';
const lang = ref(miLocalStorage.getItem('lang'));
const fontSize = ref(miLocalStorage.getItem('fontSize'));
diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue
index b9e4c58f79..b20add724c 100644
--- a/packages/frontend/src/pages/settings/notifications.vue
+++ b/packages/frontend/src/pages/settings/notifications.vue
@@ -13,6 +13,11 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</FormSection>
<FormSection>
+ <div class="_gaps_m">
+ <FormLink @click="testNotification('server')">{{ i18n.ts._notification.sendTestNotification }}</FormLink>
+ </div>
+ </FormSection>
+ <FormSection>
<template #label>{{ i18n.ts.pushNotification }}</template>
<div class="_gaps_m">
@@ -41,6 +46,7 @@ import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue';
import { notificationTypes } from '@/const';
+import { testNotification } from '@/scripts/test-notification';
let allowButton = $shallowRef<InstanceType<typeof MkPushNotificationAllowButton>>();
let pushRegistrationInServer = $computed(() => allowButton?.pushRegistrationInServer);
diff --git a/packages/frontend/src/scripts/test-notification.ts b/packages/frontend/src/scripts/test-notification.ts
new file mode 100644
index 0000000000..0e8289e19e
--- /dev/null
+++ b/packages/frontend/src/scripts/test-notification.ts
@@ -0,0 +1,34 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and other misskey contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import * as Misskey from 'misskey-js';
+import * as os from '@/os';
+import { globalEvents } from '@/events';
+
+/**
+ * テスト通知を送信
+ *
+ * - `client` … 通知ポップアップのみを表示
+ * - `server` … サーバー側から通知を送信
+ *
+ * @param type 通知タイプを指定
+ */
+export function testNotification(type: 'client' | 'server'): void {
+ const notification: Misskey.entities.Notification = {
+ id: Math.random().toString(),
+ createdAt: new Date().toUTCString(),
+ isRead: false,
+ type: 'test',
+ };
+
+ switch (type) {
+ case 'server':
+ os.api('notifications/test-notification');
+ break;
+ case 'client':
+ globalEvents.emit('clientNotification', notification);
+ break;
+ }
+}
diff --git a/packages/frontend/src/ui/_common_/common.vue b/packages/frontend/src/ui/_common_/common.vue
index 61fcb0b171..65c5dbb384 100644
--- a/packages/frontend/src/ui/_common_/common.vue
+++ b/packages/frontend/src/ui/_common_/common.vue
@@ -56,6 +56,7 @@ import { $i } from '@/account';
import { useStream } from '@/stream';
import { i18n } from '@/i18n';
import { defaultStore } from '@/store';
+import { globalEvents } from '@/events';
const XStreamIndicator = defineAsyncComponent(() => import('./stream-indicator.vue'));
const XUpload = defineAsyncComponent(() => import('./upload.vue'));
@@ -64,11 +65,13 @@ const dev = _DEV_;
let notifications = $ref<Misskey.entities.Notification[]>([]);
-function onNotification(notification) {
+function onNotification(notification: Misskey.entities.Notification, isClient: boolean = false) {
if ($i.mutingNotificationTypes.includes(notification.type)) return;
if (document.visibilityState === 'visible') {
- useStream().send('readNotification');
+ if (!isClient) {
+ useStream().send('readNotification');
+ }
notifications.unshift(notification);
window.setTimeout(() => {
@@ -86,6 +89,7 @@ function onNotification(notification) {
if ($i) {
const connection = useStream().useChannel('main', null, 'UI');
connection.on('notification', onNotification);
+ globalEvents.on('clientNotification', notification => onNotification(notification, true));
//#region Listen message from SW
if ('serviceWorker' in navigator) {
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index e3f8a65cf8..ab2cc15d52 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -1915,6 +1915,10 @@ export type Endpoints = {
};
res: null;
};
+ 'notifications/test-notification': {
+ req: NoParams;
+ res: null;
+ };
'notifications/mark-all-as-read': {
req: NoParams;
res: null;
@@ -2627,6 +2631,8 @@ type Notification_2 = {
header?: string | null;
body: string;
icon?: string | null;
+} | {
+ type: 'test';
});
// @public (undocumented)
@@ -2842,7 +2848,7 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u
//
// src/api.types.ts:16:32 - (ae-forgotten-export) The symbol "TODO" needs to be exported by the entry point index.d.ts
// src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts
-// src/api.types.ts:630:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts
+// src/api.types.ts:631:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts
// src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts
// (No @packageDocumentation comment for this package)
diff --git a/packages/misskey-js/src/api.types.ts b/packages/misskey-js/src/api.types.ts
index 70ef570166..46d790fe31 100644
--- a/packages/misskey-js/src/api.types.ts
+++ b/packages/misskey-js/src/api.types.ts
@@ -534,6 +534,7 @@ export type Endpoints = {
// notifications
'notifications/create': { req: { body: string; header?: string | null; icon?: string | null; }; res: null; };
+ 'notifications/test-notification': { req: NoParams; res: null; };
'notifications/mark-all-as-read': { req: NoParams; res: null; };
// page-push
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index 64742fa5b6..3782d81c2e 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -257,6 +257,8 @@ export type Notification = {
header?: string | null;
body: string;
icon?: string | null;
+} | {
+ type: 'test';
});
export type MessagingMessage = {