diff --git a/package-lock.json b/package-lock.json
index 23885876fc..4a24d05ea6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -18,6 +18,7 @@
"@viz-js/viz": "^3.2.4",
"amplitude-js": "^5.11.0",
"array-move": "^2.2.2",
+ "audio-react-recorder": "^1.0.5",
"blueimp-load-image": "^2.31.0",
"copy-webpack-plugin": "^12.0.2",
"d3": "^7.0.1",
@@ -2702,6 +2703,20 @@
"node": ">= 4.0.0"
}
},
+ "node_modules/audio-react-recorder": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/audio-react-recorder/-/audio-react-recorder-1.0.5.tgz",
+ "integrity": "sha512-pvzb0rnSDmzd5IjZNSbl3TQ2X/ZQ9vnVHfdtUCYaafrFm4jcdIzSqqM64qqqt+LXbQGVr3nMale1GH6z/yCRww==",
+ "dependencies": {
+ "prop-types": "^15.7.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "react": "^16.0.0"
+ }
+ },
"node_modules/available-typed-arrays": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
diff --git a/package.json b/package.json
index c0fe915dd2..741eb8a120 100644
--- a/package.json
+++ b/package.json
@@ -96,6 +96,7 @@
"@viz-js/viz": "^3.2.4",
"amplitude-js": "^5.11.0",
"array-move": "^2.2.2",
+ "audio-react-recorder": "^1.0.5",
"blueimp-load-image": "^2.31.0",
"copy-webpack-plugin": "^12.0.2",
"d3": "^7.0.1",
diff --git a/src/img/icon/chat/buttons/voice0.svg b/src/img/icon/chat/buttons/voice0.svg
new file mode 100644
index 0000000000..b5591678f9
--- /dev/null
+++ b/src/img/icon/chat/buttons/voice0.svg
@@ -0,0 +1 @@
+
diff --git a/src/img/icon/chat/buttons/voice1.svg b/src/img/icon/chat/buttons/voice1.svg
new file mode 100644
index 0000000000..984c2e33be
--- /dev/null
+++ b/src/img/icon/chat/buttons/voice1.svg
@@ -0,0 +1 @@
+
diff --git a/src/json/constant.ts b/src/json/constant.ts
index de42980878..c87370b079 100644
--- a/src/json/constant.ts
+++ b/src/json/constant.ts
@@ -73,7 +73,7 @@ export default {
image: [ 'jpg', 'jpeg', 'png', 'gif', 'svg', 'webp' ],
video: [ 'mp4', 'm4v', 'mov' ],
cover: [ 'jpg', 'jpeg', 'png', 'gif', 'webp' ],
- audio: [ 'mp3', 'm4a', 'flac', 'ogg', 'wav' ],
+ audio: [ 'mp3', 'm4a', 'flac', 'ogg', 'wav', 'wave', 'x-wav' ],
pdf: [ 'pdf' ],
import: {
1: [ 'zip', 'md' ],
diff --git a/src/json/text.json b/src/json/text.json
index 9165914303..237c16ba3f 100644
--- a/src/json/text.json
+++ b/src/json/text.json
@@ -684,6 +684,7 @@
"blockChatButtonObject": "Add object",
"blockChatButtonEmoji": "Add emoji",
"blockChatButtonMention": "Add mention",
+ "blockChatButtonVoice": "Record voice message",
"blockChatNewMessages": "New messages",
"blockChatMessageEdited": "(edited)",
"blockChatEditing": "Editing message",
diff --git a/src/scss/block/chat/form.scss b/src/scss/block/chat/form.scss
index 93ce766704..cb28bc53b3 100644
--- a/src/scss/block/chat/form.scss
+++ b/src/scss/block/chat/form.scss
@@ -75,6 +75,8 @@
.icon.text { background-image: url('~img/icon/chat/buttons/text.svg'); }
.icon.emoji { background-image: url('~img/icon/chat/buttons/emoji.svg'); }
.icon.mention { background-image: url('~img/icon/chat/buttons/mention.svg'); }
+ .icon.voice { background-image: url('~img/icon/chat/buttons/voice0.svg'); background-size: 20px 20px; }
+ .icon.isActive.voice { background-image: url('~img/icon/chat/buttons/voice1.svg'); background-color: var(--color-red) !important; }
/* Text buttons */
.icon.bold { background-image: url('~img/icon/menu/action/mark/bold0.svg'); }
@@ -111,6 +113,8 @@
.inner.textColor-teal { background: var(--color-teal); }
.inner.textColor-lime { background: var(--color-lime); }
}
+
+ .audio-react-recorder { position: absolute; width: 0px; height: 0px; opacity: 0; overflow: hidden; }
}
.icon.send { position: absolute; bottom: 10px; right: 16px; width: 20px; height: 20px; background: url('~img/icon/chat/buttons/send.svg'); }
diff --git a/src/ts/component/block/chat/attachment/index.tsx b/src/ts/component/block/chat/attachment/index.tsx
index e8346db830..c622fbcbef 100644
--- a/src/ts/component/block/chat/attachment/index.tsx
+++ b/src/ts/component/block/chat/attachment/index.tsx
@@ -52,6 +52,15 @@ const ChatAttachment = observer(class ChatAttachment extends React.Component;
@@ -232,4 +242,4 @@ const ChatAttachment = observer(class ChatAttachment extends React.Component void;
addAttachments: (attachments: any[], callBack?: () => void) => void;
removeBookmark: (url: string) => void;
+ onVoice: (audio: any) => void;
};
interface State {
buttons: any[];
+ recordState: any;
};
const ChatButtons = observer(class ChatButtons extends React.Component {
state = {
buttons: [],
+ recordState: null,
};
+ isRecording: boolean = false;
constructor (props: Props) {
super(props);
@@ -36,18 +41,21 @@ const ChatButtons = observer(class ChatButtons extends React.Component
{buttons.map((item: any, i: number) => {
const cn = [ item.icon, 'withBackground' ];
+ const isRecording = (item.type == I.ChatButton.Voice) && this.isRecording;
- if (item.isActive) {
+ if (item.isActive || isRecording) {
cn.push('isActive');
};
@@ -64,6 +72,8 @@ const ChatButtons = observer(class ChatButtons extends React.Component
);
})}
+
+
);
};
@@ -109,6 +119,13 @@ const ChatButtons = observer(class ChatButtons extends React.Component {
this.removeBookmark = this.removeBookmark.bind(this);
this.getMarksAndRange = this.getMarksAndRange.bind(this);
this.getObjectFromPath = this.getObjectFromPath.bind(this);
+ this.onVoice = this.onVoice.bind(this);
};
render () {
@@ -187,6 +188,7 @@ const ChatForm = observer(class ChatForm extends React.Component {
addAttachments={this.addAttachments}
onMenuClose={this.onMenuClose}
removeBookmark={this.removeBookmark}
+ onVoice={this.onVoice}
/>
) : ''}
@@ -430,9 +432,7 @@ const ChatForm = observer(class ChatForm extends React.Component {
const cb = e.clipboardData || e.originalEvent.clipboardData;
const text = U.Common.normalizeLineEndings(String(cb.getData('text/plain') || ''));
const electron = U.Common.getElectron();
- const list = U.Common.getDataTransferFiles((e.clipboardData || e.originalEvent.clipboardData).items).map((it: File) => this.getObjectFromFile(it)).filter(it => {
- return !electron.isDirectory(it.path);
- });
+ const list = U.Common.getDataTransferFiles((e.clipboardData || e.originalEvent.clipboardData).items).map((it: File) => this.getObjectFromFile(it));
let value = this.getTextValue();
let url = U.Common.matchUrl(text);
@@ -746,6 +746,22 @@ const ChatForm = observer(class ChatForm extends React.Component {
});
};
+ onVoice (audio) {
+ const { blob } = audio;
+ const ext = blob.type.split('/')[1];
+ const file = new File([ blob ], `Voice Message.${ext}`, { type: blob.type });
+
+ U.Common.saveClipboardFiles([ file ], {}, (data) => {
+ if (!data.files || !data.files.length) {
+ return;
+ };
+
+ const obj = this.getObjectFromPath(data.files[0].path);
+
+ this.addAttachments([ obj ]);
+ });
+ };
+
getMarksAndRange (): any {
return { marks: this.marks, range: this.range };
};
diff --git a/src/ts/interface/block/chat.ts b/src/ts/interface/block/chat.ts
index 627c2aa72f..27c6bde328 100644
--- a/src/ts/interface/block/chat.ts
+++ b/src/ts/interface/block/chat.ts
@@ -5,6 +5,7 @@ export enum ChatButton {
Text = 1,
Emoji = 2,
Mention = 3,
+ Voice = 4,
};
export enum AttachmentType {
@@ -40,4 +41,4 @@ export interface ChatMessageAttachment {
type: AttachmentType;
};
-export interface BlockChat extends I.Block {};
\ No newline at end of file
+export interface BlockChat extends I.Block {};