Skip to content

Delay loading and rendering gui #4800

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/components/gui/gui.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,11 @@ const GUIComponent = props => {
};

if (isRendererSupported === null) {
isRendererSupported = Renderer.isSupported();
if (vm.renderer) {
isRendererSupported = true;
} else {
isRendererSupported = Renderer.isSupported();
}
}

return (<MediaQuery minWidth={layout.fullSizeMinWidth}>{isFullSize => {
Expand Down
75 changes: 74 additions & 1 deletion src/containers/gui.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,13 @@ import storage from '../lib/storage';
import vmListenerHOC from '../lib/vm-listener-hoc.jsx';
import vmManagerHOC from '../lib/vm-manager-hoc.jsx';
import cloudManagerHOC from '../lib/cloud-manager-hoc.jsx';
import Loader from '../components/loader/loader.jsx';

import GUIComponent from '../components/gui/gui.jsx';
// import GUIComponent from '../components/gui/gui.jsx';
import {setIsScratchDesktop} from '../lib/isScratchDesktop.js';

import VideoProvider from '../lib/video/video-provider';

const messages = defineMessages({
defaultProjectTitle: {
id: 'gui.gui.defaultProjectTitle',
Expand All @@ -52,6 +55,22 @@ class GUI extends React.Component {
setIsScratchDesktop(this.props.isScratchDesktop);
this.setReduxTitle(this.props.projectTitle);
this.props.onStorageInit(storage);

// Use setTimeout. Do not use requestAnimationFrame or a resolved
// Promise. We want this work delayed until after the data request is
// made.
setTimeout(this.ensureRenderer.bind(this));

// Once the GUI component has been rendered, always render GUI and do
// not revert back to a Loader in this component.
//
// This makes GUI container not a pure component. We don't want to use
// state for this. That would possibly cause a full second render of GUI
// after the first one.
const {fontsLoaded, fetchingProject, isLoading} = this.props;
this.isAfterGUI = this.isAfterGUI || (
fontsLoaded && !fetchingProject && !isLoading
);
}
componentDidUpdate (prevProps) {
if (this.props.projectId !== prevProps.projectId && this.props.projectId !== null) {
Expand All @@ -65,6 +84,17 @@ class GUI extends React.Component {
// At this time the project view in www doesn't need to know when a project is unloaded
this.props.onProjectLoaded();
}

// Once the GUI component has been rendered, always render GUI and do
// not revert back to a Loader in this component.
//
// This makes GUI container not a pure component. We don't want to use
// state for this. That would possibly cause a full second render of GUI
// after the first one.
const {fontsLoaded, fetchingProject, isLoading} = this.props;
this.isAfterGUI = this.isAfterGUI || (
fontsLoaded && !fetchingProject && !isLoading
);
}
setReduxTitle (newTitle) {
if (newTitle === null || typeof newTitle === 'undefined') {
Expand All @@ -75,6 +105,36 @@ class GUI extends React.Component {
this.props.onUpdateReduxProjectTitle(newTitle);
}
}
ensureRenderer () {
if (this.props.vm.renderer) {
return;
}

// Wait to load svg-renderer and render after the data request. This
// way the data request is made earlier.
const Renderer = require('scratch-render');
const {
SVGRenderer: V2SVGAdapter,
BitmapAdapter: V2BitmapAdapter
} = require('scratch-svg-renderer');

const vm = this.props.vm;
this.canvas = document.createElement('canvas');
this.renderer = new Renderer(this.canvas);
vm.attachRenderer(this.renderer);

vm.attachV2SVGAdapter(new V2SVGAdapter());
vm.attachV2BitmapAdapter(new V2BitmapAdapter());

// Only attach a video provider once because it is stateful
vm.setVideoProvider(new VideoProvider());

// Calling draw a single time before any project is loaded just
// makes the canvas white instead of solid black–needed because it
// is not possible to use CSS to style the canvas to have a
// different default color
vm.renderer.draw();
}
render () {
if (this.props.isError) {
throw new Error(
Expand All @@ -85,6 +145,7 @@ class GUI extends React.Component {
assetHost,
cloudHost,
error,
fontsLoaded,
isError,
isScratchDesktop,
isShowingProject,
Expand All @@ -102,6 +163,16 @@ class GUI extends React.Component {
loadingStateVisible,
...componentProps
} = this.props;

if (!this.isAfterGUI && (
!fontsLoaded || fetchingProject || isLoading
)) {
// Make sure a renderer exists.
if (fontsLoaded && !fetchingProject) this.ensureRenderer();
return <Loader />;
}

const GUIComponent = require('../components/gui/gui.jsx').default;
return (
<GUIComponent
loading={fetchingProject || isLoading || loadingStateVisible}
Expand All @@ -119,6 +190,7 @@ GUI.propTypes = {
cloudHost: PropTypes.string,
error: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
fetchingProject: PropTypes.bool,
fontsLoaded: PropTypes.bool,
intl: intlShape,
isError: PropTypes.bool,
isLoading: PropTypes.bool,
Expand Down Expand Up @@ -157,6 +229,7 @@ const mapStateToProps = state => {
costumeLibraryVisible: state.scratchGui.modals.costumeLibrary,
costumesTabVisible: state.scratchGui.editorTab.activeTabIndex === COSTUMES_TAB_INDEX,
error: state.scratchGui.projectState.error,
fontsLoaded: state.scratchGui.fontsLoaded,
isError: getIsError(loadingState),
isFullScreen: state.scratchGui.mode.isFullScreen,
isPlayerOnly: state.scratchGui.mode.isPlayerOnly,
Expand Down