2018-06-16 18:19:31 -04:00
|
|
|
// 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
|
|
|
|
|
2018-04-24 11:00:47 -04:00
|
|
|
const bindAll = require('lodash.bindall');
|
2018-02-20 17:04:45 -05:00
|
|
|
const React = require('react');
|
2018-03-08 15:57:19 -05:00
|
|
|
const PropTypes = require('prop-types');
|
|
|
|
const connect = require('react-redux').connect;
|
2018-05-02 15:27:49 -04:00
|
|
|
const injectIntl = require('react-intl').injectIntl;
|
2018-06-20 09:55:09 -04:00
|
|
|
const parser = require('scratch-parser');
|
2018-03-08 15:57:19 -05:00
|
|
|
const Page = require('../../components/page/www/page.jsx');
|
2018-02-20 17:04:45 -05:00
|
|
|
const render = require('../../lib/render.jsx');
|
2018-06-20 09:55:09 -04:00
|
|
|
const storage = require('../../lib/storage.js').default;
|
2018-06-26 08:41:06 -04:00
|
|
|
const log = require('../../lib/log');
|
2018-06-20 09:55:09 -04:00
|
|
|
const EXTENSION_INFO = require('../../lib/extensions.js').default;
|
2018-02-20 17:04:45 -05:00
|
|
|
|
2018-03-08 15:57:19 -05:00
|
|
|
const PreviewPresentation = require('./presentation.jsx');
|
2018-05-28 15:32:23 -04:00
|
|
|
const projectShape = require('./projectshape.jsx').projectShape;
|
2018-03-08 15:57:19 -05:00
|
|
|
|
|
|
|
const sessionActions = require('../../redux/session.js');
|
2018-03-14 15:50:27 -04:00
|
|
|
const previewActions = require('../../redux/preview.js');
|
2018-05-24 16:23:07 -04:00
|
|
|
|
|
|
|
const GUI = require('scratch-gui');
|
|
|
|
const IntlGUI = injectIntl(GUI.default);
|
2018-02-20 17:04:45 -05:00
|
|
|
|
|
|
|
class Preview extends React.Component {
|
2018-04-24 11:00:47 -04:00
|
|
|
constructor (props) {
|
|
|
|
super(props);
|
|
|
|
bindAll(this, [
|
2018-05-24 16:23:07 -04:00
|
|
|
'addEventListeners',
|
2018-07-13 09:50:17 -04:00
|
|
|
'handleToggleStudio',
|
2018-04-24 11:00:47 -04:00
|
|
|
'handleFavoriteToggle',
|
|
|
|
'handleLoveToggle',
|
|
|
|
'handlePermissions',
|
2018-05-24 16:23:07 -04:00
|
|
|
'handlePopState',
|
2018-05-31 16:49:17 -04:00
|
|
|
'handleReportClick',
|
|
|
|
'handleReportClose',
|
|
|
|
'handleReportSubmit',
|
2018-07-18 18:52:15 -04:00
|
|
|
'handleAddToStudioClick',
|
|
|
|
'handleAddToStudioClose',
|
2018-05-02 15:27:49 -04:00
|
|
|
'handleSeeInside',
|
2018-04-24 11:00:47 -04:00
|
|
|
'handleUpdate',
|
2018-05-24 16:23:07 -04:00
|
|
|
'initCounts',
|
2018-05-31 16:49:17 -04:00
|
|
|
'isShared',
|
|
|
|
'pushHistory',
|
|
|
|
'userOwnsProject'
|
2018-04-24 11:00:47 -04:00
|
|
|
]);
|
2018-05-31 16:49:17 -04:00
|
|
|
const pathname = window.location.pathname.toLowerCase();
|
|
|
|
const parts = pathname.split('/').filter(Boolean);
|
|
|
|
// parts[0]: 'preview'
|
|
|
|
// parts[1]: either :id or 'editor'
|
|
|
|
// parts[2]: undefined if no :id, otherwise either 'editor' or 'fullscreen'
|
|
|
|
this.state = {
|
|
|
|
editable: false,
|
2018-06-20 09:55:09 -04:00
|
|
|
extensions: [],
|
2018-05-31 16:49:17 -04:00
|
|
|
favoriteCount: 0,
|
|
|
|
loveCount: 0,
|
|
|
|
projectId: parts[1] === 'editor' ? 0 : parts[1],
|
2018-07-18 18:52:15 -04:00
|
|
|
addToStudioOpen: false,
|
2018-06-01 14:25:45 -04:00
|
|
|
report: {
|
|
|
|
category: '',
|
|
|
|
notes: '',
|
|
|
|
open: false,
|
|
|
|
waiting: false
|
|
|
|
}
|
2018-05-31 16:49:17 -04:00
|
|
|
};
|
2018-06-21 09:45:53 -04:00
|
|
|
this.getExtensions(this.state.projectId);
|
2018-05-24 16:23:07 -04:00
|
|
|
this.addEventListeners();
|
2018-04-24 11:00:47 -04:00
|
|
|
}
|
|
|
|
componentDidUpdate (prevProps) {
|
|
|
|
if (this.props.sessionStatus !== prevProps.sessionStatus &&
|
2018-05-02 15:27:49 -04:00
|
|
|
this.props.sessionStatus === sessionActions.Status.FETCHED &&
|
|
|
|
this.state.projectId) {
|
2018-04-24 11:00:47 -04:00
|
|
|
if (this.props.user) {
|
|
|
|
const username = this.props.user.username;
|
|
|
|
const token = this.props.user.token;
|
2018-05-02 15:27:49 -04:00
|
|
|
this.props.getProjectInfo(this.state.projectId, token);
|
|
|
|
this.props.getRemixes(this.state.projectId, token);
|
2018-07-04 18:01:11 -04:00
|
|
|
this.props.getProjectStudios(this.state.projectId, token);
|
|
|
|
this.props.getCuratedStudios(username, token);
|
2018-05-02 15:27:49 -04:00
|
|
|
this.props.getFavedStatus(this.state.projectId, username, token);
|
|
|
|
this.props.getLovedStatus(this.state.projectId, username, token);
|
2018-04-24 11:00:47 -04:00
|
|
|
} else {
|
2018-05-02 15:27:49 -04:00
|
|
|
this.props.getProjectInfo(this.state.projectId);
|
|
|
|
this.props.getRemixes(this.state.projectId);
|
2018-07-04 18:01:11 -04:00
|
|
|
this.props.getProjectStudios(this.state.projectId);
|
2018-04-24 11:00:47 -04:00
|
|
|
}
|
2018-06-16 18:19:31 -04:00
|
|
|
|
2018-04-24 11:00:47 -04:00
|
|
|
}
|
|
|
|
if (this.props.projectInfo.id !== prevProps.projectInfo.id) {
|
2018-06-20 17:08:06 -04:00
|
|
|
this.getExtensions(this.state.projectId);
|
2018-04-24 11:00:47 -04:00
|
|
|
this.initCounts(this.props.projectInfo.stats.favorites, this.props.projectInfo.stats.loves);
|
|
|
|
this.handlePermissions();
|
2018-05-24 09:57:06 -04:00
|
|
|
if (this.props.projectInfo.remix.parent !== null) {
|
|
|
|
this.props.getParentInfo(this.props.projectInfo.remix.parent);
|
|
|
|
}
|
|
|
|
if (this.props.projectInfo.remix.root !== null &&
|
|
|
|
this.props.projectInfo.remix.root !== this.props.projectInfo.remix.parent
|
|
|
|
) {
|
|
|
|
this.props.getOriginalInfo(this.props.projectInfo.remix.root);
|
2018-04-24 11:00:47 -04:00
|
|
|
}
|
|
|
|
}
|
2018-05-24 16:23:07 -04:00
|
|
|
if (this.props.playerMode !== prevProps.playerMode || this.props.fullScreen !== prevProps.fullScreen) {
|
|
|
|
this.pushHistory(history.state === null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
componentWillUnmount () {
|
|
|
|
this.removeEventListeners();
|
|
|
|
}
|
|
|
|
addEventListeners () {
|
|
|
|
window.addEventListener('popstate', this.handlePopState);
|
|
|
|
}
|
|
|
|
removeEventListeners () {
|
|
|
|
window.removeEventListener('popstate', this.handlePopState);
|
|
|
|
}
|
2018-06-20 09:55:09 -04:00
|
|
|
getExtensions (projectId) {
|
|
|
|
storage
|
|
|
|
.load(storage.AssetType.Project, projectId, storage.DataFormat.JSON)
|
|
|
|
.then(projectAsset => {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
parser(projectAsset.data, false, (err, projectData) => {
|
2018-06-20 17:04:16 -04:00
|
|
|
if (err) {
|
2018-06-26 08:41:06 -04:00
|
|
|
log.error(`Unhandled project parsing error: ${err}`);
|
2018-06-20 17:04:16 -04:00
|
|
|
return;
|
|
|
|
}
|
2018-06-20 09:55:09 -04:00
|
|
|
const extensionSet = new Set();
|
2018-06-22 09:02:11 -04:00
|
|
|
if (projectData[0].extensions) {
|
|
|
|
projectData[0].extensions.forEach(extension => {
|
2018-06-21 09:45:53 -04:00
|
|
|
extensionSet.add(EXTENSION_INFO[extension]);
|
2018-06-22 09:02:11 -04:00
|
|
|
});
|
2018-06-21 09:45:32 -04:00
|
|
|
}
|
2018-06-20 09:55:09 -04:00
|
|
|
this.setState({
|
|
|
|
extensions: Array.from(extensionSet)
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2018-05-31 16:49:17 -04:00
|
|
|
handleReportClick () {
|
2018-06-01 14:25:45 -04:00
|
|
|
this.setState({report: {...this.state.report, open: true}});
|
2018-05-31 16:49:17 -04:00
|
|
|
}
|
|
|
|
handleReportClose () {
|
2018-06-01 14:25:45 -04:00
|
|
|
this.setState({report: {...this.state.report, open: false}});
|
2018-05-31 16:49:17 -04:00
|
|
|
}
|
2018-07-18 18:52:15 -04:00
|
|
|
handleAddToStudioClick () {
|
|
|
|
this.setState({addToStudioOpen: true});
|
|
|
|
}
|
|
|
|
handleAddToStudioClose () {
|
|
|
|
this.setState({addToStudioOpen: false});
|
|
|
|
}
|
|
|
|
// NOTE: this is a copy, change it
|
2018-05-31 16:49:17 -04:00
|
|
|
handleReportSubmit (formData) {
|
2018-06-01 14:25:45 -04:00
|
|
|
this.setState({report: {
|
|
|
|
category: formData.report_category,
|
|
|
|
notes: formData.notes,
|
|
|
|
open: this.state.report.open,
|
|
|
|
waiting: true}
|
|
|
|
});
|
|
|
|
|
2018-05-31 16:49:17 -04:00
|
|
|
const data = {
|
|
|
|
...formData,
|
|
|
|
id: this.state.projectId,
|
2018-06-01 14:25:45 -04:00
|
|
|
user: this.props.user.username
|
2018-05-31 16:49:17 -04:00
|
|
|
};
|
|
|
|
console.log('submit report data', data); // eslint-disable-line no-console
|
2018-06-01 14:25:45 -04:00
|
|
|
this.setState({report: {
|
|
|
|
category: '',
|
|
|
|
notes: '',
|
|
|
|
open: false,
|
|
|
|
waiting: false}
|
|
|
|
});
|
2018-05-31 16:49:17 -04:00
|
|
|
}
|
2018-05-24 16:23:07 -04:00
|
|
|
handlePopState () {
|
|
|
|
const path = window.location.pathname.toLowerCase();
|
|
|
|
const playerMode = path.indexOf('editor') === -1;
|
|
|
|
const fullScreen = path.indexOf('fullscreen') !== -1;
|
|
|
|
if (this.props.playerMode !== playerMode) {
|
|
|
|
this.props.setPlayer(playerMode);
|
|
|
|
}
|
|
|
|
if (this.props.fullScreen !== fullScreen) {
|
|
|
|
this.props.setFullScreen(fullScreen);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pushHistory (push) {
|
|
|
|
// update URI to match mode
|
|
|
|
const idPath = this.state.projectId ? `${this.state.projectId}/` : '';
|
|
|
|
let modePath = '';
|
|
|
|
if (!this.props.playerMode) modePath = 'editor/';
|
|
|
|
// fullscreen overrides editor
|
|
|
|
if (this.props.fullScreen) modePath = 'fullscreen/';
|
|
|
|
const newPath = `/preview/${idPath}${modePath}`;
|
|
|
|
if (push) {
|
|
|
|
history.pushState(
|
|
|
|
{},
|
|
|
|
document.title,
|
|
|
|
newPath
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
history.replaceState(
|
|
|
|
{},
|
|
|
|
document.title,
|
|
|
|
newPath
|
|
|
|
);
|
|
|
|
}
|
2018-03-08 15:57:19 -05:00
|
|
|
}
|
2018-07-25 15:08:12 -04:00
|
|
|
handleToggleStudio (event) {
|
|
|
|
const studioId = parseInt(event.currentTarget.dataset.id, 10);
|
|
|
|
if (!isNaN(studioId)) { // probably unnecessary test; should always be a number
|
|
|
|
const studio = this.props.studios.find(thisStudio => (thisStudio.id === studioId));
|
|
|
|
// only send add or leave request to server if we know current status
|
|
|
|
if ((typeof studio !== 'undefined') && ('includesProject' in studio)) {
|
|
|
|
this.props.toggleStudio(
|
|
|
|
(studio.includesProject === false),
|
|
|
|
studioId,
|
|
|
|
this.props.projectInfo.id,
|
|
|
|
this.props.user.token
|
|
|
|
);
|
|
|
|
}
|
2018-07-18 18:52:15 -04:00
|
|
|
}
|
2018-07-11 15:08:01 -04:00
|
|
|
}
|
2018-04-24 11:00:47 -04:00
|
|
|
handleFavoriteToggle () {
|
|
|
|
this.props.setFavedStatus(
|
|
|
|
!this.props.faved,
|
|
|
|
this.props.projectInfo.id,
|
|
|
|
this.props.user.username,
|
|
|
|
this.props.user.token
|
|
|
|
);
|
|
|
|
if (this.props.faved) {
|
|
|
|
this.setState(state => ({
|
|
|
|
favoriteCount: state.favoriteCount - 1
|
|
|
|
}));
|
|
|
|
} else {
|
|
|
|
this.setState(state => ({
|
|
|
|
favoriteCount: state.favoriteCount + 1
|
|
|
|
}));
|
2018-03-14 15:50:27 -04:00
|
|
|
}
|
2018-04-24 11:00:47 -04:00
|
|
|
}
|
|
|
|
handleLoveToggle () {
|
|
|
|
this.props.setLovedStatus(
|
|
|
|
!this.props.loved,
|
|
|
|
this.props.projectInfo.id,
|
|
|
|
this.props.user.username,
|
|
|
|
this.props.user.token
|
|
|
|
);
|
|
|
|
if (this.props.loved) {
|
|
|
|
this.setState(state => ({
|
|
|
|
loveCount: state.loveCount - 1
|
|
|
|
}));
|
|
|
|
} else {
|
|
|
|
this.setState(state => ({
|
|
|
|
loveCount: state.loveCount + 1
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
handlePermissions () {
|
2018-06-16 18:19:31 -04:00
|
|
|
// TODO: handle admins and mods
|
2018-04-24 11:00:47 -04:00
|
|
|
if (this.props.projectInfo.author.username === this.props.user.username) {
|
|
|
|
this.setState({editable: true});
|
|
|
|
}
|
|
|
|
}
|
2018-05-02 15:27:49 -04:00
|
|
|
handleSeeInside () {
|
2018-05-24 16:23:07 -04:00
|
|
|
this.props.setPlayer(false);
|
2018-05-02 15:27:49 -04:00
|
|
|
}
|
2018-04-24 11:00:47 -04:00
|
|
|
handleUpdate (jsonData) {
|
|
|
|
this.props.updateProject(
|
|
|
|
this.props.projectInfo.id,
|
|
|
|
jsonData,
|
|
|
|
this.props.user.username,
|
|
|
|
this.props.user.token
|
|
|
|
);
|
|
|
|
}
|
|
|
|
initCounts (favorites, loves) {
|
|
|
|
this.setState({
|
|
|
|
favoriteCount: favorites,
|
|
|
|
loveCount: loves
|
|
|
|
});
|
2018-02-20 17:04:45 -05:00
|
|
|
}
|
2018-05-31 16:49:17 -04:00
|
|
|
isShared () {
|
|
|
|
return (
|
|
|
|
// if we don't have projectInfo assume shared until we know otherwise
|
|
|
|
Object.keys(this.props.projectInfo).length === 0 || (
|
|
|
|
this.props.projectInfo.history &&
|
|
|
|
this.props.projectInfo.history.shared.length > 0
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}
|
2018-06-01 14:25:45 -04:00
|
|
|
isLoggedIn () {
|
2018-05-31 16:49:17 -04:00
|
|
|
return (
|
|
|
|
this.props.sessionStatus === sessionActions.Status.FETCHED &&
|
2018-06-01 14:25:45 -04:00
|
|
|
Object.keys(this.props.user).length > 0
|
|
|
|
);
|
|
|
|
}
|
|
|
|
userOwnsProject () {
|
|
|
|
return (
|
|
|
|
this.isLoggedIn() &&
|
2018-05-31 16:49:17 -04:00
|
|
|
Object.keys(this.props.projectInfo).length > 0 &&
|
|
|
|
this.props.user.id === this.props.projectInfo.author.id
|
|
|
|
);
|
|
|
|
}
|
2018-02-20 17:04:45 -05:00
|
|
|
render () {
|
|
|
|
return (
|
2018-05-24 16:23:07 -04:00
|
|
|
this.props.playerMode ?
|
2018-05-02 15:27:49 -04:00
|
|
|
<Page>
|
|
|
|
<PreviewPresentation
|
2018-07-25 15:08:12 -04:00
|
|
|
addToStudioOpen={this.state.addToStudioOpen}
|
2018-05-02 15:27:49 -04:00
|
|
|
comments={this.props.comments}
|
|
|
|
editable={this.state.editable}
|
2018-06-20 09:55:09 -04:00
|
|
|
extensions={this.state.extensions}
|
2018-05-02 15:27:49 -04:00
|
|
|
faved={this.props.faved}
|
|
|
|
favoriteCount={this.state.favoriteCount}
|
|
|
|
isFullScreen={this.state.isFullScreen}
|
2018-06-01 14:25:45 -04:00
|
|
|
isLoggedIn={this.isLoggedIn()}
|
2018-05-31 16:49:17 -04:00
|
|
|
isShared={this.isShared()}
|
2018-05-02 15:27:49 -04:00
|
|
|
loveCount={this.state.loveCount}
|
|
|
|
loved={this.props.loved}
|
2018-05-24 09:57:06 -04:00
|
|
|
originalInfo={this.props.original}
|
|
|
|
parentInfo={this.props.parent}
|
2018-05-02 15:27:49 -04:00
|
|
|
projectId={this.state.projectId}
|
|
|
|
projectInfo={this.props.projectInfo}
|
2018-07-25 15:08:12 -04:00
|
|
|
projectStudios={this.props.projectStudios}
|
2018-05-02 15:27:49 -04:00
|
|
|
remixes={this.props.remixes}
|
2018-06-01 14:25:45 -04:00
|
|
|
report={this.state.report}
|
2018-07-18 18:52:15 -04:00
|
|
|
studios={this.props.studios}
|
2018-05-02 15:27:49 -04:00
|
|
|
user={this.props.user}
|
2018-05-31 16:49:17 -04:00
|
|
|
userOwnsProject={this.userOwnsProject()}
|
2018-07-25 15:08:12 -04:00
|
|
|
onAddToStudioClicked={this.handleAddToStudioClick}
|
|
|
|
onAddToStudioClosed={this.handleAddToStudioClose}
|
2018-05-02 15:27:49 -04:00
|
|
|
onFavoriteClicked={this.handleFavoriteToggle}
|
|
|
|
onLoveClicked={this.handleLoveToggle}
|
2018-05-31 16:49:17 -04:00
|
|
|
onReportClicked={this.handleReportClick}
|
|
|
|
onReportClose={this.handleReportClose}
|
|
|
|
onReportSubmit={this.handleReportSubmit}
|
2018-05-02 15:27:49 -04:00
|
|
|
onSeeInside={this.handleSeeInside}
|
2018-07-25 15:08:12 -04:00
|
|
|
onToggleStudio={this.handleToggleStudio}
|
2018-05-02 15:27:49 -04:00
|
|
|
onUpdate={this.handleUpdate}
|
|
|
|
/>
|
2018-05-24 16:23:07 -04:00
|
|
|
</Page> :
|
|
|
|
<IntlGUI
|
|
|
|
enableCommunity
|
|
|
|
basePath="/"
|
|
|
|
className="gui"
|
|
|
|
projectId={this.state.projectId}
|
|
|
|
/>
|
2018-02-20 17:04:45 -05:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-08 15:57:19 -05:00
|
|
|
Preview.propTypes = {
|
2018-03-15 17:40:16 -04:00
|
|
|
comments: PropTypes.arrayOf(PropTypes.object),
|
2018-05-24 09:57:06 -04:00
|
|
|
faved: PropTypes.bool,
|
2018-05-28 13:12:26 -04:00
|
|
|
fullScreen: PropTypes.bool,
|
2018-07-25 15:08:12 -04:00
|
|
|
getCuratedStudios: PropTypes.func.isRequired,
|
2018-05-24 09:57:06 -04:00
|
|
|
getFavedStatus: PropTypes.func.isRequired,
|
|
|
|
getLovedStatus: PropTypes.func.isRequired,
|
|
|
|
getOriginalInfo: PropTypes.func.isRequired,
|
|
|
|
getParentInfo: PropTypes.func.isRequired,
|
|
|
|
getProjectInfo: PropTypes.func.isRequired,
|
2018-07-04 18:01:11 -04:00
|
|
|
getProjectStudios: PropTypes.func.isRequired,
|
2018-07-25 15:08:12 -04:00
|
|
|
getRemixes: PropTypes.func.isRequired,
|
2018-05-24 09:57:06 -04:00
|
|
|
loved: PropTypes.bool,
|
2018-05-28 15:32:23 -04:00
|
|
|
original: projectShape,
|
|
|
|
parent: projectShape,
|
2018-05-24 16:23:07 -04:00
|
|
|
playerMode: PropTypes.bool,
|
2018-05-28 15:32:23 -04:00
|
|
|
projectInfo: projectShape,
|
2018-07-25 15:08:12 -04:00
|
|
|
projectStudios: PropTypes.arrayOf(PropTypes.object),
|
2018-03-15 17:40:16 -04:00
|
|
|
remixes: PropTypes.arrayOf(PropTypes.object),
|
2018-03-08 15:57:19 -05:00
|
|
|
sessionStatus: PropTypes.string,
|
2018-04-24 11:00:47 -04:00
|
|
|
setFavedStatus: PropTypes.func.isRequired,
|
2018-05-24 16:23:07 -04:00
|
|
|
setFullScreen: PropTypes.func.isRequired,
|
2018-04-24 11:00:47 -04:00
|
|
|
setLovedStatus: PropTypes.func.isRequired,
|
2018-05-24 16:23:07 -04:00
|
|
|
setPlayer: PropTypes.func.isRequired,
|
2018-07-18 18:52:15 -04:00
|
|
|
studios: PropTypes.arrayOf(PropTypes.object),
|
2018-07-25 15:08:12 -04:00
|
|
|
toggleStudio: PropTypes.func.isRequired,
|
2018-04-24 11:00:47 -04:00
|
|
|
updateProject: PropTypes.func.isRequired,
|
2018-03-08 15:57:19 -05:00
|
|
|
user: PropTypes.shape({
|
|
|
|
id: PropTypes.number,
|
|
|
|
banned: PropTypes.bool,
|
|
|
|
username: PropTypes.string,
|
|
|
|
token: PropTypes.string,
|
|
|
|
thumbnailUrl: PropTypes.string,
|
|
|
|
dateJoined: PropTypes.string,
|
|
|
|
email: PropTypes.string,
|
|
|
|
classroomId: PropTypes.string
|
|
|
|
})
|
|
|
|
};
|
|
|
|
|
|
|
|
Preview.defaultProps = {
|
|
|
|
sessionStatus: sessionActions.Status.NOT_FETCHED,
|
|
|
|
user: {}
|
|
|
|
};
|
|
|
|
|
2018-07-13 09:50:17 -04:00
|
|
|
// Build consolidated curatedStudios object from all studio info.
|
2018-07-24 09:12:13 -04:00
|
|
|
// We add flags to indicate whether the project is currently in the studio,
|
|
|
|
// and the status of requests to join/leave studios.
|
2018-07-25 15:08:12 -04:00
|
|
|
const consolidateStudiosInfo = (curatedStudios, projectStudios, currentStudioIds, studioRequests) => {
|
2018-07-24 09:12:13 -04:00
|
|
|
const consolidatedStudios = [];
|
2018-07-13 09:50:17 -04:00
|
|
|
|
2018-07-18 18:52:15 -04:00
|
|
|
projectStudios.forEach(projectStudio => {
|
2018-07-24 11:50:03 -04:00
|
|
|
const includesProject = (projectStudio.id in currentStudioIds);
|
2018-07-24 09:12:13 -04:00
|
|
|
const consolidatedStudio =
|
|
|
|
Object.assign({}, projectStudio, {includesProject: includesProject});
|
|
|
|
consolidatedStudios.push(consolidatedStudio);
|
|
|
|
});
|
|
|
|
|
|
|
|
// copy the curated studios that project is not in
|
|
|
|
curatedStudios.forEach(curatedStudio => {
|
2018-07-24 11:50:03 -04:00
|
|
|
if (!projectStudios.some(projectStudio => (projectStudio.id === curatedStudio.id))) {
|
|
|
|
const includesProject = (curatedStudio.id in currentStudioIds);
|
2018-07-24 09:12:13 -04:00
|
|
|
const consolidatedStudio =
|
2018-07-24 11:50:03 -04:00
|
|
|
Object.assign({}, curatedStudio, {includesProject: includesProject});
|
2018-07-24 09:12:13 -04:00
|
|
|
consolidatedStudios.push(consolidatedStudio);
|
2018-07-13 09:50:17 -04:00
|
|
|
}
|
|
|
|
});
|
2018-07-24 09:12:13 -04:00
|
|
|
|
2018-07-18 18:52:15 -04:00
|
|
|
// set studio state to hasRequestOutstanding==true if it's being fetched,
|
|
|
|
// false if it's not
|
|
|
|
consolidatedStudios.forEach(consolidatedStudio => {
|
|
|
|
const id = consolidatedStudio.id;
|
|
|
|
consolidatedStudio.hasRequestOutstanding =
|
|
|
|
((id in studioRequests) &&
|
2018-07-24 11:50:03 -04:00
|
|
|
(studioRequests[id] === previewActions.Status.FETCHING));
|
2018-07-18 18:52:15 -04:00
|
|
|
});
|
|
|
|
return consolidatedStudios;
|
2018-07-25 15:08:12 -04:00
|
|
|
};
|
2018-07-13 09:50:17 -04:00
|
|
|
|
2018-03-08 15:57:19 -05:00
|
|
|
const mapStateToProps = state => ({
|
2018-03-14 15:50:27 -04:00
|
|
|
projectInfo: state.preview.projectInfo,
|
|
|
|
comments: state.preview.comments,
|
2018-04-24 11:00:47 -04:00
|
|
|
faved: state.preview.faved,
|
|
|
|
loved: state.preview.loved,
|
2018-05-24 09:57:06 -04:00
|
|
|
original: state.preview.original,
|
|
|
|
parent: state.preview.parent,
|
2018-03-14 15:50:27 -04:00
|
|
|
remixes: state.preview.remixes,
|
2018-03-08 15:57:19 -05:00
|
|
|
sessionStatus: state.session.status,
|
2018-07-04 18:01:11 -04:00
|
|
|
projectStudios: state.preview.projectStudios,
|
2018-07-18 18:52:15 -04:00
|
|
|
studios: consolidateStudiosInfo(state.preview.curatedStudios,
|
2018-07-24 09:12:13 -04:00
|
|
|
state.preview.projectStudios, state.preview.currentStudioIds,
|
|
|
|
state.preview.status.studioRequests),
|
2018-05-24 16:23:07 -04:00
|
|
|
user: state.session.session.user,
|
|
|
|
playerMode: state.scratchGui.mode.isPlayerOnly,
|
|
|
|
fullScreen: state.scratchGui.mode.isFullScreen
|
2018-03-08 15:57:19 -05:00
|
|
|
});
|
|
|
|
|
2018-03-14 15:50:27 -04:00
|
|
|
|
2018-03-08 15:57:19 -05:00
|
|
|
const mapDispatchToProps = dispatch => ({
|
2018-05-24 09:57:06 -04:00
|
|
|
getOriginalInfo: id => {
|
|
|
|
dispatch(previewActions.getOriginalInfo(id));
|
|
|
|
},
|
|
|
|
getParentInfo: id => {
|
|
|
|
dispatch(previewActions.getParentInfo(id));
|
2018-03-14 15:50:27 -04:00
|
|
|
},
|
2018-04-24 11:00:47 -04:00
|
|
|
getProjectInfo: (id, token) => {
|
|
|
|
dispatch(previewActions.getProjectInfo(id, token));
|
2018-03-14 15:50:27 -04:00
|
|
|
},
|
2018-03-15 17:40:16 -04:00
|
|
|
getRemixes: id => {
|
2018-03-14 15:50:27 -04:00
|
|
|
dispatch(previewActions.getRemixes(id));
|
|
|
|
},
|
2018-07-04 18:01:11 -04:00
|
|
|
getProjectStudios: id => {
|
|
|
|
dispatch(previewActions.getProjectStudios(id));
|
|
|
|
},
|
|
|
|
getCuratedStudios: (username, token) => {
|
|
|
|
dispatch(previewActions.getCuratedStudios(username, token));
|
2018-04-24 11:00:47 -04:00
|
|
|
},
|
2018-07-13 09:50:17 -04:00
|
|
|
toggleStudio: (isAdd, studioId, id, token) => {
|
|
|
|
if (isAdd === true) {
|
|
|
|
dispatch(previewActions.addToStudio(studioId, id, token));
|
|
|
|
} else {
|
|
|
|
dispatch(previewActions.leaveStudio(studioId, id, token));
|
|
|
|
}
|
2018-07-11 15:08:01 -04:00
|
|
|
},
|
2018-04-24 11:00:47 -04:00
|
|
|
getFavedStatus: (id, username, token) => {
|
|
|
|
dispatch(previewActions.getFavedStatus(id, username, token));
|
|
|
|
},
|
|
|
|
setFavedStatus: (faved, id, username, token) => {
|
2018-05-30 14:39:25 -04:00
|
|
|
dispatch(previewActions.setFavedStatus(faved, id, username, token));
|
2018-04-24 11:00:47 -04:00
|
|
|
},
|
|
|
|
getLovedStatus: (id, username, token) => {
|
|
|
|
dispatch(previewActions.getLovedStatus(id, username, token));
|
|
|
|
},
|
|
|
|
setLovedStatus: (loved, id, username, token) => {
|
|
|
|
dispatch(previewActions.setLovedStatus(loved, id, username, token));
|
|
|
|
},
|
2018-03-08 15:57:19 -05:00
|
|
|
refreshSession: () => {
|
|
|
|
dispatch(sessionActions.refreshSession());
|
2018-03-14 15:50:27 -04:00
|
|
|
},
|
2018-05-24 09:57:06 -04:00
|
|
|
setOriginalInfo: info => {
|
|
|
|
dispatch(previewActions.setOriginalInfo(info));
|
|
|
|
},
|
|
|
|
setParentInfo: info => {
|
|
|
|
dispatch(previewActions.setParentInfo(info));
|
2018-03-14 15:50:27 -04:00
|
|
|
},
|
2018-04-24 11:00:47 -04:00
|
|
|
updateProject: (id, formData, username, token) => {
|
|
|
|
dispatch(previewActions.updateProject(id, formData, username, token));
|
2018-05-24 16:23:07 -04:00
|
|
|
},
|
|
|
|
setPlayer: player => {
|
|
|
|
dispatch(GUI.setPlayer(player));
|
|
|
|
},
|
|
|
|
setFullScreen: fullscreen => {
|
|
|
|
dispatch(GUI.setFullScreen(fullscreen));
|
2018-03-08 15:57:19 -05:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const ConnectedPreview = connect(
|
|
|
|
mapStateToProps,
|
|
|
|
mapDispatchToProps
|
|
|
|
)(Preview);
|
|
|
|
|
2018-05-24 16:23:07 -04:00
|
|
|
GUI.setAppElement(document.getElementById('app'));
|
|
|
|
const initGuiState = guiInitialState => {
|
|
|
|
const pathname = window.location.pathname.toLowerCase();
|
|
|
|
const parts = pathname.split('/').filter(Boolean);
|
|
|
|
// parts[0]: 'preview'
|
|
|
|
// parts[1]: either :id or 'editor'
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
return guiInitialState;
|
|
|
|
};
|
|
|
|
|
2018-03-14 15:50:27 -04:00
|
|
|
render(
|
2018-05-02 15:27:49 -04:00
|
|
|
<ConnectedPreview />,
|
2018-03-14 15:50:27 -04:00
|
|
|
document.getElementById('app'),
|
2018-05-24 16:23:07 -04:00
|
|
|
{
|
|
|
|
preview: previewActions.previewReducer,
|
|
|
|
...GUI.guiReducers
|
|
|
|
},
|
|
|
|
{scratchGui: initGuiState(GUI.guiInitialState)},
|
|
|
|
GUI.guiMiddleware
|
2018-03-14 15:50:27 -04:00
|
|
|
);
|