refactor(client): refacotr MkMediaCaption
This commit is contained in:
		
							parent
							
								
									20fd9db77f
								
							
						
					
					
						commit
						689411c19a
					
				
					 5 changed files with 188 additions and 286 deletions
				
			
		|  | @ -908,6 +908,7 @@ sendPushNotificationReadMessage: "通知やメッセージが既読になった | ||||||
| sendPushNotificationReadMessageCaption: "「{emptyPushNotificationMessage}」という通知が一瞬表示されるようになります。端末の電池消費量が増加する可能性があります。" | sendPushNotificationReadMessageCaption: "「{emptyPushNotificationMessage}」という通知が一瞬表示されるようになります。端末の電池消費量が増加する可能性があります。" | ||||||
| windowMaximize: "最大化" | windowMaximize: "最大化" | ||||||
| windowRestore: "元に戻す" | windowRestore: "元に戻す" | ||||||
|  | caption: "キャプション" | ||||||
| 
 | 
 | ||||||
| _sensitiveMediaDetection: | _sensitiveMediaDetection: | ||||||
|   description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てることができます。サーバーの負荷が少し増えます。" |   description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てることができます。サーバーの負荷が少し増えます。" | ||||||
|  |  | ||||||
|  | @ -71,7 +71,7 @@ function getMenu() { | ||||||
| 		action: toggleSensitive, | 		action: toggleSensitive, | ||||||
| 	}, { | 	}, { | ||||||
| 		text: i18n.ts.describeFile, | 		text: i18n.ts.describeFile, | ||||||
| 		icon: 'ti ti-forms', | 		icon: 'ti ti-text-caption', | ||||||
| 		action: describe, | 		action: describe, | ||||||
| 	}, null, { | 	}, null, { | ||||||
| 		text: i18n.ts.copyUrl, | 		text: i18n.ts.copyUrl, | ||||||
|  | @ -134,20 +134,14 @@ function rename() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function describe() { | function describe() { | ||||||
| 	os.popup(defineAsyncComponent(() => import('@/components/MkMediaCaption.vue')), { | 	os.popup(defineAsyncComponent(() => import('@/components/MkFileCaptionEditWindow.vue')), { | ||||||
| 		title: i18n.ts.describeFile, | 		default: props.file.comment != null ? props.file.comment : '', | ||||||
| 		input: { | 		file: props.file, | ||||||
| 			placeholder: i18n.ts.inputNewDescription, |  | ||||||
| 			default: props.file.comment != null ? props.file.comment : '', |  | ||||||
| 		}, |  | ||||||
| 		image: props.file, |  | ||||||
| 	}, { | 	}, { | ||||||
| 		done: result => { | 		done: caption => { | ||||||
| 			if (!result || result.canceled) return; |  | ||||||
| 			let comment = result.result; |  | ||||||
| 			os.api('drive/files/update', { | 			os.api('drive/files/update', { | ||||||
| 				fileId: props.file.id, | 				fileId: props.file.id, | ||||||
| 				comment: comment.length === 0 ? null : comment, | 				comment: caption.length === 0 ? null : caption, | ||||||
| 			}); | 			}); | ||||||
| 		}, | 		}, | ||||||
| 	}, 'closed'); | 	}, 'closed'); | ||||||
|  |  | ||||||
							
								
								
									
										175
									
								
								packages/client/src/components/MkFileCaptionEditWindow.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								packages/client/src/components/MkFileCaptionEditWindow.vue
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,175 @@ | ||||||
|  | <template> | ||||||
|  | <XModalWindow | ||||||
|  | 	ref="dialog" | ||||||
|  | 	:width="400" | ||||||
|  | 	:height="450" | ||||||
|  | 	:with-ok-button="true" | ||||||
|  | 	:ok-button-disabled="false" | ||||||
|  | 	@ok="ok()" | ||||||
|  | 	@close="dialog.close()" | ||||||
|  | 	@closed="emit('closed')" | ||||||
|  | > | ||||||
|  | 	<template #header>{{ i18n.ts.describeFile }}</template> | ||||||
|  | 	<div> | ||||||
|  | 		<MkDriveFileThumbnail class="thumbnail" :file="file" fit="contain" style="height: 100px;"/> | ||||||
|  | 		<MkTextarea v-model="caption" autofocus :placeholder="i18n.ts.inputNewDescription"> | ||||||
|  | 			<template #label>{{ i18n.ts.caption }}</template> | ||||||
|  | 		</MkTextarea> | ||||||
|  | 	</div> | ||||||
|  | </XModalWindow> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
|  | <script lang="ts" setup> | ||||||
|  | import { } from 'vue'; | ||||||
|  | import * as Misskey from 'misskey-js'; | ||||||
|  | import XModalWindow from '@/components/MkModalWindow.vue'; | ||||||
|  | import MkTextarea from '@/components/form/textarea.vue'; | ||||||
|  | import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue'; | ||||||
|  | import { i18n } from '@/i18n'; | ||||||
|  | 
 | ||||||
|  | const props = defineProps<{ | ||||||
|  | 	file: Misskey.entities.DriveFile; | ||||||
|  | 	default: string; | ||||||
|  | }>(); | ||||||
|  | 
 | ||||||
|  | const emit = defineEmits<{ | ||||||
|  | 	(ev: 'done', v: string): void; | ||||||
|  | 	(ev: 'closed'): void; | ||||||
|  | }>(); | ||||||
|  | 
 | ||||||
|  | const dialog = $ref<InstanceType<typeof XModalWindow>>(); | ||||||
|  | 
 | ||||||
|  | let caption = $ref(props.default); | ||||||
|  | 
 | ||||||
|  | async function ok() { | ||||||
|  | 	emit('done', caption); | ||||||
|  | 	dialog.close(); | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <style lang="scss" scoped> | ||||||
|  | .container { | ||||||
|  | 	display: flex; | ||||||
|  | 	width: 100%; | ||||||
|  | 	height: 100%; | ||||||
|  | 	flex-direction: row; | ||||||
|  | 	overflow: scroll; | ||||||
|  | 	position: fixed; | ||||||
|  | 	left: 0; | ||||||
|  | 	top: 0; | ||||||
|  | } | ||||||
|  | @media (max-width: 850px) { | ||||||
|  | 	.container { | ||||||
|  | 		flex-direction: column; | ||||||
|  | 	} | ||||||
|  | 	.top-caption { | ||||||
|  | 		padding-bottom: 8px; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | .fullwidth { | ||||||
|  | 	width: 100%; | ||||||
|  | 	margin: auto; | ||||||
|  | } | ||||||
|  | .mk-dialog { | ||||||
|  | 	position: relative; | ||||||
|  | 	padding: 32px; | ||||||
|  | 	min-width: 320px; | ||||||
|  | 	max-width: 480px; | ||||||
|  | 	box-sizing: border-box; | ||||||
|  | 	text-align: center; | ||||||
|  | 	background: var(--panel); | ||||||
|  | 	border-radius: var(--radius); | ||||||
|  | 	margin: auto; | ||||||
|  | 
 | ||||||
|  | 	> header { | ||||||
|  | 		margin: 0 0 8px 0; | ||||||
|  | 		position: relative; | ||||||
|  | 
 | ||||||
|  | 		> .title { | ||||||
|  | 			font-weight: bold; | ||||||
|  | 			font-size: 20px; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		> .text-count { | ||||||
|  | 			opacity: 0.7; | ||||||
|  | 			position: absolute; | ||||||
|  | 			right: 0; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	> .buttons { | ||||||
|  | 		margin-top: 16px; | ||||||
|  | 
 | ||||||
|  | 		> * { | ||||||
|  | 			margin: 0 8px; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	> textarea { | ||||||
|  | 		display: block; | ||||||
|  | 		box-sizing: border-box; | ||||||
|  | 		padding: 0 24px; | ||||||
|  | 		margin: 0; | ||||||
|  | 		width: 100%; | ||||||
|  | 		font-size: 16px; | ||||||
|  | 		border: none; | ||||||
|  | 		border-radius: 0; | ||||||
|  | 		background: transparent; | ||||||
|  | 		color: var(--fg); | ||||||
|  | 		font-family: inherit; | ||||||
|  | 		max-width: 100%; | ||||||
|  | 		min-width: 100%; | ||||||
|  | 		min-height: 90px; | ||||||
|  | 
 | ||||||
|  | 		&:focus-visible { | ||||||
|  | 			outline: none; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		&:disabled { | ||||||
|  | 			opacity: 0.5; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | .hdrwpsaf { | ||||||
|  | 	display: flex; | ||||||
|  | 	flex-direction: column; | ||||||
|  | 	height: 100%; | ||||||
|  | 
 | ||||||
|  | 	> header, | ||||||
|  | 	> footer { | ||||||
|  | 		align-self: center; | ||||||
|  | 		display: inline-block; | ||||||
|  | 		padding: 6px 9px; | ||||||
|  | 		font-size: 90%; | ||||||
|  | 		background: rgba(0, 0, 0, 0.5); | ||||||
|  | 		border-radius: 6px; | ||||||
|  | 		color: #fff; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	> header { | ||||||
|  | 		margin-bottom: 8px; | ||||||
|  | 		opacity: 0.9; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	> img { | ||||||
|  | 		display: block; | ||||||
|  | 		flex: 1; | ||||||
|  | 		min-height: 0; | ||||||
|  | 		object-fit: contain; | ||||||
|  | 		width: 100%; | ||||||
|  | 		cursor: zoom-out; | ||||||
|  | 		image-orientation: from-image; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	> footer { | ||||||
|  | 		margin-top: 8px; | ||||||
|  | 		opacity: 0.8; | ||||||
|  | 
 | ||||||
|  | 		> span + span { | ||||||
|  | 			margin-left: 0.5em; | ||||||
|  | 			padding-left: 0.5em; | ||||||
|  | 			border-left: solid 1px rgba(255, 255, 255, 0.5); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | @ -1,263 +0,0 @@ | ||||||
| <template> |  | ||||||
| 	<MkModal ref="modal" @click="done(true)" @closed="$emit('closed')"> |  | ||||||
| 		<div class="container"> |  | ||||||
| 			<div class="fullwidth top-caption"> |  | ||||||
| 				<div class="mk-dialog"> |  | ||||||
| 					<header> |  | ||||||
| 						<Mfm v-if="title" class="title" :text="title"/> |  | ||||||
| 						<span class="text-count" :class="{ over: remainingLength < 0 }">{{ remainingLength }}</span> |  | ||||||
| 					</header> |  | ||||||
| 					<textarea v-model="inputValue" autofocus :placeholder="input.placeholder" @keydown="onInputKeydown"></textarea> |  | ||||||
| 					<div v-if="(showOkButton || showCancelButton)" class="buttons"> |  | ||||||
| 						<MkButton inline primary :disabled="remainingLength < 0" @click="ok">{{ $ts.ok }}</MkButton> |  | ||||||
| 						<MkButton inline @click="cancel" >{{ $ts.cancel }}</MkButton> |  | ||||||
| 					</div> |  | ||||||
| 				</div> |  | ||||||
| 			</div> |  | ||||||
| 			<div class="hdrwpsaf fullwidth"> |  | ||||||
| 				<header>{{ image.name }}</header> |  | ||||||
| 				<img :src="image.url" :alt="image.comment" :title="image.comment" @click="$refs.modal.close()"/> |  | ||||||
| 				<footer> |  | ||||||
| 					<span>{{ image.type }}</span> |  | ||||||
| 					<span>{{ bytes(image.size) }}</span> |  | ||||||
| 					<span v-if="image.properties && image.properties.width">{{ number(image.properties.width) }}px × {{ number(image.properties.height) }}px</span> |  | ||||||
| 				</footer> |  | ||||||
| 			</div> |  | ||||||
| 		</div> |  | ||||||
| 	</MkModal> |  | ||||||
| </template> |  | ||||||
| 
 |  | ||||||
| <script lang="ts"> |  | ||||||
| import { defineComponent } from 'vue'; |  | ||||||
| import { length } from 'stringz'; |  | ||||||
| import MkModal from '@/components/MkModal.vue'; |  | ||||||
| import MkButton from '@/components/MkButton.vue'; |  | ||||||
| import bytes from '@/filters/bytes'; |  | ||||||
| import number from '@/filters/number'; |  | ||||||
| 
 |  | ||||||
| export default defineComponent({ |  | ||||||
| 	components: { |  | ||||||
| 		MkModal, |  | ||||||
| 		MkButton, |  | ||||||
| 	}, |  | ||||||
| 
 |  | ||||||
| 	props: { |  | ||||||
| 		image: { |  | ||||||
| 			type: Object, |  | ||||||
| 			required: true, |  | ||||||
| 		}, |  | ||||||
| 		title: { |  | ||||||
| 			type: String, |  | ||||||
| 			required: false |  | ||||||
| 		}, |  | ||||||
| 		input: { |  | ||||||
| 			required: true |  | ||||||
| 		}, |  | ||||||
| 		showOkButton: { |  | ||||||
| 			type: Boolean, |  | ||||||
| 			default: true |  | ||||||
| 		}, |  | ||||||
| 		showCancelButton: { |  | ||||||
| 			type: Boolean, |  | ||||||
| 			default: true |  | ||||||
| 		}, |  | ||||||
| 		cancelableByBgClick: { |  | ||||||
| 			type: Boolean, |  | ||||||
| 			default: true |  | ||||||
| 		}, |  | ||||||
| 	}, |  | ||||||
| 
 |  | ||||||
| 	emits: ['done', 'closed'], |  | ||||||
| 
 |  | ||||||
| 	data() { |  | ||||||
| 		return { |  | ||||||
| 			inputValue: this.input.default ? this.input.default : null |  | ||||||
| 		}; |  | ||||||
| 	}, |  | ||||||
| 
 |  | ||||||
| 	computed: { |  | ||||||
| 		remainingLength(): number { |  | ||||||
| 			if (typeof this.inputValue !== "string") return 512; |  | ||||||
| 			return 512 - length(this.inputValue); |  | ||||||
| 		} |  | ||||||
| 	}, |  | ||||||
| 
 |  | ||||||
| 	mounted() { |  | ||||||
| 		document.addEventListener('keydown', this.onKeydown); |  | ||||||
| 	}, |  | ||||||
| 
 |  | ||||||
| 	beforeUnmount() { |  | ||||||
| 		document.removeEventListener('keydown', this.onKeydown); |  | ||||||
| 	}, |  | ||||||
| 
 |  | ||||||
| 	methods: { |  | ||||||
| 		bytes, |  | ||||||
| 		number, |  | ||||||
| 
 |  | ||||||
| 		done(canceled, result?) { |  | ||||||
| 			this.$emit('done', { canceled, result }); |  | ||||||
| 			this.$refs.modal.close(); |  | ||||||
| 		}, |  | ||||||
| 
 |  | ||||||
| 		async ok() { |  | ||||||
| 			if (!this.showOkButton) return; |  | ||||||
| 
 |  | ||||||
| 			const result = this.inputValue; |  | ||||||
| 			this.done(false, result); |  | ||||||
| 		}, |  | ||||||
| 
 |  | ||||||
| 		cancel() { |  | ||||||
| 			this.done(true); |  | ||||||
| 		}, |  | ||||||
| 
 |  | ||||||
| 		onBgClick() { |  | ||||||
| 			if (this.cancelableByBgClick) { |  | ||||||
| 				this.cancel(); |  | ||||||
| 			} |  | ||||||
| 		}, |  | ||||||
| 
 |  | ||||||
| 		onKeydown(evt) { |  | ||||||
| 			if (evt.which === 27) { // ESC |  | ||||||
| 				this.cancel(); |  | ||||||
| 			} |  | ||||||
| 		}, |  | ||||||
| 
 |  | ||||||
| 		onInputKeydown(evt) { |  | ||||||
| 			if (evt.which === 13) { // Enter |  | ||||||
| 				if (evt.ctrlKey) { |  | ||||||
| 					evt.preventDefault(); |  | ||||||
| 					evt.stopPropagation(); |  | ||||||
| 					this.ok(); |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| }); |  | ||||||
| </script> |  | ||||||
| 
 |  | ||||||
| <style lang="scss" scoped> |  | ||||||
| .container { |  | ||||||
| 	display: flex; |  | ||||||
| 	width: 100%; |  | ||||||
| 	height: 100%; |  | ||||||
| 	flex-direction: row; |  | ||||||
| 	overflow: scroll; |  | ||||||
| 	position: fixed; |  | ||||||
| 	left: 0; |  | ||||||
| 	top: 0; |  | ||||||
| } |  | ||||||
| @media (max-width: 850px) { |  | ||||||
| 	.container { |  | ||||||
| 		flex-direction: column; |  | ||||||
| 	} |  | ||||||
| 	.top-caption { |  | ||||||
| 		padding-bottom: 8px; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| .fullwidth { |  | ||||||
| 	width: 100%; |  | ||||||
| 	margin: auto; |  | ||||||
| } |  | ||||||
| .mk-dialog { |  | ||||||
| 	position: relative; |  | ||||||
| 	padding: 32px; |  | ||||||
| 	min-width: 320px; |  | ||||||
| 	max-width: 480px; |  | ||||||
| 	box-sizing: border-box; |  | ||||||
| 	text-align: center; |  | ||||||
| 	background: var(--panel); |  | ||||||
| 	border-radius: var(--radius); |  | ||||||
| 	margin: auto; |  | ||||||
| 
 |  | ||||||
| 	> header { |  | ||||||
| 		margin: 0 0 8px 0; |  | ||||||
| 		position: relative; |  | ||||||
| 
 |  | ||||||
| 		> .title { |  | ||||||
| 			font-weight: bold; |  | ||||||
| 			font-size: 20px; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		> .text-count { |  | ||||||
| 			opacity: 0.7; |  | ||||||
| 			position: absolute; |  | ||||||
| 			right: 0; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	> .buttons { |  | ||||||
| 		margin-top: 16px; |  | ||||||
| 
 |  | ||||||
| 		> * { |  | ||||||
| 			margin: 0 8px; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	> textarea { |  | ||||||
| 		display: block; |  | ||||||
| 		box-sizing: border-box; |  | ||||||
| 		padding: 0 24px; |  | ||||||
| 		margin: 0; |  | ||||||
| 		width: 100%; |  | ||||||
| 		font-size: 16px; |  | ||||||
| 		border: none; |  | ||||||
| 		border-radius: 0; |  | ||||||
| 		background: transparent; |  | ||||||
| 		color: var(--fg); |  | ||||||
| 		font-family: inherit; |  | ||||||
| 		max-width: 100%; |  | ||||||
| 		min-width: 100%; |  | ||||||
| 		min-height: 90px; |  | ||||||
| 
 |  | ||||||
| 		&:focus-visible { |  | ||||||
| 			outline: none; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		&:disabled { |  | ||||||
| 			opacity: 0.5; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| .hdrwpsaf { |  | ||||||
| 	display: flex; |  | ||||||
| 	flex-direction: column; |  | ||||||
| 	height: 100%; |  | ||||||
| 
 |  | ||||||
| 	> header, |  | ||||||
| 	> footer { |  | ||||||
| 		align-self: center; |  | ||||||
| 		display: inline-block; |  | ||||||
| 		padding: 6px 9px; |  | ||||||
| 		font-size: 90%; |  | ||||||
| 		background: rgba(0, 0, 0, 0.5); |  | ||||||
| 		border-radius: 6px; |  | ||||||
| 		color: #fff; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	> header { |  | ||||||
| 		margin-bottom: 8px; |  | ||||||
| 		opacity: 0.9; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	> img { |  | ||||||
| 		display: block; |  | ||||||
| 		flex: 1; |  | ||||||
| 		min-height: 0; |  | ||||||
| 		object-fit: contain; |  | ||||||
| 		width: 100%; |  | ||||||
| 		cursor: zoom-out; |  | ||||||
| 		image-orientation: from-image; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	> footer { |  | ||||||
| 		margin-top: 8px; |  | ||||||
| 		opacity: 0.8; |  | ||||||
| 
 |  | ||||||
| 		> span + span { |  | ||||||
| 			margin-left: 0.5em; |  | ||||||
| 			padding-left: 0.5em; |  | ||||||
| 			border-left: solid 1px rgba(255, 255, 255, 0.5); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| </style> |  | ||||||
|  | @ -70,17 +70,12 @@ async function rename(file) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function describe(file) { | async function describe(file) { | ||||||
| 	os.popup(defineAsyncComponent(() => import('@/components/MkMediaCaption.vue')), { | 	os.popup(defineAsyncComponent(() => import('@/components/MkFileCaptionEditWindow.vue')), { | ||||||
| 		title: i18n.ts.describeFile, | 		default: file.comment !== null ? file.comment : '', | ||||||
| 		input: { | 		file: file, | ||||||
| 			placeholder: i18n.ts.inputNewDescription, |  | ||||||
| 			default: file.comment !== null ? file.comment : '', |  | ||||||
| 		}, |  | ||||||
| 		image: file, |  | ||||||
| 	}, { | 	}, { | ||||||
| 		done: result => { | 		done: caption => { | ||||||
| 			if (!result || result.canceled) return; | 			let comment = caption.length === 0 ? null : caption; | ||||||
| 			let comment = result.result.length === 0 ? null : result.result; |  | ||||||
| 			os.api('drive/files/update', { | 			os.api('drive/files/update', { | ||||||
| 				fileId: file.id, | 				fileId: file.id, | ||||||
| 				comment: comment, | 				comment: comment, | ||||||
|  | @ -103,7 +98,7 @@ function showFileMenu(file, ev: MouseEvent) { | ||||||
| 		action: () => { toggleSensitive(file); }, | 		action: () => { toggleSensitive(file); }, | ||||||
| 	}, { | 	}, { | ||||||
| 		text: i18n.ts.describeFile, | 		text: i18n.ts.describeFile, | ||||||
| 		icon: 'ti ti-forms', | 		icon: 'ti ti-text-caption', | ||||||
| 		action: () => { describe(file); }, | 		action: () => { describe(file); }, | ||||||
| 	}, { | 	}, { | ||||||
| 		text: i18n.ts.attachCancel, | 		text: i18n.ts.attachCancel, | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue