Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/spreed.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/NewMessageForm/AudioRecorder/AudioRecorder.vue')
-rw-r--r--src/components/NewMessageForm/AudioRecorder/AudioRecorder.vue277
1 files changed, 277 insertions, 0 deletions
diff --git a/src/components/NewMessageForm/AudioRecorder/AudioRecorder.vue b/src/components/NewMessageForm/AudioRecorder/AudioRecorder.vue
new file mode 100644
index 000000000..5a4119e95
--- /dev/null
+++ b/src/components/NewMessageForm/AudioRecorder/AudioRecorder.vue
@@ -0,0 +1,277 @@
+<!--
+ - @copyright Copyright (c) 2021 Marco Ambrosini <marcoambrosini@pm.me>
+ -
+ - @author Marco Ambrosini <marcoambrosini@pm.me>
+ -
+ - @license GNU AGPL version 3 or any later version
+ -
+ - This program is free software: you can redistribute it and/or modify
+ - it under the terms of the GNU Affero General Public License as
+ - published by the Free Software Foundation, either version 3 of the
+ - License, or (at your option) any later version.
+ -
+ - This program is distributed in the hope that it will be useful,
+ - but WITHOUT ANY WARRANTY; without even the implied warranty of
+ - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ - GNU Affero General Public License for more details.
+ -
+ - You should have received a copy of the GNU Affero General Public License
+ - along with this program. If not, see <http://www.gnu.org/licenses/>.
+-->
+
+<template>
+ <div
+ class="audio-recorder">
+ <button
+ v-if="!isRecording"
+ class="audio-recorder__trigger nc-button nc-button__main"
+ @click="start">
+ <Microphone
+ :size="16"
+ title=""
+ decorative />
+ </button>
+ <div v-else class="wrapper">
+ <button
+ class="audio-recorder__stop nc-button nc-button__main"
+ @click="abortRecording">
+ <Close
+ :size="16"
+ title=""
+ decorative />
+ </button>
+ <div class="audio-recorder__info">
+ <div class="recording-indicator fadeOutIn" />
+ <span
+ class="time">
+ {{ parsedRecordTime }}</span>
+ </div>
+ <button
+ class="audio-recorder__trigger nc-button nc-button__main"
+ :class="{'audio-recorder__trigger--recording': isRecording}"
+ @click="stop">
+ <Check
+ :size="16"
+ title=""
+ decorative />
+ </button>
+ </div>
+ </div>
+</template>
+
+<script>
+import Microphone from 'vue-material-design-icons/Microphone'
+import Close from 'vue-material-design-icons/Close'
+import Check from 'vue-material-design-icons/Check'
+
+import { mediaDevicesManager } from '../../../utils/webrtc/index'
+
+export default {
+ name: 'AudioRecorder',
+
+ components: {
+ Microphone,
+ Close,
+ Check,
+ },
+
+ data() {
+ return {
+ // The audio stream object
+ audioStream: null,
+ // The media recorder which generate the recorded chunks
+ mediaRecorder: null,
+ // The chunks array
+ chunks: [],
+ // The final audio file blob
+ blob: null,
+ // The blob url
+ URL: '',
+ // Switched to true if the recording is aborted
+ aborted: false,
+ // recordTimer
+ recordTimer: null,
+ // the record timer
+ recordTime: {
+ minutes: 0,
+ seconds: 0,
+ },
+ }
+ },
+
+ computed: {
+ // Recording state of the mediaRecorder
+ isRecording() {
+ if (this.mediaRecorder) {
+ return this.mediaRecorder.state === 'recording'
+ } else {
+ return false
+ }
+ },
+
+ parsedRecordTime() {
+ const seconds = this.recordTime.seconds.toString().length === 2 ? this.recordTime.seconds : `0${this.recordTime.seconds}`
+ const minutes = this.recordTime.minutes.toString().length === 2 ? this.recordTime.minutes : `0${this.recordTime.minutes}`
+ return `${minutes}:${seconds}`
+ },
+ },
+
+ watch: {
+
+ isRecording(newValue) {
+ console.debug('isRecording', newValue)
+ },
+ },
+
+ methods: {
+ /**
+ * Initialize the media stream and start capturing the audio
+ */
+ async start() {
+ try {
+ // Create new audio stream
+ this.audioStream = await mediaDevicesManager.getUserMedia({
+ audio: true,
+ })
+ // Create a mediarecorder to capture the stream
+ this.mediaRecorder = new MediaRecorder(this.audioStream)
+ // Add event handler to ondataAvailable
+ this.mediaRecorder.ondataavailable = (e) => {
+ this.chunks.push(e.data)
+ }
+ // Add event handler to onstop
+ this.mediaRecorder.onstop = this.generateFile
+ // Start the recording
+ this.mediaRecorder.start()
+ // Start the timer
+ this.recordTimer = setInterval(() => {
+ if (this.recordTime.seconds === 59) {
+ this.recordTime.minutes++
+ this.recordTime.seconds = 0
+ }
+ this.recordTime.seconds++
+ }, 1000)
+ // Forward an event to let the parent NewMessageForm component
+ // that there's an undergoing recording operation
+ this.$emit('recording', true)
+ console.debug(this.mediaRecorder.state)
+ } catch (exception) {
+ console.debug(exception)
+ }
+ },
+
+ /**
+ * Stop the mediaRecorder
+ */
+ stop() {
+ this.mediaRecorder.stop()
+ clearInterval(this.recordTimer)
+ this.$emit('recording', false)
+ },
+
+ /**
+ * Generate the file
+ */
+ generateFile() {
+ if (!this.aborted) {
+ this.blob = new Blob(this.chunks, { 'type': 'audio/mpeg-3' })
+ // Convert blob to file
+ const audioFile = new File([this.blob], `${Date.now()}.mp3`)
+ this.$emit('audioFile', audioFile)
+ this.$emit('recording', false)
+ this.URL = window.URL.createObjectURL(this.blob)
+ }
+ this.resetComponentData()
+ },
+
+ /**
+ * Aborts the recording operation.
+ */
+ abortRecording() {
+ this.aborted = true
+ this.stop()
+ },
+
+ /**
+ * Resets this component to its initial state
+ */
+ resetComponentData() {
+ this.audioStream = null
+ this.mediaRecorder = null
+ this.chunks = []
+ this.blob = null
+ this.aborted = false
+ this.isAudiorecorderActive = false
+ this.recordTime = {
+ minutes: 0,
+ seconds: 0,
+ }
+ },
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+
+@import '../../../assets/buttons';
+
+.audio-recorder {
+ display: flex;
+ // Audio record button
+ &__trigger {
+ &--recording {
+ background-color: var(--color-success) !important;
+ opacity: .8;
+ color: white;
+ &:hover,
+ &:focus {
+ opacity: 1;
+ }
+ }
+ }
+
+ &__stop {
+ margin-left: 4px;
+ background-color: var(--color-error) !important;
+ color: white;
+ opacity: .8;
+ &:hover,
+ &:focus {
+ opacity: 1;
+ }
+ }
+
+ &__info {
+ width: 86px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ .time {
+ flex: 0 0 50px;
+ }
+ .recording-indicator {
+ width: 16px;
+ height: 16px;
+ flex: 0 0 16px;
+ border-radius: 8px;
+ background-color: var(--color-error);
+ margin: 8px;
+ }
+ }
+}
+
+.wrapper {
+ display: flex;
+}
+
+@keyframes fadeOutIn {
+ 0% { opacity:1; }
+ 50% { opacity:.3; }
+ 100% { opacity:1; }
+}
+
+.fadeOutIn {
+ animation: fadeOutIn 3s infinite;
+}
+
+</style>