表示する通知を種別ごとに設定できるように (#6647)
* ストリーミング以外は一通り実装 * ストリーミング分も適用 * 通知のグローバル設定をサーバーサイドに保存 * グローバル通知を使うようにしたら更新されなくなるのを修正 * サーバーサイド処理 * i/notifications のパラメーター includeTypes に空配列を渡すと全部の通知が来る問題を修正 * 全て有効/無効ボタンを実装 * Squashed commit of the following: commitc3c111529eAuthor: syuilo <syuilotan@yahoo.co.jp> Date: Wed Aug 19 22:29:04 2020 +0900 12.47.0 commit2dbab66cfeAuthor: syuilo <Syuilotan@yahoo.co.jp> Date: Wed Aug 19 22:24:39 2020 +0900 New Crowdin updates (#6617) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Arabic) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Spanish) * New translations ja-JP.yml (German) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (German) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Spanish) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (German) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (Spanish) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (German) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Spanish) * New translations ja-JP.yml (German) * New translations ja-JP.yml (English) * New translations ja-JP.yml (German) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (Korean) * New translations ja-JP.yml (Korean) * New translations ja-JP.yml (Korean) * New translations ja-JP.yml (Spanish) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Korean) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (German) * New translations ja-JP.yml (Spanish) * New translations ja-JP.yml (Arabic) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (German) * New translations ja-JP.yml (German) * New translations ja-JP.yml (German) * New translations ja-JP.yml (English) * New translations ja-JP.yml (German) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (Japanese, Kansai) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Korean) * New translations ja-JP.yml (Chinese Simplified) * New translations ja-JP.yml (German) * New translations ja-JP.yml (Spanish) * New translations ja-JP.yml (Arabic) * New translations ja-JP.yml (French) * New translations ja-JP.yml (Chinese Traditional) * New translations ja-JP.yml (German) * New translations ja-JP.yml (English) * New translations ja-JP.yml (Chinese Simplified) commit01238d6b1aAuthor: Acid Chicken (硫酸鶏) <root@acid-chicken.com> Date: Wed Aug 19 22:24:02 2020 +0900 Update README.md [AUTOGEN] (#6593) commitc34f302b1cAuthor: syuilo <syuilotan@yahoo.co.jp> Date: Wed Aug 19 21:47:18 2020 +0900 enhance(client): サーバーから切断されたときにダイアログで警告を表示できるように commit6870262f8dAuthor: syuilo <syuilotan@yahoo.co.jp> Date: Wed Aug 19 17:52:11 2020 +0900 enhance(client): Better element visible detection commitc54d5e7040Author: syuilo <syuilotan@yahoo.co.jp> Date: Wed Aug 19 17:51:31 2020 +0900 fix(clinet): 誤字によりスクロールイベントリスナが解除されていなかったのを修正 commit0ace009a54Author: syuilo <syuilotan@yahoo.co.jp> Date: Tue Aug 18 22:52:54 2020 +0900 fix(server): Prevent error when recieve non-json data from websocket Fix #6658 commit48e8ee440bAuthor: MeiMei <30769358+mei23@users.noreply.github.com> Date: Tue Aug 18 22:48:52 2020 +0900 WebPのアニメーションが失われるのを修正 Fix #6625 (#6649) commit9855405b89Author: syuilo <Syuilotan@yahoo.co.jp> Date: Tue Aug 18 22:44:21 2020 +0900 Channel (#6621) * wip * wip * wip * wip * wip * wip * wip * wip * wop * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * add notes * wip * wip * wip * wip * sound * wip * add kick_gaba2 * wip commit122076e8eaAuthor: MeiMei <30769358+mei23@users.noreply.github.com> Date: Sat Aug 15 04:27:19 2020 +0900 Sign (request-target) Fix #6652 (#6656) commit7c5ac2cbb4Author: syuilo <syuilotan@yahoo.co.jp> Date: Fri Aug 14 15:24:55 2020 +0900 perf(server): Add isSensitive index to improve query performance commitccda2181c1Author: MeiMei <30769358+mei23@users.noreply.github.com> Date: Fri Aug 14 00:54:33 2020 +0900 GCSに大きいファイルがアップロードできないのを修正 Fix #6254 (#6648) commitb5fe4ba9beAuthor: syuilo <syuilotan@yahoo.co.jp> Date: Thu Aug 13 23:02:43 2020 +0900 WIP: Improve admin dashboard commitfd9c7d525aMerge:080574e13ee0a44559Author: syuilo <syuilotan@yahoo.co.jp> Date: Thu Aug 13 21:27:10 2020 +0900 Merge branch 'develop' of https://github.com/syuilo/misskey into develop commit080574e13dAuthor: syuilo <syuilotan@yahoo.co.jp> Date: Thu Aug 13 21:27:06 2020 +0900 WIP: Improve admin dashboard commitee0a445590Author: MeiMei <30769358+mei23@users.noreply.github.com> Date: Thu Aug 13 20:05:01 2020 +0900 Option objectStorageSetPublicRead (#6645) commitbb342c7601Author: syuilo <syuilotan@yahoo.co.jp> Date: Thu Aug 13 19:56:46 2020 +0900 WIP: Improve admin dashboard commited17636fb9Author: syuilo <syuilotan@yahoo.co.jp> Date: Thu Aug 13 17:58:16 2020 +0900 WIP: Improve admin dashboard commitc59d7d941aAuthor: syuilo <Syuilotan@yahoo.co.jp> Date: Wed Aug 12 17:42:12 2020 +0900 Update README.md Close #6644 commit377377595aAuthor: syuilo <syuilotan@yahoo.co.jp> Date: Mon Aug 10 20:23:51 2020 +0900 enhance(client): Improve admin page commitd63aef9963Author: syuilo <syuilotan@yahoo.co.jp> Date: Mon Aug 10 13:55:00 2020 +0900 chore(client): Fix style commite9b28fa3c0Author: syuilo <syuilotan@yahoo.co.jp> Date: Mon Aug 10 13:00:10 2020 +0900 chore(client): Design tweaks commitbe255dc583Author: syuilo <syuilotan@yahoo.co.jp> Date: Mon Aug 10 12:42:51 2020 +0900 chore(client): Design tweak commit18eb7c6087Author: syuilo <syuilotan@yahoo.co.jp> Date: Mon Aug 10 12:31:22 2020 +0900 chore(client): Design tweaks commitcf29e69813Author: syuilo <syuilotan@yahoo.co.jp> Date: Mon Aug 10 12:28:35 2020 +0900 chore(client): Fix bug commit132da7e3c0Author: syuilo <syuilotan@yahoo.co.jp> Date: Mon Aug 10 12:20:58 2020 +0900 Update ja-JP.yml commit26df23bb64Author: syuilo <syuilotan@yahoo.co.jp> Date: Mon Aug 10 12:18:02 2020 +0900 chore(client): fix style commit76389ad619Author: syuilo <syuilotan@yahoo.co.jp> Date: Mon Aug 10 12:15:58 2020 +0900 chore(client): Design tweaks commit7cde8cfbf2Author: syuilo <syuilotan@yahoo.co.jp> Date: Mon Aug 10 11:51:43 2020 +0900 chore(client): Design tweaks commit4eb2ddac4eAuthor: syuilo <syuilotan@yahoo.co.jp> Date: Mon Aug 10 11:24:30 2020 +0900 chore(client): Design tweaks commitdc51eef27cMerge:bff8a23cb9c5efb9daAuthor: syuilo <syuilotan@yahoo.co.jp> Date: Mon Aug 10 10:38:00 2020 +0900 Merge branch 'develop' of https://github.com/syuilo/misskey into develop commitbff8a23cbcAuthor: syuilo <syuilotan@yahoo.co.jp> Date: Mon Aug 10 10:37:57 2020 +0900 chore(client): Design tweaks commit9c5efb9da0Author: rinsuki <428rinsuki+git@gmail.com> Date: Mon Aug 10 01:33:01 2020 +0900 Dockerのビルド時にgitを入れるように (#6639)917d3d0bd3でgitの依存関係が追加されたのにgitが入っていないのでコケていた commit48b8320e5eAuthor: rinsuki <428rinsuki+git@gmail.com> Date: Mon Aug 10 01:32:27 2020 +0900 Fix #6637 (#6638) * Fix #6637 * fix lint commit9b2ed96c1cAuthor: syuilo <syuilotan@yahoo.co.jp> Date: Sun Aug 9 15:59:38 2020 +0900 chore: Clean up commit69d9aa71f2Author: syuilo <Syuilotan@yahoo.co.jp> Date: Sun Aug 9 15:51:02 2020 +0900 Full view mode (#6636) * wuip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * Update folder.vue * wip * Update size.ts * wip * wip * Update index.vue * wip commit13683780cdAuthor: syuilo <syuilotan@yahoo.co.jp> Date: Sun Aug 9 13:49:44 2020 +0900 ✌️ commitd780e5b251Author: syuilo <syuilotan@yahoo.co.jp> Date: Sun Aug 9 13:46:19 2020 +0900 enhance(client): ミュートされたノート数を表示するようにしたり commit917d3d0bd3Author: syuilo <syuilotan@yahoo.co.jp> Date: Sat Aug 8 10:30:38 2020 +0900 chore: Update dependencies 🚀 commit4b19c53697Author: syuilo <syuilotan@yahoo.co.jp> Date: Sat Aug 8 10:27:37 2020 +0900 client: テーマコードをコピーできるようにしたり commit2d40a15d2bAuthor: syuilo <syuilotan@yahoo.co.jp> Date: Fri Aug 7 11:27:37 2020 +0900 refactor: Extract well-known services commit2bdcd22ad4Author: syuilo <syuilotan@yahoo.co.jp> Date: Tue Aug 4 23:09:48 2020 +0900 enhance(api): アクセストークンを作成する際、createdAtをlastUsedAtを揃えるようにして、未使用かどうかを判定できるように commitf73a4e1304Author: MeiMei <30769358+mei23@users.noreply.github.com> Date: Tue Aug 4 21:12:55 2020 +0900 Update .dockerignore (#6620) commitb265cdbd84Author: Xeltica <7106976+Xeltica@users.noreply.github.com> Date: Mon Aug 3 13:40:32 2020 +0900 Update CHANGELOG.md commita04d8b95c2Author: Xeltica <7106976+Xeltica@users.noreply.github.com> Date: Mon Aug 3 13:40:13 2020 +0900 Update CHANGELOG.md commit0e9a8c0cd4Author: syuilo <syuilotan@yahoo.co.jp> Date: Sun Aug 2 13:59:05 2020 +0900 fix(client): Message read state is not reactive commit5ae8a3c7e8Author: syuilo <syuilotan@yahoo.co.jp> Date: Sun Aug 2 13:49:28 2020 +0900 refactor * fix: includeTypes 未指定時に通知が返ってこなくなるバグを修正 * 最適化とバグ修正 * 挙動を修正 * Update ja-JP.yml * 不要なimportを削除 * ✌ * 不要なコードの削除 * Update notification-setting-window.vue * Update notification-setting-window.vue * 🎨 Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
This commit is contained in:
		
							parent
							
								
									6dac505af9
								
							
						
					
					
						commit
						cd0f8a4ef9
					
				
					 15 changed files with 245 additions and 44 deletions
				
			
		|  | @ -328,6 +328,10 @@ export default Vue.extend({ | |||
| 		}, | ||||
| 
 | ||||
| 		async onNotification(notification) { | ||||
| 			const t = this.$store.state.i.includingNotificationTypes; | ||||
| 			if (!!t && !t.includes(notification.type)) { | ||||
| 				return; | ||||
| 			} | ||||
| 			if (document.visibilityState === 'visible') { | ||||
| 				this.$root.stream.send('readNotification', { | ||||
| 					id: notification.id | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| <x-column :column="column" :is-stacked="isStacked" :menu="menu"> | ||||
| 	<template #header><fa :icon="faBell" style="margin-right: 8px;"/>{{ column.name }}</template> | ||||
| 
 | ||||
| 	<x-notifications/> | ||||
| 	<x-notifications :include-types="column.includingTypes"/> | ||||
| </x-column> | ||||
| </template> | ||||
| 
 | ||||
|  | @ -38,28 +38,14 @@ export default Vue.extend({ | |||
| 	}, | ||||
| 
 | ||||
| 	created() { | ||||
| 		if (this.column.notificationType == null) { | ||||
| 			this.column.notificationType = 'all'; | ||||
| 			this.$store.commit('deviceUser/updateDeckColumn', this.column); | ||||
| 		} | ||||
| 
 | ||||
| 		this.menu = [{ | ||||
| 			icon: faCog, | ||||
| 			text: this.$t('notificationType'), | ||||
| 			action: () => { | ||||
| 				this.$root.dialog({ | ||||
| 					title: this.$t('notificationType'), | ||||
| 					type: null, | ||||
| 					select: { | ||||
| 						items: ['all', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest'].map(x => ({ | ||||
| 							value: x, text: this.$t(`_notification._types.${x}`) | ||||
| 						})) | ||||
| 						default: this.column.notificationType, | ||||
| 					}, | ||||
| 					showCancelButton: true | ||||
| 				}).then(({ canceled, result: type }) => { | ||||
| 					if (canceled) return; | ||||
| 					this.column.notificationType = type; | ||||
| 			text: this.$t('notificationSetting'), | ||||
| 			action: async () => { | ||||
| 				this.$root.new(await import('../notification-setting-window.vue').then(m => m.default), { | ||||
| 					includingTypes: this.column.includingTypes, | ||||
| 				}).$on('ok', async ({ includingTypes }) => { | ||||
| 					this.$set(this.column, 'includingTypes', includingTypes); | ||||
| 					this.$store.commit('deviceUser/updateDeckColumn', this.column); | ||||
| 				}); | ||||
| 			} | ||||
|  |  | |||
							
								
								
									
										98
									
								
								src/client/components/notification-setting-window.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								src/client/components/notification-setting-window.vue
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,98 @@ | |||
| <template> | ||||
| <x-window ref="window" :width="400" :height="450" :no-padding="true" @closed="() => { $emit('closed'); destroyDom(); }" :with-ok-button="true" :ok-button-disabled="false" @ok="ok()"> | ||||
| 	<template #header>{{ $t('notificationSetting') }}</template> | ||||
| 	<div class="vv94n3oa"> | ||||
| 		<div v-if="showGlobalToggle"> | ||||
| 			<mk-switch v-model="useGlobalSetting"> | ||||
| 				{{ $t('useGlobalSetting') }} | ||||
| 				<template #desc>{{ $t('useGlobalSettingDesc') }}</template> | ||||
| 			</mk-switch> | ||||
| 		</div> | ||||
| 		<div v-if="!useGlobalSetting"> | ||||
| 			<mk-info>{{ $t('notificationSettingDesc') }}</mk-info> | ||||
| 			<mk-button inline @click="disableAll">{{ $t('disableAll') }}</mk-button> | ||||
| 			<mk-button inline @click="enableAll">{{ $t('enableAll') }}</mk-button> | ||||
| 			<mk-switch v-for="type in notificationTypes" :key="type" v-model="typesMap[type]">{{ $t(`_notification._types.${type}`) }}</mk-switch> | ||||
| 		</div> | ||||
| 	</div> | ||||
| </x-window> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import Vue, { PropType } from 'vue'; | ||||
| import XWindow from './window.vue'; | ||||
| import MkSwitch from './ui/switch.vue'; | ||||
| import MkInfo from './ui/info.vue'; | ||||
| import MkButton from './ui/button.vue'; | ||||
| import { notificationTypes } from '../../types'; | ||||
| 
 | ||||
| export default Vue.extend({ | ||||
| 	components: { | ||||
| 		XWindow, | ||||
| 		MkSwitch, | ||||
| 		MkInfo, | ||||
| 		MkButton | ||||
| 	}, | ||||
| 
 | ||||
| 	props: { | ||||
| 		includingTypes: { | ||||
| 			// TODO: これで型に合わないものを弾いてくれるのかどうか要調査 | ||||
| 			type: Array as PropType<typeof notificationTypes[number][]>, | ||||
| 			required: false, | ||||
| 			default: null, | ||||
| 		}, | ||||
| 		showGlobalToggle: { | ||||
| 			type: Boolean, | ||||
| 			required: false, | ||||
| 			default: true, | ||||
| 		} | ||||
| 	}, | ||||
| 
 | ||||
| 	data() { | ||||
| 		return { | ||||
| 			typesMap: {} as Record<typeof notificationTypes[number], boolean>, | ||||
| 			useGlobalSetting: false, | ||||
| 			notificationTypes, | ||||
| 		}; | ||||
| 	}, | ||||
| 
 | ||||
| 	created() { | ||||
| 		this.useGlobalSetting = this.includingTypes === null && this.showGlobalToggle; | ||||
| 
 | ||||
| 		for (const type of this.notificationTypes) { | ||||
| 			Vue.set(this.typesMap, type, this.includingTypes === null || this.includingTypes.includes(type)); | ||||
| 		} | ||||
| 	}, | ||||
| 
 | ||||
| 	methods: { | ||||
| 		ok() { | ||||
| 			const includingTypes = this.useGlobalSetting ? null : (Object.keys(this.typesMap) as typeof notificationTypes[number][]) | ||||
| 				.filter(type => this.typesMap[type]); | ||||
| 
 | ||||
| 			this.$emit('ok', { includingTypes }); | ||||
| 			this.$refs.window.close(); | ||||
| 		}, | ||||
| 
 | ||||
| 		disableAll() { | ||||
| 			for (const type in this.typesMap) { | ||||
| 				this.typesMap[type as typeof notificationTypes[number]] = false; | ||||
| 			} | ||||
| 		}, | ||||
| 
 | ||||
| 		enableAll() { | ||||
| 			for (const type in this.typesMap) { | ||||
| 				this.typesMap[type as typeof notificationTypes[number]] = true; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
| <style lang="scss" scoped> | ||||
| .vv94n3oa { | ||||
| 	> div { | ||||
| 		border-top: solid 1px var(--divider); | ||||
| 		padding: 24px; | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
|  | @ -17,11 +17,12 @@ | |||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import Vue from 'vue'; | ||||
| import Vue, { PropType } from 'vue'; | ||||
| import paging from '../scripts/paging'; | ||||
| import XNotification from './notification.vue'; | ||||
| import XList from './date-separated-list.vue'; | ||||
| import XNote from './note.vue'; | ||||
| import { notificationTypes } from '../../types'; | ||||
| 
 | ||||
| export default Vue.extend({ | ||||
| 	components: { | ||||
|  | @ -35,9 +36,10 @@ export default Vue.extend({ | |||
| 	], | ||||
| 
 | ||||
| 	props: { | ||||
| 		type: { | ||||
| 			type: String, | ||||
| 			required: false | ||||
| 		includeTypes: { | ||||
| 			type: Array as PropType<typeof notificationTypes[number][]>, | ||||
| 			required: false, | ||||
| 			default: null, | ||||
| 		}, | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -48,15 +50,26 @@ export default Vue.extend({ | |||
| 				endpoint: 'i/notifications', | ||||
| 				limit: 10, | ||||
| 				params: () => ({ | ||||
| 					includeTypes: this.type ? [this.type] : undefined | ||||
| 					includeTypes: this.allIncludeTypes || undefined, | ||||
| 				}) | ||||
| 			}, | ||||
| 		}; | ||||
| 	}, | ||||
| 
 | ||||
| 	computed: { | ||||
| 		allIncludeTypes() { | ||||
| 			return this.includeTypes ?? this.$store.state.i.includingNotificationTypes; | ||||
| 		} | ||||
| 	}, | ||||
| 
 | ||||
| 	watch: { | ||||
| 		type() { | ||||
| 		includeTypes() { | ||||
| 			this.reload(); | ||||
| 		}, | ||||
| 		'$store.state.i.includingNotificationTypes'() { | ||||
| 			if (this.includeTypes === null) { | ||||
| 				this.reload(); | ||||
| 			} | ||||
| 		} | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -71,16 +84,20 @@ export default Vue.extend({ | |||
| 
 | ||||
| 	methods: { | ||||
| 		onNotification(notification) { | ||||
| 			if (document.visibilityState === 'visible') { | ||||
| 			//  | ||||
| 			const isMuted = !!this.allIncludeTypes && !this.allIncludeTypes.includes(notification.type); | ||||
| 			if (isMuted || document.visibilityState === 'visible') { | ||||
| 				this.$root.stream.send('readNotification', { | ||||
| 					id: notification.id | ||||
| 				}); | ||||
| 			} | ||||
| 
 | ||||
| 			this.prepend({ | ||||
| 				...notification, | ||||
| 				isRead: document.visibilityState === 'visible' | ||||
| 			}); | ||||
| 			if (!isMuted) { | ||||
| 				this.prepend({ | ||||
| 					...notification, | ||||
| 					isRead: document.visibilityState === 'visible' | ||||
| 				}); | ||||
| 			} | ||||
| 		}, | ||||
| 
 | ||||
| 		noteUpdated(oldValue, newValue) { | ||||
|  |  | |||
|  | @ -161,6 +161,11 @@ export default Vue.extend({ | |||
| 		}, | ||||
| 
 | ||||
| 		async onNotification(notification) { | ||||
| 			const t = this.$store.state.i.includingNotificationTypes; | ||||
| 			if (!!t && !t.includes(notification.type)) { | ||||
| 				return; | ||||
| 			} | ||||
| 
 | ||||
| 			if (document.visibilityState === 'visible') { | ||||
| 				this.$root.stream.send('readNotification', { | ||||
| 					id: notification.id | ||||
|  | @ -170,7 +175,6 @@ export default Vue.extend({ | |||
| 					notification | ||||
| 				}); | ||||
| 			} | ||||
| 
 | ||||
| 			this.$root.sound('notification'); | ||||
| 		}, | ||||
| 
 | ||||
|  |  | |||
|  | @ -22,6 +22,9 @@ | |||
| 			<mk-button @click="readAllUnreadNotes">{{ $t('markAsReadAllUnreadNotes') }}</mk-button> | ||||
| 			<mk-button @click="readAllMessagingMessages">{{ $t('markAsReadAllTalkMessages') }}</mk-button> | ||||
| 		</div> | ||||
| 		<div class="_content"> | ||||
| 			<mk-button @click="configure">{{ $t('notificationSetting') }}</mk-button> | ||||
| 		</div> | ||||
| 	</section> | ||||
| 
 | ||||
| 	<x-import-export class="_vMargin"/> | ||||
|  | @ -109,6 +112,24 @@ export default Vue.extend({ | |||
| 		readAllNotifications() { | ||||
| 			this.$root.api('notifications/mark-all-as-read'); | ||||
| 		}, | ||||
| 
 | ||||
| 		async configure() { | ||||
| 			this.$root.new(await import('../../components/notification-setting-window.vue').then(m => m.default), { | ||||
| 				includingTypes: this.$store.state.i.includingNotificationTypes, | ||||
| 				showGlobalToggle: false, | ||||
| 			}).$on('ok', async ({ includingTypes: value }: any) => { | ||||
| 				await this.$root.api('i/update', { | ||||
| 					includingNotificationTypes: value, | ||||
| 				}).then(i => { | ||||
| 					this.$store.state.i.includingNotificationTypes = i.includingNotificationTypes; | ||||
| 				}).catch(err => { | ||||
| 					this.$root.dialog({ | ||||
| 						type: 'error', | ||||
| 						text: err.message | ||||
| 					}); | ||||
| 				}); | ||||
| 			}); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
|  |  | |||
|  | @ -21,6 +21,11 @@ export type FormItem = { | |||
| 	default: string | null; | ||||
| 	hidden?: boolean; | ||||
| 	enum: string[]; | ||||
| } | { | ||||
| 	label?: string; | ||||
| 	type: 'array'; | ||||
| 	default: unknown[] | null; | ||||
| 	hidden?: boolean; | ||||
| }; | ||||
| 
 | ||||
| export type Form = Record<string, FormItem>; | ||||
|  |  | |||
|  | @ -1,15 +1,16 @@ | |||
| <template> | ||||
| <mk-container :style="`height: ${props.height}px;`" :show-header="props.showHeader" :scrollable="true"> | ||||
| 	<template #header><fa :icon="faBell"/>{{ $t('notifications') }}</template> | ||||
| 	<template #func><button @click="configure()" class="_button"><fa :icon="faCog"/></button></template> | ||||
| 
 | ||||
| 	<div> | ||||
| 		<x-notifications/> | ||||
| 		<x-notifications :include-types="props.includingTypes"/> | ||||
| 	</div> | ||||
| </mk-container> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts"> | ||||
| import { faBell } from '@fortawesome/free-solid-svg-icons'; | ||||
| import { faBell, faCog } from '@fortawesome/free-solid-svg-icons'; | ||||
| import MkContainer from '../components/ui/container.vue'; | ||||
| import XNotifications from '../components/notifications.vue'; | ||||
| import define from './define'; | ||||
|  | @ -25,6 +26,11 @@ export default define({ | |||
| 			type: 'number', | ||||
| 			default: 300, | ||||
| 		}, | ||||
| 		includingTypes: { | ||||
| 			type: 'array', | ||||
| 			hidden: true, | ||||
| 			default: null, | ||||
| 		}, | ||||
| 	}) | ||||
| }).extend({ | ||||
| 	components: { | ||||
|  | @ -34,8 +40,19 @@ export default define({ | |||
| 
 | ||||
| 	data() { | ||||
| 		return { | ||||
| 			faBell | ||||
| 			faBell, faCog | ||||
| 		}; | ||||
| 	}, | ||||
| 
 | ||||
| 	methods: { | ||||
| 		async configure() { | ||||
| 			this.$root.new(await import('../components/notification-setting-window.vue').then(m => m.default), { | ||||
| 				includingTypes: this.props.includingTypes, | ||||
| 			}).$on('ok', async ({ includingTypes }) => { | ||||
| 				this.props.includingTypes = includingTypes; | ||||
| 				this.save(); | ||||
| 			}); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'type | |||
| import { id } from '../id'; | ||||
| import { User } from './user'; | ||||
| import { Page } from './page'; | ||||
| import { notificationTypes } from '../../types'; | ||||
| 
 | ||||
| @Entity() | ||||
| export class UserProfile { | ||||
|  | @ -158,6 +159,13 @@ export class UserProfile { | |||
| 	}) | ||||
| 	public mutedWords: string[][]; | ||||
| 
 | ||||
| 	@Column('enum', { | ||||
| 		enum: notificationTypes, | ||||
| 		array: true, | ||||
| 		nullable: true, | ||||
| 	}) | ||||
| 	public includingNotificationTypes: typeof notificationTypes[number][] | null; | ||||
| 
 | ||||
| 	//#region Denormalized fields
 | ||||
| 	@Index() | ||||
| 	@Column('varchar', { | ||||
|  |  | |||
|  | @ -248,6 +248,7 @@ export class UserRepository extends Repository<User> { | |||
| 				hasPendingReceivedFollowRequest: this.getHasPendingReceivedFollowRequest(user.id), | ||||
| 				integrations: profile!.integrations, | ||||
| 				mutedWords: profile!.mutedWords, | ||||
| 				includingNotificationTypes: profile?.includingNotificationTypes, | ||||
| 			} : {}), | ||||
| 
 | ||||
| 			...(opts.includeSecrets ? { | ||||
|  |  | |||
|  | @ -44,12 +44,10 @@ export const meta = { | |||
| 
 | ||||
| 		includeTypes: { | ||||
| 			validator: $.optional.arr($.str.or(notificationTypes as unknown as string[])), | ||||
| 			default: [] as string[] | ||||
| 		}, | ||||
| 
 | ||||
| 		excludeTypes: { | ||||
| 			validator: $.optional.arr($.str.or(notificationTypes as unknown as string[])), | ||||
| 			default: [] as string[] | ||||
| 		} | ||||
| 	}, | ||||
| 
 | ||||
|  | @ -65,6 +63,14 @@ export const meta = { | |||
| }; | ||||
| 
 | ||||
| export default define(meta, async (ps, user) => { | ||||
| 	// includeTypes が空の場合はクエリしない
 | ||||
| 	if (ps.includeTypes && ps.includeTypes.length === 0) { | ||||
| 		return []; | ||||
| 	} | ||||
| 	// excludeTypes に全指定されている場合はクエリしない
 | ||||
| 	if (notificationTypes.every(type => ps.excludeTypes?.includes(type))) { | ||||
| 		return []; | ||||
| 	} | ||||
| 	const followingQuery = Followings.createQueryBuilder('following') | ||||
| 		.select('following.followeeId') | ||||
| 		.where('following.followerId = :followerId', { followerId: user.id }); | ||||
|  | @ -91,9 +97,9 @@ export default define(meta, async (ps, user) => { | |||
| 		query.setParameters(followingQuery.getParameters()); | ||||
| 	} | ||||
| 
 | ||||
| 	if (ps.includeTypes!.length > 0) { | ||||
| 	if (ps.includeTypes?.length > 0) { | ||||
| 		query.andWhere(`notification.type IN (:...includeTypes)`, { includeTypes: ps.includeTypes }); | ||||
| 	} else if (ps.excludeTypes!.length > 0) { | ||||
| 	} else if (ps.excludeTypes?.length > 0) { | ||||
| 		query.andWhere(`notification.type NOT IN (:...excludeTypes)`, { excludeTypes: ps.excludeTypes }); | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -14,6 +14,7 @@ import { Users, DriveFiles, UserProfiles, Pages } from '../../../../models'; | |||
| import { User } from '../../../../models/entities/user'; | ||||
| import { UserProfile } from '../../../../models/entities/user-profile'; | ||||
| import { ensure } from '../../../../prelude/ensure'; | ||||
| import { notificationTypes } from '../../../../types'; | ||||
| 
 | ||||
| export const meta = { | ||||
| 	desc: { | ||||
|  | @ -147,6 +148,10 @@ export const meta = { | |||
| 		mutedWords: { | ||||
| 			validator: $.optional.arr($.arr($.str)) | ||||
| 		}, | ||||
| 
 | ||||
| 		includingNotificationTypes: { | ||||
| 			validator: $.optional.arr($.str.or(notificationTypes as unknown as string[])) | ||||
| 		}, | ||||
| 	}, | ||||
| 
 | ||||
| 	errors: { | ||||
|  | @ -201,6 +206,7 @@ export default define(meta, async (ps, user, token) => { | |||
| 		profileUpdates.mutedWords = ps.mutedWords; | ||||
| 		profileUpdates.enableWordMute = ps.mutedWords.length > 0; | ||||
| 	} | ||||
| 	if (ps.includingNotificationTypes !== undefined) profileUpdates.includingNotificationTypes = ps.includingNotificationTypes as typeof notificationTypes[number][]; | ||||
| 	if (typeof ps.isLocked === 'boolean') updates.isLocked = ps.isLocked; | ||||
| 	if (typeof ps.isBot === 'boolean') updates.isBot = ps.isBot; | ||||
| 	if (typeof ps.carefulBot === 'boolean') profileUpdates.carefulBot = ps.carefulBot; | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| import { publishMainStream } from './stream'; | ||||
| import pushSw from './push-notification'; | ||||
| import { Notifications, Mutings } from '../models'; | ||||
| import { Notifications, Mutings, UserProfiles } from '../models'; | ||||
| import { genId } from '../misc/gen-id'; | ||||
| import { User } from '../models/entities/user'; | ||||
| import { Notification } from '../models/entities/notification'; | ||||
|  | @ -14,13 +14,18 @@ export async function createNotification( | |||
| 		return null; | ||||
| 	} | ||||
| 
 | ||||
| 	const profile = await UserProfiles.findOne({ userId: notifieeId }); | ||||
| 
 | ||||
| 	const isMuted = !profile?.includingNotificationTypes?.includes(type); | ||||
| 
 | ||||
| 	// Create notification
 | ||||
| 	const notification = await Notifications.save({ | ||||
| 		id: genId(), | ||||
| 		createdAt: new Date(), | ||||
| 		notifieeId: notifieeId, | ||||
| 		type: type, | ||||
| 		isRead: false, | ||||
| 		// 相手がこの通知をミュートしているようなら、既読を予めつけておく
 | ||||
| 		isRead: isMuted, | ||||
| 		...data | ||||
| 	} as Partial<Notification>); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue