refactor newMessage, add fileUpload and soundRecorder

dev
adb 4 years ago
parent c35b5ba446
commit c57c1d348f

@ -0,0 +1,62 @@
<template>
<div class="fileUpload">
<icon
title="upload media"
class="leftBtn attachFile"
ic="./sym/ic_attach_file_white.svg"
@click.native="$refs.fileInput.click()"
/>
<input
type="file" id="fileInput" ref="fileInput"
@change="setFile({file: $refs.fileInput.files[0]})"
>
</div>
</template>
<script>
import icon from '@/components/icon';
export default {
name: 'soundRecorder',
components: {
icon
},
props:{
onChange: Function
},
methods: {
setFile({file}){
this.readFile(file).then(blob => {
blob.name = file.name;
this.onChange({blob})
});
},
readFile(file){
return new Promise(resolve => {
let reader = new FileReader();
reader.onerror = console.error;
reader.onload = async event => {
resolve(await (await fetch(event.target.result)).blob());
}
reader.readAsDataURL(file);
});
},
}
}
</script>
<style scoped>
.fileUpload{
display: inline-block;
position: relative;
}
.leftBtn{
background-color: unset;
height: 2.5rem;
width: 2.5rem;
box-shadow: none;
}
#fileInput{
display: none;
}
</style>

