From 5209a584a2ed76057ec5edc351cf155154f3f68f Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 1 Sep 2018 20:47:49 +0900
Subject: [PATCH] Better post form

---
 package.json                                  |   2 +-
 src/client/app/app.styl                       |  14 +-
 src/client/app/app.vue                        |   5 +-
 .../app/desktop/views/pages/welcome.vue       |  16 +--
 src/client/app/init.ts                        |   7 +-
 src/client/app/mobile/api/post.ts             |  23 +++
 src/client/app/mobile/script.ts               |   3 +-
 .../mobile/views/components/note-detail.vue   |  28 +---
 .../app/mobile/views/components/note.vue      |  28 +---
 .../views/components/post-form-dialog.vue     | 131 ++++++++++++++++++
 src/client/app/mobile/views/pages/home.vue    |  10 +-
 11 files changed, 184 insertions(+), 83 deletions(-)
 create mode 100644 src/client/app/mobile/api/post.ts
 create mode 100644 src/client/app/mobile/views/components/post-form-dialog.vue

diff --git a/package.json b/package.json
index afdd47b3ce..4c88e02a45 100644
--- a/package.json
+++ b/package.json
@@ -210,12 +210,12 @@
 		"vue": "2.5.17",
 		"vue-chartjs": "3.4.0",
 		"vue-cropperjs": "2.2.1",
+		"vue-js-modal": "1.3.25",
 		"vue-json-tree-view": "2.1.4",
 		"vue-loader": "15.4.1",
 		"vue-router": "3.0.1",
 		"vue-style-loader": "4.1.2",
 		"vue-template-compiler": "2.5.17",
-		"vue-thin-modal": "1.1.1",
 		"vuedraggable": "2.16.0",
 		"vuex": "3.0.1",
 		"vuex-persistedstate": "2.5.4",
diff --git a/src/client/app/app.styl b/src/client/app/app.styl
index aaa0d34c71..3911f83a61 100644
--- a/src/client/app/app.styl
+++ b/src/client/app/app.styl
@@ -6,6 +6,10 @@ html
 		&, *
 			cursor progress !important
 
+html
+	// iOSのため
+	overflow auto
+
 body
 	overflow-wrap break-word
 
@@ -126,13 +130,3 @@ pre
 
 [data-fa]
 	display inline-block
-
-.modal-backdrop
-	z-index 10000 !important
-
-.modal-content-wrapper
-	z-index 10001 !important
-
-.modal-content
-	padding 0 !important
-	background-color transparent !important
diff --git a/src/client/app/app.vue b/src/client/app/app.vue
index 011eb14661..7a46e7dea0 100644
--- a/src/client/app/app.vue
+++ b/src/client/app/app.vue
@@ -1,6 +1,3 @@
 <template>
-<div>
-	<router-view id="app"></router-view>
-	<modal-portal/>
-</div>
+<router-view id="app"></router-view>
 </template>
diff --git a/src/client/app/desktop/views/pages/welcome.vue b/src/client/app/desktop/views/pages/welcome.vue
index 37c776130e..ae9bf7e678 100644
--- a/src/client/app/desktop/views/pages/welcome.vue
+++ b/src/client/app/desktop/views/pages/welcome.vue
@@ -38,11 +38,9 @@
 		<mk-welcome-timeline :max="20"/>
 	</div>
 
-	<modal name="signup">
-		<div :class="$style.modal">
-			<header :class="$style.signupFormHeader">%i18n:@signup%</header>
-			<mk-signup :class="$style.signupForm"/>
-		</div>
+	<modal name="signup" width="500px" height="auto" scrollable>
+		<header :class="$style.signupFormHeader">%i18n:@signup%</header>
+		<mk-signup :class="$style.signupForm"/>
 	</modal>
 </div>
 </template>
