diff --git a/src/client/app/common/scripts/compose-notification.ts b/src/client/app/common/scripts/compose-notification.ts
index cc28f75998..2e58649ac2 100644
--- a/src/client/app/common/scripts/compose-notification.ts
+++ b/src/client/app/common/scripts/compose-notification.ts
@@ -20,34 +20,6 @@ export default function(type, data): Notification {
 				icon: data.url + '?thumbnail&size=64'
 			};
 
-		case 'mention':
-			return {
-				title: `${getUserName(data.user)}さんから:`,
-				body: getNoteSummary(data),
-				icon: data.user.avatarUrl + '?thumbnail&size=64'
-			};
-
-		case 'reply':
-			return {
-				title: `${getUserName(data.user)}さんから返信:`,
-				body: getNoteSummary(data),
-				icon: data.user.avatarUrl + '?thumbnail&size=64'
-			};
-
-		case 'quote':
-			return {
-				title: `${getUserName(data.user)}さんが引用:`,
-				body: getNoteSummary(data),
-				icon: data.user.avatarUrl + '?thumbnail&size=64'
-			};
-
-		case 'reaction':
-			return {
-				title: `${getUserName(data.user)}: ${getReactionEmoji(data.reaction)}:`,
-				body: getNoteSummary(data.note),
-				icon: data.user.avatarUrl + '?thumbnail&size=64'
-			};
-
 		case 'unread_messaging_message':
 			return {
 				title: `${getUserName(data.user)}さんからメッセージ:`,
@@ -62,6 +34,40 @@ export default function(type, data): Notification {
 				icon: data.parent.avatarUrl + '?thumbnail&size=64'
 			};
 
+		case 'notification':
+			switch (data.type) {
+				case 'mention':
+					return {
+						title: `${getUserName(data.user)}さんから:`,
+						body: getNoteSummary(data),
+						icon: data.user.avatarUrl + '?thumbnail&size=64'
+					};
+
+				case 'reply':
+					return {
+						title: `${getUserName(data.user)}さんから返信:`,
+						body: getNoteSummary(data),
+						icon: data.user.avatarUrl + '?thumbnail&size=64'
+					};
+
+				case 'quote':
+					return {
+						title: `${getUserName(data.user)}さんが引用:`,
+						body: getNoteSummary(data),
+						icon: data.user.avatarUrl + '?thumbnail&size=64'
+					};
+
+				case 'reaction':
+					return {
+						title: `${getUserName(data.user)}: ${getReactionEmoji(data.reaction)}:`,
+						body: getNoteSummary(data.note),
+						icon: data.user.avatarUrl + '?thumbnail&size=64'
+					};
+
+				default:
+					return null;
+			}
+
 		default:
 			return null;
 	}
