Skip to content
Snippets Groups Projects
messaging-room.form.vue 6.05 KiB
Newer Older
こぴなたみぽ's avatar
wip
こぴなたみぽ committed
<template>
syuilo's avatar
syuilo committed
<div class="mk-messaging-form"
syuilo's avatar
syuilo committed
	@dragover.stop="onDragover"
	@drop.stop="onDrop"
syuilo's avatar
syuilo committed
>
syuilo's avatar
syuilo committed
	<textarea
		v-model="text"
		ref="textarea"
		@keypress="onKeypress"
		@paste="onPaste"
syuilo's avatar
syuilo committed
		placeholder="%i18n:@input-message-here%"
syuilo's avatar
syuilo committed
		v-autocomplete="'text'"
	></textarea>
syuilo's avatar
syuilo committed
	<div class="file" @click="file = null" v-if="file">{{ file.name }}</div>
	<mk-uploader ref="uploader" @uploaded="onUploaded"/>
syuilo's avatar
syuilo committed
	<button class="send" @click="send" :disabled="!canSend || sending" title="%i18n:@send%">
こぴなたみぽ's avatar
wip
こぴなたみぽ committed
		<template v-if="!sending">%fa:paper-plane%</template><template v-if="sending">%fa:spinner .spin%</template>
	</button>
syuilo's avatar
syuilo committed
	<button class="attach-from-local" @click="chooseFile" title="%i18n:@attach-from-local%">
こぴなたみぽ's avatar
wip
こぴなたみぽ committed
		%fa:upload%
	</button>
syuilo's avatar
syuilo committed
	<button class="attach-from-drive" @click="chooseFileFromDrive" title="%i18n:@attach-from-drive%">
こぴなたみぽ's avatar
wip
こぴなたみぽ committed
		%fa:R folder-open%
	</button>
syuilo's avatar
syuilo committed
	<input ref="file" type="file" @change="onChangeFile"/>
こぴなたみぽ's avatar
wip
こぴなたみぽ committed
</div>
</template>

<script lang="ts">
import Vue from 'vue';
syuilo's avatar
syuilo committed
import * as autosize from 'autosize';

こぴなたみぽ's avatar
wip
こぴなたみぽ committed
export default Vue.extend({
	props: ['user'],
	data() {
		return {
			text: null,
syuilo's avatar
syuilo committed
			file: null,
こぴなたみぽ's avatar
wip
こぴなたみぽ committed
			sending: false
		};
	},
syuilo's avatar
syuilo committed
	computed: {
		draftId(): string {
			return this.user.id;
syuilo's avatar
syuilo committed
		},
		canSend(): boolean {
			return (this.text != null && this.text != '') || this.file != null;
syuilo's avatar
syuilo committed
		},
		room(): any {
			return this.$parent;
syuilo's avatar
syuilo committed
		}
	},
	watch: {
		text() {
			this.saveDraft();
		},
		file() {
			this.saveDraft();
syuilo's avatar
syuilo committed

			if (this.room.isBottom()) {
				this.room.scrollToBottom();
			}
syuilo's avatar
syuilo committed
		}
	},
syuilo's avatar
syuilo committed
	mounted() {
		autosize(this.$refs.textarea);
syuilo's avatar
syuilo committed

		// 書きかけの投稿を復元
		const draft = JSON.parse(localStorage.getItem('message_drafts') || '{}')[this.draftId];
		if (draft) {
			this.text = draft.data.text;
			this.file = draft.data.file;
		}
syuilo's avatar
syuilo committed
	},
こぴなたみぽ's avatar
wip
こぴなたみぽ committed
	methods: {
		onPaste(e) {
			const data = e.clipboardData;
			const items = data.items;
syuilo's avatar
syuilo committed

			if (items.length == 1) {
				if (items[0].kind == 'file') {
					this.upload(items[0].getAsFile());
				}
			} else {
				if (items[0].kind == 'file') {
					alert('メッセージに添付できるのはひとつのファイルのみです');
				}
			}
		},

		onDragover(e) {
syuilo's avatar
syuilo committed
			const isFile = e.dataTransfer.items[0].kind == 'file';
			const isDriveFile = e.dataTransfer.types[0] == 'mk_drive_file';
			if (isFile || isDriveFile) {
				e.preventDefault();
				e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move';
			}
syuilo's avatar
syuilo committed
		},

		onDrop(e): void {
			// ファイルだったら
			if (e.dataTransfer.files.length == 1) {
syuilo's avatar
syuilo committed
				e.preventDefault();
syuilo's avatar
syuilo committed
				this.upload(e.dataTransfer.files[0]);
				return;
			} else if (e.dataTransfer.files.length > 1) {
syuilo's avatar
syuilo committed
				e.preventDefault();
syuilo's avatar
syuilo committed
				alert('メッセージに添付できるのはひとつのファイルのみです');
				return;
			}

syuilo's avatar
syuilo committed
			//#region ドライブのファイル
			const driveFile = e.dataTransfer.getData('mk_drive_file');
			if (driveFile != null && driveFile != '') {
				this.file = JSON.parse(driveFile);
				e.preventDefault();
こぴなたみぽ's avatar
wip
こぴなたみぽ committed
			}
syuilo's avatar
syuilo committed
			//#endregion
こぴなたみぽ's avatar
wip
こぴなたみぽ committed
		},

		onKeypress(e) {
			if ((e.which == 10 || e.which == 13) && e.ctrlKey) {
				this.send();
			}
		},

		chooseFile() {
			(this.$refs.file as any).click();
		},

		chooseFileFromDrive() {
syuilo's avatar
syuilo committed
			(this as any).apis.chooseDriveFile({
				multiple: false
			}).then(file => {
				this.file = file;
こぴなたみぽ's avatar
wip
こぴなたみぽ committed
			});
		},

syuilo's avatar
syuilo committed
		onChangeFile() {
			this.upload((this.$refs.file as any).files[0]);
		},

		upload(file) {
			(this.$refs.uploader as any).upload(file);
		},

		onUploaded(file) {
			this.file = file;
syuilo's avatar
syuilo committed
		},
syuilo's avatar
syuilo committed

こぴなたみぽ's avatar
wip
こぴなたみぽ committed
		send() {
			this.sending = true;
syuilo's avatar
syuilo committed
			(this as any).api('messaging/messages/create', {
syuilo's avatar
syuilo committed
				userId: this.user.id,
syuilo's avatar
syuilo committed
				text: this.text ? this.text : undefined,
syuilo's avatar
syuilo committed
				fileId: this.file ? this.file.id : undefined
こぴなたみぽ's avatar
wip
こぴなたみぽ committed
			}).then(message => {
				this.clear();
			}).catch(err => {
				console.error(err);
			}).then(() => {
				this.sending = false;
			});
		},

		clear() {
			this.text = '';
syuilo's avatar
syuilo committed
			this.file = null;
syuilo's avatar
syuilo committed
			this.deleteDraft();
		},

		saveDraft() {
			const data = JSON.parse(localStorage.getItem('message_drafts') || '{}');

			data[this.draftId] = {
syuilo's avatar
syuilo committed
				updatedAt: new Date(),
syuilo's avatar
syuilo committed
				data: {
					text: this.text,
					file: this.file
				}
			}

			localStorage.setItem('message_drafts', JSON.stringify(data));
		},

		deleteDraft() {
			const data = JSON.parse(localStorage.getItem('message_drafts') || '{}');

			delete data[this.draftId];

			localStorage.setItem('message_drafts', JSON.stringify(data));
		},
こぴなたみぽ's avatar
wip
こぴなたみぽ committed
	}
});
</script>

