2019-05-17 04:11:10 +08:00
|
|
|
|
import React from 'react';
|
|
|
|
|
import PropTypes from 'prop-types';
|
2019-05-21 00:43:31 +08:00
|
|
|
|
import CaptionsService from './service';
|
2019-05-17 04:11:10 +08:00
|
|
|
|
|
|
|
|
|
const CAPTIONS_CONFIG = Meteor.settings.public.captions;
|
|
|
|
|
|
|
|
|
|
class Captions extends React.Component {
|
|
|
|
|
constructor(props) {
|
|
|
|
|
super(props);
|
2019-05-18 04:53:59 +08:00
|
|
|
|
this.state = { initial: true };
|
2019-05-29 22:31:27 +08:00
|
|
|
|
this.text = '';
|
2019-07-06 03:44:12 +08:00
|
|
|
|
this.ariaText = '';
|
2019-05-21 00:43:31 +08:00
|
|
|
|
this.timer = null;
|
|
|
|
|
this.settings = CaptionsService.getCaptionsSettings();
|
2019-05-17 04:11:10 +08:00
|
|
|
|
|
|
|
|
|
this.updateText = this.updateText.bind(this);
|
2019-05-21 00:43:31 +08:00
|
|
|
|
this.resetTimer = this.resetTimer.bind(this);
|
2019-05-17 04:11:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-23 22:51:01 +08:00
|
|
|
|
componentDidMount() {
|
|
|
|
|
this.setState({ initial: false });
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-18 04:53:59 +08:00
|
|
|
|
shouldComponentUpdate(nextProps, nextState) {
|
2019-05-21 00:43:31 +08:00
|
|
|
|
const {
|
|
|
|
|
padId,
|
|
|
|
|
revs,
|
|
|
|
|
} = this.props;
|
|
|
|
|
|
|
|
|
|
if (padId === nextProps.padId) {
|
2019-07-06 03:44:12 +08:00
|
|
|
|
if (this.text !== '') this.ariaText = this.text;
|
2019-05-21 00:43:31 +08:00
|
|
|
|
if (revs === nextProps.revs && !nextState.clear) return false;
|
2019-05-17 04:11:10 +08:00
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-29 22:31:27 +08:00
|
|
|
|
componentDidUpdate() {
|
|
|
|
|
/* https://reactjs.org/docs/react-component.html#componentdidupdate
|
|
|
|
|
You may call setState() immediately in componentDidUpdate()
|
|
|
|
|
but note that it must be wrapped in a condition (...),
|
|
|
|
|
or you’ll cause an infinite loop. */
|
2019-05-23 22:51:01 +08:00
|
|
|
|
const { clear } = this.state;
|
|
|
|
|
if (clear) {
|
2019-05-21 00:43:31 +08:00
|
|
|
|
this.setState({ clear: false });
|
|
|
|
|
} else {
|
|
|
|
|
this.resetTimer();
|
|
|
|
|
this.timer = setTimeout(() => { this.setState({ clear: true }); }, CAPTIONS_CONFIG.time);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-23 22:51:01 +08:00
|
|
|
|
componentWillUnmount() {
|
|
|
|
|
this.resetTimer();
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-17 04:11:10 +08:00
|
|
|
|
updateText(data) {
|
2019-05-23 22:51:01 +08:00
|
|
|
|
const { clear } = this.state;
|
|
|
|
|
if (clear) {
|
2019-05-29 22:31:27 +08:00
|
|
|
|
this.text = '';
|
2019-05-21 00:43:31 +08:00
|
|
|
|
} else {
|
2019-05-21 01:39:04 +08:00
|
|
|
|
const text = this.text + data;
|
|
|
|
|
this.text = CaptionsService.formatCaptionsText(text);
|
2019-05-21 00:43:31 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resetTimer() {
|
|
|
|
|
if (this.timer) {
|
|
|
|
|
clearTimeout(this.timer);
|
|
|
|
|
this.timer = null;
|
|
|
|
|
}
|
2019-05-17 04:11:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
render() {
|
2019-05-21 00:43:31 +08:00
|
|
|
|
const { data } = this.props;
|
2019-05-23 22:51:01 +08:00
|
|
|
|
const { initial } = this.state;
|
2019-05-17 04:11:10 +08:00
|
|
|
|
const {
|
|
|
|
|
fontFamily,
|
|
|
|
|
fontSize,
|
|
|
|
|
fontColor,
|
|
|
|
|
backgroundColor,
|
2019-05-21 00:43:31 +08:00
|
|
|
|
} = this.settings;
|
2019-05-17 04:11:10 +08:00
|
|
|
|
|
2019-05-23 22:51:01 +08:00
|
|
|
|
if (!initial) {
|
2019-05-21 00:43:31 +08:00
|
|
|
|
this.updateText(data);
|
2019-05-17 04:11:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const captionStyles = {
|
|
|
|
|
whiteSpace: 'pre-wrap',
|
|
|
|
|
wordWrap: 'break-word',
|
|
|
|
|
fontFamily,
|
|
|
|
|
fontSize,
|
|
|
|
|
background: backgroundColor,
|
|
|
|
|
color: fontColor,
|
|
|
|
|
};
|
|
|
|
|
|
2019-07-06 03:44:12 +08:00
|
|
|
|
const visuallyHidden = {
|
|
|
|
|
position: 'absolute',
|
|
|
|
|
overflow: 'hidden',
|
|
|
|
|
clip: 'rect(0 0 0 0)',
|
|
|
|
|
height: '1px',
|
|
|
|
|
width: '1px',
|
|
|
|
|
margin: '-1px',
|
|
|
|
|
padding: '0',
|
|
|
|
|
border: '0',
|
|
|
|
|
};
|
|
|
|
|
|
2019-05-17 04:11:10 +08:00
|
|
|
|
return (
|
2019-07-06 03:44:12 +08:00
|
|
|
|
<div>
|
|
|
|
|
<div
|
|
|
|
|
aria-hidden
|
|
|
|
|
style={captionStyles}
|
|
|
|
|
dangerouslySetInnerHTML={{ __html: this.text }}
|
|
|
|
|
/>
|
|
|
|
|
<div
|
|
|
|
|
style={visuallyHidden}
|
|
|
|
|
aria-live={this.text === '' && this.ariaText !== '' ? 'polite' : 'off'}
|
|
|
|
|
dangerouslySetInnerHTML={{ __html: this.ariaText }}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2019-05-17 04:11:10 +08:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default Captions;
|
|
|
|
|
|
|
|
|
|
Captions.propTypes = {
|
2019-05-21 00:43:31 +08:00
|
|
|
|
padId: PropTypes.string.isRequired,
|
|
|
|
|
revs: PropTypes.number.isRequired,
|
|
|
|
|
data: PropTypes.string.isRequired,
|
2019-05-17 04:11:10 +08:00
|
|
|
|
};
|