summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2020-06-04 22:07:39 +0900
committerGitHub <noreply@github.com>2020-06-04 22:07:39 +0900
commit66de51c1ca2535b6c4de47e405e922690a1a9160 (patch)
treee6168bfd2976a75aa1d0776629abdcb506330954 /src
parentローカルのみボタンを公開範囲ピッカーに統合 (#6428) (diff)
parentmissing ; (diff)
downloadmisskey-66de51c1ca2535b6c4de47e405e922690a1a9160.tar.gz
misskey-66de51c1ca2535b6c4de47e405e922690a1a9160.tar.bz2
misskey-66de51c1ca2535b6c4de47e405e922690a1a9160.zip
feat: Observe notification read and fix #6406 (#6407)
* Resolve https://github.com/syuilo/misskey/pull/6406#issuecomment-633203670 * Improve typing * Observe notification read * capture readAllNotifications * fix * fix * Refactor * Update src/client/components/notification.vue Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> * Update src/client/components/notification.vue Co-authored-by: syuilo <Syuilotan@yahoo.co.jp> * missing ; Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
Diffstat (limited to 'src')
-rw-r--r--src/client/components/notification.vue27
-rw-r--r--src/client/components/notifications.vue11
-rw-r--r--src/client/components/post-form.vue3
-rw-r--r--src/models/entities/note.ts6
-rw-r--r--src/models/entities/notification.ts5
-rw-r--r--src/models/entities/poll.ts5
-rw-r--r--src/models/repositories/notification.ts1
-rw-r--r--src/server/api/endpoints/i/notifications.ts5
-rw-r--r--src/server/api/endpoints/notes/create.ts3
-rw-r--r--src/types.ts3
10 files changed, 55 insertions, 14 deletions
diff --git a/src/client/components/notification.vue b/src/client/components/notification.vue
index de233d14ac..9e4806f05f 100644
--- a/src/client/components/notification.vue
+++ b/src/client/components/notification.vue
@@ -90,9 +90,36 @@ export default Vue.extend({
getNoteSummary: (text: string) => noteSummary(text, this.$root.i18n.messages[this.$root.i18n.locale]),
followRequestDone: false,
groupInviteDone: false,
+ connection: null,
+ readObserver: null,
faIdCardAlt, faPlus, faQuoteLeft, faQuoteRight, faRetweet, faReply, faAt, faClock, faCheck, faPollH
};
},
+
+ mounted() {
+ 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));
+ }
+ },
+
+ beforeDestroy() {
+ if (!this.notification.isRead) {
+ this.readObserver.unobserve(this.$el);
+ this.connection.dispose();
+ }
+ },
+
methods: {
acceptFollowRequest() {
this.followRequestDone = true;
diff --git a/src/client/components/notifications.vue b/src/client/components/notifications.vue
index 36464a3096..5c5b5fb810 100644
--- a/src/client/components/notifications.vue
+++ b/src/client/components/notifications.vue
@@ -71,10 +71,13 @@ 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
+ });
+
+ notification.isRead = true;
+ }
this.prepend(notification);
},
diff --git a/src/client/components/post-form.vue b/src/client/components/post-form.vue
index 07f6f0b5d6..444bf229e0 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: {
@@ -407,7 +408,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<Notification> {
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;