diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 394577f378..bb1c41d866 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1567,7 +1567,7 @@ _notification:
youWereFollowed: "フォローされました"
youReceivedFollowRequest: "フォローリクエストが来ました"
yourFollowRequestAccepted: "フォローリクエストが承認されました"
- youWereInvitedToGroup: "グループに招待されました"
+ youWereInvitedToGroup: "{userName}があなたをグループに招待しました"
_types:
all: "すべて"
@@ -1583,6 +1583,11 @@ _notification:
groupInvited: "グループに招待された"
app: "連携アプリからの通知"
+ _actions:
+ followBack: "フォローバック"
+ reply: "返信"
+ renote: "Renote"
+
_deck:
alwaysShowMainColumn: "常にメインカラムを表示"
columnAlign: "カラムの寄せ"
diff --git a/src/client/account.ts b/src/client/account.ts
index 2c5e959dec..1ba7a28908 100644
--- a/src/client/account.ts
+++ b/src/client/account.ts
@@ -2,7 +2,7 @@ import { get, set } from 'idb-keyval';
import { reactive } from 'vue';
import { apiUrl } from '@/config';
import { waiting } from '@/os';
-import { unisonReload } from '@/scripts/unison-reload';
+import { unisonReload, reloadChannel } from '@/scripts/unison-reload';
// TODO: 他のタブと永続化されたstateを同期
@@ -89,18 +89,23 @@ export function updateAccount(data) {
}
export function refreshAccount() {
- fetchAccount($i.token).then(updateAccount);
+ return fetchAccount($i.token).then(updateAccount);
}
-export async function login(token: Account['token'], showTimeline: boolean = false) {
+export async function login(token: Account['token'], href?: string) {
waiting();
if (_DEV_) console.log('logging as token ', token);
const me = await fetchAccount(token);
localStorage.setItem('account', JSON.stringify(me));
await addAccount(me.id, token);
- if (showTimeline) location.href = '/';
- else unisonReload();
+ if (href) {
+ reloadChannel.postMessage('reload');
+ location.href = href;
+ return;
+ }
+
+ unisonReload();
}
// このファイルに書きたくないけどここに書かないと何故かVeturが認識しない
diff --git a/src/client/init.ts b/src/client/init.ts
index d8dc8d4e16..96ac6f8009 100644
--- a/src/client/init.ts
+++ b/src/client/init.ts
@@ -61,11 +61,14 @@ import * as sound from '@/scripts/sound';
import { $i, refreshAccount, login, updateAccount, signout } from '@/account';
import { defaultStore, ColdDeviceStorage } from '@/store';
import { fetchInstance, instance } from '@/instance';
-import { makeHotkey } from './scripts/hotkey';
-import { search } from './scripts/search';
-import { getThemes } from './theme-store';
-import { initializeSw } from './scripts/initialize-sw';
-import { reloadChannel } from './scripts/unison-reload';
+import { makeHotkey } from '@/scripts/hotkey';
+import { search } from '@/scripts/search';
+import { getThemes } from '@/theme-store';
+import { initializeSw } from '@/scripts/initialize-sw';
+import { reloadChannel } from '@/scripts/unison-reload';
+import { deleteLoginId } from '@/scripts/login-id';
+import { getAccountFromId } from '@/scripts/get-account-from-id';
+import { SwMessage } from '@/sw/types';
console.info(`Misskey v${version}`);
@@ -142,6 +145,25 @@ const html = document.documentElement;
html.setAttribute('lang', lang);
//#endregion
+//#region loginId
+const params = new URLSearchParams(location.href);
+const loginId = params.get('loginId');
+
+if (loginId) {
+ const target = deleteLoginId(location.toString());
+
+ if (!$i || $i.id !== loginId) {
+ const account = await getAccountFromId(loginId);
+ if (account) {
+ login(account.token, target)
+ }
+ }
+
+ history.replaceState({ misskey: 'loginId' }, '', target)
+}
+
+//#endregion
+
//#region Fetch user
if ($i && $i.token) {
if (_DEV_) {
@@ -188,7 +210,7 @@ fetchInstance().then(() => {
stream.init($i);
const app = createApp(await (
- window.location.search === '?zen' ? import('@/ui/zen.vue') :
+ location.search === '?zen' ? import('@/ui/zen.vue') :
!$i ? import('@/ui/visitor.vue') :
ui === 'deck' ? import('@/ui/deck.vue') :
ui === 'desktop' ? import('@/ui/desktop.vue') :
@@ -217,6 +239,33 @@ components(app);
await router.isReady();
+//#region Listen message from SW
+navigator.serviceWorker.addEventListener('message', ev => {
+ if (_DEV_) {
+ console.log('sw msg', ev.data);
+ }
+
+ const data = ev.data as SwMessage;
+ if (data.type !== 'order') return;
+
+ if (data.loginId !== $i?.id) {
+ return getAccountFromId(data.loginId).then(account => {
+ if (!account) return;
+ return login(account.token, data.url);
+ })
+ }
+
+ switch (data.order) {
+ case 'post':
+ return post(data.options);
+ case 'push':
+ return router.push(data.url);
+ default:
+ return;
+ }
+});
+//#endregion
+
//document.body.innerHTML = '
';
app.mount('body');
diff --git a/src/client/scripts/get-account-from-id.ts b/src/client/scripts/get-account-from-id.ts
new file mode 100644
index 0000000000..52b758b39c
--- /dev/null
+++ b/src/client/scripts/get-account-from-id.ts
@@ -0,0 +1,7 @@
+import { get } from 'idb-keyval';
+
+export async function getAccountFromId(id: string) {
+ const accounts = await get('accounts') as { token: string; id: string; }[];
+ if (!accounts) console.log('Accounts are not recorded');
+ return accounts.find(e => e.id === id)
+}
diff --git a/src/client/scripts/initialize-sw.ts b/src/client/scripts/initialize-sw.ts
index 1eef8aa0f8..785096f284 100644
--- a/src/client/scripts/initialize-sw.ts
+++ b/src/client/scripts/initialize-sw.ts
@@ -1,8 +1,7 @@
import { instance } from '@/instance';
import { $i } from '@/account';
-import { api, post } from '@/os';
+import { api } from '@/os';
import { lang } from '@/config';
-import { SwMessage } from '@/sw/types';
export async function initializeSw() {
if (instance.swPublickey &&
@@ -50,18 +49,6 @@ export async function initializeSw() {
}
}
-navigator.serviceWorker.addEventListener('message', ev => {
- const data = ev.data as SwMessage;
- if (data.type !== 'order') return;
-
- switch (data.order) {
- case 'post':
- return post(data.options);
- default:
- return;
- }
-});
-
/**
* Convert the URL safe base64 string to a Uint8Array
* @param base64String base64 string
diff --git a/src/client/scripts/login-id.ts b/src/client/scripts/login-id.ts
new file mode 100644
index 0000000000..157b40c572
--- /dev/null
+++ b/src/client/scripts/login-id.ts
@@ -0,0 +1,11 @@
+export function appendLoginId(url: string, loginId: string) {
+ const u = new URL(url, origin);
+ u.searchParams.append('loginId', loginId);
+ return u.toString();
+}
+
+export function deleteLoginId(url: string) {
+ const u = new URL(url);
+ u.searchParams.delete('loginId');
+ return u.toString();
+}
diff --git a/src/client/sw/create-notification.ts b/src/client/sw/create-notification.ts
index 9382b88345..1519ce48f3 100644
--- a/src/client/sw/create-notification.ts
+++ b/src/client/sw/create-notification.ts
@@ -6,7 +6,7 @@ declare var self: ServiceWorkerGlobalScope;
import { getNoteSummary } from '../../misc/get-note-summary';
import getUserName from '../../misc/get-user-name';
import { swLang } from '@/sw/lang';
-import { I18n } from '@/scripts/i18n';
+import { I18n } from '../../misc/i18n';
import { pushNotificationData } from '../../types';
export async function createNotification(data: pushNotificationData) {
@@ -18,107 +18,162 @@ async function composeNotification(data: pushNotificationData): Promise<[string,
if (!swLang.i18n) swLang.fetchLocale();
const i18n = await swLang.i18n as I18n;
const { t } = i18n;
+ const { body } = data;
switch (data.type) {
/*
case 'driveFileCreated': // TODO (Server Side)
return [t('_notification.fileUploaded'), {
- body: data.body.name,
- icon: data.body.url,
+ body: body.name,
+ icon: body.url,
data
}];
*/
case 'notification':
- switch (data.body.type) {
+ switch (body.type) {
+ case 'follow':
+ return [t('_notification.youWereFollowed'), {
+ body: getUserName(body.user),
+ icon: body.user.avatarUrl,
+ data,
+ actions: [
+ {
+ action: 'follow',
+ title: t('_notification._actions.followBack')
+ }
+ ],
+ }];
+
case 'mention':
- return [t('_notification.youGotMention', { name: getUserName(data.body.user) }), {
- body: getNoteSummary(data.body.note, i18n.locale),
- icon: data.body.user.avatarUrl,
+ return [t('_notification.youGotMention', { name: getUserName(body.user) }), {
+ body: getNoteSummary(body.note, i18n.locale),
+ icon: body.user.avatarUrl,
+ data,
+ actions: [
+ {
+ action: 'reply',
+ title: t('_notification._actions.reply')
+ }
+ ],
+ }];
+
+ case 'reply':
+ return [t('_notification.youGotReply', { name: getUserName(body.user) }), {
+ body: getNoteSummary(body.note, i18n.locale),
+ icon: body.user.avatarUrl,
+ data,
+ actions: [
+ {
+ action: 'reply',
+ title: t('_notification._actions.reply')
+ }
+ ],
+ }];
+
+ case 'renote':
+ return [t('_notification.youRenoted', { name: getUserName(body.user) }), {
+ body: getNoteSummary(body.note.renote, i18n.locale),
+ icon: body.user.avatarUrl,
data,
actions: [
{
action: 'showUser',
- title: 'showUser'
+ title: getUserName(body.user)
}
- ]
- }];
-
- case 'reply':
- return [t('_notification.youGotReply', { name: getUserName(data.body.user) }), {
- body: getNoteSummary(data.body.note, i18n.locale),
- icon: data.body.user.avatarUrl,
- data,
- }];
-
- case 'renote':
- return [t('_notification.youRenoted', { name: getUserName(data.body.user) }), {
- body: getNoteSummary(data.body.note, i18n.locale),
- icon: data.body.user.avatarUrl,
- data,
+ ],
}];
case 'quote':
- return [t('_notification.youGotQuote', { name: getUserName(data.body.user) }), {
- body: getNoteSummary(data.body.note, i18n.locale),
- icon: data.body.user.avatarUrl,
+ return [t('_notification.youGotQuote', { name: getUserName(body.user) }), {
+ body: getNoteSummary(body.note, i18n.locale),
+ icon: body.user.avatarUrl,
data,
+ actions: [
+ {
+ action: 'reply',
+ title: t('_notification._actions.reply')
+ },
+ {
+ action: 'renote',
+ title: t('_notification._actions.renote')
+ }
+ ],
}];
case 'reaction':
- return [`${data.body.reaction} ${getUserName(data.body.user)}`, {
- body: getNoteSummary(data.body.note, i18n.locale),
- icon: data.body.user.avatarUrl,
+ return [`${body.reaction} ${getUserName(body.user)}`, {
+ body: getNoteSummary(body.note, i18n.locale),
+ icon: body.user.avatarUrl,
data,
+ actions: [
+ {
+ action: 'showUser',
+ title: getUserName(body.user)
+ }
+ ],
}];
case 'pollVote':
- return [t('_notification.youGotPoll', { name: getUserName(data.body.user) }), {
- body: getNoteSummary(data.body.note, i18n.locale),
- icon: data.body.user.avatarUrl,
- data,
- }];
-
- case 'follow':
- return [t('_notification.youWereFollowed'), {
- body: getUserName(data.body.user),
- icon: data.body.user.avatarUrl,
+ return [t('_notification.youGotPoll', { name: getUserName(body.user) }), {
+ body: getNoteSummary(body.note, i18n.locale),
+ icon: body.user.avatarUrl,
data,
}];
case 'receiveFollowRequest':
return [t('_notification.youReceivedFollowRequest'), {
- body: getUserName(data.body.user),
- icon: data.body.user.avatarUrl,
+ body: getUserName(body.user),
+ icon: body.user.avatarUrl,
data,
+ actions: [
+ {
+ action: 'accept',
+ title: t('accept')
+ },
+ {
+ action: 'reject',
+ title: t('reject')
+ }
+ ],
}];
case 'followRequestAccepted':
return [t('_notification.yourFollowRequestAccepted'), {
- body: getUserName(data.body.user),
- icon: data.body.user.avatarUrl,
+ body: getUserName(body.user),
+ icon: body.user.avatarUrl,
data,
}];
case 'groupInvited':
- return [t('_notification.youWereInvitedToGroup'), {
- body: data.body.group.name,
+ return [t('_notification.youWereInvitedToGroup', { userName: getUserName(body.user) }), {
+ body: body.invitation.group.name,
data,
+ actions: [
+ {
+ action: 'accept',
+ title: t('accept')
+ },
+ {
+ action: 'reject',
+ title: t('reject')
+ }
+ ],
}];
default:
return null;
}
case 'unreadMessagingMessage':
- if (data.body.groupId === null) {
- return [t('_notification.youGotMessagingMessageFromUser', { name: getUserName(data.body.user) }), {
- icon: data.body.user.avatarUrl,
- tag: `messaging:user:${data.body.user.id}`,
+ if (body.groupId === null) {
+ return [t('_notification.youGotMessagingMessageFromUser', { name: getUserName(body.user) }), {
+ icon: body.user.avatarUrl,
+ tag: `messaging:user:${body.userId}`,
data,
}];
}
- return [t('_notification.youGotMessagingMessageFromGroup', { name: data.body.group.name }), {
- icon: data.body.user.avatarUrl,
- tag: `messaging:group:${data.body.group.id}`,
+ return [t('_notification.youGotMessagingMessageFromGroup', { name: body.group.name }), {
+ icon: body.user.avatarUrl,
+ tag: `messaging:group:${body.groupId}`,
data,
}];
default:
diff --git a/src/client/sw/notification-read.ts b/src/client/sw/notification-read.ts
index 4f9664a8fe..093322a8b7 100644
--- a/src/client/sw/notification-read.ts
+++ b/src/client/sw/notification-read.ts
@@ -2,12 +2,12 @@ declare var self: ServiceWorkerGlobalScope;
import { get } from 'idb-keyval';
import { pushNotificationData } from '../../types';
+import { api } from './operations';
type Accounts = {
[x: string]: {
queue: string[],
- timeout: number | null,
- token: string,
+ timeout: number | null
}
};
@@ -15,14 +15,13 @@ class SwNotificationRead {
private accounts: Accounts = {};
public async construct() {
- const accounts = await get('accounts') as { token: string, id: string }[];
- if (!accounts) Error('Account is not recorded');
+ const accounts = await get('accounts');
+ if (!accounts) Error('Accounts are not recorded');
this.accounts = accounts.reduce((acc, e) => {
acc[e.id] = {
queue: [],
- timeout: null,
- token: e.token,
+ timeout: null
};
return acc;
}, {} as Accounts);
@@ -36,21 +35,14 @@ class SwNotificationRead {
const account = this.accounts[data.userId];
- account.queue.push(data.body.id);
+ account.queue.push(data.body.id as string);
// 最後の呼び出しから200ms待ってまとめて処理する
if (account.timeout) clearTimeout(account.timeout);
account.timeout = setTimeout(() => {
account.timeout = null;
- console.info(account.token, account.queue);
- fetch(`${location.origin}/api/notifications/read`, {
- method: 'POST',
- body: JSON.stringify({
- i: account.token,
- notificationIds: account.queue
- })
- });
+ api('notifications/read', data.userId, { notificationIds: account.queue });
}, 200);
}
}
diff --git a/src/client/sw/open-client.ts b/src/client/sw/open-client.ts
deleted file mode 100644
index 5ef1cee434..0000000000
--- a/src/client/sw/open-client.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Openers
- * クライアントを開く関数。
- * ユーザー、ノート、投稿フォーム、トークルーム
- */
-declare var self: ServiceWorkerGlobalScope;
-
-import { SwMessage, swMessageOrderType } from './types';
-
-// rendered acctからユーザーを開く
-export async function openUser(acct: string, loginId: string) {
- open('push-user', { acct }, `${origin}/@${acct}?loginId=${loginId}`, loginId)
-}
-
-// post-formのオプションから投稿フォームを開く
-export async function openPost(options: any, loginId: string) {
- // Build share queries from options
- let url = `${origin}/?`;
- if (options.initialText) url += `text=${options.initialText}&`;
- if (options.reply) url += `replyId=${options.reply.id}&`;
- if (options.renote) url += `renoteId=${options.renote.id}&`;
- url += `loginId=${loginId}`;
-
- open('post', { options }, url, loginId)
-}
-
-async function open(order: swMessageOrderType, query: any, url: string, loginId: string) {
- const client = await self.clients.matchAll({
- includeUncontrolled: true,
- type: 'window'
- }).then(clients => clients.length > 0 ? clients[0] : null);
-
- if (client) {
- client.postMessage({ type: 'order', ...query, order, loginId, url } as SwMessage);
-
- if ('focus' in client) (client as any).focus();
- return;
- }
-
- return self.clients.openWindow(url);
-}
diff --git a/src/client/sw/operations.ts b/src/client/sw/operations.ts
new file mode 100644
index 0000000000..03a0b03b65
--- /dev/null
+++ b/src/client/sw/operations.ts
@@ -0,0 +1,72 @@
+/*
+ * Operations
+ * 各種操作
+ */
+declare var self: ServiceWorkerGlobalScope;
+
+import { SwMessage, swMessageOrderType } from './types';
+import renderAcct from '../../misc/acct/render';
+import { getAccountFromId } from '@/scripts/get-account-from-id';
+import { appendLoginId } from '@/scripts/login-id';
+
+export async function api(endpoint: string, userId: string, options: any = {}) {
+ const account = await getAccountFromId(userId)
+ if (!account) return;
+
+ return fetch(`${origin}/api/${endpoint}`, {
+ method: 'POST',
+ body: JSON.stringify({
+ i: account.token,
+ ...options
+ }),
+ credentials: 'omit',
+ cache: 'no-cache',
+ }).then(async res => {
+ if (!res.ok) Error(`Error while fetching: ${await res.text()}`);
+
+ if (res.status === 200) return res.json();
+ return;
+ })
+}
+
+// rendered acctからユーザーを開く
+export function openUser(acct: string, loginId: string) {
+ return openClient('push', `/@${acct}`, loginId, { acct })
+}
+
+// noteIdからノートを開く
+export function openNote(noteId: string, loginId: string) {
+ return openClient('push', `/notes/${noteId}`, loginId, { noteId })
+}
+
+export async function openChat(body: any, loginId: string) {
+ if (body.groupId === null) {
+ return openClient('push', `/my/messaging/${renderAcct(body.user)}`, loginId, { body })
+ } else {
+ return openClient('push', `/my/messaging/group/${body.groupId}`, loginId, { body })
+ }
+}
+
+// post-formのオプションから投稿フォームを開く
+export async function openPost(options: any, loginId: string) {
+ // クエリを作成しておく
+ let url = `/share?`;
+ if (options.initialText) url += `text=${options.initialText}&`;
+ if (options.reply) url += `replyId=${options.reply.id}&`;
+ if (options.renote) url += `renoteId=${options.renote.id}&`;
+
+ return openClient('post', url, loginId, { options })
+}
+
+export async function openClient(order: swMessageOrderType, url: string, loginId: string, query: any = {}) {
+ const client = await self.clients.matchAll({
+ type: 'window'
+ }).then(clients => clients.length > 0 ? clients[0] as WindowClient : null);
+
+ if (client) {
+ client.postMessage({ type: 'order', ...query, order, loginId, url } as SwMessage);
+ return client;
+ }
+
+ return self.clients.openWindow(appendLoginId(url, loginId));
+}
diff --git a/src/client/sw/sw.ts b/src/client/sw/sw.ts
index 8570c30987..8af03eb130 100644
--- a/src/client/sw/sw.ts
+++ b/src/client/sw/sw.ts
@@ -7,7 +7,7 @@ import { createNotification } from '@/sw/create-notification';
import { swLang } from '@/sw/lang';
import { swNotificationRead } from '@/sw/notification-read';
import { pushNotificationData } from '../../types';
-import { openUser } from './open-client';
+import * as ope from './operations';
import renderAcct from '../../misc/acct/render';
//#region Lifecycle: Install
@@ -55,51 +55,125 @@ self.addEventListener('push', ev => {
return createNotification(data);
case 'readAllNotifications':
for (const n of await self.registration.getNotifications()) {
- n.close();
+ 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 notification of await self.registration.getNotifications()) {
- if (data.body.notificationIds.includes(notification.data.body.id)) {
- notification.close();
+ 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;
}
}));
});
//#endregion
//#region Notification
-self.addEventListener('notificationclick', async ev => {
- const { action, notification } = ev;
- const data: pushNotificationData = notification.data;
+self.addEventListener('notificationclick', ev => {
+ ev.waitUntil((async () => {
- switch (action) {
- case 'showUser':
- switch (data.body.type) {
- case 'reaction':
- return openUser(renderAcct(data.body.user), data.userId);
-
- default:
- if ('note' in data.body) {
- return openUser(renderAcct(data.body.data.user), data.userId);
- }
- }
- break;
- default:
+ if (_DEV_) {
+ console.log('notificationclick', ev.action, ev.notification.data);
}
- // notification.close();
+ const { action, notification } = ev;
+ const data: pushNotificationData = notification.data;
+ const { type, userId: id, body } = data;
+ let client: WindowClient | null = null;
+ let close = true;
+
+ switch (action) {
+ case 'follow':
+ client = await ope.api('following/create', id, { userId: body.userId });
+ break;
+ case 'showUser':
+ client = await ope.openUser(renderAcct(body.user), id);
+ if (body.type !== 'renote') close = false;
+ break;
+ case 'reply':
+ client = await ope.openPost({ reply: body.note }, id);
+ break;
+ case 'renote':
+ await ope.api('notes/create', id, { renoteId: body.note.id });
+ break;
+ case 'accept':
+ if (body.type === 'receiveFollowRequest') {
+ await ope.api('following/requests/accept', id, { userId: body.userId });
+ } else if (body.type === 'groupInvited') {
+ await ope.api('users/groups/invitations/accept', id, { invitationId: body.invitation.id });
+ }
+ break;
+ case 'reject':
+ if (body.type === 'receiveFollowRequest') {
+ await ope.api('following/requests/reject', id, { userId: body.userId });
+ } else if (body.type === 'groupInvited') {
+ await ope.api('users/groups/invitations/reject', id, { invitationId: body.invitation.id });
+ }
+ break;
+ case 'showFollowRequests':
+ client = await ope.openClient('push', '/my/follow-requests', id);
+ break;
+ default:
+ if (type === 'unreadMessagingMessage') {
+ client = await ope.openChat(body, id);
+ break;
+ }
+
+ switch (body.type) {
+ case 'receiveFollowRequest':
+ client = await ope.openClient('push', '/my/follow-requests', id);
+ break;
+ case 'groupInvited':
+ client = await ope.openClient('push', '/my/groups', id);
+ break;
+ case 'reaction':
+ client = await ope.openNote(body.note.id, id);
+ break;
+ default:
+ if ('note' in body) {
+ client = await ope.openNote(body.note.id, id);
+ break;
+ }
+ if ('user' in body) {
+ client = await ope.openUser(renderAcct(body.data.user), id);
+ break;
+ }
+ }
+ }
+
+ if (client) {
+ client.focus();
+ }
+ if (type === 'notification') {
+ swNotificationRead.then(that => that.read(data));
+ }
+ if (close) {
+ notification.close();
+ }
+
+ })())
});
self.addEventListener('notificationclose', ev => {
- const { notification } = ev;
-
- if (!notification.title.startsWith('notification')) {
- self.registration.showNotification('notificationclose', { body: `${notification?.data?.body?.id}` });
- }
- const data: pushNotificationData = notification.data;
+ const data: pushNotificationData = ev.notification.data;
if (data.type === 'notification') {
swNotificationRead.then(that => that.read(data));
@@ -108,12 +182,15 @@ self.addEventListener('notificationclose', ev => {
//#endregion
//#region When: Caught a message from the client
-self.addEventListener('message', ev => {
+self.addEventListener('message', async ev => {
switch (ev.data) {
case 'clear':
+ // Cache Storage全削除
+ await caches.keys()
+ .then(cacheNames => Promise.all(
+ cacheNames.map(name => caches.delete(name))
+ ))
return; // TODO
- default:
- break;
}
if (typeof ev.data === 'object') {
diff --git a/src/client/sw/types.ts b/src/client/sw/types.ts
index a3594d8a2e..14bf7af96a 100644
--- a/src/client/sw/types.ts
+++ b/src/client/sw/types.ts
@@ -1,4 +1,4 @@
-export type swMessageOrderType = 'post' | 'push-user' | 'push-note' | 'push-messaging-room';
+export type swMessageOrderType = 'post' | 'push';
export type SwMessage = {
type: 'order';
diff --git a/src/server/api/common/read-messaging-message.ts b/src/server/api/common/read-messaging-message.ts
index 90510bb393..14b11f9992 100644
--- a/src/server/api/common/read-messaging-message.ts
+++ b/src/server/api/common/read-messaging-message.ts
@@ -1,6 +1,7 @@
import { publishMainStream, publishGroupMessagingStream } from '../../../services/stream';
import { publishMessagingStream } from '../../../services/stream';
import { publishMessagingIndexStream } from '../../../services/stream';
+import { pushNotification } from '../../../services/push-notification';
import { User, ILocalUser, IRemoteUser } from '../../../models/entities/user';
import { MessagingMessage } from '../../../models/entities/messaging-message';
import { MessagingMessages, UserGroupJoinings, Users } from '../../../models';
@@ -12,6 +13,7 @@ import { renderReadActivity } from '../../../remote/activitypub/renderer/read';
import { renderActivity } from '../../../remote/activitypub/renderer';
import { deliver } from '../../../queue';
import orderedCollection from '../../../remote/activitypub/renderer/ordered-collection';
+import { use } from 'matter-js';
/**
* Mark messages as read
@@ -50,6 +52,23 @@ export async function readUserMessagingMessage(
if (!await Users.getHasUnreadMessagingMessage(userId)) {
// 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行
publishMainStream(userId, 'readAllMessagingMessages');
+ pushNotification(userId, 'readAllMessagingMessages', undefined);
+ } else {
+ // そのユーザーとのメッセージで未読がなければイベント発行
+ const count = await MessagingMessages.count({
+ where: {
+ userId: otherpartyId,
+ recipientId: userId,
+ isRead: false,
+ },
+ take: 1
+ })
+
+ if (!count) {
+ pushNotification(userId, 'readAllMessagingMessagesOfARoom', { userId: otherpartyId });
+ } else {
+ console.log('count')
+ }
}
}
@@ -104,6 +123,21 @@ export async function readGroupMessagingMessage(
if (!await Users.getHasUnreadMessagingMessage(userId)) {
// 全ての(いままで未読だった)自分宛てのメッセージを(これで)読みましたよというイベントを発行
publishMainStream(userId, 'readAllMessagingMessages');
+ pushNotification(userId, 'readAllMessagingMessages', undefined);
+ } else {
+ // そのグループにおいて未読がなければイベント発行
+ const unreadExist = await MessagingMessages.createQueryBuilder('message')
+ .where(`message.groupId = :groupId`, { groupId: groupId })
+ .andWhere('message.userId != :userId', { userId: userId })
+ .andWhere('NOT (:userId = ANY(message.reads))', { userId: userId })
+ .andWhere('message.createdAt > :joinedAt', { joinedAt: joining.createdAt }) // 自分が加入する前の会話については、未読扱いしない
+ .getOne().then(x => x != null)
+
+ if (!unreadExist) {
+ pushNotification(userId, 'readAllMessagingMessagesOfARoom', { groupId });
+ } else {
+ console.log('unread exist')
+ }
}
}
diff --git a/src/services/push-notification.ts b/src/services/push-notification.ts
index 70300de018..1090646924 100644
--- a/src/services/push-notification.ts
+++ b/src/services/push-notification.ts
@@ -11,6 +11,8 @@ type pushNotificationsTypes = {
'unreadMessagingMessage': PackedMessagingMessage;
'readNotifications': { notificationIds: string[] };
'readAllNotifications': undefined;
+ 'readAllMessagingMessages': undefined;
+ 'readAllMessagingMessagesOfARoom': { userId: string } | { groupId: string };
};
export async function pushNotification(userId: string, type: T, body: pushNotificationsTypes[T]) {
diff --git a/src/types.ts b/src/types.ts
index b9b676c861..c6a40510f2 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -5,7 +5,18 @@ export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as
export const mutedNoteReasons = ['word', 'manual', 'spam', 'other'] as const;
export type pushNotificationData = {
- type: 'notification' | 'unreadMessagingMessage' | 'readNotifications' | 'readAllNotifications',
- body: any,
- userId: string
+ type: 'notification' | 'unreadMessagingMessage' | 'readNotifications' | 'readAllMessagingMessagesOfARoom' | 'readAllNotifications' | 'readAllMessagingMessages';
+ body: {
+ [x: string]: any;
+ id?: string;
+ type?: typeof notificationTypes[number];
+ notificationIds?: string[];
+ user?: any;
+ userId?: string | null;
+ note?: any;
+ choice?: number;
+ reaction?: string;
+ invitation?: any;
+ };
+ userId: string;
};