<style lang="stylus" scoped>
syuilo's avatar
syuilo committed
@import '~const.styl'

syuilo's avatar
syuilo committed
root(isDark)
こぴなたみぽ's avatar
wip
こぴなたみぽ committed
	> textarea
		cursor auto
		display block
		width 100%
		min-width 100%
		max-width 100%
		height 64px
		margin 0
		padding 8px
syuilo's avatar
syuilo committed
		resize none
こぴなたみぽ's avatar
wip
こぴなたみぽ committed
		font-size 1em
syuilo's avatar
syuilo committed
		color isDark ? #fff : #000
こぴなたみぽ's avatar
wip
こぴなたみぽ committed
		outline none
		border none
syuilo's avatar
syuilo committed
		border-top solid 1px isDark ? #4b5056 : #eee
こぴなたみぽ's avatar
wip
こぴなたみぽ committed
		border-radius 0
		box-shadow none
		background transparent

syuilo's avatar
syuilo committed
	> .file
		padding 8px
		color #444
		background #eee
		cursor pointer

こぴなたみぽ's avatar
wip
こぴなたみぽ committed
	> .send
		position absolute
		bottom 0
		right 0
		margin 0
		padding 10px 14px
		font-size 1em
		color #aaa
		transition color 0.1s ease

		&:hover
			color $theme-color

		&:active
			color darken($theme-color, 10%)
			transition color 0s ease

	.files
		display block
		margin 0
		padding 0 8px
		list-style none

		&:after
			content ''
			display block
			clear both

		> li
			display block
			float left
			margin 4px
			padding 0
			width 64px
			height 64px
			background-color #eee
			background-repeat no-repeat
			background-position center center
			background-size cover
			cursor move

			&:hover
				> .remove
					display block

			> .remove
				display none
				position absolute
				right -6px
				top -6px
				margin 0
				padding 0
				background transparent
				outline none
				border none
				border-radius 0
				box-shadow none
				cursor pointer

	.attach-from-local
	.attach-from-drive
		margin 0
		padding 10px 14px
		font-size 1em
		font-weight normal
		text-decoration none
		color #aaa
		transition color 0.1s ease

		&:hover
			color $theme-color

		&:active
			color darken($theme-color, 10%)
			transition color 0s ease

	input[type=file]
		display none

syuilo's avatar
syuilo committed
.mk-messaging-form[data-darkmode]
	root(true)

.mk-messaging-form:not([data-darkmode])
	root(false)

こぴなたみぽ's avatar
wip
こぴなたみぽ committed
</style>