@@ -91,10 +89,10 @@ export default Vue.extend({
 			this.$refs.pointer.style.left = x.left + 'px';
 		},
 		signup() {
-			this.$modal.push('signup');
+			this.$modal.show('signup');
 		},
 		signin() {
-			this.$modal.push('signin');
+			this.$modal.show('signin');
 		},
 		dark() {
 			this.$store.commit('device/set', {
@@ -268,10 +266,6 @@ root(isDark)
 </style>
 
 <style lang="stylus" module>
-.modal
-	width 500px
-	background #fff !important
-
 .signupForm
 	padding 24px 48px 48px 48px
 
diff --git a/src/client/app/init.ts b/src/client/app/init.ts
index 0b22509870..82924e92e3 100644
--- a/src/client/app/init.ts
+++ b/src/client/app/init.ts
@@ -10,8 +10,7 @@ import VAnimateCss from 'v-animate-css';
 import Element from 'element-ui';
 import ElementLocaleEn from 'element-ui/lib/locale/lang/en';
 import ElementLocaleJa from 'element-ui/lib/locale/lang/ja';
-import VueThinModal from 'vue-thin-modal';
-import 'vue-thin-modal/dist/vue-thin-modal.css';
+import VModal from 'vue-js-modal';
 
 import App from './app.vue';
 import checkForUpdate from './common/scripts/check-for-update';
@@ -30,9 +29,7 @@ Vue.use(VueRouter);
 Vue.use(TreeView);
 Vue.use(VAnimateCss);
 Vue.use(Element, { locale: elementLocale });
-Vue.use(VueThinModal, {
-	autoMountPortal: false
-});
+Vue.use(VModal);
 
 // Register global directives
 require('./common/views/directives');
diff --git a/src/client/app/mobile/api/post.ts b/src/client/app/mobile/api/post.ts
new file mode 100644
index 0000000000..5c0f0af852
--- /dev/null
+++ b/src/client/app/mobile/api/post.ts
@@ -0,0 +1,23 @@
+import PostForm from '../views/components/post-form-dialog.vue';
+
+export default (os) => (opts) => {
+	const o = opts || {};
+
+	document.documentElement.style.overflow = 'hidden';
+
+	function recover() {
+		document.documentElement.style.overflow = 'auto';
+	}
+
+	const vm = new PostForm({
+		parent: os.app,
+		propsData: {
+			reply: o.reply,
+			renote: o.renote
+		}
+	}).$mount();
+	vm.$once('cancel', recover);
+	vm.$once('posted', recover);
+	document.body.appendChild(vm.$el);
+	(vm as any).focus();
+};
diff --git a/src/client/app/mobile/script.ts b/src/client/app/mobile/script.ts
index edc2b35529..5b9d45462a 100644
--- a/src/client/app/mobile/script.ts
+++ b/src/client/app/mobile/script.ts
@@ -14,6 +14,7 @@ import chooseDriveFolder from './api/choose-drive-folder';
 import chooseDriveFile from './api/choose-drive-file';
 import dialog from './api/dialog';
 import input from './api/input';
+import post from './api/post';
 import notify from './api/notify';
 
 import MkIndex from './views/pages/index.vue';
@@ -90,7 +91,7 @@ init((launch) => {
 		chooseDriveFile,
 		dialog: dialog(os),
 		input,
-		post: () => alert('deprecated'),
+		post: post(os),
 		notify
 	}));
 }, true);
diff --git a/src/client/app/mobile/views/components/note-detail.vue b/src/client/app/mobile/views/components/note-detail.vue
index 8a0305cedd..786e57bb22 100644
--- a/src/client/app/mobile/views/components/note-detail.vue
+++ b/src/client/app/mobile/views/components/note-detail.vue
@@ -75,19 +75,11 @@
 	<div class="replies" v-if="!compact">
 		<x-sub v-for="note in replies" :key="note.id" :note="note"/>
 	</div>
-
-	<modal :name="replyFormId">
-		<mk-post-form @posted="replyFormClosed" @cancel="replyFormClosed" :reply="p"/>
-	</modal>
-	<modal :name="renoteFormId">
-		<mk-post-form @posted="renoteFormClosed" @cancel="renoteFormClosed" :renote="p"/>
-	</modal>
 </div>
 </template>
 
 <script lang="ts">
 import Vue from 'vue';
-import * as uuid from 'uuid';
 import parse from '../../../../../mfm/parse';
 
 import MkNoteMenu from '../../../common/views/components/note-menu.vue';
@@ -113,9 +105,7 @@ export default Vue.extend({
 		return {
 			conversation: [],
 			conversationFetching: false,
-			replies: [],
-			replyFormId: uuid(),
-			renoteFormId: uuid()
+			replies: []
 		};
 	},
 
@@ -195,19 +185,15 @@ export default Vue.extend({
 		},
 
 		reply() {
-			this.$modal.push(this.replyFormId);
-		},
-
-		replyFormClosed() {
-			this.$modal.pop();
+			(this as any).apis.post({
+				reply: this.p
+			});
 		},
 
 		renote() {
-			this.$modal.push(this.renoteFormId);
-		},
-
-		renoteFormClosed() {
-			this.$modal.pop();
+			(this as any).apis.post({
+				renote: this.p
+			});
 		},
 
 		react() {
diff --git a/src/client/app/mobile/views/components/note.vue b/src/client/app/mobile/views/components/note.vue
index 4bf4eb34e3..d0cea135f9 100644
--- a/src/client/app/mobile/views/components/note.vue
+++ b/src/client/app/mobile/views/components/note.vue
@@ -60,19 +60,11 @@
 			</footer>
 		</div>
 	</article>
-
-	<modal :name="replyFormId">
-		<mk-post-form @posted="replyFormClosed" @cancel="replyFormClosed" :reply="p"/>
-	</modal>
-	<modal :name="renoteFormId">
-		<mk-post-form @posted="renoteFormClosed" @cancel="renoteFormClosed" :renote="p"/>
-	</modal>
 </div>
 </template>
 
 <script lang="ts">
 import Vue from 'vue';
-import * as uuid from 'uuid';
 import parse from '../../../../../mfm/parse';
 
 import MkNoteMenu from '../../../common/views/components/note-menu.vue';
@@ -90,9 +82,7 @@ export default Vue.extend({
 		return {
 			showContent: false,
 			connection: null,
-			connectionId: null,
-			replyFormId: uuid(),
-			renoteFormId: uuid()
+			connectionId: null
 		};
 	},
 
@@ -205,19 +195,15 @@ export default Vue.extend({
 		},
 
 		reply() {
-			this.$modal.push(this.replyFormId);
-		},
-
-		replyFormClosed() {
-			this.$modal.pop();
+			(this as any).apis.post({
+				reply: this.p
+			});
 		},
 
 		renote() {
-			this.$modal.push(this.renoteFormId);
-		},
-
-		renoteFormClosed() {
-			this.$modal.pop();
+			(this as any).apis.post({
+				renote: this.p
+			});
 		},
 
 		react() {
diff --git a/src/client/app/mobile/views/components/post-form-dialog.vue b/src/client/app/mobile/views/components/post-form-dialog.vue
new file mode 100644
index 0000000000..6fe9249321
--- /dev/null
+++ b/src/client/app/mobile/views/components/post-form-dialog.vue
@@ -0,0 +1,131 @@
+<template>
+<div class="ulveipglmagnxfgvitaxyszerjwiqmwl">
+	<div class="bg" ref="bg" @click="onBgClick"></div>
+	<div class="main" ref="main" @click.self="onBgClick">
+		<mk-post-form ref="form"
+			:reply="reply"
+			:renote="renote"
+			:initial-text="initialText"
+			:instant="instant"
+			@posted="onPosted"
+			@cancel="onCanceled"/>
+	</div>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import * as anime from 'animejs';
+
+export default Vue.extend({
+	props: {
+		reply: {
+			type: Object,
+			required: false
+		},
+		renote: {
+			type: Object,
+			required: false
+		},
+		initialText: {
+			type: String,
+			required: false
+		},
+		instant: {
+			type: Boolean,
+			required: false,
+			default: false
+		}
+	},
+
+	mounted() {
+		this.$nextTick(() => {
+			(this.$refs.bg as any).style.pointerEvents = 'auto';
+			anime({
+				targets: this.$refs.bg,
+				opacity: 1,
+				duration: 100,
+				easing: 'linear'
+			});
+
+			anime({
+				targets: this.$refs.main,
+				opacity: 1,
+				translateY: [-16, 0],
+				duration: 300,
+				easing: 'easeOutQuad'
+			});
+		});
+	},
+
+	methods: {
+		focus() {
+			this.$refs.form.focus();
+		},
+
+		close() {
+			(this.$refs.bg as any).style.pointerEvents = 'none';
+			anime({
+				targets: this.$refs.bg,
+				opacity: 0,
+				duration: 300,
+				easing: 'linear'
+			});
+
+			(this.$refs.main as any).style.pointerEvents = 'none';
+			anime({
+				targets: this.$refs.main,
+				opacity: 0,
+				translateY: 16,
+				duration: 300,
+				easing: 'easeOutQuad',
+				complete: () => this.$destroy()
+			});
+		},
+
+		onBgClick() {
+			this.$emit('cancel');
+			this.close();
+		},
+
+		onPosted() {
+			this.$emit('posted');
+			this.close();
+		},
+
+		onCanceled() {
+			this.$emit('cancel');
+			this.close();
+		}
+	}
+});
+</script>
+
+<style lang="stylus" scoped>
+.ulveipglmagnxfgvitaxyszerjwiqmwl
+	> .bg
+		display block
+		position fixed
+		z-index 10000
+		top 0
+		left 0
+		width 100%
+		height 100%
+		background rgba(#000, 0.7)
+		opacity 0
+		pointer-events none
+
+	> .main
+		display block
+		position fixed
+		z-index 10000
+		top 0
+		left 0
+		right 0
+		height 100%
+		overflow auto
+		margin 0 auto 0 auto
+		opacity 0
+		transform translateY(-16px)
+
+</style>
diff --git a/src/client/app/mobile/views/pages/home.vue b/src/client/app/mobile/views/pages/home.vue
index a03fa03c5f..706c9cd28b 100644
--- a/src/client/app/mobile/views/pages/home.vue
+++ b/src/client/app/mobile/views/pages/home.vue
@@ -42,10 +42,6 @@
 			<mk-user-list-timeline v-if="src == 'list'" ref="tl" :key="list.id" :list="list"/>
 		</div>
 	</main>
-
-	<modal name="postForm">
-		<mk-post-form @posted="postFormClosed" @cancel="postFormClosed"/>
-	</modal>
 </mk-ui>
 </template>
 
@@ -111,11 +107,7 @@ export default Vue.extend({
 
 	methods: {
 		fn() {
-			this.$modal.push('postForm');
-		},
-
-		postFormClosed() {
-			this.$modal.pop();
+			(this as any).apis.post();
 		},
 
 		saveSrc() {