From 7199e6f4e0b3a2c2bc198e689c3e0cd0d0f0354a Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Sat, 17 Oct 2020 20:12:00 +0900
Subject: Migrate to Vue3 (#6587)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Update reaction.vue

* fix  bug

* wip

* wip

* wjio

* wip

* Revert "wip"

This reverts commit e427f2160adf4e8a4147006e25a89854edab0033.

* wip

* wip

* wip

* Update init.ts

* Update drive-window.vue

* wip

* wip

* Use PascalCase for components

* Use PascalCase for components

* update dep

* wip

* wip

* wip

* Update init.ts

* wip

* Update paging.ts

* Update test.vue

* watch deep

* wip

* lint

* wip

* wip

* wip

* wip

* wiop

* wip

* Update webpack.config.ts

* alllow null poll

* wip

* wip

* wip

* wiop

* UI redesign & refactor (#6714)

* wip

* wip

* wip

* wip

* wip

* Update drive.vue

* Update word-mute.vue

* wip

* wip

* wip

* clean up

* wip

* Update default.vue

* wip

* Update notes.vue

* Update mfm.ts

* Update index.home.vue

* Update post-form.vue

* Update post-form-attaches.vue

* wip

* Update post-form.vue

* Update sidebar.vue

* wip

* wip

* Update index.vue

* wip

* Update default.vue

* Update index.vue

* Update index.vue

* wip

* Update post-form-attaches.vue

* Update note.vue

* wip

* clean up

* Update notes.vue

* wip

* wip

* Update ja-JP.yml

* wip

* wip

* Update index.vue

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* Update default.vue

* wip

* Update _dark.json5

* wip

* wip

* wip

* clean up

* wip

* wip

* Update index.vue

* Update test.vue

* wip

* wip

* fix

* wip

* wip

* wip

* wip

* clena yop

* wip

* wip

* Update store.ts

* Update messaging-room.vue

* Update default.widgets.vue

* fix

* wip

* wip

* Update modal.vue

* wip

* Update os.ts

* Update os.ts

* Update deck.vue

* Update init.ts

* wip

* Update ja-JP.yml

* v-sizeは単にwindowのresizeを監視するだけで良いかもしれない

* Update modal.vue

* wip

* Update tooltip.ts

* wip

* wip

* wip

* wip

* wip

* Update image-viewer.vue

* wip

* wip

* Update style.scss

* Update style.scss

* Update visitor.vue

* wip

* Update init.ts

* Update init.ts

* wip

* wip

* Update visitor.vue

* Update visitor.vue

* Update visitor.vue

* Update visitor.vue

* wip

* wip

* Update modal.vue

* Update header.vue

* Update menu.vue

* Update about.vue

* Update about-misskey.vue

* wip

* wip

* Update visitor.vue

* Update tooltip.ts

* wip

* Update drive.vue

* wip

* Update style.scss

* Update header.vue

* wip

* wip

* Update users.user.vue

* Update announcements.vue

* wip

* wip

* wip

* Update emojis.vue

* wip

* Update emojis.vue

* Update style.scss

* Update users.vue

* wip

* Update style.scss

* wip

* Update welcome.entrance.vue

* Update radio.vue

* Update size.ts

* Update emoji-edit-dialog.vue

* wip

* Update emojis.vue

* wip

* Update emojis.vue

* Update emojis.vue

* Update emojis.vue

* wip

* wip

* wip

* wip

* Update file-dialog.vue

* wip

* wip

* Update token-generate-window.vue

* Update notification-setting-window.vue

* wip

* wip

* Update _error_.vue

* Update ja-JP.yml

* wip

* wip

* Update store.ts

* Update emojis.vue

* Update emojis.vue

* Update emojis.vue

* Update announcements.vue

* Update store.ts

* wip

* Update page-editor.vue

* wip

* wip

* Update modal.vue

* wip

* Update select-file.ts

* Update timeline.vue

* Update emojis.vue

* Update os.ts

* wip

* Update user-select.vue

* Update mfm.ts

* Update get-file-info.ts

* Update drive.vue

* Update init.ts

* Update mfm.ts

* wip

* wip

* Update window.vue

* Update note.vue

* wip

* wip

* Update user-info.vue

* wip

* wip

* wip

* wip

* wip

* Update header.vue

* Update header.vue

* wip

* Update explore.vue

* wip

* wip

* wip

* Update webpack.config.ts

* wip

* wip

* wip

* wip

* wip

* wip

* Update autocomplete.ts

* wip

* wip

* wip

* Update toast.vue

* wip

* Update post-form-dialog.vue

* wip

* wip

* wip

* wip

* wip

* Update users.vue

* wip

* Update explore.vue

* wip

* wip

* wip

* Update package.json

* wip

* Update icon-dialog.vue

* wip

* wip

* Update user-preview.ts

* wip

* wip

* wip

* wip

* wip

* Update instance.vue

* Update user-name.vue

* Update federation.vue

* Update instance.vue

* wip

* wip

* Update tag.vue

* wip

* wip

* wip

* wip

* wip

* Update instance.vue

* wip

* Update os.ts

* Update os.ts

* wip

* wip

* wip

* Update router.ts

* wip

* Update init.ts

* Update note.vue

* Update messages.vue

* wip

* wip

* wip

* wip

* wip

* google

* wip

* wip

* wip

* wip

* Update theme-editor.vue

* wip

* wip

* Update room.vue

* Update channel-editor.vue

* wip

* Update window.vue

* Update window.vue

* wip

* Update window.vue

* Update window.vue

* wip

* Update menu.vue

* wip

* wip

* wip

* wip

* Update messaging-room.vue

* wip

* Update post-form.vue

* Update default.widgets.vue

* Update window.vue

* wip
---
 src/client/components/post-form.vue | 384 +++++++++++++++++-------------------
 1 file changed, 177 insertions(+), 207 deletions(-)

(limited to 'src/client/components/post-form.vue')

diff --git a/src/client/components/post-form.vue b/src/client/components/post-form.vue
index a0d2cd153c..ba7770345a 100644
--- a/src/client/components/post-form.vue
+++ b/src/client/components/post-form.vue
@@ -1,84 +1,84 @@
 <template>
-<div class="gafaadew"
+<div class="gafaadew" :class="{ modal, _popup: modal }"
+	v-size="{ max: [500] }"
 	@dragover.stop="onDragover"
 	@dragenter="onDragenter"
 	@dragleave="onDragleave"
 	@drop.stop="onDrop"
 >
 	<header>
-		<button v-if="!fixed" class="cancel _button" @click="cancel"><fa :icon="faTimes"/></button>
+		<button v-if="!fixed" class="cancel _button" @click="cancel"><Fa :icon="faTimes"/></button>
 		<div>
-			<span class="local-only" v-if="localOnly" v-text="$t('_visibility.localOnly')" />
 			<span class="text-count" :class="{ over: trimmedLength(text) > max }">{{ max - trimmedLength(text) }}</span>
+			<span class="local-only" v-if="localOnly"><Fa :icon="faBiohazard"/></span>
 			<button class="_button visibility" @click="setVisibility" ref="visibilityButton" v-tooltip="$t('visibility')" :disabled="channel != null">
-				<span v-if="visibility === 'public'"><fa :icon="faGlobe"/></span>
-				<span v-if="visibility === 'home'"><fa :icon="faHome"/></span>
-				<span v-if="visibility === 'followers'"><fa :icon="faUnlock"/></span>
-				<span v-if="visibility === 'specified'"><fa :icon="faEnvelope"/></span>
+				<span v-if="visibility === 'public'"><Fa :icon="faGlobe"/></span>
+				<span v-if="visibility === 'home'"><Fa :icon="faHome"/></span>
+				<span v-if="visibility === 'followers'"><Fa :icon="faUnlock"/></span>
+				<span v-if="visibility === 'specified'"><Fa :icon="faEnvelope"/></span>
 			</button>
-			<button class="submit _buttonPrimary" :disabled="!canPost" @click="post">{{ submitText }}<fa :icon="reply ? faReply : renote ? faQuoteRight : faPaperPlane"/></button>
+			<button class="submit _buttonPrimary" :disabled="!canPost" @click="post">{{ submitText }}<Fa :icon="reply ? faReply : renote ? faQuoteRight : faPaperPlane"/></button>
 		</div>
 	</header>
 	<div class="form" :class="{ fixed }">
-		<x-note-preview class="preview" v-if="reply" :note="reply"/>
-		<x-note-preview class="preview" v-if="renote" :note="renote"/>
-		<div class="with-quote" v-if="quoteId"><fa icon="quote-left"/> {{ $t('quoteAttached') }}<button @click="quoteId = null"><fa icon="times"/></button></div>
+		<XNotePreview class="preview" v-if="reply" :note="reply"/>
+		<XNotePreview class="preview" v-if="renote" :note="renote"/>
+		<div class="with-quote" v-if="quoteId"><Fa icon="quote-left"/> {{ $t('quoteAttached') }}<button @click="quoteId = null"><Fa icon="times"/></button></div>
 		<div v-if="visibility === 'specified'" class="to-specified">
 			<span style="margin-right: 8px;">{{ $t('recipient') }}</span>
 			<div class="visibleUsers">
 				<span v-for="u in visibleUsers" :key="u.id">
-					<mk-acct :user="u"/>
-					<button class="_button" @click="removeVisibleUser(u)"><fa :icon="faTimes"/></button>
+					<MkAcct :user="u"/>
+					<button class="_button" @click="removeVisibleUser(u)"><Fa :icon="faTimes"/></button>
 				</span>
-				<button @click="addVisibleUser" class="_buttonPrimary"><fa :icon="faPlus" fixed-width/></button>
+				<button @click="addVisibleUser" class="_buttonPrimary"><Fa :icon="faPlus" fixed-width/></button>
 			</div>
 		</div>
-		<input v-show="useCw" ref="cw" class="cw" v-model="cw" :placeholder="$t('annotation')" v-autocomplete="{ model: 'cw' }" @keydown="onKeydown">
-		<textarea v-model="text" class="text" :class="{ withCw: useCw }" ref="text" :disabled="posting" :placeholder="placeholder" v-autocomplete="{ model: 'text' }" @keydown="onKeydown" @paste="onPaste"></textarea>
-		<x-post-form-attaches class="attaches" :files="files"/>
-		<x-poll-editor v-if="poll" ref="poll" @destroyed="poll = false" @updated="onPollUpdate()"/>
-		<x-uploader ref="uploader" @uploaded="attachMedia" @change="onChangeUploadings"/>
+		<input v-show="useCw" ref="cw" class="cw" v-model="cw" :placeholder="$t('annotation')" @keydown="onKeydown">
+		<textarea v-model="text" class="text" :class="{ withCw: useCw }" ref="text" :disabled="posting" :placeholder="placeholder" @keydown="onKeydown" @paste="onPaste"></textarea>
+		<XPostFormAttaches class="attaches" :files="files" @updated="updateMedia" @detach="detachMedia"/>
+		<XPollEditor v-if="poll" :poll="poll" @destroyed="poll = null" @updated="onPollUpdate"/>
 		<footer>
-			<button class="_button" @click="chooseFileFrom" v-tooltip="$t('attachFile')"><fa :icon="faPhotoVideo"/></button>
-			<button class="_button" @click="poll = !poll" :class="{ active: poll }" v-tooltip="$t('poll')"><fa :icon="faPollH"/></button>
-			<button class="_button" @click="useCw = !useCw" :class="{ active: useCw }" v-tooltip="$t('useCw')"><fa :icon="faEyeSlash"/></button>
-			<button class="_button" @click="insertMention" v-tooltip="$t('mention')"><fa :icon="faAt"/></button>
-			<button class="_button" @click="insertEmoji" v-tooltip="$t('emoji')"><fa :icon="faLaughSquint"/></button>
-			<button class="_button" @click="showActions" v-tooltip="$t('plugin')" v-if="$store.state.postFormActions.length > 0"><fa :icon="faPlug"/></button>
+			<button class="_button" @click="chooseFileFrom" v-tooltip="$t('attachFile')"><Fa :icon="faPhotoVideo"/></button>
+			<button class="_button" @click="togglePoll" :class="{ active: poll }" v-tooltip="$t('poll')"><Fa :icon="faPollH"/></button>
+			<button class="_button" @click="useCw = !useCw" :class="{ active: useCw }" v-tooltip="$t('useCw')"><Fa :icon="faEyeSlash"/></button>
+			<button class="_button" @click="insertMention" v-tooltip="$t('mention')"><Fa :icon="faAt"/></button>
+			<button class="_button" @click="insertEmoji" v-tooltip="$t('emoji')"><Fa :icon="faLaughSquint"/></button>
+			<button class="_button" @click="showActions" v-tooltip="$t('plugin')" v-if="postFormActions.length > 0"><Fa :icon="faPlug"/></button>
 		</footer>
-		<input ref="file" class="file _button" type="file" multiple="multiple" @change="onChangeFile"/>
 	</div>
 </div>
 </template>
 
 <script lang="ts">
-import Vue from 'vue';
-import { faReply, faQuoteRight, faPaperPlane, faTimes, faUpload, faPollH, faGlobe, faHome, faUnlock, faEnvelope, faPlus, faPhotoVideo, faCloud, faLink, faAt, faBiohazard, faPlug } from '@fortawesome/free-solid-svg-icons';
+import { defineComponent, defineAsyncComponent } from 'vue';
+import { faReply, faQuoteRight, faPaperPlane, faTimes, faUpload, faPollH, faGlobe, faHome, faUnlock, faEnvelope, faPlus, faPhotoVideo, faAt, faBiohazard, faPlug } from '@fortawesome/free-solid-svg-icons';
 import { faEyeSlash, faLaughSquint } from '@fortawesome/free-regular-svg-icons';
 import insertTextAtCursor from 'insert-text-at-cursor';
 import { length } from 'stringz';
 import { toASCII } from 'punycode';
-import MkVisibilityChooser from './visibility-chooser.vue';
-import MkUserSelect from './user-select.vue';
 import XNotePreview from './note-preview.vue';
 import { parse } from '../../mfm/parse';
-import { host, url } from '../config';
+import { host, url } from '@/config';
 import { erase, unique } from '../../prelude/array';
 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 { Autocomplete } from '@/scripts/autocomplete';
 import { noteVisibilities } from '../../types';
-import { utils } from '@syuilo/aiscript';
+import * as os from '@/os';
+import { selectFile } from '@/scripts/select-file';
+import { notePostInterruptors, postFormActions } from '@/store';
 
-export default Vue.extend({
+export default defineComponent({
 	components: {
 		XNotePreview,
-		XUploader: () => import('./uploader.vue').then(m => m.default),
-		XPostFormAttaches: () => import('./post-form-attaches.vue').then(m => m.default),
-		XPollEditor: () => import('./poll-editor.vue').then(m => m.default)
+		XPostFormAttaches: defineAsyncComponent(() => import('./post-form-attaches.vue')),
+		XPollEditor: defineAsyncComponent(() => import('./poll-editor.vue'))
 	},
 
+	inject: ['modal'],
+
 	props: {
 		reply: {
 			type: Object,
@@ -117,19 +117,22 @@ export default Vue.extend({
 			type: Boolean,
 			required: false,
 			default: false
-		}
+		},
+		autofocus: {
+			type: Boolean,
+			required: false,
+			default: true
+		},
 	},
 
+	emits: ['posted', 'done', 'esc'],
+
 	data() {
 		return {
 			posting: false,
 			text: '',
 			files: [],
-			uploadings: [],
-			poll: false,
-			pollChoices: [],
-			pollMultiple: false,
-			pollExpiration: [],
+			poll: null,
 			useCw: false,
 			cw: null,
 			localOnly: false,
@@ -139,7 +142,8 @@ export default Vue.extend({
 			draghover: false,
 			quoteId: null,
 			recentHashtags: JSON.parse(localStorage.getItem('hashtags') || '[]'),
-			faReply, faQuoteRight, faPaperPlane, faTimes, faUpload, faPollH, faGlobe, faHome, faUnlock, faEnvelope, faEyeSlash, faLaughSquint, faPlus, faPhotoVideo, faCloud, faLink, faAt, faBiohazard, faPlug
+			postFormActions,
+			faReply, faQuoteRight, faPaperPlane, faTimes, faUpload, faPollH, faGlobe, faHome, faUnlock, faEnvelope, faEyeSlash, faLaughSquint, faPlus, faPhotoVideo, faAt, faBiohazard, faPlug
 		};
 	},
 
@@ -190,7 +194,7 @@ export default Vue.extend({
 			return !this.posting &&
 				(1 <= this.text.length || 1 <= this.files.length || this.poll || this.renote) &&
 				(length(this.text.trim()) <= this.max) &&
-				(!this.poll || this.pollChoices.length >= 2);
+				(!this.poll || this.poll.choices.length >= 2);
 		},
 
 		max(): number {
@@ -246,14 +250,14 @@ export default Vue.extend({
 		if (this.reply && ['home', 'followers', 'specified'].includes(this.reply.visibility)) {
 			this.visibility = this.reply.visibility;
 			if (this.reply.visibility === 'specified') {
-				this.$root.api('users/show', {
+				os.api('users/show', {
 					userIds: this.reply.visibleUserIds.filter(uid => uid !== this.$store.state.i.id && uid !== this.reply.userId)
 				}).then(users => {
 					this.visibleUsers.push(...users);
 				});
 
 				if (this.reply.userId !== this.$store.state.i.id) {
-					this.$root.api('users/show', { userId: this.reply.userId }).then(user => {
+					os.api('users/show', { userId: this.reply.userId }).then(user => {
 						this.visibleUsers.push(user);
 					});
 				}
@@ -271,15 +275,21 @@ export default Vue.extend({
 			this.cw = this.reply.cw;
 		}
 
-		this.focus();
-
-		this.$nextTick(() => {
+		if (this.autofocus) {
 			this.focus();
-		});
+
+			this.$nextTick(() => {
+				this.focus();
+			});
+		}
+
+		// TODO: detach when unmount
+		new Autocomplete(this.$refs.text, this, { model: 'text' });
+		new Autocomplete(this.$refs.cw, this, { model: 'cw' });
 
 		this.$nextTick(() => {
 			// 書きかけの投稿を復元
-			if (!this.instant && !this.mention) {
+			if (!this.instant && !this.mention && !this.specified) {
 				const draft = JSON.parse(localStorage.getItem('drafts') || '{}')[this.draftKey];
 				if (draft) {
 					this.text = draft.data.text;
@@ -289,10 +299,7 @@ export default Vue.extend({
 					this.localOnly = draft.data.localOnly;
 					this.files = (draft.data.files || []).filter(e => e);
 					if (draft.data.poll) {
-						this.poll = true;
-						this.$nextTick(() => {
-							(this.$refs.poll as any).set(draft.data.poll);
-						});
+						this.poll = draft.data.poll;
 					}
 				}
 			}
@@ -305,13 +312,7 @@ export default Vue.extend({
 				this.cw = init.cw;
 				this.useCw = init.cw != null;
 				if (init.poll) {
-					this.poll = true;
-					this.$nextTick(() => {
-						(this.$refs.poll as any).set({
-							choices: init.poll.choices.map(c => c.text),
-							multiple: init.poll.multiple
-						});
-					});
+					this.poll = init.poll;
 				}
 				this.visibility = init.visibility;
 				this.localOnly = init.localOnly;
@@ -328,11 +329,24 @@ export default Vue.extend({
 			this.$watch('useCw', () => this.saveDraft());
 			this.$watch('cw', () => this.saveDraft());
 			this.$watch('poll', () => this.saveDraft());
-			this.$watch('files', () => this.saveDraft());
+			this.$watch('files', () => this.saveDraft(), { deep: true });
 			this.$watch('visibility', () => this.saveDraft());
 			this.$watch('localOnly', () => this.saveDraft());
 		},
 
+		togglePoll() {
+			if (this.poll) {
+				this.poll = null;
+			} else {
+				this.poll = {
+					choices: ['', ''],
+					multiple: false,
+					expiresAt: null,
+					expiredAfter: null,
+				};
+			}
+		},
+
 		trimmedLength(text: string) {
 			return length(text.trim());
 		},
@@ -346,85 +360,50 @@ export default Vue.extend({
 		},
 
 		chooseFileFrom(ev) {
-			this.$root.menu({
-				items: [{
-					type: 'label',
-					text: this.$t('attachFile'),
-				}, {
-					text: this.$t('upload'),
-					icon: faUpload,
-					action: () => { this.chooseFileFromPc() }
-				}, {
-					text: this.$t('fromDrive'),
-					icon: faCloud,
-					action: () => { this.chooseFileFromDrive() }
-				}, {
-					text: this.$t('fromUrl'),
-					icon: faLink,
-					action: () => { this.chooseFileFromUrl() }
-				}],
-				source: ev.currentTarget || ev.target
-			});
-		},
-
-		chooseFileFromPc() {
-			(this.$refs.file as any).click();
-		},
-
-		chooseFileFromDrive() {
-			selectDriveFile(this.$root, true).then(files => {
+			selectFile(ev.currentTarget || ev.target, this.$t('attachFile'), true).then(files => {
 				for (const file of files) {
-					this.attachMedia(file);
+					this.files.push(file);
 				}
 			});
 		},
 
-		attachMedia(driveFile) {
-			this.files.push(driveFile);
-		},
-
 		detachMedia(id) {
 			this.files = this.files.filter(x => x.id != id);
 		},
 
 		updateMedia(file) {
-			Vue.set(this.files, this.files.findIndex(x => x.id === file.id), file);
-		},
-
-		onChangeFile() {
-			for (const x of Array.from((this.$refs.file as any).files)) this.upload(x);
+			this.files[this.files.findIndex(x => x.id === file.id)] = file;
 		},
 
 		upload(file: File, name?: string) {
-			(this.$refs.uploader as any).upload(file, this.$store.state.settings.uploadFolder, name);
-		},
-
-		onChangeUploadings(uploads) {
-			this.$emit('change-uploadings', uploads);
+			os.upload(file, this.$store.state.settings.uploadFolder, name).then(res => {
+				this.files.push(res);
+			});
 		},
 
-		onPollUpdate() {
-			const got = this.$refs.poll.get();
-			this.pollChoices = got.choices;
-			this.pollMultiple = got.multiple;
-			this.pollExpiration = [got.expiration, got.expiresAt || got.expiredAfter];
+		onPollUpdate(poll) {
+			this.poll = poll;
 			this.saveDraft();
 		},
 
-		setVisibility() {
+		async setVisibility() {
 			if (this.channel) {
 				// TODO: information dialog
 				return;
 			}
-			const w = this.$root.new(MkVisibilityChooser, {
-				source: this.$refs.visibilityButton,
+
+			os.popup(await import('./visibility-picker.vue'), {
 				currentVisibility: this.visibility,
-				currentLocalOnly: this.localOnly
-			});
-			w.$once('chosen', ({ visibility, localOnly }) => {
-				this.applyVisibility(visibility);
-				this.localOnly = localOnly;
-			});
+				currentLocalOnly: this.localOnly,
+				src: this.$refs.visibilityButton
+			}, {
+				changeVisibility: visibility => {
+					this.applyVisibility(visibility);
+				},
+				changeLocalOnly: localOnly => {
+					this.localOnly = localOnly;
+				}
+			}, 'closed');
 		},
 
 		applyVisibility(v: string) {
@@ -432,8 +411,7 @@ export default Vue.extend({
 		},
 
 		addVisibleUser() {
-			const vm = this.$root.new(MkUserSelect, {});
-			vm.$once('selected', user => {
+			os.selectUser().then(user => {
 				this.visibleUsers.push(user);
 			});
 		},
@@ -445,12 +423,13 @@ export default Vue.extend({
 		clear() {
 			this.text = '';
 			this.files = [];
-			this.poll = false;
+			this.poll = null;
 			this.quoteId = null;
 		},
 
 		onKeydown(e) {
-			if ((e.which == 10 || e.which == 13) && (e.ctrlKey || e.metaKey) && this.canPost) this.post();
+			if ((e.which === 10 || e.which === 13) && (e.ctrlKey || e.metaKey) && this.canPost) this.post();
+			if (e.which === 27) this.$emit('esc');
 		},
 
 		async onPaste(e: ClipboardEvent) {
@@ -469,7 +448,7 @@ export default Vue.extend({
 			if (!this.renote && !this.quoteId && paste.startsWith(url + '/notes/')) {
 				e.preventDefault();
 
-				this.$root.dialog({
+				os.dialog({
 					type: 'info',
 					text: this.$t('quoteQuestion'),
 					showCancelButton: true
@@ -487,7 +466,7 @@ export default Vue.extend({
 		onDragover(e) {
 			if (!e.dataTransfer.items[0]) return;
 			const isFile = e.dataTransfer.items[0].kind == 'file';
-			const isDriveFile = e.dataTransfer.types[0] == 'mk_drive_file';
+			const isDriveFile = e.dataTransfer.types[0] == _DATA_TRANSFER_DRIVE_FILE_;
 			if (isFile || isDriveFile) {
 				e.preventDefault();
 				this.draghover = true;
@@ -514,7 +493,7 @@ export default Vue.extend({
 			}
 
 			//#region ドライブのファイル
-			const driveFile = e.dataTransfer.getData('mk_drive_file');
+			const driveFile = e.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
 			if (driveFile != null && driveFile != '') {
 				const file = JSON.parse(driveFile);
 				this.files.push(file);
@@ -537,7 +516,7 @@ export default Vue.extend({
 					visibility: this.visibility,
 					localOnly: this.localOnly,
 					files: this.files,
-					poll: this.poll && this.$refs.poll ? (this.$refs.poll as any).get() : undefined
+					poll: this.poll
 				}
 			};
 
@@ -559,29 +538,30 @@ export default Vue.extend({
 				replyId: this.reply ? this.reply.id : undefined,
 				renoteId: this.renote ? this.renote.id : this.quoteId ? this.quoteId : undefined,
 				channelId: this.channel ? this.channel.id : undefined,
-				poll: this.poll ? (this.$refs.poll as any).get() : undefined,
+				poll: this.poll,
 				cw: this.useCw ? this.cw || '' : undefined,
 				localOnly: this.localOnly,
 				visibility: this.visibility,
 				visibleUserIds: this.visibility == 'specified' ? this.visibleUsers.map(u => u.id) : undefined,
-				viaMobile: this.$root.isMobile
+				viaMobile: os.isMobile
 			};
 
 			// plugin
-			if (this.$store.state.notePostInterruptors.length > 0) {
-				for (const interruptor of this.$store.state.notePostInterruptors) {
-					data = utils.valToJs(await interruptor.handler(JSON.parse(JSON.stringify(data))));
+			if (notePostInterruptors.length > 0) {
+				for (const interruptor of notePostInterruptors) {
+					data = await interruptor.handler(JSON.parse(JSON.stringify(data)));
 				}
 			}
 
 			this.posting = true;
-			this.$root.api('notes/create', data).then(() => {
+			os.api('notes/create', data).then(() => {
 				this.clear();
 				this.deleteDraft();
 				this.$emit('posted');
 			}).catch(err => {
 			}).then(() => {
 				this.posting = false;
+				this.$emit('done');
 			});
 
 			if (this.text && this.text != '') {
@@ -592,39 +572,32 @@ export default Vue.extend({
 		},
 
 		cancel() {
-			this.$emit('cancel');
+			this.$emit('done');
 		},
 
 		insertMention() {
-			const vm = this.$root.new(MkUserSelect, {});
-			vm.$once('selected', user => {
-				insertTextAtCursor(this.$refs.text, getAcct(user) + ' ');
+			os.selectUser().then(user => {
+				insertTextAtCursor(this.$refs.text, '@' + getAcct(user) + ' ');
 			});
 		},
 
 		async insertEmoji(ev) {
-			const vm = this.$root.new(await import('./emoji-picker.vue').then(m => m.default), {
-				source: ev.currentTarget || ev.target
-			}).$once('chosen', emoji => {
+			os.pickEmoji(ev.currentTarget || ev.target).then(emoji => {
 				insertTextAtCursor(this.$refs.text, emoji);
-				vm.close();
 			});
 		},
 
 		showActions(ev) {
-			this.$root.menu({
-				items: this.$store.state.postFormActions.map(action => ({
-					text: action.title,
-					action: () => {
-						action.handler({
-							text: this.text
-						}, (key, value) => {
-							if (key === 'text') { this.text = value; }
-						});
-					}
-				})),
-				source: ev.currentTarget || ev.target,
-			});
+			os.modalMenu(postFormActions.map(action => ({
+				text: action.title,
+				action: () => {
+					action.handler({
+						text: this.text
+					}, (key, value) => {
+						if (key === 'text') { this.text = value; }
+					});
+				}
+			})), ev.currentTarget || ev.target);
 		}
 	}
 });
@@ -632,26 +605,22 @@ export default Vue.extend({
 
 <style lang="scss" scoped>
 .gafaadew {
-	background: var(--panel);
+	position: relative;
+
+	&.modal {
+		width: 100%;
+		max-width: 520px;
+	}
 
 	> header {
 		z-index: 1000;
 		height: 66px;
 
-		@media (max-width: 500px) {
-			height: 50px;
-		}
-
 		> .cancel {
 			padding: 0;
 			font-size: 20px;
 			width: 64px;
 			line-height: 66px;
-
-			@media (max-width: 500px) {
-				width: 50px;
-				line-height: 50px;
-			}
 		}
 
 		> div {
@@ -662,10 +631,6 @@ export default Vue.extend({
 			> .text-count {
 				opacity: 0.7;
 				line-height: 66px;
-
-				@media (max-width: 500px) {
-					line-height: 50px;
-				}
 			}
 
 			> .visibility {
@@ -678,8 +643,9 @@ export default Vue.extend({
 				}
 			}
 			
-			.local-only {
-				margin: 0 8px;
+			> .local-only {
+				margin: 0 0 0 12px;
+				opacity: 0.7;
 			}
 
 			> .submit {
@@ -690,10 +656,6 @@ export default Vue.extend({
 				vertical-align: bottom;
 				border-radius: 4px;
 
-				@media (max-width: 500px) {
-					margin: 8px;
-				}
-
 				&:disabled {
 					opacity: 0.7;
 				}
@@ -706,13 +668,6 @@ export default Vue.extend({
 	}
 
 	> .form {
-		max-width: 500px;
-		margin: 0 auto;
-
-		&.fixed {
-			max-width: unset;
-		}
-
 		> .preview {
 			padding: 16px;
 		}
@@ -741,10 +696,6 @@ export default Vue.extend({
 			overflow: auto;
 			white-space: nowrap;
 
-			@media (max-width: 500px) {
-				padding: 6px 16px;
-			}
-
 			> .visibleUsers {
 				display: inline;
 				top: -1px;
@@ -782,10 +733,6 @@ export default Vue.extend({
 			color: var(--fg);
 			font-family: inherit;
 
-			@media (max-width: 500px) {
-				padding: 0 16px;
-			}
-
 			&:focus {
 				outline: none;
 			}
@@ -806,31 +753,14 @@ export default Vue.extend({
 			min-width: 100%;
 			min-height: 90px;
 
-			@media (max-width: 500px) {
-				min-height: 80px;
-			}
-
 			&.withCw {
 				padding-top: 8px;
 			}
 		}
 
-		> .mk-uploader {
-			margin: 8px 0 0 0;
-			padding: 8px;
-		}
-
-		> .file {
-			display: none;
-		}
-
 		> footer {
 			padding: 0 16px 16px 16px;
 
-			@media (max-width: 500px) {
-				padding: 0 8px 8px 8px;
-			}
-
 			> button {
 				display: inline-block;
 				padding: 0;
@@ -850,5 +780,45 @@ export default Vue.extend({
 			}
 		}
 	}
+
+	&.max-width_500px {
+		> header {
+			height: 50px;
+
+			> .cancel {
+				width: 50px;
+				line-height: 50px;
+			}
+
+			> div {
+				> .text-count {
+					line-height: 50px;
+				}
+
+				> .submit {
+					margin: 8px;
+				}
+			}
+		}
+
+		> .form {
+			> .to-specified {
+				padding: 6px 16px;
+			}
+
+			> .cw,
+			> .text {
+				padding: 0 16px;
+			}
+
+			> .text {
+				min-height: 80px;
+			}
+
+			> footer {
+				padding: 0 8px 8px 8px;
+			}
+		}
+	}
 }
 </style>
-- 
cgit v1.2.3-freya

