import React from 'react';
import {FormattedMessage, injectIntl, intlShape, defineMessages} from 'react-intl';
import {connect} from 'react-redux';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import bindAll from 'lodash.bindall';
import styles from './loader.css';
import {getIsLoadingWithId} from '../../reducers/project-state';
import topBlock from './top-block.svg';
import middleBlock from './middle-block.svg';
import bottomBlock from './bottom-block.svg';
const mainMessages = {
'gui.loader.headline': (
),
'gui.loader.creating': (
)
};
const messages = defineMessages({
projectData: {
defaultMessage: 'Loading project …',
description: 'Appears when loading project data, but not assets yet',
id: 'tw.loader.projectData'
},
downloadingAssets: {
defaultMessage: 'Downloading assets ({complete}/{total}) …',
description: 'Appears when loading project assets from a project on a remote website',
id: 'tw.loader.downloadingAssets'
},
loadingAssets: {
defaultMessage: 'Loading assets ({complete}/{total}) …',
description: 'Appears when loading project assets from a project file on the user\'s computer',
id: 'tw.loader.loadingAssets'
}
});
// Because progress events are fired so often during the very performance-critical loading
// process and React updates are very slow, we bypass React for updating the progress bar.
class LoaderComponent extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'handleAssetProgress',
'handleProjectLoaded',
'barInnerRef',
'messageRef'
]);
this.barInnerEl = null;
this.messageEl = null;
this.ignoreProgress = false;
}
componentDidMount () {
this.handleAssetProgress(
this.props.vm.runtime.finishedAssetRequests,
this.props.vm.runtime.totalAssetRequests
);
this.props.vm.on('ASSET_PROGRESS', this.handleAssetProgress);
this.props.vm.runtime.on('PROJECT_LOADED', this.handleProjectLoaded);
}
componentWillUnmount () {
this.props.vm.off('ASSET_PROGRESS', this.handleAssetProgress);
this.props.vm.runtime.off('PROJECT_LOADED', this.handleProjectLoaded);
}
handleAssetProgress (finished, total) {
if (this.ignoreProgress || !this.barInnerEl || !this.messageEl) {
return;
}
if (total === 0) {
// Started loading a new project.
this.barInnerEl.style.width = '0';
this.messageEl.textContent = this.props.intl.formatMessage(messages.projectData);
} else {
this.barInnerEl.style.width = `${finished / total * 100}%`;
const message = this.props.isRemote ? messages.downloadingAssets : messages.loadingAssets;
this.messageEl.textContent = this.props.intl.formatMessage(message, {
complete: finished,
total
});
}
}
handleProjectLoaded () {
if (this.ignoreProgress || !this.barInnerEl || !this.messageEl) {
return;
}
this.ignoreProgress = true;
this.props.vm.runtime.resetProgress();
}
barInnerRef (barInner) {
this.barInnerEl = barInner;
}
messageRef (message) {
this.messageEl = message;
}
render () {
return (
{mainMessages[this.props.messageId]}
);
}
}
LoaderComponent.propTypes = {
intl: intlShape,
isFullScreen: PropTypes.bool,
isRemote: PropTypes.bool,
messageId: PropTypes.string,
vm: PropTypes.shape({
on: PropTypes.func,
off: PropTypes.func,
runtime: PropTypes.shape({
totalAssetRequests: PropTypes.number,
finishedAssetRequests: PropTypes.number,
resetProgress: PropTypes.func,
on: PropTypes.func,
off: PropTypes.func
})
})
};
LoaderComponent.defaultProps = {
isFullScreen: false,
messageId: 'gui.loader.headline'
};
const mapStateToProps = state => ({
isRemote: getIsLoadingWithId(state.scratchGui.projectState.loadingState),
vm: state.scratchGui.vm
});
const mapDispatchToProps = () => ({});
export default connect(
mapStateToProps,
mapDispatchToProps
)(injectIntl(LoaderComponent));