2018-02-13 06:56:11 +00:00
|
|
|
<template>
|
2021-04-10 03:40:50 +00:00
|
|
|
<div class="pemppnzi _block"
|
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
|
2020-02-06 10:22:15 +00:00
|
|
|
ref="text"
|
2021-11-19 10:36:12 +00:00
|
|
|
v-model="text"
|
|
|
|
:placeholder="$ts.inputMessageHere"
|
2022-01-25 09:01:29 +00:00
|
|
|
@keydown="onKeydown"
|
2021-02-21 03:26:49 +00:00
|
|
|
@compositionupdate="onCompositionUpdate"
|
2018-02-25 04:05:55 +00:00
|
|
|
@paste="onPaste"
|
|
|
|
></textarea>
|
2021-11-19 10:36:12 +00:00
|
|
|
<div v-if="file" class="file" @click="file = null">{{ file.name }}</div>
|
|
|
|
<button class="send _button" :disabled="!canSend || sending" :title="$ts.send" @click="send">
|
2021-04-20 14:22:59 +00:00
|
|
|
<template v-if="!sending"><i class="fas fa-paper-plane"></i></template><template v-if="sending"><i class="fas fa-spinner fa-pulse fa-fw"></i></template>
|
2018-02-13 06:56:11 +00:00
|
|
|
</button>
|
2021-04-20 14:22:59 +00:00
|
|
|
<button class="_button" @click="chooseFile"><i class="fas fa-photo-video"></i></button>
|
|
|
|
<button class="_button" @click="insertEmoji"><i class="fas fa-laugh-squint"></i></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">
|
2020-10-17 11:12:00 +00:00
|
|
|
import { defineComponent, defineAsyncComponent } from 'vue';
|
2020-02-06 10:22:15 +00:00
|
|
|
import insertTextAtCursor from 'insert-text-at-cursor';
|
2022-01-27 08:55:11 +00:00
|
|
|
import autosize from 'autosize';
|
2021-11-11 17:02:25 +00:00
|
|
|
import { formatTimeString } from '@/scripts/format-time-string';
|
|
|
|
import { selectFile } from '@/scripts/select-file';
|
|
|
|
import * as os from '@/os';
|
2021-12-29 13:13:09 +00:00
|
|
|
import { stream } from '@/stream';
|
2021-11-11 17:02:25 +00:00
|
|
|
import { Autocomplete } from '@/scripts/autocomplete';
|
2021-02-21 03:26:49 +00:00
|
|
|
import { throttle } from 'throttle-debounce';
|
2022-04-28 02:14:03 +00:00
|
|
|
import { uploadFile } from '@/scripts/upload';
|
2018-02-24 17:57:19 +00:00
|
|
|
|
2020-10-17 11:12:00 +00:00
|
|
|
export default defineComponent({
|
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,
|
2020-01-29 19:37:25 +00:00
|
|
|
sending: false,
|
2021-02-21 03:26:49 +00:00
|
|
|
typing: throttle(3000, () => {
|
2021-12-29 13:13:09 +00:00
|
|
|
stream.send('typingOnMessaging', this.user ? { partner: this.user.id } : { group: this.group.id });
|
2021-02-21 03:26:49 +00:00
|
|
|
}),
|
2018-02-13 06:56:11 +00:00
|
|
|
};
|
|
|
|
},
|
2018-02-25 13:56:23 +00:00
|
|
|
computed: {
|
2020-08-18 13:44:21 +00:00
|
|
|
draftKey(): 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 {
|
2022-05-26 13:53:09 +00:00
|
|
|
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-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
|
|
|
|
2020-10-17 11:12:00 +00:00
|
|
|
// TODO: detach when unmount
|
2022-01-27 08:55:11 +00:00
|
|
|
// TODO
|
|
|
|
//new Autocomplete(this.$refs.text, this, { model: 'text' });
|
2020-10-17 11:12:00 +00:00
|
|
|
|
2018-02-25 13:56:23 +00:00
|
|
|
// 書きかけの投稿を復元
|
2020-08-18 13:44:21 +00:00
|
|
|
const draft = JSON.parse(localStorage.getItem('message_drafts') || '{}')[this.draftKey];
|
2018-02-25 13:56:23 +00:00
|
|
|
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: {
|
2022-05-26 13:53:09 +00:00
|
|
|
async onPaste(evt: ClipboardEvent) {
|
|
|
|
const items = evt.clipboardData.items;
|
2018-02-26 19:36:16 +00:00
|
|
|
|
2022-05-26 13:53:09 +00:00
|
|
|
if (items.length === 1) {
|
|
|
|
if (items[0].kind === 'file') {
|
2019-07-08 04:46:31 +00:00
|
|
|
const file = items[0].getAsFile();
|
|
|
|
const lio = file.name.lastIndexOf('.');
|
|
|
|
const ext = lio >= 0 ? file.name.slice(lio) : '';
|
2020-12-19 01:55:52 +00:00
|
|
|
const formatted = `${formatTimeString(new Date(file.lastModified), this.$store.state.pastedFileName).replace(/{{number}}/g, '1')}${ext}`;
|
2021-11-18 09:45:58 +00:00
|
|
|
if (formatted) this.upload(file, formatted);
|
2018-02-26 19:36:16 +00:00
|
|
|
}
|
|
|
|
} else {
|
2022-05-26 13:53:09 +00:00
|
|
|
if (items[0].kind === 'file') {
|
2021-11-18 09:45:58 +00:00
|
|
|
os.alert({
|
2019-04-16 04:05:10 +00:00
|
|
|
type: 'error',
|
2020-12-26 01:47:36 +00:00
|
|
|
text: this.$ts.onlyOneFileCanBeAttached
|
2019-04-16 04:05:10 +00:00
|
|
|
});
|
2018-02-26 19:36:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2022-05-26 13:53:09 +00:00
|
|
|
onDragover(evt) {
|
|
|
|
const isFile = evt.dataTransfer.items[0].kind === 'file';
|
|
|
|
const isDriveFile = evt.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_;
|
2018-02-26 21:25:17 +00:00
|
|
|
if (isFile || isDriveFile) {
|
2022-05-26 13:53:09 +00:00
|
|
|
evt.preventDefault();
|
|
|
|
evt.dataTransfer.dropEffect = evt.dataTransfer.effectAllowed === 'all' ? 'copy' : 'move';
|
2018-02-26 21:25:17 +00:00
|
|
|
}
|
2018-02-26 19:36:16 +00:00
|
|
|
},
|
|
|
|
|
2022-05-26 13:53:09 +00:00
|
|
|
onDrop(evt): void {
|
2018-02-26 19:36:16 +00:00
|
|
|
// ファイルだったら
|
2022-05-26 13:53:09 +00:00
|
|
|
if (evt.dataTransfer.files.length === 1) {
|
|
|
|
evt.preventDefault();
|
|
|
|
this.upload(evt.dataTransfer.files[0]);
|
2018-02-26 19:36:16 +00:00
|
|
|
return;
|
2022-05-26 13:53:09 +00:00
|
|
|
} else if (evt.dataTransfer.files.length > 1) {
|
|
|
|
evt.preventDefault();
|
2021-11-18 09:45:58 +00:00
|
|
|
os.alert({
|
2019-04-16 04:05:10 +00:00
|
|
|
type: 'error',
|
2020-12-26 01:47:36 +00:00
|
|
|
text: this.$ts.onlyOneFileCanBeAttached
|
2019-04-16 04:05:10 +00:00
|
|
|
});
|
2018-02-26 19:36:16 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-02-26 21:25:17 +00:00
|
|
|
//#region ドライブのファイル
|
2022-05-26 13:53:09 +00:00
|
|
|
const driveFile = evt.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_);
|
|
|
|
if (driveFile != null && driveFile !== '') {
|
2018-02-26 21:25:17 +00:00
|
|
|
this.file = JSON.parse(driveFile);
|
2022-05-26 13:53:09 +00:00
|
|
|
evt.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
|
|
|
},
|
|
|
|
|
2022-05-26 13:53:09 +00:00
|
|
|
onKeydown(evt) {
|
2021-02-21 03:26:49 +00:00
|
|
|
this.typing();
|
2022-05-26 13:53:09 +00:00
|
|
|
if ((evt.which === 10 || evt.which === 13) && (evt.ctrlKey || evt.metaKey) && this.canSend) {
|
2018-02-13 06:56:11 +00:00
|
|
|
this.send();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2021-02-21 03:26:49 +00:00
|
|
|
onCompositionUpdate() {
|
|
|
|
this.typing();
|
|
|
|
},
|
|
|
|
|
2022-05-26 13:53:09 +00:00
|
|
|
chooseFile(evt) {
|
|
|
|
selectFile(evt.currentTarget ?? evt.target, this.$ts.selectFile).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]);
|
|
|
|
},
|
|
|
|
|
2019-07-08 04:46:31 +00:00
|
|
|
upload(file: File, name?: string) {
|
2022-04-28 02:14:03 +00:00
|
|
|
uploadFile(file, this.$store.state.uploadFolder, name).then(res => {
|
2020-10-17 11:12:00 +00:00
|
|
|
this.file = res;
|
|
|
|
});
|
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;
|
2020-10-17 11:12:00 +00:00
|
|
|
os.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() {
|
2022-05-26 13:53:09 +00:00
|
|
|
const drafts = JSON.parse(localStorage.getItem('message_drafts') || '{}');
|
2018-02-25 13:56:23 +00:00
|
|
|
|
2022-05-26 13:53:09 +00:00
|
|
|
drafts[this.draftKey] = {
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-26 13:53:09 +00:00
|
|
|
localStorage.setItem('message_drafts', JSON.stringify(drafts));
|
2018-02-25 13:56:23 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
deleteDraft() {
|
2022-05-26 13:53:09 +00:00
|
|
|
const drafts = JSON.parse(localStorage.getItem('message_drafts') || '{}');
|
2018-02-25 13:56:23 +00:00
|
|
|
|
2022-05-26 13:53:09 +00:00
|
|
|
delete drafts[this.draftKey];
|
2018-02-25 13:56:23 +00:00
|
|
|
|
2022-05-26 13:53:09 +00:00
|
|
|
localStorage.setItem('message_drafts', JSON.stringify(drafts));
|
2018-02-25 13:56:23 +00:00
|
|
|
},
|
2020-02-06 10:22:15 +00:00
|
|
|
|
|
|
|
async insertEmoji(ev) {
|
2022-01-28 02:53:12 +00:00
|
|
|
os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, this.$refs.text);
|
2020-02-06 10:22:15 +00:00
|
|
|
}
|
2018-02-13 06:56:11 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
</script>
|
|
|
|
|
2020-01-29 19:37:25 +00:00
|
|
|
<style lang="scss" scoped>
|
2021-04-10 03:40:50 +00:00
|
|
|
.pemppnzi {
|
2020-01-29 19:37:25 +00:00
|
|
|
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;
|
2020-02-09 10:59:28 +00:00
|
|
|
font-family: inherit;
|
2020-01-29 19:37:25 +00:00
|
|
|
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
|
|
|
|
2020-01-29 19:37:25 +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);
|
2020-01-29 19:37:25 +00:00
|
|
|
|
|
|
|
&: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 {
|
2020-01-29 19:37:25 +00:00
|
|
|
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>
|