@ -18,33 +18,14 @@
rows="1" placeholder="type a message ..." rows="1" placeholder="type a message ..."
/> />
<icon <icon
v-if="event.content.body && !isRecording || attachment" v-if="event.content.body && !getRecordingState() || attachment"
type="submit" type="submit"
title="press enter to submit" title="press enter to submit"
class="sendMessageBtn" class="sendMessageBtn"
ic="./sym/ic_send_white.svg" ic="./sym/ic_send_white.svg"
@click.native="onSubmit(event)" @click.native="onSubmit(event)"
/> />
<div v-else class="recorder"> <sound-recorder v-else class="recorder" :on-stop="setAttachment" ref="recorder"/>
<icon
v-if="!isRecording"
title="record voice"
class="recordBtn start"
ic="./sym/ic_mic_white.svg"
@click.native="startRecording()"
ref="startRecord"
/>
<div v-else class="voiceMeterContainer">
<div class="voiceMeter" ref="voiceMeter"></div>
<icon
title="record voice"
class="recordBtn stop"
ic="./sym/ic_mic_white.svg"
@click.native="stopRecording()"
ref="stopRecord"
/>
</div>
</div>
<div class="mediaButtons"> <div class="mediaButtons">
<icon <icon
title="toggle emoji" title="toggle emoji"
@ -52,16 +33,7 @@
ic="./sym/ic_insert_emoticon_white.svg" ic="./sym/ic_insert_emoticon_white.svg"
@click.native="toggleEmojiPicker()" @click.native="toggleEmojiPicker()"
/> />
<icon <fileUpload class="leftBtn" :on-change="setAttachment"/>
title="upload media"
class="leftBtn attachFile"
ic="./sym/ic_attach_file_white.svg"
@click.native="$refs.fileInput.click()"
/>
<input
type="file" id="fileInput" ref="fileInput"
@change="setAttachment({file: $refs.fileInput.files[0]})"
>
</div> </div>
<v-emoji-picker <v-emoji-picker
v-if="showEmojiPicker" v-if="showEmojiPicker"
@ -80,13 +52,15 @@ import {parseMessage} from '@/lib/eventUtils';
import {calcUserName} from '@/lib/matrixUtils'; import {calcUserName} from '@/lib/matrixUtils';
import ReplyEvent from '@/components/replyEvent'; import ReplyEvent from '@/components/replyEvent';
import {VEmojiPicker} from 'v-emoji-picker'; import {VEmojiPicker} from 'v-emoji-picker';
import Recorder from 'recorder-js';
import EventContent from '@/components/eventContent'; import EventContent from '@/components/eventContent';
const audioContext = new (window.AudioContext || window.webkitAudioContext)(); import SoundRecorder from '@/components/soundRecorder';
import FileUpload from '@/components/fileUpload';
export default { export default {
name: 'newMessage', name: 'newMessage',
components: { components: {
FileUpload,
SoundRecorder,
EventContent, EventContent,
ReplyEvent, ReplyEvent,
icon, icon,
@ -145,44 +119,16 @@ export default {
onSelectEmoji(emoji) { onSelectEmoji(emoji) {
this.event.content.body += emoji.data; this.event.content.body += emoji.data;
}, },
startRecording(){ getRecordingState(){
navigator.mediaDevices.getUserMedia({audio: true}) return this.$refs.recorder && this.$refs.recorder.isRecording
.then(stream => {
this.recorder.init(stream);
this.recorder.start().then(()=>this.isRecording=true);
})
.catch(err => console.log('unable to get stream', err));
},
stopRecording(){
this.recorder.stop()
.then(({blob}) => {
this.isRecording=false;
blob.name = `Recording-${new Date().toISOString()}.${blob.type.split('/')[1]}`;
this.setAttachment({blob});
});
},
setVoiceMeter(value){
if (!this.$refs.stopRecord) return;
this.$refs.voiceMeter.style.height = `calc(3rem + ${value/4}px`;
this.$refs.voiceMeter.style.width = `calc(3rem + ${value/4}px`;
},
async readFile(file){
return await new Promise(resolve => {
let reader = new FileReader();
reader.onerror = console.error;
reader.onload = async event => {
resolve(await (await fetch(event.target.result)).blob());
}
reader.readAsDataURL(file);
});
}, },
async setAttachment({blob, file = blob}){ async setAttachment({blob, file = blob}){
this.attachment = { this.attachment = {
msgtype: this.getMsgType(file.type), msgtype: this.getMsgType(file.type),
mimetype: file.type, mimetype: file.type,
url: window.URL.createObjectURL(file), url: window.URL.createObjectURL(file),
blob: blob || await this.readFile(file),
filename: file.name, filename: file.name,
blob,
file file
}; };
this.event.content = { this.event.content = {
@ -196,7 +142,7 @@ export default {
if (!this.attachment) return; if (!this.attachment) return;
window.URL.revokeObjectURL(this.attachment.file); window.URL.revokeObjectURL(this.attachment.file);
this.event.content = { this.event.content = {
body: this.attachment?this.event.content.body.replace(this.attachment.file.name, ''):'', body: this.attachment?this.event.content.body.replace(this.attachment.filename, ''):'',
msgtype: 'm.text' msgtype: 'm.text'
}; };
this.attachment = undefined; this.attachment = undefined;
@ -225,11 +171,6 @@ export default {
}, },
showEmojiPicker: false, showEmojiPicker: false,
waitForSendTyping: false, waitForSendTyping: false,
recorder: new Recorder(audioContext, {
onAnalysed: data => this.setVoiceMeter(data.lineTo)
}),
isRecording: false,
recBlob: undefined,
attachment: undefined, attachment: undefined,
eventProxyHandler: { eventProxyHandler: {
set: () => true, set: () => true,
@ -320,48 +261,12 @@ export default {
width: 2.5rem; width: 2.5rem;
box-shadow: none; box-shadow: none;
} }
.emojiToggle{
}
.recordBtn{
position: absolute;
right: 1rem;
bottom: 0.25rem;
background-color: #1d1d1d;
border-radius: 50%;
}
.recordBtn.stop{
right: 0;
bottom: 0;
background-color: #c63e3e;
box-shadow: none;
}
.voiceMeter{
position: absolute;
background-color: #fff;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
border-radius: 50%;
box-shadow: 3px 3px 10px #111;
}
.voiceMeterContainer{
position: absolute;
height: 3rem;
width: 3rem;
bottom: 0.25rem;
right: 1rem;
}
.recorder{ .recorder{
position: absolute; position: absolute;
height: 100%; height: 100%;
width: 8rem;
bottom: 0; bottom: 0;
right: 0; right: 0;
border-radius: 0 1rem 0 0; border-radius: 0 1rem 0 0;
overflow: hidden;
}
#fileInput{
display: none;
} }
.attachment{ .attachment{
top: 0.5rem; top: 0.5rem;

@ -0,0 +1,114 @@
<template>
<div class="recorder">
<icon
v-if="!isRecording"
title="record voice"
class="recordBtn start"
ic="./sym/ic_mic_white.svg"
@click.native="startRecording()"
ref="startRecord"
/>
<div v-else class="voiceMeterContainer">
<div class="voiceMeter" ref="voiceMeter"></div>
<icon
title="record voice"
class="recordBtn stop"
ic="./sym/ic_mic_white.svg"
@click.native="stopRecording()"
ref="stopRecord"
/>
</div>
</div>
</template>
<script>
import icon from '@/components/icon';
import Recorder from 'recorder-js';
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
export default {
name: 'soundRecorder',
components: {
icon
},
props: {
onStart: {
type: Function,
default: ()=>{}
},
onStop: {
type: Function,
default: ()=>{}
}
},
methods: {
startRecording(){
this.onStart();
navigator.mediaDevices.getUserMedia({audio: true})
.then(stream => {
this.recorder.init(stream);
this.recorder.start().then(()=>this.isRecording=true);
})
.catch(err => console.log('unable to get stream', err));
},
stopRecording(){
this.recorder.stop()
.then(({blob}) => {
blob.name = `Recording-${new Date().toISOString()}.${blob.type.split('/')[1]}`;
this.onStop({blob});
this.isRecording=false;
});
},
setVoiceMeter(value){
if (!this.$refs.stopRecord) return;
this.$refs.voiceMeter.style.height = `calc(3rem + ${value/4}px`;
this.$refs.voiceMeter.style.width = `calc(3rem + ${value/4}px`;
},
},
data(){
return{
recorder: new Recorder(audioContext, {
onAnalysed: data => this.setVoiceMeter(data.lineTo)
}),
isRecording: false
}
}
}
</script>
<style scoped lang="scss">
.recordBtn{
position: absolute;
right: 1rem;
bottom: 0.25rem;
background-color: #1d1d1d;
border-radius: 50%;
}
.recordBtn.stop{
right: 0;
bottom: 0;
background-color: #c63e3e;
box-shadow: none;
}
.voiceMeter{
position: absolute;
background-color: #fff;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
border-radius: 50%;
box-shadow: 3px 3px 10px #111;
}
.voiceMeterContainer{
position: absolute;
height: 3rem;
width: 3rem;
bottom: 0.25rem;
right: 1rem;
}
.recorder{
height: 100%;
width: 8rem;
overflow: hidden;
}
</style>
Loading…
Cancel
Save