Merge pull request #3377 from benjiwheeler/embed-view

embed view with minimal functionality, route
This commit is contained in:
Benjamin Wheeler 2019-10-02 17:32:45 -04:00 committed by GitHub
commit 18fd960a6a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 158 additions and 17 deletions

18
src/lib/sentry.js Normal file
View file

@ -0,0 +1,18 @@
const initSentry = () => {
// initialize Sentry instance, making sure it hasn't been initialized already
if (!window.Sentry && `${process.env.SENTRY_DSN}` !== '') {
const Sentry = require('@sentry/browser');
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
}
};
module.exports = initSentry;

View file

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

View file

@ -0,0 +1,97 @@
// embed view
const React = require('react');
const PropTypes = require('prop-types');
const connect = require('react-redux').connect;
const injectIntl = require('react-intl').injectIntl;
const ErrorBoundary = require('../../components/errorboundary/errorboundary.jsx');
const projectShape = require('./projectshape.jsx').projectShape;
const NotAvailable = require('../../components/not-available/not-available.jsx');
const Meta = require('./meta.jsx');
const previewActions = require('../../redux/preview.js');
const GUI = require('scratch-gui');
const IntlGUI = injectIntl(GUI.default);
const initSentry = require('../../lib/sentry.js');
initSentry();
class EmbedView extends React.Component {
constructor (props) {
super(props);
const pathname = window.location.pathname.toLowerCase();
const parts = pathname.split('/').filter(Boolean);
this.state = {
extensions: [],
invalidProject: parts.length === 1,
projectId: parts[1]
};
}
componentDidMount () {
this.props.getProjectInfo(this.state.projectId);
}
render () {
if (this.props.projectNotAvailable || this.state.invalidProject) {
return (
<ErrorBoundary>
<div className="preview">
<NotAvailable />
</div>
</ErrorBoundary>
);
}
return (
<React.Fragment>
<Meta projectInfo={this.props.projectInfo} />
<IntlGUI
assetHost={this.props.assetHost}
basePath="/"
className="gui"
projectHost={this.props.projectHost}
projectId={this.state.projectId}
projectTitle={this.props.projectInfo.title}
/>
</React.Fragment>
);
}
}
EmbedView.propTypes = {
assetHost: PropTypes.string.isRequired,
getProjectInfo: PropTypes.func.isRequired,
projectHost: PropTypes.string.isRequired,
projectInfo: projectShape,
projectNotAvailable: PropTypes.bool
};
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);
GUI.setAppElement(document.getElementById('app'));
module.exports.initGuiState = GUI.initEmbedded;
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,30 @@
const React = require('react');
const ErrorBoundary = require('../../components/errorboundary/errorboundary.jsx');
const render = require('../../lib/render.jsx');
// Require this even though we don't use it because, without it, webpack runs out of memory...
const Page = require('../../components/page/www/page.jsx'); // eslint-disable-line no-unused-vars
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(<ErrorBoundary><UnsupportedBrowser /></ErrorBoundary>, document.getElementById('app'));
}

View file

@ -35,17 +35,8 @@ const IntlGUI = injectIntl(GUI.default);
const localStorageAvailable = 'localStorage' in window && window.localStorage !== null; const localStorageAvailable = 'localStorage' in window && window.localStorage !== null;
const Sentry = require('@sentry/browser'); const initSentry = require('../../lib/sentry.js');
if (`${process.env.SENTRY_DSN}` !== '') { initSentry();
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 Preview extends React.Component { class Preview extends React.Component {
constructor (props) { constructor (props) {
@ -258,7 +249,7 @@ class Preview extends React.Component {
// this is a project.json as an object // this is a project.json as an object
// validate expects a string or buffer as input // validate expects a string or buffer as input
// TODO not sure if we need to check that it also isn't a data view // TODO not sure if we need to check that it also isn't a data view
input = JSON.stringify(input); input = JSON.stringify(input); // NOTE: what is the point of doing this??
} }
parser(projectAsset.data, false, (err, projectData) => { parser(projectAsset.data, false, (err, projectData) => {
if (err) { if (err) {
@ -1092,16 +1083,13 @@ module.exports.initGuiState = guiInitialState => {
const parts = pathname.split('/').filter(Boolean); const parts = pathname.split('/').filter(Boolean);
// parts[0]: 'projects' // parts[0]: 'projects'
// parts[1]: either :id or 'editor' // 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) { if (parts.indexOf('editor') === -1) {
guiInitialState = GUI.initPlayer(guiInitialState); guiInitialState = GUI.initPlayer(guiInitialState);
} }
if (parts.indexOf('fullscreen') !== -1) { if (parts.indexOf('fullscreen') !== -1) {
guiInitialState = GUI.initFullScreen(guiInitialState); guiInitialState = GUI.initFullScreen(guiInitialState);
} }
if (parts.indexOf('embed') !== -1) {
guiInitialState = GUI.initEmbedded(guiInitialState);
}
return guiInitialState; return guiInitialState;
}; };