diff --git a/packages/backend/src/server/api/common/read-notification.ts b/packages/backend/src/server/api/common/read-notification.ts index 8c4ba41a3..1445ede9f 100644 --- a/packages/backend/src/server/api/common/read-notification.ts +++ b/packages/backend/src/server/api/common/read-notification.ts @@ -38,10 +38,8 @@ export async function readNotificationByQuery( function postReadAllNotifications(userId: User['id']) { publishMainStream(userId, 'readAllNotifications'); - return pushNotification(userId, 'readAllNotifications', undefined); } function postReadNotifications(userId: User['id'], notificationIds: Notification['id'][]) { publishMainStream(userId, 'readNotifications', notificationIds); - return pushNotification(userId, 'readNotifications', { notificationIds }); } diff --git a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts index d169afbb3..ea84bd9b3 100644 --- a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts +++ b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts @@ -29,5 +29,4 @@ export default define(meta, paramDef, async (ps, user) => { // 全ての通知を読みましたよというイベントを発行 publishMainStream(user.id, 'readAllNotifications'); - pushNotification(user.id, 'readAllNotifications', undefined); }); diff --git a/packages/backend/src/server/api/endpoints/notifications/read.ts b/packages/backend/src/server/api/endpoints/notifications/read.ts index 7bce525a5..6b8fc8c8d 100644 --- a/packages/backend/src/server/api/endpoints/notifications/read.ts +++ b/packages/backend/src/server/api/endpoints/notifications/read.ts @@ -20,30 +20,14 @@ export const meta = { } as const; export const paramDef = { - oneOf: [ - { type: 'object', properties: { notificationId: { type: 'string', format: 'misskey:id' }, }, required: ['notificationId'], - }, - { - type: 'object', - properties: { - notificationIds: { - type: 'array', - items: { type: 'string', format: 'misskey:id' }, - maxItems: 100, - }, - }, - required: ['notificationIds'], - }, - ], } as const; // eslint-disable-next-line import/no-default-export export default define(meta, paramDef, async (ps, user) => { - if ('notificationId' in ps) return readNotification(user.id, [ps.notificationId]); - return readNotification(user.id, ps.notificationIds); + return readNotification(user.id, [ps.notificationId]); }); diff --git a/packages/backend/src/services/push-notification.ts b/packages/backend/src/services/push-notification.ts index 5c3bafbb3..d674271cb 100644 --- a/packages/backend/src/services/push-notification.ts +++ b/packages/backend/src/services/push-notification.ts @@ -60,6 +60,8 @@ export async function pushNotification(u }, }; + //console.log(`send: ${subscription.endpoint?.substring(0, 64)}`); + //console.log(`send: ${type} ${JSON.stringify(body)}`); push.sendNotification(pushSubscription, JSON.stringify({ type, body: type === 'notification' ? truncateNotification(body as Packed<'Notification'>) : body, @@ -67,9 +69,9 @@ export async function pushNotification(u }), { proxy: config.proxy, }).catch((err: any) => { - //swLogger.info(err.statusCode); - //swLogger.info(err.headers); - //swLogger.info(err.body); + //console.log(err.statusCode); + //console.log(err.headers); + //console.log(err.body); if (err.statusCode === 410) { SwSubscriptions.delete({ diff --git a/packages/sw/src/scripts/create-notification.ts b/packages/sw/src/scripts/create-notification.ts index 7c95e8e41..fb4221fdb 100644 --- a/packages/sw/src/scripts/create-notification.ts +++ b/packages/sw/src/scripts/create-notification.ts @@ -21,7 +21,6 @@ export async function createNotification(data // users/showの型定義をswos.apiへ当てはめるのが困難なのでapiFetch.requestを直接使用 const account = await getAccountFromId(data.userId); if (!account) return null; - const userDetail = await cli.request('users/show', { userId: data.body.userId }, account.token); return [t('_notification.youWereFollowed'), { body: getUserName(data.body.user), icon: data.body.user.avatarUrl, badge: iconUrl('user-plus'), data, - actions: userDetail.isFollowing ? [] : [ - { - action: 'follow', - title: t('_notification._actions.followBack') - } - ], }]; case 'mention': @@ -64,12 +56,6 @@ async function composeNotification(data icon: data.body.user.avatarUrl, badge: iconUrl('at'), data, - actions: [ - { - action: 'reply', - title: t('_notification._actions.reply') - } - ], }]; case 'reply': @@ -78,12 +64,6 @@ async function composeNotification(data icon: data.body.user.avatarUrl, badge: iconUrl('reply'), data, - actions: [ - { - action: 'reply', - title: t('_notification._actions.reply') - } - ], }]; case 'renote': @@ -92,12 +72,6 @@ async function composeNotification(data icon: data.body.user.avatarUrl, badge: iconUrl('retweet'), data, - actions: [ - { - action: 'showUser', - title: getUserName(data.body.user) - } - ], }]; case 'quote': @@ -106,18 +80,6 @@ async function composeNotification(data icon: data.body.user.avatarUrl, badge: iconUrl('quote-right'), data, - actions: [ - { - action: 'reply', - title: t('_notification._actions.reply') - }, - ...((data.body.note.visibility === 'public' || data.body.note.visibility === 'home') ? [ - { - action: 'renote', - title: t('_notification._actions.renote') - } - ] : []) - ], }]; case 'reaction': @@ -160,12 +122,6 @@ async function composeNotification(data icon: data.body.user.avatarUrl, badge, data, - actions: [ - { - action: 'showUser', - title: getUserName(data.body.user) - } - ], }]; case 'pollVote': @@ -189,16 +145,6 @@ async function composeNotification(data icon: data.body.user.avatarUrl, badge: iconUrl('clock'), data, - actions: [ - { - action: 'accept', - title: t('accept') - }, - { - action: 'reject', - title: t('reject') - } - ], }]; case 'followRequestAccepted': @@ -214,16 +160,6 @@ async function composeNotification(data body: data.body.invitation.group.name, badge: iconUrl('id-card-alt'), data, - actions: [ - { - action: 'accept', - title: t('accept') - }, - { - action: 'reject', - title: t('reject') - } - ], }]; case 'app': @@ -257,33 +193,3 @@ async function composeNotification(data return null; } } - -export async function createEmptyNotification() { - return new Promise(async res => { - if (!swLang.i18n) swLang.fetchLocale(); - const i18n = await swLang.i18n as I18n; - const { t } = i18n; - - await self.registration.showNotification( - t('_notification.emptyPushNotificationMessage'), - { - silent: true, - badge: iconUrl('null'), - tag: 'read_notification', - } - ); - - res(); - - setTimeout(async () => { - for (const n of - [ - ...(await self.registration.getNotifications({ tag: 'user_visible_auto_notification' })), - ...(await self.registration.getNotifications({ tag: 'read_notification' })) - ] - ) { - n.close(); - } - }, 1000); - }); -} diff --git a/packages/sw/src/scripts/notification-read.ts b/packages/sw/src/scripts/notification-read.ts deleted file mode 100644 index 5c1de8908..000000000 --- a/packages/sw/src/scripts/notification-read.ts +++ /dev/null @@ -1,60 +0,0 @@ -declare var self: ServiceWorkerGlobalScope; - -import { get } from 'idb-keyval'; -import { pushNotificationDataMap } from '@/types'; -import { api } from '@/scripts/operations'; - -type Accounts = { - [x: string]: { - queue: string[], - timeout: number | null - } -}; - -class SwNotificationReadManager { - private accounts: Accounts = {}; - - public async construct() { - const accounts = await get('accounts'); - if (!accounts) Error('Accounts are not recorded'); - - this.accounts = accounts.reduce((acc, e) => { - acc[e.id] = { - queue: [], - timeout: null - }; - return acc; - }, {} as Accounts); - - return this; - } - - // プッシュ通知の既読をサーバーに送信 - public async read(data: pushNotificationDataMap[K]) { - if (data.type !== 'notification' || !(data.userId in this.accounts)) return; - - const account = this.accounts[data.userId]; - - account.queue.push(data.body.id as string); - - if (account.queue.length >= 20) { - if (account.timeout) clearTimeout(account.timeout); - const notificationIds = account.queue; - account.queue = []; - await api('notifications/read', data.userId, { notificationIds }); - return; - } - - // 最後の呼び出しから200ms待ってまとめて処理する - if (account.timeout) clearTimeout(account.timeout); - account.timeout = setTimeout(() => { - account.timeout = null; - - const notificationIds = account.queue; - account.queue = []; - api('notifications/read', data.userId, { notificationIds }); - }, 200); - } -} - -export const swNotificationRead = (new SwNotificationReadManager()).construct(); diff --git a/packages/sw/src/sw.ts b/packages/sw/src/sw.ts index 0ba6a6e4a..24aa07be0 100644 --- a/packages/sw/src/sw.ts +++ b/packages/sw/src/sw.ts @@ -1,11 +1,8 @@ declare var self: ServiceWorkerGlobalScope; -import { createEmptyNotification, createNotification } from '@/scripts/create-notification'; +import { createNotification } from '@/scripts/create-notification'; import { swLang } from '@/scripts/lang'; -import { swNotificationRead } from '@/scripts/notification-read'; import { pushNotificationDataMap } from '@/types'; -import * as swos from '@/scripts/operations'; -import { acct as getAcct } from '@/filters/user'; self.addEventListener('install', ev => { ev.waitUntil(self.skipWaiting()); @@ -39,139 +36,33 @@ self.addEventListener('push', ev => { const data: pushNotificationDataMap[K] = ev.data?.json(); switch (data.type) { - // case 'driveFileCreated': case 'notification': case 'unreadMessagingMessage': // クライアントがあったらストリームに接続しているということなので通知しない if (clients.length != 0) return; return createNotification(data); - case 'readAllNotifications': - for (const n of await self.registration.getNotifications()) { - if (n?.data?.type === 'notification') n.close(); - } - break; - case 'readAllMessagingMessages': - for (const n of await self.registration.getNotifications()) { - if (n?.data?.type === 'unreadMessagingMessage') n.close(); - } - break; - case 'readNotifications': - for (const n of await self.registration.getNotifications()) { - if (data.body?.notificationIds?.includes(n.data.body.id)) { - n.close(); - } - } - break; - case 'readAllMessagingMessagesOfARoom': - for (const n of await self.registration.getNotifications()) { - if (n.data.type === 'unreadMessagingMessage' - && ('userId' in data.body - ? data.body.userId === n.data.body.userId - : data.body.groupId === n.data.body.groupId) - ) { - n.close(); - } - } - break; } - - return createEmptyNotification(); })); }); self.addEventListener('notificationclick', (ev: ServiceWorkerGlobalScopeEventMap['notificationclick']) => { - ev.waitUntil((async () => { - if (_DEV_) { - console.log('notificationclick', ev.action, ev.notification.data); - } - - const { action, notification } = ev; - const data: pushNotificationDataMap[K] = notification.data; - const { userId: id } = data; - let client: WindowClient | null = null; - - switch (data.type) { - case 'notification': - switch (action) { - case 'follow': - if ('userId' in data.body) await swos.api('following/create', id, { userId: data.body.userId }); - break; - case 'showUser': - if ('user' in data.body) client = await swos.openUser(getAcct(data.body.user), id); - break; - case 'reply': - if ('note' in data.body) client = await swos.openPost({ reply: data.body.note }, id); - break; - case 'renote': - if ('note' in data.body) await swos.api('notes/create', id, { renoteId: data.body.note.id }); - break; - case 'accept': - switch (data.body.type) { - case 'receiveFollowRequest': - await swos.api('following/requests/accept', id, { userId: data.body.userId }); - break; - case 'groupInvited': - await swos.api('users/groups/invitations/accept', id, { invitationId: data.body.invitation.id }); - break; - } - break; - case 'reject': - switch (data.body.type) { - case 'receiveFollowRequest': - await swos.api('following/requests/reject', id, { userId: data.body.userId }); - break; - case 'groupInvited': - await swos.api('users/groups/invitations/reject', id, { invitationId: data.body.invitation.id }); - break; - } - break; - case 'showFollowRequests': - client = await swos.openClient('push', '/my/follow-requests', id); - break; - default: - switch (data.body.type) { - case 'receiveFollowRequest': - client = await swos.openClient('push', '/my/follow-requests', id); - break; - case 'groupInvited': - client = await swos.openClient('push', '/my/groups', id); - break; - case 'reaction': - client = await swos.openNote(data.body.note.id, id); - break; - default: - if ('note' in data.body) { - client = await swos.openNote(data.body.note.id, id); - } else if ('user' in data.body) { - client = await swos.openUser(getAcct(data.body.user), id); - } - break; - } - } - break; - case 'unreadMessagingMessage': - client = await swos.openChat(data.body, id); - break; - } - - if (client) { - client.focus(); - } - if (data.type === 'notification') { - swNotificationRead.then(that => that.read(data)); - } - - notification.close(); - - })()); -}); + ev.notification.close(); -self.addEventListener('notificationclose', (ev: ServiceWorkerGlobalScopeEventMap['notificationclose']) => { - const data: pushNotificationDataMap[K] = ev.notification.data; + ev.waitUntil(self.clients.matchAll({ + type: "window" + }).then(clientList => { + for (let i = 0; i < clientList.length; i++) { + const client = clientList[i]; + if (client.url == '/' && 'focus' in client) { + return client.focus(); + } + } + if (self.clients.openWindow) { + return self.clients.openWindow('/'); + } + return null; + })); - if (data.type === 'notification') { - swNotificationRead.then(that => that.read(data)); - } }); self.addEventListener('message', (ev: ServiceWorkerGlobalScopeEventMap['message']) => {