embed view with minimal functionality, route

This commit is contained in:
Ben Wheeler 2019-09-17 21:49:48 -04:00
parent 303481b61d
commit 775173661f
4 changed files with 193 additions and 5 deletions

View file

@ -177,12 +177,20 @@
},
{
"name": "projects",
"pattern": "^/projects(/editor|(/\\d+(/editor|/fullscreen|/embed)?)?)?/?(\\?.*)?$",
"pattern": "^/projects(/editor|(/\\d+(/editor|/fullscreen)?)?)?/?(\\?.*)?$",
"routeAlias": "/projects/?$",
"view": "preview/preview",
"title": "Scratch Project",
"dynamicMetaTags": true
},
{
"name": "projects",
"pattern": "^/projects/\\d+/embed/?(\\?.*)?$",
"routeAlias": "/projects/?$",
"view": "preview/embed",
"title": "Scratch Project",
"dynamicMetaTags": true
},
{
"name": "parents",
"pattern": "^/parents/?(\\?.*)?$",

View file

@ -0,0 +1,154 @@
// embed view
const bindAll = require('lodash.bindall');
const React = require('react');
const PropTypes = require('prop-types');
const connect = require('react-redux').connect;
const injectIntl = require('react-intl').injectIntl;
const Page = require('../../components/page/www/page.jsx');
const storage = require('../../lib/storage.js').default;
const jar = require('../../lib/jar.js');
const projectShape = require('./projectshape.jsx').projectShape;
const NotAvailable = require('../../components/not-available/not-available.jsx');
const Meta = require('./meta.jsx');
const sessionActions = require('../../redux/session.js');
const previewActions = require('../../redux/preview.js');
const GUI = require('scratch-gui');
const IntlGUI = injectIntl(GUI.default);
const Sentry = require('@sentry/browser');
if (`${process.env.SENTRY_DSN}` !== '') {
Sentry.init({
dsn: `${process.env.SENTRY_DSN}`,
// Do not collect global onerror, only collect specifically from React error boundaries.
// TryCatch plugin also includes errors from setTimeouts (i.e. the VM)
integrations: integrations => integrations.filter(i =>
!(i.name === 'GlobalHandlers' || i.name === 'TryCatch'))
});
window.Sentry = Sentry; // Allow GUI access to Sentry via window
}
class EmbedView extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
]);
const pathname = window.location.pathname.toLowerCase();
const parts = pathname.split('/').filter(Boolean);
this.state = {
extensions: [],
invalidProject: parts.length === 1,
projectId: parts[1]
};
}
componentDidUpdate (prevProps) {
if (this.state.projectId > 0 &&
((this.props.sessionStatus !== prevProps.sessionStatus &&
this.props.sessionStatus === sessionActions.Status.FETCHED))) {
this.props.getProjectInfo(this.state.projectId);
this.getProjectData(this.state.projectId, true /* Show cloud/username alerts */);
}
}
getProjectData (projectId) {
if (projectId <= 0) return 0;
storage
.load(storage.AssetType.Project, projectId, storage.DataFormat.JSON)
.then(projectAsset => { // NOTE: this is turning up null, breaking the line below.
let input = projectAsset.data;
if (typeof input === 'object' && !(input instanceof ArrayBuffer) &&
!ArrayBuffer.isView(input)) { // taken from scratch-vm
// If the input is an object and not any ArrayBuffer
// or an ArrayBuffer view (this includes all typed arrays and DataViews)
// turn the object into a JSON string, because we suspect
// this is a project.json as an object
// validate expects a string or buffer as input
// TODO not sure if we need to check that it also isn't a data view
input = JSON.stringify(input);
}
});
}
handleSetLanguage (locale) {
jar.set('scratchlanguage', locale);
}
render () {
if (this.props.projectNotAvailable || this.state.invalidProject) {
return (
<Page>
<div className="preview">
<NotAvailable />
</div>
</Page>
);
}
return (
<React.Fragment>
<Meta projectInfo={this.props.projectInfo} />
<React.Fragment>
<IntlGUI
assetHost={this.props.assetHost}
basePath="/"
className="gui"
projectHost={this.props.projectHost}
projectId={this.state.projectId}
projectTitle={this.props.projectInfo.title}
onSetLanguage={this.handleSetLanguage}
/>
</React.Fragment>
</React.Fragment>
);
}
}
EmbedView.propTypes = {
assetHost: PropTypes.string.isRequired,
getProjectInfo: PropTypes.func.isRequired,
projectHost: PropTypes.string.isRequired,
projectInfo: projectShape,
projectNotAvailable: PropTypes.bool,
sessionStatus: PropTypes.string
};
EmbedView.defaultProps = {
assetHost: process.env.ASSET_HOST,
projectHost: process.env.PROJECT_HOST
};
const mapStateToProps = state => ({
projectInfo: state.preview.projectInfo,
projectNotAvailable: state.preview.projectNotAvailable
});
const mapDispatchToProps = dispatch => ({
getProjectInfo: (id, token) => {
dispatch(previewActions.getProjectInfo(id, token));
}
});
module.exports.View = connect(
mapStateToProps,
mapDispatchToProps
)(EmbedView);
// initialize GUI by calling its reducer functions depending on URL
GUI.setAppElement(document.getElementById('app'));
module.exports.initGuiState = guiInitialState => {
const pathname = window.location.pathname.toLowerCase();
const parts = pathname.split('/').filter(Boolean);
// parts[0]: 'projects'
// parts[1]: either :id or 'editor'
// parts[2]: undefined if no :id, otherwise either 'editor', 'fullscreen' or 'embed'
if (parts.indexOf('embed') !== -1) {
guiInitialState = GUI.initEmbedded(guiInitialState);
}
return guiInitialState;
};
module.exports.guiReducers = GUI.guiReducers;
module.exports.guiInitialState = GUI.guiInitialState;
module.exports.guiMiddleware = GUI.guiMiddleware;
module.exports.initLocale = GUI.initLocale;
module.exports.localesInitialState = GUI.localesInitialState;

