From 689411c19a267630855251f7d54999250faabbf7 Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Wed, 21 Dec 2022 16:00:00 +0900
Subject: [PATCH] refactor(client): refacotr MkMediaCaption

---
 locales/ja-JP.yml                             |   1 +
 .../client/src/components/MkDrive.file.vue    |  18 +-
 .../components/MkFileCaptionEditWindow.vue    | 175 ++++++++++++
 .../client/src/components/MkMediaCaption.vue  | 263 ------------------
 .../src/components/MkPostFormAttaches.vue     |  17 +-
 5 files changed, 188 insertions(+), 286 deletions(-)
 create mode 100644 packages/client/src/components/MkFileCaptionEditWindow.vue
 delete mode 100644 packages/client/src/components/MkMediaCaption.vue

diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 0e8e22752f..7737e6044e 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -908,6 +908,7 @@ sendPushNotificationReadMessage: "通知やメッセージが既読になった
 sendPushNotificationReadMessageCaption: "「{emptyPushNotificationMessage}」という通知が一瞬表示されるようになります。端末の電池消費量が増加する可能性があります。"
 windowMaximize: "最大化"
 windowRestore: "元に戻す"
+caption: "キャプション"
 
 _sensitiveMediaDetection:
   description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てることができます。サーバーの負荷が少し増えます。"
diff --git a/packages/client/src/components/MkDrive.file.vue b/packages/client/src/components/MkDrive.file.vue
index 11bd9e9e3b..8c17c0530a 100644
--- a/packages/client/src/components/MkDrive.file.vue
+++ b/packages/client/src/components/MkDrive.file.vue
@@ -71,7 +71,7 @@ function getMenu() {
 		action: toggleSensitive,
 	}, {
 		text: i18n.ts.describeFile,
-		icon: 'ti ti-forms',
+		icon: 'ti ti-text-caption',
 		action: describe,
 	}, null, {
 		text: i18n.ts.copyUrl,
@@ -134,20 +134,14 @@ function rename() {
 }
 
 function describe() {
-	os.popup(defineAsyncComponent(() => import('@/components/MkMediaCaption.vue')), {
-		title: i18n.ts.describeFile,
-		input: {
-			placeholder: i18n.ts.inputNewDescription,
-			default: props.file.comment != null ? props.file.comment : '',
-		},
-		image: props.file,
+	os.popup(defineAsyncComponent(() => import('@/components/MkFileCaptionEditWindow.vue')), {
+		default: props.file.comment != null ? props.file.comment : '',
+		file: props.file,
 	}, {
-		done: result => {
-			if (!result || result.canceled) return;
-			let comment = result.result;
+		done: caption => {
 			os.api('drive/files/update', {
 				fileId: props.file.id,
-				comment: comment.length === 0 ? null : comment,
+				comment: caption.length === 0 ? null : caption,
 			});
 		},
 	}, 'closed');
diff --git a/packages/client/src/components/MkFileCaptionEditWindow.vue b/packages/client/src/components/MkFileCaptionEditWindow.vue
new file mode 100644
index 0000000000..73875251f0
--- /dev/null
+++ b/packages/client/src/components/MkFileCaptionEditWindow.vue
@@ -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>
diff --git a/packages/client/src/components/MkMediaCaption.vue b/packages/client/src/components/MkMediaCaption.vue
deleted file mode 100644
index c25755d762..0000000000
--- a/packages/client/src/components/MkMediaCaption.vue
+++ /dev/null
@@ -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>
diff --git a/packages/client/src/components/MkPostFormAttaches.vue b/packages/client/src/components/MkPostFormAttaches.vue
index 955d835f25..2e16cc52a7 100644
--- a/packages/client/src/components/MkPostFormAttaches.vue
+++ b/packages/client/src/components/MkPostFormAttaches.vue
@@ -70,17 +70,12 @@ async function rename(file) {
 }
 
 async function describe(file) {
-	os.popup(defineAsyncComponent(() => import('@/components/MkMediaCaption.vue')), {
-		title: i18n.ts.describeFile,
-		input: {
-			placeholder: i18n.ts.inputNewDescription,
-			default: file.comment !== null ? file.comment : '',
-		},
-		image: file,
+	os.popup(defineAsyncComponent(() => import('@/components/MkFileCaptionEditWindow.vue')), {
+		default: file.comment !== null ? file.comment : '',
+		file: file,
 	}, {
-		done: result => {
-			if (!result || result.canceled) return;
-			let comment = result.result.length === 0 ? null : result.result;
+		done: caption => {
+			let comment = caption.length === 0 ? null : caption;
 			os.api('drive/files/update', {
 				fileId: file.id,
 				comment: comment,
@@ -103,7 +98,7 @@ function showFileMenu(file, ev: MouseEvent) {
 		action: () => { toggleSensitive(file); },
 	}, {
 		text: i18n.ts.describeFile,
-		icon: 'ti ti-forms',
+		icon: 'ti ti-text-caption',
 		action: () => { describe(file); },
 	}, {
 		text: i18n.ts.attachCancel,