* 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>
This commit is contained in:
commit
66de51c1ca
10 changed files with 55 additions and 14 deletions
|
@ -90,9 +90,36 @@ export default Vue.extend({
|
||||||
getNoteSummary: (text: string) => noteSummary(text, this.$root.i18n.messages[this.$root.i18n.locale]),
|
getNoteSummary: (text: string) => noteSummary(text, this.$root.i18n.messages[this.$root.i18n.locale]),
|
||||||
followRequestDone: false,
|
followRequestDone: false,
|
||||||
groupInviteDone: false,
|
groupInviteDone: false,
|
||||||
|
connection: null,
|
||||||
|
readObserver: null,
|
||||||
faIdCardAlt, faPlus, faQuoteLeft, faQuoteRight, faRetweet, faReply, faAt, faClock, faCheck, faPollH
|
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: {
|
methods: {
|
||||||
acceptFollowRequest() {
|
acceptFollowRequest() {
|
||||||
this.followRequestDone = true;
|
this.followRequestDone = true;
|
||||||
|
|
|
@ -71,11 +71,14 @@ export default Vue.extend({
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
onNotification(notification) {
|
onNotification(notification) {
|
||||||
// TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない
|
if (document.visibilityState === 'visible') {
|
||||||
this.$root.stream.send('readNotification', {
|
this.$root.stream.send('readNotification', {
|
||||||
id: notification.id
|
id: notification.id
|
||||||
});
|
});
|
||||||
|
|
||||||
|
notification.isRead = true;
|
||||||
|
}
|
||||||
|
|
||||||
this.prepend(notification);
|
this.prepend(notification);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,7 @@ import extractMentions from '../../misc/extract-mentions';
|
||||||
import getAcct from '../../misc/acct/render';
|
import getAcct from '../../misc/acct/render';
|
||||||
import { formatTimeString } from '../../misc/format-time-string';
|
import { formatTimeString } from '../../misc/format-time-string';
|
||||||
import { selectDriveFile } from '../scripts/select-drive-file';
|
import { selectDriveFile } from '../scripts/select-drive-file';
|
||||||
|
import { noteVisibilities } from '../../types';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
components: {
|
components: {
|
||||||
|
@ -407,7 +408,7 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
applyVisibility(v: string) {
|
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() {
|
addVisibleUser() {
|
||||||
|
|
|
@ -2,6 +2,8 @@ import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typ
|
||||||
import { User } from './user';
|
import { User } from './user';
|
||||||
import { DriveFile } from './drive-file';
|
import { DriveFile } from './drive-file';
|
||||||
import { id } from '../id';
|
import { id } from '../id';
|
||||||
|
import { noteVisibilities } from '../../types';
|
||||||
|
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
@Index('IDX_NOTE_TAGS', { synchronize: false })
|
@Index('IDX_NOTE_TAGS', { synchronize: false })
|
||||||
|
@ -102,8 +104,8 @@ export class Note {
|
||||||
* followers ... フォロワーのみ
|
* followers ... フォロワーのみ
|
||||||
* specified ... visibleUserIds で指定したユーザーのみ
|
* specified ... visibleUserIds で指定したユーザーのみ
|
||||||
*/
|
*/
|
||||||
@Column('enum', { enum: ['public', 'home', 'followers', 'specified'] })
|
@Column('enum', { enum: noteVisibilities })
|
||||||
public visibility: 'public' | 'home' | 'followers' | 'specified';
|
public visibility: typeof noteVisibilities[number];
|
||||||
|
|
||||||
@Index({ unique: true })
|
@Index({ unique: true })
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { Note } from './note';
|
||||||
import { FollowRequest } from './follow-request';
|
import { FollowRequest } from './follow-request';
|
||||||
import { UserGroupInvitation } from './user-group-invitation';
|
import { UserGroupInvitation } from './user-group-invitation';
|
||||||
import { AccessToken } from './access-token';
|
import { AccessToken } from './access-token';
|
||||||
|
import { notificationTypes } from '../../types';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class Notification {
|
export class Notification {
|
||||||
|
@ -66,10 +67,10 @@ export class Notification {
|
||||||
*/
|
*/
|
||||||
@Index()
|
@Index()
|
||||||
@Column('enum', {
|
@Column('enum', {
|
||||||
enum: ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app'],
|
enum: notificationTypes,
|
||||||
comment: 'The type of the Notification.'
|
comment: 'The type of the Notification.'
|
||||||
})
|
})
|
||||||
public type: 'follow' | 'mention' | 'reply' | 'renote' | 'quote' | 'reaction' | 'pollVote' | 'receiveFollowRequest' | 'followRequestAccepted' | 'groupInvited' | 'app';
|
public type: typeof notificationTypes[number];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通知が読まれたかどうか
|
* 通知が読まれたかどうか
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'type
|
||||||
import { id } from '../id';
|
import { id } from '../id';
|
||||||
import { Note } from './note';
|
import { Note } from './note';
|
||||||
import { User } from './user';
|
import { User } from './user';
|
||||||
|
import { noteVisibilities } from '../../types';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class Poll {
|
export class Poll {
|
||||||
|
@ -34,10 +35,10 @@ export class Poll {
|
||||||
|
|
||||||
//#region Denormalized fields
|
//#region Denormalized fields
|
||||||
@Column('enum', {
|
@Column('enum', {
|
||||||
enum: ['public', 'home', 'followers', 'specified'],
|
enum: noteVisibilities,
|
||||||
comment: '[Denormalized]'
|
comment: '[Denormalized]'
|
||||||
})
|
})
|
||||||
public noteVisibility: 'public' | 'home' | 'followers' | 'specified';
|
public noteVisibility: typeof noteVisibilities[number];
|
||||||
|
|
||||||
@Index()
|
@Index()
|
||||||
@Column({
|
@Column({
|
||||||
|
|
|
@ -19,6 +19,7 @@ export class NotificationRepository extends Repository<Notification> {
|
||||||
id: notification.id,
|
id: notification.id,
|
||||||
createdAt: notification.createdAt.toISOString(),
|
createdAt: notification.createdAt.toISOString(),
|
||||||
type: notification.type,
|
type: notification.type,
|
||||||
|
isRead: notification.isRead,
|
||||||
userId: notification.notifierId,
|
userId: notification.notifierId,
|
||||||
user: notification.notifierId ? Users.pack(notification.notifier || notification.notifierId) : null,
|
user: notification.notifierId ? Users.pack(notification.notifier || notification.notifierId) : null,
|
||||||
...(notification.type === 'mention' ? {
|
...(notification.type === 'mention' ? {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { readNotification } from '../../common/read-notification';
|
||||||
import define from '../../define';
|
import define from '../../define';
|
||||||
import { makePaginationQuery } from '../../common/make-pagination-query';
|
import { makePaginationQuery } from '../../common/make-pagination-query';
|
||||||
import { Notifications, Followings, Mutings, Users } from '../../../../models';
|
import { Notifications, Followings, Mutings, Users } from '../../../../models';
|
||||||
|
import { notificationTypes } from '../../../../types';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
desc: {
|
desc: {
|
||||||
|
@ -42,12 +43,12 @@ export const meta = {
|
||||||
},
|
},
|
||||||
|
|
||||||
includeTypes: {
|
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[]
|
default: [] as string[]
|
||||||
},
|
},
|
||||||
|
|
||||||
excludeTypes: {
|
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[]
|
default: [] as string[]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { Users, DriveFiles, Notes } from '../../../../models';
|
||||||
import { DriveFile } from '../../../../models/entities/drive-file';
|
import { DriveFile } from '../../../../models/entities/drive-file';
|
||||||
import { Note } from '../../../../models/entities/note';
|
import { Note } from '../../../../models/entities/note';
|
||||||
import { DB_MAX_NOTE_TEXT_LENGTH } from '../../../../misc/hard-limits';
|
import { DB_MAX_NOTE_TEXT_LENGTH } from '../../../../misc/hard-limits';
|
||||||
|
import { noteVisibilities } from '../../../../types';
|
||||||
|
|
||||||
let maxNoteTextLength = 500;
|
let maxNoteTextLength = 500;
|
||||||
|
|
||||||
|
@ -38,7 +39,7 @@ export const meta = {
|
||||||
|
|
||||||
params: {
|
params: {
|
||||||
visibility: {
|
visibility: {
|
||||||
validator: $.optional.str.or(['public', 'home', 'followers', 'specified']),
|
validator: $.optional.str.or(noteVisibilities as unknown as string[]),
|
||||||
default: 'public',
|
default: 'public',
|
||||||
desc: {
|
desc: {
|
||||||
'ja-JP': '投稿の公開範囲'
|
'ja-JP': '投稿の公開範囲'
|
||||||
|
|
3
src/types.ts
Normal file
3
src/types.ts
Normal file
|
@ -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;
|
Loading…
Reference in a new issue