View file

@ -0,0 +1,29 @@
// preview view can show either project page or editor page;
// idea is that we shouldn't require a page reload to switch back and forth
const React = require('react');
const Page = require('../../components/page/www/page.jsx');
const render = require('../../lib/render.jsx');
const previewActions = require('../../redux/preview.js');
const isSupportedBrowser = require('../../lib/supported-browser').default;
const UnsupportedBrowser = require('./unsupported-browser.jsx');
if (isSupportedBrowser()) {
const EmbedView = require('./embed-view.jsx');
render(
<EmbedView.View />,
document.getElementById('app'),
{
preview: previewActions.previewReducer,
...EmbedView.guiReducers
},
{
locales: EmbedView.initLocale(EmbedView.localesInitialState, window._locale),
scratchGui: EmbedView.initGuiState(EmbedView.guiInitialState)
},
EmbedView.guiMiddleware
);
} else {
render(<Page><UnsupportedBrowser /></Page>, document.getElementById('app'));
}

View file

@ -1092,16 +1092,13 @@ module.exports.initGuiState = guiInitialState => {
const parts = pathname.split('/').filter(Boolean);
// parts[0]: 'projects'
// parts[1]: either :id or 'editor'
// parts[2]: undefined if no :id, otherwise either 'editor', 'fullscreen' or 'embed'
// parts[2]: undefined if no :id, otherwise either 'editor' or 'fullscreen'
if (parts.indexOf('editor') === -1) {
guiInitialState = GUI.initPlayer(guiInitialState);
}
if (parts.indexOf('fullscreen') !== -1) {
guiInitialState = GUI.initFullScreen(guiInitialState);
}
if (parts.indexOf('embed') !== -1) {
guiInitialState = GUI.initEmbedded(guiInitialState);
}
return guiInitialState;
};