misskey/src/client/pages/messaging-room.form.vue

354 lines
7.6 KiB
Vue
Raw Normal View History

2018-02-13 06:56:11 +00:00
<template>
<div class="mk-messaging-form _panel"
2018-02-26 21:25:17 +00:00
@dragover.stop="onDragover"
@drop.stop="onDrop"
2018-02-26 19:36:16 +00:00
>
2018-02-25 04:05:55 +00:00
<textarea
v-model="text"
2020-02-06 10:22:15 +00:00
ref="text"
2018-02-25 04:05:55 +00:00
@keypress="onKeypress"
@paste="onPaste"
:placeholder="$t('inputMessageHere')"
v-autocomplete="{ model: 'text' }"
2018-02-25 04:05:55 +00:00
></textarea>
2018-02-26 17:10:52 +00:00
<div class="file" @click="file = null" v-if="file">{{ file.name }}</div>
<x-uploader ref="uploader" @uploaded="onUploaded"/>
<button class="send _button" @click="send" :disabled="!canSend || sending" :title="$t('send')">
<template v-if="!sending"><fa :icon="faPaperPlane"/></template><template v-if="sending"><fa icon="spinner .spin"/></template>
2018-02-13 06:56:11 +00:00
</button>
2020-02-06 10:22:15 +00:00
<button class="_button" @click="chooseFile"><fa :icon="faPhotoVideo"/></button>
<button class="_button" @click="insertEmoji"><fa :icon="faLaughSquint"/></button>
2018-02-26 17:10:52 +00:00
<input ref="file" type="file" @change="onChangeFile"/>
2018-02-13 06:56:11 +00:00
</div>
</template>
<script lang="ts">
import Vue from 'vue';
2020-02-06 10:22:15 +00:00
import { faPaperPlane, faPhotoVideo, faLaughSquint } from '@fortawesome/free-solid-svg-icons';
import insertTextAtCursor from 'insert-text-at-cursor';
2018-02-24 17:57:19 +00:00
import * as autosize from 'autosize';
2020-02-06 10:22:15 +00:00
import i18n from '../i18n';
import { formatTimeString } from '../../misc/format-time-string';
2020-02-06 10:22:15 +00:00
import { selectFile } from '../scripts/select-file';
2018-02-24 17:57:19 +00:00
2018-02-13 06:56:11 +00:00
export default Vue.extend({
i18n,
components: {
XUploader: () => import('../components/uploader.vue').then(m => m.default),
},
2019-05-18 11:36:33 +00:00
props: {
user: {
type: Object,
requird: false,
},
group: {
type: Object,
requird: false,
},
},
2018-02-13 06:56:11 +00:00
data() {
return {
text: null,
2018-02-18 14:51:41 +00:00
file: null,
sending: false,
2020-02-06 10:22:15 +00:00
faPaperPlane, faPhotoVideo, faLaughSquint
2018-02-13 06:56:11 +00:00
};
},
2018-02-25 13:56:23 +00:00
computed: {
draftId(): string {
2019-05-18 11:36:33 +00:00
return this.user ? 'user:' + this.user.id : 'group:' + this.group.id;
2018-02-26 17:10:52 +00:00
},
canSend(): boolean {
return (this.text != null && this.text != '') || this.file != null;
2018-02-26 19:36:16 +00:00
},
room(): any {
return this.$parent;
2018-02-25 13:56:23 +00:00
}
},
watch: {
text() {
this.saveDraft();
},
file() {
this.saveDraft();
2018-02-26 19:36:16 +00:00
if (this.room.isBottom()) {
this.room.scrollToBottom();
}
2018-02-25 13:56:23 +00:00
}
},
2018-02-24 17:57:19 +00:00
mounted() {
2020-02-06 10:22:15 +00:00
autosize(this.$refs.text);
2018-02-25 13:56:23 +00:00
// 書きかけの投稿を復元
const draft = JSON.parse(localStorage.getItem('message_drafts') || '{}')[this.draftId];
if (draft) {
this.text = draft.data.text;
this.file = draft.data.file;
}
2018-02-24 17:57:19 +00:00
},
2018-02-13 06:56:11 +00:00
methods: {
async onPaste(e: ClipboardEvent) {
2018-02-13 06:56:11 +00:00
const data = e.clipboardData;
const items = data.items;
2018-02-26 19:36:16 +00:00
if (items.length == 1) {
if (items[0].kind == 'file') {
const file = items[0].getAsFile();
const lio = file.name.lastIndexOf('.');
const ext = lio >= 0 ? file.name.slice(lio) : '';
const formatted = `${formatTimeString(new Date(file.lastModified), this.$store.state.settings.pastedFileName).replace(/{{number}}/g, '1')}${ext}`;
const name = this.$store.state.settings.pasteDialog
? await this.$root.dialog({
title: this.$t('@.post-form.enter-file-name'),
input: {
default: formatted
},
allowEmpty: false
}).then(({ canceled, result }) => canceled ? false : result)
: formatted;
if (name) this.upload(file, name);
2018-02-26 19:36:16 +00:00
}
} else {
if (items[0].kind == 'file') {
this.$root.dialog({
type: 'error',
text: this.$t('only-one-file-attached')
});
2018-02-26 19:36:16 +00:00
}
}
},
onDragover(e) {
2018-02-26 21:25:17 +00:00
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';
}
2018-02-26 19:36:16 +00:00
},
onDrop(e): void {
// ファイルだったら
if (e.dataTransfer.files.length == 1) {
2018-02-26 21:25:17 +00:00
e.preventDefault();
2018-02-26 19:36:16 +00:00
this.upload(e.dataTransfer.files[0]);
return;
} else if (e.dataTransfer.files.length > 1) {
2018-02-26 21:25:17 +00:00
e.preventDefault();
this.$root.dialog({
type: 'error',
text: this.$t('only-one-file-attached')
});
2018-02-26 19:36:16 +00:00
return;
}
2018-02-26 21:25:17 +00:00
//#region ドライブのファイル
const driveFile = e.dataTransfer.getData('mk_drive_file');
if (driveFile != null && driveFile != '') {
this.file = JSON.parse(driveFile);
e.preventDefault();
2018-02-13 06:56:11 +00:00
}
2018-02-26 21:25:17 +00:00
//#endregion
2018-02-13 06:56:11 +00:00
},
onKeypress(e) {
2018-07-20 17:07:50 +00:00
if ((e.which == 10 || e.which == 13) && e.ctrlKey && this.canSend) {
2018-02-13 06:56:11 +00:00
this.send();
}
},
2020-02-06 10:22:15 +00:00
chooseFile(e) {
selectFile(this, e.currentTarget || e.target, this.$t('selectFile'), false).then(file => {
2018-02-18 14:51:41 +00:00
this.file = file;
2018-02-13 06:56:11 +00:00
});
},
2018-02-26 17:10:52 +00:00
onChangeFile() {
this.upload((this.$refs.file as any).files[0]);
},
upload(file: File, name?: string) {
(this.$refs.uploader as any).upload(file, this.$store.state.settings.uploadFolder, name);
2018-02-26 17:10:52 +00:00
},
onUploaded(file) {
this.file = file;
2018-02-18 14:56:25 +00:00
},
2018-02-18 14:51:41 +00:00
2018-02-13 06:56:11 +00:00
send() {
this.sending = true;
2018-11-08 23:13:34 +00:00
this.$root.api('messaging/messages/create', {
2019-05-18 11:36:33 +00:00
userId: this.user ? this.user.id : undefined,
groupId: this.group ? this.group.id : undefined,
2018-02-26 17:10:52 +00:00
text: this.text ? this.text : undefined,
2018-03-29 05:48:47 +00:00
fileId: this.file ? this.file.id : undefined
2018-02-13 06:56:11 +00:00
}).then(message => {
this.clear();
}).catch(err => {
console.error(err);
}).then(() => {
this.sending = false;
});
},
clear() {
this.text = '';
2018-02-18 14:56:25 +00:00
this.file = null;
2018-02-25 13:56:23 +00:00
this.deleteDraft();
},
saveDraft() {
const data = JSON.parse(localStorage.getItem('message_drafts') || '{}');
data[this.draftId] = {
2018-03-29 05:48:47 +00:00
updatedAt: new Date(),
2018-02-25 13:56:23 +00:00
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));
},
2020-02-06 10:22:15 +00:00
async insertEmoji(ev) {
const vm = this.$root.new(await import('../components/emoji-picker.vue').then(m => m.default), {
source: ev.currentTarget || ev.target
}).$once('chosen', emoji => {
insertTextAtCursor(this.$refs.text, emoji);
vm.close();
});
}
2018-02-13 06:56:11 +00:00
}
});
</script>
<style lang="scss" scoped>
.mk-messaging-form {
position: relative;
> textarea {
cursor: auto;
display: block;
width: 100%;
min-width: 100%;
max-width: 100%;
height: 80px;
margin: 0;
padding: 16px 16px 0 16px;
resize: none;
font-size: 1em;
font-family: inherit;
outline: none;
border: none;
border-radius: 0;
box-shadow: none;
background: transparent;
box-sizing: border-box;
color: var(--fg);
}
> .file {
padding: 8px;
color: #444;
background: #eee;
cursor: pointer;
}
2018-02-13 06:56:11 +00:00
> .send {
position: absolute;
bottom: 0;
right: 0;
margin: 0;
padding: 16px;
font-size: 1em;
transition: color 0.1s ease;
2020-02-10 12:30:59 +00:00
color: var(--accent);
&:active {
color: var(--accentDarken);
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;
}
}
}
2020-02-06 10:22:15 +00:00
._button {
margin: 0;
padding: 16px;
font-size: 1em;
font-weight: normal;
text-decoration: none;
transition: color 0.1s ease;
&:hover {
color: var(--accent);
}
&:active {
color: var(--accentDarken);
transition: color 0s ease;
}
}
input[type=file] {
display: none;
}
}
2018-02-13 06:56:11 +00:00
</style>