810deb907b
Move all Etherpad's access control from Meteor to a separated [Node application](https://github.com/bigbluebutton/bbb-pads). This new app uses [Etherpad's API](https://etherpad.org/doc/v1.8.4/#index_overview) to create groups and manage session tokens for users to access them. Each group represents one distinct pad at the html5 client. - Removed locked users' access to pads: replaced readOnly pad's access with a new pad's content sharing routine - Pad's access is now controlled by [Etherpad's API](https://etherpad.org/doc/v1.8.4/#index_overview) - Closed captions edited content now reflects at it's live feedback - Improved closed caption's dictation mode live feedback - Moved all Etherpad's API control from Meteor to a separated [app](https://github.com/bigbluebutton/bbb-pads) - Included access control both in akka-apps and bbb-pads
144 lines
3.4 KiB
JavaScript
144 lines
3.4 KiB
JavaScript
import { PureComponent } from 'react';
|
|
import PropTypes from 'prop-types';
|
|
import { defineMessages, injectIntl } from 'react-intl';
|
|
import { notify } from '/imports/ui/services/notification';
|
|
import logger from '/imports/startup/client/logger';
|
|
import CaptionsService from '/imports/ui/components/captions/service';
|
|
import Service from './service';
|
|
|
|
const intlMessages = defineMessages({
|
|
start: {
|
|
id: 'app.captions.speech.start',
|
|
description: 'Notification on speech recognition start',
|
|
},
|
|
stop: {
|
|
id: 'app.captions.speech.stop',
|
|
description: 'Notification on speech recognition stop',
|
|
},
|
|
error: {
|
|
id: 'app.captions.speech.error',
|
|
description: 'Notification on speech recognition error',
|
|
},
|
|
});
|
|
|
|
class Speech extends PureComponent {
|
|
constructor(props) {
|
|
super(props);
|
|
|
|
this.onStop = this.onStop.bind(this);
|
|
this.onError = this.onError.bind(this);
|
|
this.onResult = this.onResult.bind(this);
|
|
|
|
this.result = {
|
|
transcript: '',
|
|
isFinal: true,
|
|
};
|
|
|
|
this.speechRecognition = Service.initSpeechRecognition();
|
|
|
|
if (this.speechRecognition) {
|
|
this.speechRecognition.onstart = () => notify(props.intl.formatMessage(intlMessages.start), 'info', 'closed_caption');
|
|
this.speechRecognition.onend = () => notify(props.intl.formatMessage(intlMessages.stop), 'info', 'closed_caption');
|
|
this.speechRecognition.onerror = (event) => this.onError(event);
|
|
this.speechRecognition.onresult = (event) => this.onResult(event);
|
|
}
|
|
}
|
|
|
|
componentDidUpdate(prevProps) {
|
|
const {
|
|
locale,
|
|
dictating,
|
|
} = this.props;
|
|
|
|
// Start dictating
|
|
if (!prevProps.dictating && dictating) {
|
|
if (this.speechRecognition) {
|
|
this.speechRecognition.lang = locale;
|
|
try {
|
|
this.speechRecognition.start();
|
|
} catch (event) {
|
|
this.onError(event.error);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Stop dictating
|
|
if (prevProps.dictating && !dictating) {
|
|
this.onStop();
|
|
}
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
this.onStop();
|
|
}
|
|
|
|
onError(error) {
|
|
this.onStop();
|
|
|
|
const {
|
|
intl,
|
|
locale,
|
|
} = this.props;
|
|
|
|
notify(intl.formatMessage(intlMessages.error), 'error', 'warning');
|
|
CaptionsService.stopDictation(locale);
|
|
logger.error({
|
|
logCode: 'captions_speech_recognition',
|
|
extraInfo: { error },
|
|
}, 'Captions speech recognition error');
|
|
}
|
|
|
|
onStop() {
|
|
const { locale } = this.props;
|
|
|
|
if (this.speechRecognition) {
|
|
const {
|
|
isFinal,
|
|
transcript,
|
|
} = this.result;
|
|
|
|
if (!isFinal) {
|
|
Service.pushFinalTranscript(locale, transcript);
|
|
this.speechRecognition.abort();
|
|
} else {
|
|
this.speechRecognition.stop();
|
|
}
|
|
}
|
|
}
|
|
|
|
onResult(event) {
|
|
const { locale } = this.props;
|
|
|
|
const {
|
|
resultIndex,
|
|
results,
|
|
} = event;
|
|
|
|
const { transcript } = results[resultIndex][0];
|
|
const { isFinal } = results[resultIndex];
|
|
|
|
this.result.transcript = transcript;
|
|
this.result.isFinal = isFinal;
|
|
|
|
if (isFinal) {
|
|
Service.pushFinalTranscript(locale, transcript);
|
|
} else {
|
|
Service.pushInterimTranscript(locale, transcript);
|
|
}
|
|
}
|
|
|
|
render() {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
Speech.propTypes = {
|
|
locale: PropTypes.string.isRequired,
|
|
dictating: PropTypes.bool.isRequired,
|
|
intl: PropTypes.shape({
|
|
formatMessage: PropTypes.func.isRequired,
|
|
}).isRequired,
|
|
};
|
|
|
|
export default injectIntl(Speech);
|