From e8de29ae79f8b4157f6522ed895b2415fa3c877a Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Fri, 19 Oct 2018 14:34:51 +0900
Subject: [PATCH] Resolve #2935

---
 .../app/desktop/views/components/notes.vue    | 23 +++----------------
 .../desktop/views/pages/deck/deck.notes.vue   |  5 ++++
 src/client/app/init.ts                        |  6 +++++
 .../app/mobile/views/components/notes.vue     | 23 +++----------------
 src/client/app/store.ts                       | 16 ++++++++++++-
 5 files changed, 32 insertions(+), 41 deletions(-)

diff --git a/src/client/app/desktop/views/components/notes.vue b/src/client/app/desktop/views/components/notes.vue
index 768dd4f9d..38a27a129 100644
--- a/src/client/app/desktop/views/components/notes.vue
+++ b/src/client/app/desktop/views/components/notes.vue
@@ -38,7 +38,6 @@
 <script lang="ts">
 import Vue from 'vue';
 import * as config from '../../../config';
-import getNoteSummary from '../../../../../misc/get-note-summary';
 
 import XNote from './note.vue';
 
@@ -61,7 +60,6 @@ export default Vue.extend({
 			requestInitPromise: null as () => Promise<any[]>,
 			notes: [],
 			queue: [],
-			unreadCount: 0,
 			fetching: true,
 			moreFetching: false
 		};
@@ -80,12 +78,10 @@ export default Vue.extend({
 	},
 
 	mounted() {
-		document.addEventListener('visibilitychange', this.onVisibilitychange, false);
 		window.addEventListener('scroll', this.onScroll, { passive: true });
 	},
 
 	beforeDestroy() {
-		document.removeEventListener('visibilitychange', this.onVisibilitychange);
 		window.removeEventListener('scroll', this.onScroll);
 	},
 
@@ -147,10 +143,9 @@ export default Vue.extend({
 			}
 			//#endregion
 
-			// 投稿が自分のものではないかつ、タブが非表示またはスクロール位置が最上部ではないならタイトルで通知
-			if ((document.hidden || !this.isScrollTop()) && note.userId !== this.$store.state.i.id) {
-				this.unreadCount++;
-				document.title = `(${this.unreadCount}) ${getNoteSummary(note)}`;
+			// タブが非表示またはスクロール位置が最上部ではないならタイトルで通知
+			if (document.hidden || !this.isScrollTop()) {
+				this.$store.commit('pushBehindNote', note);
 			}
 
 			if (this.isScrollTop()) {
@@ -195,21 +190,9 @@ export default Vue.extend({
 			this.moreFetching = false;
 		},
 
-		clearNotification() {
-			this.unreadCount = 0;
-			document.title = (this as any).os.instanceName;
-		},
-
-		onVisibilitychange() {
-			if (!document.hidden) {
-				this.clearNotification();
-			}
-		},
-
 		onScroll() {
 			if (this.isScrollTop()) {
 				this.releaseQueue();
-				this.clearNotification();
 			}
 
 			if (this.$store.state.settings.fetchOnScroll !== false) {
diff --git a/src/client/app/desktop/views/pages/deck/deck.notes.vue b/src/client/app/desktop/views/pages/deck/deck.notes.vue
index c656fe917..e9fcba65f 100644
--- a/src/client/app/desktop/views/pages/deck/deck.notes.vue
+++ b/src/client/app/desktop/views/pages/deck/deck.notes.vue
@@ -154,6 +154,11 @@ export default Vue.extend({
 			}
 			//#endregion
 
+			// タブが非表示またはスクロール位置が最上部ではないならタイトルで通知
+			if (document.hidden || !this.isScrollTop()) {
+				this.$store.commit('pushBehindNote', note);
+			}
+
 			if (this.isScrollTop()) {
 				// Prepend the note
 				this.notes.unshift(note);
diff --git a/src/client/app/init.ts b/src/client/app/init.ts
index 7277e70d8..17d8f64c6 100644
--- a/src/client/app/init.ts
+++ b/src/client/app/init.ts
@@ -161,6 +161,12 @@ export default (callback: (launch: (router: VueRouter, api?: (os: MiOS) => API)
 				}
 			});
 
+			document.addEventListener('visibilitychange', () => {
+				if (!document.hidden) {
+					os.store.commit('clearBehindNotes');
+				}
+			}, false);
+
 			Vue.mixin({
 				data() {
 					return {
diff --git a/src/client/app/mobile/views/components/notes.vue b/src/client/app/mobile/views/components/notes.vue
index 055b731f3..53ffe49c5 100644
--- a/src/client/app/mobile/views/components/notes.vue
+++ b/src/client/app/mobile/views/components/notes.vue
@@ -37,7 +37,6 @@
 
 <script lang="ts">
 import Vue from 'vue';
-import getNoteSummary from '../../../../../misc/get-note-summary';
 
 const displayLimit = 30;
 
@@ -54,7 +53,6 @@ export default Vue.extend({
 			requestInitPromise: null as () => Promise<any[]>,
 			notes: [],
 			queue: [],
-			unreadCount: 0,
 			fetching: true,
 			moreFetching: false
 		};
@@ -83,12 +81,10 @@ export default Vue.extend({
 	},
 
 	mounted() {
-		document.addEventListener('visibilitychange', this.onVisibilitychange, false);
 		window.addEventListener('scroll', this.onScroll, { passive: true });
 	},
 
 	beforeDestroy() {
-		document.removeEventListener('visibilitychange', this.onVisibilitychange);
 		window.removeEventListener('scroll', this.onScroll);
 	},
 
@@ -146,10 +142,9 @@ export default Vue.extend({
 			}
 			//#endregion
 
-			// 投稿が自分のものではないかつ、タブが非表示またはスクロール位置が最上部ではないならタイトルで通知
-			if ((document.hidden || !this.isScrollTop()) && note.userId !== this.$store.state.i.id) {
-				this.unreadCount++;
-				document.title = `(${this.unreadCount}) ${getNoteSummary(note)}`;
+			// タブが非表示またはスクロール位置が最上部ではないならタイトルで通知
+			if (document.hidden || !this.isScrollTop()) {
+				this.$store.commit('pushBehindNote', note);
 			}
 
 			if (this.isScrollTop()) {
@@ -187,21 +182,9 @@ export default Vue.extend({
 			this.moreFetching = false;
 		},
 
-		clearNotification() {
-			this.unreadCount = 0;
-			document.title = (this as any).os.instanceName;
-		},
-
-		onVisibilitychange() {
-			if (!document.hidden) {
-				this.clearNotification();
-			}
-		},
-
 		onScroll() {
 			if (this.isScrollTop()) {
 				this.releaseQueue();
-				this.clearNotification();
 			}
 
 			if (this.$store.state.settings.fetchOnScroll !== false) {
diff --git a/src/client/app/store.ts b/src/client/app/store.ts
index 160e62fa4..7c3a403c8 100644
--- a/src/client/app/store.ts
+++ b/src/client/app/store.ts
@@ -5,6 +5,7 @@ import * as nestedProperty from 'nested-property';
 import MiOS from './mios';
 import { hostname } from './config';
 import { erase } from '../../prelude/array';
+import getNoteSummary from '../../misc/get-note-summary';
 
 const defaultSettings = {
 	home: null,
@@ -73,7 +74,8 @@ export default (os: MiOS) => new Vuex.Store({
 		i: null,
 		indicate: false,
 		uiHeaderHeight: 0,
-		navHook: null
+		navHook: null,
+		behindNotes: []
 	},
 
 	getters: {
@@ -99,6 +101,18 @@ export default (os: MiOS) => new Vuex.Store({
 
 		navHook(state, callback) {
 			state.navHook = callback;
+		},
+
+		pushBehindNote(state, note) {
+			if (note.userId === state.i.id) return;
+			if (state.behindNotes.some(n => n.id === note.id)) return;
+			state.behindNotes.push(note);
+			document.title = `(${state.behindNotes.length}) ${getNoteSummary(note)}`;
+		},
+
+		clearBehindNotes(state) {
+			state.behindNotes = [];
+			document.title = os.instanceName;
 		}
 	},