From 05c7cacdd1ddc3684f0375686d99b5e4043908cd Mon Sep 17 00:00:00 2001 From: tamaina Date: Sun, 24 May 2020 18:41:40 +0900 Subject: [PATCH 01/10] Resolve https://github.com/syuilo/misskey/pull/6406#issuecomment-633203670 --- src/client/components/notifications.vue | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/client/components/notifications.vue b/src/client/components/notifications.vue index 3ed198a04c..434a87eb41 100644 --- a/src/client/components/notifications.vue +++ b/src/client/components/notifications.vue @@ -71,10 +71,11 @@ export default Vue.extend({ methods: { onNotification(notification) { - // TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない - this.$root.stream.send('readNotification', { - id: notification.id - }); + if (document.visibilityState === 'visible') { + this.$root.stream.send('readNotification', { + id: notification.id + }); + } this.prepend(notification); }, From 9bee9d20f751d2cf5833c5b2cf8be875496250c2 Mon Sep 17 00:00:00 2001 From: tamaina Date: Tue, 26 May 2020 14:33:55 +0900 Subject: [PATCH 02/10] Improve typing --- src/client/components/note.vue | 3 +++ src/client/components/post-form.vue | 3 ++- src/models/entities/note.ts | 6 ++++-- src/models/entities/notification.ts | 5 +++-- src/models/entities/poll.ts | 5 +++-- src/models/repositories/notification.ts | 1 + src/server/api/endpoints/i/notifications.ts | 5 +++-- src/server/api/endpoints/notes/create.ts | 3 ++- src/types.ts | 3 +++ 9 files changed, 24 insertions(+), 10 deletions(-) create mode 100644 src/types.ts diff --git a/src/client/components/note.vue b/src/client/components/note.vue index 6e513a4b2a..1136781ca2 100644 --- a/src/client/components/note.vue +++ b/src/client/components/note.vue @@ -278,6 +278,9 @@ export default Vue.extend({ (this as any).$root.api('promo/read', { noteId: this.appearNote.id }); + this.$root.stream.send('readNotification', { + id: notification.id + }); this.hideThisNote = true; }, diff --git a/src/client/components/post-form.vue b/src/client/components/post-form.vue index cdb61f51d5..3be09b4aea 100644 --- a/src/client/components/post-form.vue +++ b/src/client/components/post-form.vue @@ -67,6 +67,7 @@ import extractMentions from '../../misc/extract-mentions'; import getAcct from '../../misc/acct/render'; import { formatTimeString } from '../../misc/format-time-string'; import { selectDriveFile } from '../scripts/select-drive-file'; +import { noteVisibilities } from '../../types' export default Vue.extend({ components: { @@ -405,7 +406,7 @@ export default Vue.extend({ }, applyVisibility(v: string) { - this.visibility = ['public', 'home', 'followers', 'specified'].includes(v) ? v : 'public'; // v11互換性のため + this.visibility = (noteVisibilities as unknown as string[]).includes(v) ? v : 'public'; // v11互換性のため }, addVisibleUser() { diff --git a/src/models/entities/note.ts b/src/models/entities/note.ts index 79b6b5ab7d..196be1e350 100644 --- a/src/models/entities/note.ts +++ b/src/models/entities/note.ts @@ -2,6 +2,8 @@ import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typ import { User } from './user'; import { DriveFile } from './drive-file'; import { id } from '../id'; +import { noteVisibilities } from '../../types'; + @Entity() @Index('IDX_NOTE_TAGS', { synchronize: false }) @@ -102,8 +104,8 @@ export class Note { * followers ... フォロワーのみ * specified ... visibleUserIds で指定したユーザーのみ */ - @Column('enum', { enum: ['public', 'home', 'followers', 'specified'] }) - public visibility: 'public' | 'home' | 'followers' | 'specified'; + @Column('enum', { enum: noteVisibilities }) + public visibility: typeof noteVisibilities[number]; @Index({ unique: true }) @Column('varchar', { diff --git a/src/models/entities/notification.ts b/src/models/entities/notification.ts index 565645a5d6..988fdb341f 100644 --- a/src/models/entities/notification.ts +++ b/src/models/entities/notification.ts @@ -5,6 +5,7 @@ import { Note } from './note'; import { FollowRequest } from './follow-request'; import { UserGroupInvitation } from './user-group-invitation'; import { AccessToken } from './access-token'; +import { notificationTypes } from '../../types'; @Entity() export class Notification { @@ -66,10 +67,10 @@ export class Notification { */ @Index() @Column('enum', { - enum: ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app'], + enum: notificationTypes, comment: 'The type of the Notification.' }) - public type: 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollVote' | 'receiveFollowRequest' | 'followRequestAccepted' | 'groupInvited' | 'app'; + public type: typeof notificationTypes[number]; /** * 通知が読まれたかどうか diff --git a/src/models/entities/poll.ts b/src/models/entities/poll.ts index 6bb67163a2..e3bbb1c3f2 100644 --- a/src/models/entities/poll.ts +++ b/src/models/entities/poll.ts @@ -2,6 +2,7 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'type import { id } from '../id'; import { Note } from './note'; import { User } from './user'; +import { noteVisibilities } from '../../types'; @Entity() export class Poll { @@ -34,10 +35,10 @@ export class Poll { //#region Denormalized fields @Column('enum', { - enum: ['public', 'home', 'followers', 'specified'], + enum: noteVisibilities, comment: '[Denormalized]' }) - public noteVisibility: 'public' | 'home' | 'followers' | 'specified'; + public noteVisibility: typeof noteVisibilities[number]; @Index() @Column({ diff --git a/src/models/repositories/notification.ts b/src/models/repositories/notification.ts index b484c43c5e..40f43d6c15 100644 --- a/src/models/repositories/notification.ts +++ b/src/models/repositories/notification.ts @@ -19,6 +19,7 @@ export class NotificationRepository extends Repository { id: notification.id, createdAt: notification.createdAt.toISOString(), type: notification.type, + isRead: notification.isRead, userId: notification.notifierId, user: notification.notifierId ? Users.pack(notification.notifier || notification.notifierId) : null, ...(notification.type === 'mention' ? { diff --git a/src/server/api/endpoints/i/notifications.ts b/src/server/api/endpoints/i/notifications.ts index 9a2e17a717..db6772beb3 100644 --- a/src/server/api/endpoints/i/notifications.ts +++ b/src/server/api/endpoints/i/notifications.ts @@ -4,6 +4,7 @@ import { readNotification } from '../../common/read-notification'; import define from '../../define'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { Notifications, Followings, Mutings, Users } from '../../../../models'; +import { notificationTypes } from '../../../../types'; export const meta = { desc: { @@ -42,12 +43,12 @@ export const meta = { }, includeTypes: { - validator: $.optional.arr($.str.or(['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted'])), + validator: $.optional.arr($.str.or(notificationTypes as unknown as string[])), default: [] as string[] }, excludeTypes: { - validator: $.optional.arr($.str.or(['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted'])), + validator: $.optional.arr($.str.or(notificationTypes as unknown as string[])), default: [] as string[] } }, diff --git a/src/server/api/endpoints/notes/create.ts b/src/server/api/endpoints/notes/create.ts index cccf138add..5076dad82a 100644 --- a/src/server/api/endpoints/notes/create.ts +++ b/src/server/api/endpoints/notes/create.ts @@ -11,6 +11,7 @@ import { Users, DriveFiles, Notes } from '../../../../models'; import { DriveFile } from '../../../../models/entities/drive-file'; import { Note } from '../../../../models/entities/note'; import { DB_MAX_NOTE_TEXT_LENGTH } from '../../../../misc/hard-limits'; +import { noteVisibilities } from '../../../../types'; let maxNoteTextLength = 500; @@ -38,7 +39,7 @@ export const meta = { params: { visibility: { - validator: $.optional.str.or(['public', 'home', 'followers', 'specified']), + validator: $.optional.str.or(noteVisibilities as unknown as string[]), default: 'public', desc: { 'ja-JP': '投稿の公開範囲' diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000000..30a62412a8 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,3 @@ +export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app'] as const; + +export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const; From 61bdf51e900fe8a4554cd52aad1f2aa319fce7e8 Mon Sep 17 00:00:00 2001 From: tamaina Date: Tue, 26 May 2020 14:34:49 +0900 Subject: [PATCH 03/10] Observe notification read --- src/client/components/notification.vue | 16 ++++++++++++++++ src/client/components/notifications.vue | 2 ++ 2 files changed, 18 insertions(+) diff --git a/src/client/components/notification.vue b/src/client/components/notification.vue index de233d14ac..d0c85b6e2e 100644 --- a/src/client/components/notification.vue +++ b/src/client/components/notification.vue @@ -90,9 +90,25 @@ export default Vue.extend({ getNoteSummary: (text: string) => noteSummary(text, this.$root.i18n.messages[this.$root.i18n.locale]), followRequestDone: false, groupInviteDone: false, + readObserver: new IntersectionObserver((entries, observer) => { + if (!entries.some(entry => entry.isIntersecting)) return; + this.$root.stream.send('readNotification', { + id: this.notification.id + }); + entries.map(({ target }) => observer.unobserve(target)); + }), faIdCardAlt, faPlus, faQuoteLeft, faQuoteRight, faRetweet, faReply, faAt, faClock, faCheck, faPollH }; }, + + mounted() { + if (!this.notification.isRead) this.readObserver.observe(this.$el); + }, + + beforeDestroy() { + if (!this.notification.isRead) this.readObserver.unobserve(this.$el); + }, + methods: { acceptFollowRequest() { this.followRequestDone = true; diff --git a/src/client/components/notifications.vue b/src/client/components/notifications.vue index 434a87eb41..9dcb4eb109 100644 --- a/src/client/components/notifications.vue +++ b/src/client/components/notifications.vue @@ -75,6 +75,8 @@ export default Vue.extend({ this.$root.stream.send('readNotification', { id: notification.id }); + + notification.isRead = true; } this.prepend(notification); From 974abbe826ee424fe99afc39b6d20afe5f4fe5db Mon Sep 17 00:00:00 2001 From: tamaina Date: Tue, 26 May 2020 15:00:15 +0900 Subject: [PATCH 04/10] capture readAllNotifications --- src/client/components/notification.vue | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/client/components/notification.vue b/src/client/components/notification.vue index d0c85b6e2e..3c6b8524ea 100644 --- a/src/client/components/notification.vue +++ b/src/client/components/notification.vue @@ -90,6 +90,7 @@ export default Vue.extend({ getNoteSummary: (text: string) => noteSummary(text, this.$root.i18n.messages[this.$root.i18n.locale]), followRequestDone: false, groupInviteDone: false, + connection: null, readObserver: new IntersectionObserver((entries, observer) => { if (!entries.some(entry => entry.isIntersecting)) return; this.$root.stream.send('readNotification', { @@ -102,11 +103,17 @@ export default Vue.extend({ }, mounted() { - if (!this.notification.isRead) this.readObserver.observe(this.$el); + if (!this.notification.isRead) { + this.readObserver.observe(this.$el); + + this.connection = this.$root.stream.useSharedConnection('main'); + this.connection.on('readAllNotifications', () => this.readObserver.unobserve(this.$el)); + } }, beforeDestroy() { if (!this.notification.isRead) this.readObserver.unobserve(this.$el); + this.connection.dispose(); }, methods: { From 886766cca5a822ea9ad1b2b8b2c00eab37bbc109 Mon Sep 17 00:00:00 2001 From: tamaina Date: Tue, 26 May 2020 15:04:44 +0900 Subject: [PATCH 05/10] fix --- src/client/components/notification.vue | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/client/components/notification.vue b/src/client/components/notification.vue index 3c6b8524ea..4fde7d3748 100644 --- a/src/client/components/notification.vue +++ b/src/client/components/notification.vue @@ -112,8 +112,10 @@ export default Vue.extend({ }, beforeDestroy() { - if (!this.notification.isRead) this.readObserver.unobserve(this.$el); - this.connection.dispose(); + if (!this.notification.isRead) { + this.readObserver.unobserve(this.$el); + this.connection.dispose(); + } }, methods: { From d3f5001679aecf12a679d9b55b70f7bc85f28018 Mon Sep 17 00:00:00 2001 From: tamaina Date: Sun, 31 May 2020 14:54:04 +0900 Subject: [PATCH 06/10] fix --- src/client/components/note.vue | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/client/components/note.vue b/src/client/components/note.vue index 1136781ca2..6e513a4b2a 100644 --- a/src/client/components/note.vue +++ b/src/client/components/note.vue @@ -278,9 +278,6 @@ export default Vue.extend({ (this as any).$root.api('promo/read', { noteId: this.appearNote.id }); - this.$root.stream.send('readNotification', { - id: notification.id - }); this.hideThisNote = true; }, From 7c38cda8eecaf1dd54971400d8cfb568a9dc8023 Mon Sep 17 00:00:00 2001 From: tamaina Date: Sun, 31 May 2020 14:57:22 +0900 Subject: [PATCH 07/10] Refactor --- src/client/components/notification.vue | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/client/components/notification.vue b/src/client/components/notification.vue index 4fde7d3748..6c423fdecd 100644 --- a/src/client/components/notification.vue +++ b/src/client/components/notification.vue @@ -91,21 +91,23 @@ export default Vue.extend({ followRequestDone: false, groupInviteDone: false, connection: null, - readObserver: new IntersectionObserver((entries, observer) => { - if (!entries.some(entry => entry.isIntersecting)) return; - this.$root.stream.send('readNotification', { - id: this.notification.id - }); - entries.map(({ target }) => observer.unobserve(target)); - }), + readObserver: null, faIdCardAlt, faPlus, faQuoteLeft, faQuoteRight, faRetweet, faReply, faAt, faClock, faCheck, faPollH }; }, mounted() { - if (!this.notification.isRead) { + if (!this.notification.isRead ) { + this.readObserver = new IntersectionObserver((entries, observer) => { + if (!entries.some(entry => entry.isIntersecting)) return; + this.$root.stream.send('readNotification', { + id: this.notification.id + }); + entries.map(({ target }) => observer.unobserve(target)); + }) + this.readObserver.observe(this.$el); - + this.connection = this.$root.stream.useSharedConnection('main'); this.connection.on('readAllNotifications', () => this.readObserver.unobserve(this.$el)); } From 7e093aee5efaaff4d04c9bde680da712d2e4e9f7 Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 3 Jun 2020 13:42:26 +0900 Subject: [PATCH 08/10] Update src/client/components/notification.vue Co-authored-by: syuilo --- src/client/components/notification.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/components/notification.vue b/src/client/components/notification.vue index 6c423fdecd..a68a988d19 100644 --- a/src/client/components/notification.vue +++ b/src/client/components/notification.vue @@ -97,7 +97,7 @@ export default Vue.extend({ }, mounted() { - if (!this.notification.isRead ) { + if (!this.notification.isRead) { this.readObserver = new IntersectionObserver((entries, observer) => { if (!entries.some(entry => entry.isIntersecting)) return; this.$root.stream.send('readNotification', { From 2480fcdc5384ed8f4de04ac37262a04cef88b0b5 Mon Sep 17 00:00:00 2001 From: tamaina Date: Wed, 3 Jun 2020 13:42:32 +0900 Subject: [PATCH 09/10] Update src/client/components/notification.vue Co-authored-by: syuilo --- src/client/components/notification.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/components/notification.vue b/src/client/components/notification.vue index a68a988d19..9e4806f05f 100644 --- a/src/client/components/notification.vue +++ b/src/client/components/notification.vue @@ -104,7 +104,7 @@ export default Vue.extend({ id: this.notification.id }); entries.map(({ target }) => observer.unobserve(target)); - }) + }); this.readObserver.observe(this.$el); From fecb12bae1291b03a7a77eb0a71f81df812e5b7b Mon Sep 17 00:00:00 2001 From: tamaina Date: Thu, 4 Jun 2020 19:13:24 +0900 Subject: [PATCH 10/10] missing ; --- src/client/components/post-form.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/components/post-form.vue b/src/client/components/post-form.vue index 3be09b4aea..3b8d1f377d 100644 --- a/src/client/components/post-form.vue +++ b/src/client/components/post-form.vue @@ -67,7 +67,7 @@ import extractMentions from '../../misc/extract-mentions'; import getAcct from '../../misc/acct/render'; import { formatTimeString } from '../../misc/format-time-string'; import { selectDriveFile } from '../scripts/select-drive-file'; -import { noteVisibilities } from '../../types' +import { noteVisibilities } from '../../types'; export default Vue.extend({ components: {