send "recording" notifications when audio is being recorded
This commit is contained in:
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
import { PACKAGE_VERSION } from './version.js';
|
import { PACKAGE_VERSION } from './version.js';
|
||||||
|
|
||||||
// Name of this application, used in the User-Agent.
|
// Name of this application, used in the User-Agent.
|
||||||
export const APP_NAME = 'TinodeWeb/' + (PACKAGE_VERSION || '0.17');
|
export const APP_NAME = 'TinodeWeb/' + (PACKAGE_VERSION || '0.21');
|
||||||
|
|
||||||
// API key. Use https://github.com/tinode/chat/tree/master/keygen to generate your own
|
// API key. Use https://github.com/tinode/chat/tree/master/keygen to generate your own
|
||||||
export const API_KEY = 'AQEAAAABAAD_rAp4DJh05a1HAwFT3A6K';
|
export const API_KEY = 'AQEAAAABAAD_rAp4DJh05a1HAwFT3A6K';
|
||||||
|
|||||||
@@ -743,10 +743,14 @@ class MessagesView extends React.Component {
|
|||||||
this.props.onChangePermissions(this.state.topic, DEFAULT_P2P_ACCESS_MODE, this.state.topic);
|
this.props.onChangePermissions(this.state.topic, DEFAULT_P2P_ACCESS_MODE, this.state.topic);
|
||||||
}
|
}
|
||||||
|
|
||||||
sendKeyPress() {
|
sendKeyPress(audio) {
|
||||||
const topic = this.props.tinode.getTopic(this.state.topic);
|
const topic = this.props.tinode.getTopic(this.state.topic);
|
||||||
if (topic.isSubscribed()) {
|
if (topic.isSubscribed()) {
|
||||||
topic.noteKeyPress();
|
if (audio) {
|
||||||
|
topic.noteRecording(true);
|
||||||
|
} else {
|
||||||
|
topic.noteKeyPress();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1335,6 +1339,7 @@ class MessagesView extends React.Component {
|
|||||||
reply={this.state.reply}
|
reply={this.state.reply}
|
||||||
initMessage={this.state.contentToEdit}
|
initMessage={this.state.contentToEdit}
|
||||||
onKeyPress={this.sendKeyPress}
|
onKeyPress={this.sendKeyPress}
|
||||||
|
onRecordingProgress={this.sendKeyPress}
|
||||||
onSendMessage={this.sendMessage}
|
onSendMessage={this.sendMessage}
|
||||||
onAttachFile={this.props.forwardMessage ? null : this.handleAttachFile}
|
onAttachFile={this.props.forwardMessage ? null : this.handleAttachFile}
|
||||||
onAttachImage={this.props.forwardMessage ? null : this.handleAttachImage}
|
onAttachImage={this.props.forwardMessage ? null : this.handleAttachImage}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import fixWebmDuration from 'webm-duration-fix';
|
|||||||
|
|
||||||
import { intArrayToBase64 } from '../lib/blob-helpers.js'
|
import { intArrayToBase64 } from '../lib/blob-helpers.js'
|
||||||
import { secondsToTime } from '../lib/strformat';
|
import { secondsToTime } from '../lib/strformat';
|
||||||
import { MAX_DURATION, MIN_DURATION } from '../config.js';
|
import { KEYPRESS_DELAY, MAX_DURATION, MIN_DURATION } from '../config.js';
|
||||||
|
|
||||||
// FFT resolution.
|
// FFT resolution.
|
||||||
const BUFFER_SIZE = 256;
|
const BUFFER_SIZE = 256;
|
||||||
@@ -86,6 +86,9 @@ class AudioRecorder extends React.PureComponent {
|
|||||||
this.startedOn = null;
|
this.startedOn = null;
|
||||||
this.viewBuffer = [];
|
this.viewBuffer = [];
|
||||||
this.canvasRef = React.createRef();
|
this.canvasRef = React.createRef();
|
||||||
|
|
||||||
|
// Timestamp for sending "recording" notifications.
|
||||||
|
this.recordingTimestamp = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@@ -192,6 +195,13 @@ class AudioRecorder extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
// Actually draw the bars on canvas.
|
// Actually draw the bars on canvas.
|
||||||
this.canvasContext.stroke();
|
this.canvasContext.stroke();
|
||||||
|
|
||||||
|
// Send notification, if needed.
|
||||||
|
const now = new Date().getTime();
|
||||||
|
if (now - this.recordingTimestamp > KEYPRESS_DELAY) {
|
||||||
|
this.props.onRecordingProgress();
|
||||||
|
this.recordingTimestamp = now;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drawFrame();
|
drawFrame();
|
||||||
@@ -262,7 +272,7 @@ class AudioRecorder extends React.PureComponent {
|
|||||||
|
|
||||||
this.mediaRecorder.onstop = _ => {
|
this.mediaRecorder.onstop = _ => {
|
||||||
if (this.durationMillis > MIN_DURATION) {
|
if (this.durationMillis > MIN_DURATION) {
|
||||||
this.getRecording(this.mediaRecorder.mimeType, this.durationMillis)
|
this.getRecording(this.mediaRecorder.mimeType)
|
||||||
.then(result => this.props.onFinished(result.url, result.preview, this.durationMillis));
|
.then(result => this.props.onFinished(result.url, result.preview, this.durationMillis));
|
||||||
} else {
|
} else {
|
||||||
this.props.onDeleted();
|
this.props.onDeleted();
|
||||||
@@ -289,11 +299,14 @@ class AudioRecorder extends React.PureComponent {
|
|||||||
this.startedOn = Date.now();
|
this.startedOn = Date.now();
|
||||||
this.mediaRecorder.start();
|
this.mediaRecorder.start();
|
||||||
this.visualize();
|
this.visualize();
|
||||||
|
|
||||||
|
this.props.onRecordingProgress();
|
||||||
|
this.recordingTimestamp = this.startedOn;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Obtain data in a form sutable for sending or playing back.
|
// Obtain data in a form sutable for sending or playing back.
|
||||||
// If duration is valid, apply fix for Chrome's WebM duration bug.
|
// If duration is valid, apply fix for Chrome's WebM duration bug.
|
||||||
getRecording(mimeType, duration) {
|
getRecording(mimeType) {
|
||||||
mimeType = mimeType || AUDIO_MIME_TYPE;
|
mimeType = mimeType || AUDIO_MIME_TYPE;
|
||||||
let blob = new Blob(this.audioChunks, {type: mimeType});
|
let blob = new Blob(this.audioChunks, {type: mimeType});
|
||||||
return fixWebmDuration(blob, mimeType)
|
return fixWebmDuration(blob, mimeType)
|
||||||
|
|||||||
@@ -68,10 +68,11 @@ class SendMessage extends React.PureComponent {
|
|||||||
message: '',
|
message: '',
|
||||||
audioRec: false,
|
audioRec: false,
|
||||||
audioAvailable: !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia),
|
audioAvailable: !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia),
|
||||||
// Make initial keypress time as if it happened 5001 milliseconds in the past.
|
|
||||||
keypressTimestamp: new Date().getTime() - KEYPRESS_DELAY - 1
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Timestamp when the previous key press was sent to the server.
|
||||||
|
this.keypressTimestamp = 0;
|
||||||
|
|
||||||
this.handlePasteEvent = this.handlePasteEvent.bind(this);
|
this.handlePasteEvent = this.handlePasteEvent.bind(this);
|
||||||
this.handleAttachImage = this.handleAttachImage.bind(this);
|
this.handleAttachImage = this.handleAttachImage.bind(this);
|
||||||
this.handleAttachFile = this.handleAttachFile.bind(this);
|
this.handleAttachFile = this.handleAttachFile.bind(this);
|
||||||
@@ -194,15 +195,14 @@ class SendMessage extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleMessageTyping(e) {
|
handleMessageTyping(e) {
|
||||||
const newState = {message: e.target.value};
|
this.setState({message: e.target.value});
|
||||||
if (this.props.onKeyPress) {
|
if (this.props.onKeyPress) {
|
||||||
const now = new Date().getTime();
|
const now = new Date().getTime();
|
||||||
if (now - this.state.keypressTimestamp > KEYPRESS_DELAY) {
|
if (now - this.keypressTimestamp > KEYPRESS_DELAY) {
|
||||||
this.props.onKeyPress();
|
this.props.onKeyPress();
|
||||||
newState.keypressTimestamp = now;
|
this.keypressTimestamp = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.setState(newState);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleQuoteClick(e) {
|
handleQuoteClick(e) {
|
||||||
@@ -255,6 +255,7 @@ class SendMessage extends React.PureComponent {
|
|||||||
(this.state.audioRec ?
|
(this.state.audioRec ?
|
||||||
(<Suspense fallback={<div>Loading...</div>}>
|
(<Suspense fallback={<div>Loading...</div>}>
|
||||||
<AudioRecorder
|
<AudioRecorder
|
||||||
|
onRecordingProgress={_ => this.props.onKeyPress(true)}
|
||||||
onDeleted={_ => this.setState({audioRec: false})}
|
onDeleted={_ => this.setState({audioRec: false})}
|
||||||
onFinished={this.handleAttachAudio}/>
|
onFinished={this.handleAttachAudio}/>
|
||||||
</Suspense>) :
|
</Suspense>) :
|
||||||
@@ -262,7 +263,7 @@ class SendMessage extends React.PureComponent {
|
|||||||
value={this.state.message}
|
value={this.state.message}
|
||||||
onChange={this.handleMessageTyping}
|
onChange={this.handleMessageTyping}
|
||||||
onKeyPress={this.handleKeyPress}
|
onKeyPress={this.handleKeyPress}
|
||||||
ref={(ref) => {this.messageEditArea = ref;}}
|
ref={ref => {this.messageEditArea = ref;}}
|
||||||
autoFocus />)}
|
autoFocus />)}
|
||||||
{this.state.message || !audioEnabled ?
|
{this.state.message || !audioEnabled ?
|
||||||
<a href="#" onClick={this.handleSend} title={formatMessage(messages.icon_title_send)}>
|
<a href="#" onClick={this.handleSend} title={formatMessage(messages.icon_title_send)}>
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+15
-10
@@ -2196,7 +2196,7 @@ __webpack_require__.r(__webpack_exports__);
|
|||||||
/* harmony export */ });
|
/* harmony export */ });
|
||||||
/* harmony import */ var _version_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./version.js */ "./src/version.js");
|
/* harmony import */ var _version_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./version.js */ "./src/version.js");
|
||||||
|
|
||||||
const APP_NAME = 'TinodeWeb/' + (_version_js__WEBPACK_IMPORTED_MODULE_0__.PACKAGE_VERSION || '0.17');
|
const APP_NAME = 'TinodeWeb/' + (_version_js__WEBPACK_IMPORTED_MODULE_0__.PACKAGE_VERSION || '0.21');
|
||||||
const API_KEY = 'AQEAAAABAAD_rAp4DJh05a1HAwFT3A6K';
|
const API_KEY = 'AQEAAAABAAD_rAp4DJh05a1HAwFT3A6K';
|
||||||
const KNOWN_HOSTS = {
|
const KNOWN_HOSTS = {
|
||||||
hosted: 'web.tinode.co',
|
hosted: 'web.tinode.co',
|
||||||
@@ -5984,10 +5984,14 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.props.onChangePermissions(this.state.topic, _config_js__WEBPACK_IMPORTED_MODULE_16__.DEFAULT_P2P_ACCESS_MODE, this.state.topic);
|
this.props.onChangePermissions(this.state.topic, _config_js__WEBPACK_IMPORTED_MODULE_16__.DEFAULT_P2P_ACCESS_MODE, this.state.topic);
|
||||||
}
|
}
|
||||||
sendKeyPress() {
|
sendKeyPress(audio) {
|
||||||
const topic = this.props.tinode.getTopic(this.state.topic);
|
const topic = this.props.tinode.getTopic(this.state.topic);
|
||||||
if (topic.isSubscribed()) {
|
if (topic.isSubscribed()) {
|
||||||
topic.noteKeyPress();
|
if (audio) {
|
||||||
|
topic.noteRecording(true);
|
||||||
|
} else {
|
||||||
|
topic.noteKeyPress();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sendMessage(msg, uploadCompletionPromise, uploader) {
|
sendMessage(msg, uploadCompletionPromise, uploader) {
|
||||||
@@ -6527,6 +6531,7 @@ class MessagesView extends (react__WEBPACK_IMPORTED_MODULE_0___default().Compone
|
|||||||
reply: this.state.reply,
|
reply: this.state.reply,
|
||||||
initMessage: this.state.contentToEdit,
|
initMessage: this.state.contentToEdit,
|
||||||
onKeyPress: this.sendKeyPress,
|
onKeyPress: this.sendKeyPress,
|
||||||
|
onRecordingProgress: this.sendKeyPress,
|
||||||
onSendMessage: this.sendMessage,
|
onSendMessage: this.sendMessage,
|
||||||
onAttachFile: this.props.forwardMessage ? null : this.handleAttachFile,
|
onAttachFile: this.props.forwardMessage ? null : this.handleAttachFile,
|
||||||
onAttachImage: this.props.forwardMessage ? null : this.handleAttachImage,
|
onAttachImage: this.props.forwardMessage ? null : this.handleAttachImage,
|
||||||
@@ -14678,9 +14683,9 @@ class SendMessage extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComp
|
|||||||
quote: null,
|
quote: null,
|
||||||
message: '',
|
message: '',
|
||||||
audioRec: false,
|
audioRec: false,
|
||||||
audioAvailable: !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia),
|
audioAvailable: !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia)
|
||||||
keypressTimestamp: new Date().getTime() - _config_js__WEBPACK_IMPORTED_MODULE_3__.KEYPRESS_DELAY - 1
|
|
||||||
};
|
};
|
||||||
|
this.keypressTimestamp = 0;
|
||||||
this.handlePasteEvent = this.handlePasteEvent.bind(this);
|
this.handlePasteEvent = this.handlePasteEvent.bind(this);
|
||||||
this.handleAttachImage = this.handleAttachImage.bind(this);
|
this.handleAttachImage = this.handleAttachImage.bind(this);
|
||||||
this.handleAttachFile = this.handleAttachFile.bind(this);
|
this.handleAttachFile = this.handleAttachFile.bind(this);
|
||||||
@@ -14790,17 +14795,16 @@ class SendMessage extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
handleMessageTyping(e) {
|
handleMessageTyping(e) {
|
||||||
const newState = {
|
this.setState({
|
||||||
message: e.target.value
|
message: e.target.value
|
||||||
};
|
});
|
||||||
if (this.props.onKeyPress) {
|
if (this.props.onKeyPress) {
|
||||||
const now = new Date().getTime();
|
const now = new Date().getTime();
|
||||||
if (now - this.state.keypressTimestamp > _config_js__WEBPACK_IMPORTED_MODULE_3__.KEYPRESS_DELAY) {
|
if (now - this.keypressTimestamp > _config_js__WEBPACK_IMPORTED_MODULE_3__.KEYPRESS_DELAY) {
|
||||||
this.props.onKeyPress();
|
this.props.onKeyPress();
|
||||||
newState.keypressTimestamp = now;
|
this.keypressTimestamp = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.setState(newState);
|
|
||||||
}
|
}
|
||||||
handleQuoteClick(e) {
|
handleQuoteClick(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -14857,6 +14861,7 @@ class SendMessage extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureComp
|
|||||||
}) : this.state.audioRec ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react__WEBPACK_IMPORTED_MODULE_0__.Suspense, {
|
}) : this.state.audioRec ? react__WEBPACK_IMPORTED_MODULE_0___default().createElement(react__WEBPACK_IMPORTED_MODULE_0__.Suspense, {
|
||||||
fallback: react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, "Loading...")
|
fallback: react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", null, "Loading...")
|
||||||
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(AudioRecorder, {
|
}, react__WEBPACK_IMPORTED_MODULE_0___default().createElement(AudioRecorder, {
|
||||||
|
onRecordingProgress: _ => this.props.onKeyPress(true),
|
||||||
onDeleted: _ => this.setState({
|
onDeleted: _ => this.setState({
|
||||||
audioRec: false
|
audioRec: false
|
||||||
}),
|
}),
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -93,6 +93,7 @@ class AudioRecorder extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCo
|
|||||||
this.startedOn = null;
|
this.startedOn = null;
|
||||||
this.viewBuffer = [];
|
this.viewBuffer = [];
|
||||||
this.canvasRef = react__WEBPACK_IMPORTED_MODULE_0___default().createRef();
|
this.canvasRef = react__WEBPACK_IMPORTED_MODULE_0___default().createRef();
|
||||||
|
this.recordingTimestamp = 0;
|
||||||
}
|
}
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.stream = null;
|
this.stream = null;
|
||||||
@@ -174,6 +175,11 @@ class AudioRecorder extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCo
|
|||||||
this.canvasContext.lineTo(x, height * 0.5 + y * 0.5);
|
this.canvasContext.lineTo(x, height * 0.5 + y * 0.5);
|
||||||
}
|
}
|
||||||
this.canvasContext.stroke();
|
this.canvasContext.stroke();
|
||||||
|
const now = new Date().getTime();
|
||||||
|
if (now - this.recordingTimestamp > _config_js__WEBPACK_IMPORTED_MODULE_6__.KEYPRESS_DELAY) {
|
||||||
|
this.props.onRecordingProgress();
|
||||||
|
this.recordingTimestamp = now;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
drawFrame();
|
drawFrame();
|
||||||
}
|
}
|
||||||
@@ -242,7 +248,7 @@ class AudioRecorder extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCo
|
|||||||
this.audioInput.connect(this.analyser);
|
this.audioInput.connect(this.analyser);
|
||||||
this.mediaRecorder.onstop = _ => {
|
this.mediaRecorder.onstop = _ => {
|
||||||
if (this.durationMillis > _config_js__WEBPACK_IMPORTED_MODULE_6__.MIN_DURATION) {
|
if (this.durationMillis > _config_js__WEBPACK_IMPORTED_MODULE_6__.MIN_DURATION) {
|
||||||
this.getRecording(this.mediaRecorder.mimeType, this.durationMillis).then(result => this.props.onFinished(result.url, result.preview, this.durationMillis));
|
this.getRecording(this.mediaRecorder.mimeType).then(result => this.props.onFinished(result.url, result.preview, this.durationMillis));
|
||||||
} else {
|
} else {
|
||||||
this.props.onDeleted();
|
this.props.onDeleted();
|
||||||
}
|
}
|
||||||
@@ -265,8 +271,10 @@ class AudioRecorder extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCo
|
|||||||
this.startedOn = Date.now();
|
this.startedOn = Date.now();
|
||||||
this.mediaRecorder.start();
|
this.mediaRecorder.start();
|
||||||
this.visualize();
|
this.visualize();
|
||||||
|
this.props.onRecordingProgress();
|
||||||
|
this.recordingTimestamp = this.startedOn;
|
||||||
}
|
}
|
||||||
getRecording(mimeType, duration) {
|
getRecording(mimeType) {
|
||||||
mimeType = mimeType || AUDIO_MIME_TYPE;
|
mimeType = mimeType || AUDIO_MIME_TYPE;
|
||||||
let blob = new Blob(this.audioChunks, {
|
let blob = new Blob(this.audioChunks, {
|
||||||
type: mimeType
|
type: mimeType
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
+10
-3
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user