diff --git a/app/javascript/dashboard/components/widgets/AttachmentsPreview.vue b/app/javascript/dashboard/components/widgets/AttachmentsPreview.vue
index 250c5c3c6..3d5846215 100644
--- a/app/javascript/dashboard/components/widgets/AttachmentsPreview.vue
+++ b/app/javascript/dashboard/components/widgets/AttachmentsPreview.vue
@@ -25,6 +25,7 @@
onRemoveAttachment(index)"
@@ -58,6 +59,10 @@ export default {
const type = file.content_type || file.type;
return type.includes('image');
},
+ isTypeAudio(file) {
+ const type = file.content_type || file.type;
+ return type.includes('audio');
+ },
fileName(file) {
return file.filename || file.name;
},
diff --git a/app/javascript/dashboard/components/widgets/WootWriter/AudioRecorder.vue b/app/javascript/dashboard/components/widgets/WootWriter/AudioRecorder.vue
new file mode 100644
index 000000000..4d6df7793
--- /dev/null
+++ b/app/javascript/dashboard/components/widgets/WootWriter/AudioRecorder.vue
@@ -0,0 +1,223 @@
+
+
+
+
+
+
+
diff --git a/app/javascript/dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue b/app/javascript/dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue
index 6df1b2858..6dcb47532 100644
--- a/app/javascript/dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue
+++ b/app/javascript/dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue
@@ -11,7 +11,6 @@
size="small"
@click="toggleEmojiPicker"
/>
-
+
+
+ {{ recordingAudioDurationText }}
+
({}),
@@ -134,6 +158,10 @@ export default {
type: Boolean,
default: false,
},
+ showAudioRecorder: {
+ type: Boolean,
+ default: false,
+ },
onFileUpload: {
type: Function,
default: () => {},
@@ -146,6 +174,22 @@ export default {
type: Function,
default: () => {},
},
+ toggleAudioRecorder: {
+ type: Function,
+ default: () => {},
+ },
+ toggleAudioRecorderPlayPause: {
+ type: Function,
+ default: () => {},
+ },
+ isRecordingAudio: {
+ type: Boolean,
+ default: false,
+ },
+ recordingAudioState: {
+ type: String,
+ default: '',
+ },
isSendDisabled: {
type: Boolean,
default: false,
@@ -192,9 +236,28 @@ export default {
showAttachButton() {
return this.showFileUpload || this.isNote;
},
+ showAudioRecorderButton() {
+ return this.showAudioRecorder;
+ },
+ showAudioPlayStopButton() {
+ return this.showAudioRecorder && this.isRecordingAudio;
+ },
allowedFileTypes() {
return ALLOWED_FILE_TYPES;
},
+ audioRecorderPlayStopIcon() {
+ switch (this.recordingAudioState) {
+ // playing paused recording stopped inactive destroyed
+ case 'playing':
+ return 'microphone-pause';
+ case 'paused':
+ return 'microphone-play';
+ case 'stopped':
+ return 'microphone-play';
+ default:
+ return 'microphone-stop';
+ }
+ },
showMessageSignatureButton() {
return !this.isPrivate && this.isAnEmailChannel;
},
diff --git a/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue b/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue
index 37fbb4367..5a9253fba 100644
--- a/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue
+++ b/app/javascript/dashboard/components/widgets/conversation/ReplyBox.vue
@@ -33,8 +33,15 @@
:cc-emails.sync="ccEmails"
:bcc-emails.sync="bccEmails"
/>
+
this.$refs.messageInput.focus());
@@ -535,10 +568,26 @@ export default {
this.attachedFiles = [];
this.ccEmails = '';
this.bccEmails = '';
+ this.isRecordingAudio = false;
},
toggleEmojiPicker() {
this.showEmojiPicker = !this.showEmojiPicker;
},
+ toggleAudioRecorder() {
+ this.isRecordingAudio = !this.isRecordingAudio;
+ this.isRecorderAudioStopped = !this.isRecordingAudio;
+ if (!this.isRecordingAudio) {
+ this.clearMessage();
+ }
+ },
+ toggleAudioRecorderPlayPause() {
+ if (this.isRecordingAudio && !this.isRecorderAudioStopped) {
+ this.isRecorderAudioStopped = true;
+ this.$refs.audioRecorderInput.stopAudioRecording();
+ } else if (this.isRecordingAudio && this.isRecorderAudioStopped) {
+ this.$refs.audioRecorderInput.playPause();
+ }
+ },
hideEmojiPicker() {
if (this.showEmojiPicker) {
this.toggleEmojiPicker();
@@ -559,6 +608,20 @@ export default {
onFocus() {
this.isFocused = true;
},
+ onStateRecorderTimerChanged(time) {
+ this.recordingAudioDuration = time;
+ },
+ onStateRecorderChanged(state) {
+ this.recordingAudioState = state;
+ if (state.includes('notallowederror')) {
+ this.toggleAudioRecorder();
+ }
+ },
+ onRecorderBlob(file) {
+ if (file) {
+ this.onFileUpload(file);
+ }
+ },
toggleTyping(status) {
const conversationId = this.currentChat.id;
const isPrivate = this.isPrivate;
diff --git a/app/javascript/dashboard/i18n/locale/en/conversation.json b/app/javascript/dashboard/i18n/locale/en/conversation.json
index 96ba4b429..ac25a5aed 100644
--- a/app/javascript/dashboard/i18n/locale/en/conversation.json
+++ b/app/javascript/dashboard/i18n/locale/en/conversation.json
@@ -74,8 +74,14 @@
"TIP_FORMAT_ICON": "Show rich text editor",
"TIP_EMOJI_ICON": "Show emoji selector",
"TIP_ATTACH_ICON": "Attach files",
+ "TIP_AUDIORECORDER_ICON": "Record audio",
+ "TIP_AUDIORECORDER_PERMISSION": "Allow access to audio",
+ "TIP_AUDIORECORDER_ERROR": "Could not open the audio",
"ENTER_TO_SEND": "Enter to send",
"DRAG_DROP": "Drag and drop here to attach",
+ "START_AUDIO_RECORDING": "Start audio recording",
+ "STOP_AUDIO_RECORDING": "Stop audio recording",
+ "": "",
"EMAIL_HEAD": {
"ADD_BCC": "Add bcc",
"CC": {
diff --git a/app/javascript/shared/components/FluentIcon/dashboard-icons.json b/app/javascript/shared/components/FluentIcon/dashboard-icons.json
index 39064412c..0cbc85d22 100644
--- a/app/javascript/shared/components/FluentIcon/dashboard-icons.json
+++ b/app/javascript/shared/components/FluentIcon/dashboard-icons.json
@@ -85,6 +85,11 @@
"merge-outline": "M3 6.75A.75.75 0 0 1 3.75 6h4.5a.75.75 0 0 1 .53.22L13.56 11h5.878L15.72 7.28a.75.75 0 1 1 1.06-1.06l4.998 5a.75.75 0 0 1 0 1.06l-4.998 5a.75.75 0 1 1-1.06-1.06l3.718-3.72H13.56l-4.78 4.78a.75.75 0 0 1-.531.22h-4.5a.75.75 0 0 1 0-1.5h4.19l4.25-4.25L7.94 7.5H3.75A.75.75 0 0 1 3 6.75Z",
"more-horizontal-outline": "M7.75 12a1.75 1.75 0 1 1-3.5 0 1.75 1.75 0 0 1 3.5 0ZM13.75 12a1.75 1.75 0 1 1-3.5 0 1.75 1.75 0 0 1 3.5 0ZM18 13.75a1.75 1.75 0 1 0 0-3.5 1.75 1.75 0 0 0 0 3.5Z",
"more-vertical-outline": "M12 7.75a1.75 1.75 0 1 1 0-3.5 1.75 1.75 0 0 1 0 3.5ZM12 13.75a1.75 1.75 0 1 1 0-3.5 1.75 1.75 0 0 1 0 3.5ZM10.25 18a1.75 1.75 0 1 0 3.5 0 1.75 1.75 0 0 0-3.5 0Z",
+ "microphone-outline": "M12,2A3,3 0 0,1 15,5V11A3,3 0 0,1 12,14A3,3 0 0,1 9,11V5A3,3 0 0,1 12,2M19,11C19,14.53 16.39,17.44 13,17.93V21H11V17.93C7.61,17.44 5,14.53 5,11H7A5,5 0 0,0 12,16A5,5 0 0,0 17,11H19Z",
+ "microphone-off-outline": "M19,11C19,12.19 18.66,13.3 18.1,14.28L16.87,13.05C17.14,12.43 17.3,11.74 17.3,11H19M15,11.16L9,5.18V5A3,3 0 0,1 12,2A3,3 0 0,1 15,5V11L15,11.16M4.27,3L21,19.73L19.73,21L15.54,16.81C14.77,17.27 13.91,17.58 13,17.72V21H11V17.72C7.72,17.23 5,14.41 5,11H6.7C6.7,14 9.24,16.1 12,16.1C12.81,16.1 13.6,15.91 14.31,15.58L12.65,13.92L12,14A3,3 0 0,1 9,11V10.28L3,4.27L4.27,3Z",
+ "microphone-stop-outline": "M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4M9,9V15H15V9",
+ "microphone-pause-outline": "M13,16V8H15V16H13M9,16V8H11V16H9M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z",
+ "microphone-play-outline": "M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M10,16.5L16,12L10,7.5V16.5Z",
"number-symbol-outline": "M10.987 2.89a.75.75 0 1 0-1.474-.28L8.494 7.999 3.75 8a.75.75 0 1 0 0 1.5l4.46-.002-.946 5-4.514.002a.75.75 0 0 0 0 1.5l4.23-.002-.967 5.116a.75.75 0 1 0 1.474.278l1.02-5.395 5.474-.002-.968 5.119a.75.75 0 1 0 1.474.278l1.021-5.398 4.742-.002a.75.75 0 1 0 0-1.5l-4.458.002.946-5 4.512-.002a.75.75 0 1 0 0-1.5l-4.229.002.966-5.104a.75.75 0 0 0-1.474-.28l-1.018 5.385-5.474.002.966-5.107Zm-1.25 6.608 5.474-.003-.946 5-5.474.002.946-5Z",
"open-outline": "M6.25 4.5A1.75 1.75 0 0 0 4.5 6.25v11.5c0 .966.783 1.75 1.75 1.75h11.5a1.75 1.75 0 0 0 1.75-1.75v-4a.75.75 0 0 1 1.5 0v4A3.25 3.25 0 0 1 17.75 21H6.25A3.25 3.25 0 0 1 3 17.75V6.25A3.25 3.25 0 0 1 6.25 3h4a.75.75 0 0 1 0 1.5h-4ZM13 3.75a.75.75 0 0 1 .75-.75h6.5a.75.75 0 0 1 .75.75v6.5a.75.75 0 0 1-1.5 0V5.56l-5.22 5.22a.75.75 0 0 1-1.06-1.06l5.22-5.22h-4.69a.75.75 0 0 1-.75-.75Z",
"people-outline": "M4 13.999 13 14a2 2 0 0 1 1.995 1.85L15 16v1.5C14.999 21 11.284 22 8.5 22c-2.722 0-6.335-.956-6.495-4.27L2 17.5v-1.501c0-1.054.816-1.918 1.85-1.995L4 14ZM15.22 14H20c1.054 0 1.918.816 1.994 1.85L22 16v1c-.001 3.062-2.858 4-5 4a7.16 7.16 0 0 1-2.14-.322c.336-.386.607-.827.802-1.327A6.19 6.19 0 0 0 17 19.5l.267-.006c.985-.043 3.086-.363 3.226-2.289L20.5 17v-1a.501.501 0 0 0-.41-.492L20 15.5h-4.051a2.957 2.957 0 0 0-.595-1.34L15.22 14H20h-4.78ZM4 15.499l-.1.01a.51.51 0 0 0-.254.136.506.506 0 0 0-.136.253l-.01.101V17.5c0 1.009.45 1.722 1.417 2.242.826.445 2.003.714 3.266.753l.317.005.317-.005c1.263-.039 2.439-.308 3.266-.753.906-.488 1.359-1.145 1.412-2.057l.005-.186V16a.501.501 0 0 0-.41-.492L13 15.5l-9-.001ZM8.5 3a4.5 4.5 0 1 1 0 9 4.5 4.5 0 0 1 0-9Zm9 2a3.5 3.5 0 1 1 0 7 3.5 3.5 0 0 1 0-7Zm-9-.5c-1.654 0-3 1.346-3 3s1.346 3 3 3 3-1.346 3-3-1.346-3-3-3Zm9 2c-1.103 0-2 .897-2 2s.897 2 2 2 2-.897 2-2-.897-2-2-2Z",
diff --git a/app/javascript/shared/helpers/FileHelper.js b/app/javascript/shared/helpers/FileHelper.js
index a784b2ba2..d9ca9f943 100644
--- a/app/javascript/shared/helpers/FileHelper.js
+++ b/app/javascript/shared/helpers/FileHelper.js
@@ -19,7 +19,7 @@ export const fileSizeInMegaBytes = bytes => {
};
export const checkFileSizeLimit = (file, maximumUploadLimit) => {
- const fileSize = file?.file?.size;
+ const fileSize = file?.file?.size || file?.size;
const fileSizeInMB = fileSizeInMegaBytes(fileSize);
return fileSizeInMB <= maximumUploadLimit;
};
diff --git a/package.json b/package.json
index 1703f63e0..4f794fa6d 100644
--- a/package.json
+++ b/package.json
@@ -50,6 +50,7 @@
"prosemirror-state": "1.3.4",
"prosemirror-view": "1.18.4",
"query-string": "5",
+ "recordrtc": "^5.6.2",
"semver": "7.3.5",
"spinkit": "~1.2.5",
"tailwindcss": "^1.9.6",
@@ -71,7 +72,8 @@
"vuedraggable": "^2.24.3",
"vuelidate": "0.7.6",
"vuex": "~2.1.1",
- "vuex-router-sync": "~4.1.2"
+ "vuex-router-sync": "~4.1.2",
+ "wavesurfer.js": "^5.2.0"
},
"devDependencies": {
"@babel/core": "7.13.16",