diff --git a/src/client/app/desktop/script.ts b/src/client/app/desktop/script.ts
index 201ab0a83d..beb358c7ee 100644
--- a/src/client/app/desktop/script.ts
+++ b/src/client/app/desktop/script.ts
@@ -115,6 +115,15 @@ function registerNotifications(stream: HomeStreamManager) {
 	});
 
 	function attach(connection) {
+		connection.on('notification', notification => {
+			const _n = composeNotification('notification', notification);
+			const n = new Notification(_n.title, {
+				body: _n.body,
+				icon: _n.icon
+			});
+			setTimeout(n.close.bind(n), 6000);
+		});
+
 		connection.on('drive_file_created', file => {
 			const _n = composeNotification('drive_file_created', file);
 			const n = new Notification(_n.title, {
@@ -124,33 +133,6 @@ function registerNotifications(stream: HomeStreamManager) {
 			setTimeout(n.close.bind(n), 5000);
 		});
 
-		connection.on('mention', note => {
-			const _n = composeNotification('mention', note);
-			const n = new Notification(_n.title, {
-				body: _n.body,
-				icon: _n.icon
-			});
-			setTimeout(n.close.bind(n), 6000);
-		});
-
-		connection.on('reply', note => {
-			const _n = composeNotification('reply', note);
-			const n = new Notification(_n.title, {
-				body: _n.body,
-				icon: _n.icon
-			});
-			setTimeout(n.close.bind(n), 6000);
-		});
-
-		connection.on('quote', note => {
-			const _n = composeNotification('quote', note);
-			const n = new Notification(_n.title, {
-				body: _n.body,
-				icon: _n.icon
-			});
-			setTimeout(n.close.bind(n), 6000);
-		});
-
 		connection.on('unread_messaging_message', message => {
 			const _n = composeNotification('unread_messaging_message', message);
 			const n = new Notification(_n.title, {
diff --git a/src/publishers/notify.ts b/src/publishers/notify.ts
index 0e480ef010..5b25fbf8aa 100644
--- a/src/publishers/notify.ts
+++ b/src/publishers/notify.ts
@@ -4,6 +4,7 @@ import Mute from '../models/mute';
 import { pack } from '../models/notification';
 import stream from './stream';
 import User from '../models/user';
+import pushSw from '../publishers/push-sw';
 
 export default (
 	notifiee: mongo.ObjectID,
@@ -26,9 +27,10 @@ export default (
 
 	resolve(notification);
 
+	const packed = await pack(notification);
+
 	// Publish notification event
-	stream(notifiee, 'notification',
-		await pack(notification));
+	stream(notifiee, 'notification', packed);
 
 	// Update flag
 	User.update({ _id: notifiee }, {
@@ -52,7 +54,9 @@ export default (
 			}
 			//#endregion
 
-			stream(notifiee, 'unread_notification', await pack(notification));
+			stream(notifiee, 'unread_notification', packed);
+
+			pushSw(notifiee, 'notification', packed);
 		}
 	}, 3000);
 });
diff --git a/src/services/note/create.ts b/src/services/note/create.ts
index 782ac5a4fe..feabe2f0b7 100644
--- a/src/services/note/create.ts
+++ b/src/services/note/create.ts
@@ -12,7 +12,6 @@ import notify from '../../publishers/notify';
 import NoteWatching from '../../models/note-watching';
 import watch from './watch';
 import Mute from '../../models/mute';
-import pushSw from '../../publishers/push-sw';
 import event from '../../publishers/stream';
 import parse from '../../mfm/parse';
 import { IApp } from '../../models/app';
@@ -20,56 +19,56 @@ import UserList from '../../models/user-list';
 import resolveUser from '../../remote/resolve-user';
 import Meta from '../../models/meta';
 
-type Reason = 'reply' | 'quote' | 'mention';
+type Type = 'reply' | 'renote' | 'quote' | 'mention';
 
 /**
- * ServiceWorkerへの通知を担当
+ * 通知を担当
  */
 class NotificationManager {
-	private user: IUser;
+	private notifier: IUser;
 	private note: any;
-	private list: Array<{
-		user: ILocalUser['_id'],
-		reason: Reason;
+	private queue: Array<{
+		notifiee: ILocalUser['_id'],
+		type: Type;
 	}> = [];
 
-	constructor(user: IUser, note: any) {
-		this.user = user;
+	constructor(notifier: IUser, note: any) {
+		this.notifier = notifier;
 		this.note = note;
 	}
 
-	public push(user: ILocalUser['_id'], reason: Reason) {
+	public push(notifiee: ILocalUser['_id'], type: Type) {
 		// 自分自身へは通知しない
-		if (this.user._id.equals(user)) return;
+		if (this.notifier._id.equals(notifiee)) return;
 
-		const exist = this.list.find(x => x.user.equals(user));
+		const exist = this.queue.find(x => x.notifiee.equals(notifiee));
 
 		if (exist) {
 			// 「メンションされているかつ返信されている」場合は、メンションとしての通知ではなく返信としての通知にする
-			if (reason != 'mention') {
-				exist.reason = reason;
+			if (type != 'mention') {
+				exist.type = type;
 			}
 		} else {
-			this.list.push({
-				user, reason
+			this.queue.push({
+				notifiee, type
 			});
 		}
 	}
 
 	public deliver() {
-		this.list.forEach(async x => {
-			const mentionee = x.user;
-
+		this.queue.forEach(async x => {
 			// ミュート情報を取得
 			const mentioneeMutes = await Mute.find({
-				muterId: mentionee
+				muterId: x.notifiee
 			});
 
 			const mentioneesMutedUserIds = mentioneeMutes.map(m => m.muteeId.toString());
 
 			// 通知される側のユーザーが通知する側のユーザーをミュートしていない限りは通知する
-			if (!mentioneesMutedUserIds.includes(this.user._id.toString())) {
-				pushSw(mentionee, x.reason, this.note);
+			if (!mentioneesMutedUserIds.includes(this.notifier._id.toString())) {
+				notify(x.notifiee, this.notifier._id, x.type, {
+					noteId: this.note._id
+				});
 			}
 		});
 	}
@@ -264,10 +263,6 @@ export default async (user: IUser, data: {
 			if (data.renote && data.renote.userId.equals(u._id)) return;
 
 			// Create notification
-			notify(u._id, user._id, 'mention', {
-				noteId: note._id
-			});
-
 			nm.push(u._id, 'mention');
 		});
 
@@ -371,11 +366,6 @@ export default async (user: IUser, data: {
 			}
 		});
 
-		// (自分自身へのリプライでない限りは)通知を作成
-		notify(data.reply.userId, user._id, 'reply', {
-			noteId: note._id
-		});
-
 		// Fetch watchers
 		NoteWatching.find({
 			noteId: data.reply._id,
@@ -388,9 +378,7 @@ export default async (user: IUser, data: {
 			}
 		}).then(watchers => {
 			watchers.forEach(watcher => {
-				notify(watcher.userId, user._id, 'reply', {
-					noteId: note._id
-				});
+				nm.push(watcher.userId, 'reply');
 			});
 		});
 
@@ -399,6 +387,7 @@ export default async (user: IUser, data: {
 			watch(user._id, data.reply);
 		}
 
+		// (自分自身へのリプライでない限りは)通知を作成
 		nm.push(data.reply.userId, 'reply');
 	}
 
@@ -406,9 +395,7 @@ export default async (user: IUser, data: {
 	if (data.renote) {
 		// Notify
 		const type = data.text ? 'quote' : 'renote';
-		notify(data.renote.userId, user._id, type, {
-			noteId: note._id
-		});
+		nm.push(data.renote.userId, type);
 
 		// Fetch watchers
 		NoteWatching.find({
@@ -420,9 +407,7 @@ export default async (user: IUser, data: {
 			}
 		}).then(watchers => {
 			watchers.forEach(watcher => {
-				notify(watcher.userId, user._id, type, {
-					noteId: note._id
-				});
+				nm.push(watcher.userId, type);
 			});
 		});
 
diff --git a/src/services/note/reaction/create.ts b/src/services/note/reaction/create.ts
index 5b30bb5e15..b3235e94dc 100644
--- a/src/services/note/reaction/create.ts
+++ b/src/services/note/reaction/create.ts
@@ -1,9 +1,8 @@
-import { IUser, pack as packUser, isLocalUser, isRemoteUser } from '../../../models/user';
-import Note, { INote, pack as packNote } from '../../../models/note';
+import { IUser, isLocalUser, isRemoteUser } from '../../../models/user';
+import Note, { INote } from '../../../models/note';
 import NoteReaction from '../../../models/note-reaction';
 import { publishNoteStream } from '../../../publishers/stream';
 import notify from '../../../publishers/notify';
-import pushSw from '../../../publishers/push-sw';
 import NoteWatching from '../../../models/note-watching';
 import watch from '../watch';
 import renderLike from '../../../remote/activitypub/renderer/like';
@@ -54,12 +53,6 @@ export default async (user: IUser, note: INote, reaction: string) => new Promise
 		});
 	}
 
-	pushSw(note.userId, 'reaction', {
-		user: await packUser(user, note.userId),
-		note: await packNote(note, note.userId),
-		reaction: reaction
-	});
-
 	// Fetch watchers
 	NoteWatching
 		.find({