mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-23 07:38:07 -05:00
Pass to and receive from GUI info about project creation lifecycle; handle url changes (#2197)
* add canSaveNew prop to pass to GUI * pass to and receive from GUI info about project lifecycle * reset project data or fetch new project data depending on updates received from gui * removed canSaveNew * projectId always a string * moved handleUpdateProjectId calls that fetch or set project metadata into componentDidUpdate * changed page history object * removed comments * two small fixes to deal with edge cases * cleaning up getExtensions
This commit is contained in:
parent
8acbf05b1a
commit
a6409bbcce
2 changed files with 117 additions and 62 deletions
|
@ -46,6 +46,8 @@ module.exports.previewReducer = (state, action) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
case 'RESET_TO_INTIAL_STATE':
|
||||||
|
return module.exports.getInitialState();
|
||||||
case 'SET_PROJECT_INFO':
|
case 'SET_PROJECT_INFO':
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
projectInfo: action.info
|
projectInfo: action.info
|
||||||
|
@ -164,6 +166,10 @@ module.exports.setError = error => ({
|
||||||
error: error
|
error: error
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.exports.resetProject = () => ({
|
||||||
|
type: 'RESET_TO_INTIAL_STATE'
|
||||||
|
});
|
||||||
|
|
||||||
module.exports.setProjectInfo = info => ({
|
module.exports.setProjectInfo = info => ({
|
||||||
type: 'SET_PROJECT_INFO',
|
type: 'SET_PROJECT_INFO',
|
||||||
info: info
|
info: info
|
||||||
|
|
|
@ -33,6 +33,7 @@ class Preview extends React.Component {
|
||||||
super(props);
|
super(props);
|
||||||
bindAll(this, [
|
bindAll(this, [
|
||||||
'addEventListeners',
|
'addEventListeners',
|
||||||
|
'fetchCommunityData',
|
||||||
'handleAddComment',
|
'handleAddComment',
|
||||||
'handleDeleteComment',
|
'handleDeleteComment',
|
||||||
'handleToggleStudio',
|
'handleToggleStudio',
|
||||||
|
@ -49,6 +50,7 @@ class Preview extends React.Component {
|
||||||
'handleAddToStudioClose',
|
'handleAddToStudioClose',
|
||||||
'handleSeeInside',
|
'handleSeeInside',
|
||||||
'handleShare',
|
'handleShare',
|
||||||
|
'handleUpdateProjectId',
|
||||||
'handleUpdateProjectTitle',
|
'handleUpdateProjectTitle',
|
||||||
'handleUpdate',
|
'handleUpdate',
|
||||||
'handleToggleComments',
|
'handleToggleComments',
|
||||||
|
@ -66,7 +68,7 @@ class Preview extends React.Component {
|
||||||
extensions: [],
|
extensions: [],
|
||||||
favoriteCount: 0,
|
favoriteCount: 0,
|
||||||
loveCount: 0,
|
loveCount: 0,
|
||||||
projectId: parts[1] === 'editor' ? 0 : parts[1],
|
projectId: parts[1] === 'editor' ? '0' : parts[1],
|
||||||
addToStudioOpen: false,
|
addToStudioOpen: false,
|
||||||
reportOpen: false
|
reportOpen: false
|
||||||
};
|
};
|
||||||
|
@ -75,38 +77,30 @@ class Preview extends React.Component {
|
||||||
/* In the beginning, if user is on mobile and landscape, go to fullscreen */
|
/* In the beginning, if user is on mobile and landscape, go to fullscreen */
|
||||||
this.setScreenFromOrientation();
|
this.setScreenFromOrientation();
|
||||||
}
|
}
|
||||||
componentDidUpdate (prevProps) {
|
componentDidUpdate (prevProps, prevState) {
|
||||||
if (this.props.sessionStatus !== prevProps.sessionStatus &&
|
if (this.state.projectId > 0 &&
|
||||||
this.props.sessionStatus === sessionActions.Status.FETCHED &&
|
((this.props.sessionStatus !== prevProps.sessionStatus &&
|
||||||
this.state.projectId) {
|
this.props.sessionStatus === sessionActions.Status.FETCHED) ||
|
||||||
if (this.props.user) {
|
(this.state.projectId !== prevState.projectId))) {
|
||||||
const username = this.props.user.username;
|
this.fetchCommunityData();
|
||||||
const token = this.props.user.token;
|
}
|
||||||
this.props.getTopLevelComments(this.state.projectId, this.props.comments.length,
|
if (this.state.projectId === '0' && this.state.projectId !== prevState.projectId) {
|
||||||
this.props.isAdmin, token);
|
this.props.resetProject();
|
||||||
this.props.getProjectInfo(this.state.projectId, token);
|
|
||||||
this.props.getRemixes(this.state.projectId, token);
|
|
||||||
this.props.getProjectStudios(this.state.projectId, token);
|
|
||||||
this.props.getCuratedStudios(username);
|
|
||||||
this.props.getFavedStatus(this.state.projectId, username, token);
|
|
||||||
this.props.getLovedStatus(this.state.projectId, username, token);
|
|
||||||
} else {
|
|
||||||
this.props.getTopLevelComments(this.state.projectId, this.props.comments.length);
|
|
||||||
this.props.getProjectInfo(this.state.projectId);
|
|
||||||
this.props.getRemixes(this.state.projectId);
|
|
||||||
this.props.getProjectStudios(this.state.projectId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (this.props.projectInfo.id !== prevProps.projectInfo.id) {
|
if (this.props.projectInfo.id !== prevProps.projectInfo.id) {
|
||||||
this.getExtensions(this.state.projectId);
|
this.getExtensions(this.state.projectId);
|
||||||
this.initCounts(this.props.projectInfo.stats.favorites, this.props.projectInfo.stats.loves);
|
if (typeof this.props.projectInfo.id === 'undefined') {
|
||||||
if (this.props.projectInfo.remix.parent !== null) {
|
this.initCounts(0, 0);
|
||||||
this.props.getParentInfo(this.props.projectInfo.remix.parent);
|
} else {
|
||||||
}
|
this.initCounts(this.props.projectInfo.stats.favorites, this.props.projectInfo.stats.loves);
|
||||||
if (this.props.projectInfo.remix.root !== null &&
|
if (this.props.projectInfo.remix.parent !== null) {
|
||||||
this.props.projectInfo.remix.root !== this.props.projectInfo.remix.parent
|
this.props.getParentInfo(this.props.projectInfo.remix.parent);
|
||||||
) {
|
}
|
||||||
this.props.getOriginalInfo(this.props.projectInfo.remix.root);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.props.playerMode !== prevProps.playerMode || this.props.fullScreen !== prevProps.fullScreen) {
|
if (this.props.playerMode !== prevProps.playerMode || this.props.fullScreen !== prevProps.fullScreen) {
|
||||||
|
@ -124,6 +118,25 @@ class Preview extends React.Component {
|
||||||
window.removeEventListener('popstate', this.handlePopState);
|
window.removeEventListener('popstate', this.handlePopState);
|
||||||
window.removeEventListener('orientationchange', this.setScreenFromOrientation);
|
window.removeEventListener('orientationchange', this.setScreenFromOrientation);
|
||||||
}
|
}
|
||||||
|
fetchCommunityData () {
|
||||||
|
if (this.props.userPresent) {
|
||||||
|
const username = this.props.user.username;
|
||||||
|
const token = this.props.user.token;
|
||||||
|
this.props.getTopLevelComments(this.state.projectId, this.props.comments.length,
|
||||||
|
this.props.isAdmin, token);
|
||||||
|
this.props.getProjectInfo(this.state.projectId, token);
|
||||||
|
this.props.getRemixes(this.state.projectId, token);
|
||||||
|
this.props.getProjectStudios(this.state.projectId, token);
|
||||||
|
this.props.getCuratedStudios(username);
|
||||||
|
this.props.getFavedStatus(this.state.projectId, username, token);
|
||||||
|
this.props.getLovedStatus(this.state.projectId, username, token);
|
||||||
|
} else {
|
||||||
|
this.props.getTopLevelComments(this.state.projectId, this.props.comments.length);
|
||||||
|
this.props.getProjectInfo(this.state.projectId);
|
||||||
|
this.props.getRemixes(this.state.projectId);
|
||||||
|
this.props.getProjectStudios(this.state.projectId);
|
||||||
|
}
|
||||||
|
}
|
||||||
setScreenFromOrientation () {
|
setScreenFromOrientation () {
|
||||||
/*
|
/*
|
||||||
* If the user is on a mobile device, switching to
|
* If the user is on a mobile device, switching to
|
||||||
|
@ -141,36 +154,42 @@ class Preview extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getExtensions (projectId) {
|
getExtensions (projectId) {
|
||||||
storage
|
if (projectId > 0) {
|
||||||
.load(storage.AssetType.Project, projectId, storage.DataFormat.JSON)
|
storage
|
||||||
.then(projectAsset => { // NOTE: this is turning up null, breaking the line below.
|
.load(storage.AssetType.Project, projectId, storage.DataFormat.JSON)
|
||||||
let input = projectAsset.data;
|
.then(projectAsset => { // NOTE: this is turning up null, breaking the line below.
|
||||||
if (typeof input === 'object' && !(input instanceof ArrayBuffer) &&
|
let input = projectAsset.data;
|
||||||
!ArrayBuffer.isView(input)) { // taken from scratch-vm
|
if (typeof input === 'object' && !(input instanceof ArrayBuffer) &&
|
||||||
// If the input is an object and not any ArrayBuffer
|
!ArrayBuffer.isView(input)) { // taken from scratch-vm
|
||||||
// or an ArrayBuffer view (this includes all typed arrays and DataViews)
|
// If the input is an object and not any ArrayBuffer
|
||||||
// turn the object into a JSON string, because we suspect
|
// or an ArrayBuffer view (this includes all typed arrays and DataViews)
|
||||||
// this is a project.json as an object
|
// turn the object into a JSON string, because we suspect
|
||||||
// validate expects a string or buffer as input
|
// this is a project.json as an object
|
||||||
// TODO not sure if we need to check that it also isn't a data view
|
// validate expects a string or buffer as input
|
||||||
input = JSON.stringify(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) => {
|
|
||||||
if (err) {
|
|
||||||
log.error(`Unhandled project parsing error: ${err}`);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
const extensionSet = new Set();
|
parser(projectAsset.data, false, (err, projectData) => {
|
||||||
if (projectData[0].extensions) {
|
if (err) {
|
||||||
projectData[0].extensions.forEach(extension => {
|
log.error(`Unhandled project parsing error: ${err}`);
|
||||||
extensionSet.add(EXTENSION_INFO[extension]);
|
return;
|
||||||
|
}
|
||||||
|
const extensionSet = new Set();
|
||||||
|
if (projectData[0].extensions) {
|
||||||
|
projectData[0].extensions.forEach(extension => {
|
||||||
|
extensionSet.add(EXTENSION_INFO[extension]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
extensions: Array.from(extensionSet)
|
||||||
});
|
});
|
||||||
}
|
|
||||||
this.setState({
|
|
||||||
extensions: Array.from(extensionSet)
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
} else { // projectId is default or invalid; empty the extensions array
|
||||||
|
this.setState({
|
||||||
|
extensions: []
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
handleToggleComments () {
|
handleToggleComments () {
|
||||||
this.props.updateProject(
|
this.props.updateProject(
|
||||||
|
@ -313,6 +332,25 @@ class Preview extends React.Component {
|
||||||
title: title
|
title: title
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
handleUpdateProjectId (projectId, callback) {
|
||||||
|
this.setState({projectId: projectId}, () => {
|
||||||
|
const parts = window.location.pathname.toLowerCase()
|
||||||
|
.split('/')
|
||||||
|
.filter(Boolean);
|
||||||
|
let newUrl;
|
||||||
|
if (projectId === '0') {
|
||||||
|
newUrl = `/${parts[0]}/editor`;
|
||||||
|
} else {
|
||||||
|
newUrl = `/${parts[0]}/${projectId}/editor`;
|
||||||
|
}
|
||||||
|
history.pushState(
|
||||||
|
{projectId: projectId},
|
||||||
|
{projectId: projectId},
|
||||||
|
newUrl
|
||||||
|
);
|
||||||
|
if (callback) callback();
|
||||||
|
});
|
||||||
|
}
|
||||||
initCounts (favorites, loves) {
|
initCounts (favorites, loves) {
|
||||||
this.setState({
|
this.setState({
|
||||||
favoriteCount: favorites,
|
favoriteCount: favorites,
|
||||||
|
@ -389,7 +427,6 @@ class Preview extends React.Component {
|
||||||
</Page> :
|
</Page> :
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<IntlGUI
|
<IntlGUI
|
||||||
enableCommunity
|
|
||||||
hideIntro
|
hideIntro
|
||||||
assetHost={this.props.assetHost}
|
assetHost={this.props.assetHost}
|
||||||
backpackOptions={this.props.backpackOptions}
|
backpackOptions={this.props.backpackOptions}
|
||||||
|
@ -400,6 +437,7 @@ class Preview extends React.Component {
|
||||||
canSaveAsCopy={this.props.canSaveAsCopy}
|
canSaveAsCopy={this.props.canSaveAsCopy}
|
||||||
canShare={this.props.canShare}
|
canShare={this.props.canShare}
|
||||||
className="gui"
|
className="gui"
|
||||||
|
enableCommunity={this.props.enableCommunity}
|
||||||
projectHost={this.props.projectHost}
|
projectHost={this.props.projectHost}
|
||||||
projectId={this.state.projectId}
|
projectId={this.state.projectId}
|
||||||
projectTitle={this.props.projectInfo.title}
|
projectTitle={this.props.projectInfo.title}
|
||||||
|
@ -408,6 +446,7 @@ class Preview extends React.Component {
|
||||||
onOpenRegistration={this.props.handleOpenRegistration}
|
onOpenRegistration={this.props.handleOpenRegistration}
|
||||||
onShare={this.handleShare}
|
onShare={this.handleShare}
|
||||||
onToggleLoginOpen={this.props.handleToggleLoginOpen}
|
onToggleLoginOpen={this.props.handleToggleLoginOpen}
|
||||||
|
onUpdateProjectId={this.handleUpdateProjectId}
|
||||||
onUpdateProjectTitle={this.handleUpdateProjectTitle}
|
onUpdateProjectTitle={this.handleUpdateProjectTitle}
|
||||||
/>
|
/>
|
||||||
<Registration />
|
<Registration />
|
||||||
|
@ -432,6 +471,7 @@ Preview.propTypes = {
|
||||||
canSaveAsCopy: PropTypes.bool,
|
canSaveAsCopy: PropTypes.bool,
|
||||||
canShare: PropTypes.bool,
|
canShare: PropTypes.bool,
|
||||||
comments: PropTypes.arrayOf(PropTypes.object),
|
comments: PropTypes.arrayOf(PropTypes.object),
|
||||||
|
enableCommunity: PropTypes.bool,
|
||||||
faved: PropTypes.bool,
|
faved: PropTypes.bool,
|
||||||
fullScreen: PropTypes.bool,
|
fullScreen: PropTypes.bool,
|
||||||
getCuratedStudios: PropTypes.func.isRequired,
|
getCuratedStudios: PropTypes.func.isRequired,
|
||||||
|
@ -465,6 +505,7 @@ Preview.propTypes = {
|
||||||
remixes: PropTypes.arrayOf(PropTypes.object),
|
remixes: PropTypes.arrayOf(PropTypes.object),
|
||||||
replies: PropTypes.objectOf(PropTypes.array),
|
replies: PropTypes.objectOf(PropTypes.array),
|
||||||
reportProject: PropTypes.func,
|
reportProject: PropTypes.func,
|
||||||
|
resetProject: PropTypes.func,
|
||||||
sessionStatus: PropTypes.string,
|
sessionStatus: PropTypes.string,
|
||||||
setFavedStatus: PropTypes.func.isRequired,
|
setFavedStatus: PropTypes.func.isRequired,
|
||||||
setFullScreen: PropTypes.func.isRequired,
|
setFullScreen: PropTypes.func.isRequired,
|
||||||
|
@ -482,7 +523,8 @@ Preview.propTypes = {
|
||||||
email: PropTypes.string,
|
email: PropTypes.string,
|
||||||
classroomId: PropTypes.string
|
classroomId: PropTypes.string
|
||||||
}),
|
}),
|
||||||
userOwnsProject: PropTypes.bool
|
userOwnsProject: PropTypes.bool,
|
||||||
|
userPresent: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|
||||||
Preview.defaultProps = {
|
Preview.defaultProps = {
|
||||||
|
@ -493,12 +535,14 @@ Preview.defaultProps = {
|
||||||
},
|
},
|
||||||
projectHost: process.env.PROJECT_HOST,
|
projectHost: process.env.PROJECT_HOST,
|
||||||
sessionStatus: sessionActions.Status.NOT_FETCHED,
|
sessionStatus: sessionActions.Status.NOT_FETCHED,
|
||||||
user: {}
|
user: {},
|
||||||
|
userPresent: false
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
const projectInfoPresent = Object.keys(state.preview.projectInfo).length > 0;
|
const projectInfoPresent = Object.keys(state.preview.projectInfo).length > 0;
|
||||||
const userPresent = state.session.session.user &&
|
const userPresent = state.session.session.user !== null &&
|
||||||
|
typeof state.session.session.user !== 'undefined' &&
|
||||||
Object.keys(state.session.session.user).length > 0;
|
Object.keys(state.session.session.user).length > 0;
|
||||||
const isLoggedIn = state.session.status === sessionActions.Status.FETCHED &&
|
const isLoggedIn = state.session.status === sessionActions.Status.FETCHED &&
|
||||||
userPresent;
|
userPresent;
|
||||||
|
@ -510,13 +554,14 @@ const mapStateToProps = state => {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
canAddToStudio: isLoggedIn && userOwnsProject,
|
canAddToStudio: isLoggedIn && userOwnsProject,
|
||||||
canCreateNew: false,
|
canCreateNew: true,
|
||||||
canRemix: false,
|
canRemix: false,
|
||||||
canReport: isLoggedIn && !userOwnsProject,
|
canReport: isLoggedIn && !userOwnsProject,
|
||||||
canSave: userOwnsProject,
|
canSave: isLoggedIn && (userOwnsProject || !state.preview.projectInfo.id),
|
||||||
canSaveAsCopy: false,
|
canSaveAsCopy: false,
|
||||||
canShare: userOwnsProject && state.permissions.social,
|
canShare: userOwnsProject && state.permissions.social,
|
||||||
comments: state.preview.comments,
|
comments: state.preview.comments,
|
||||||
|
enableCommunity: state.preview.projectInfo && state.preview.projectInfo.id > 0,
|
||||||
faved: state.preview.faved,
|
faved: state.preview.faved,
|
||||||
fullScreen: state.scratchGui.mode.isFullScreen,
|
fullScreen: state.scratchGui.mode.isFullScreen,
|
||||||
// project is editable iff logged in user is the author of the project, or
|
// project is editable iff logged in user is the author of the project, or
|
||||||
|
@ -538,7 +583,8 @@ const mapStateToProps = state => {
|
||||||
replies: state.preview.replies,
|
replies: state.preview.replies,
|
||||||
sessionStatus: state.session.status, // check if used
|
sessionStatus: state.session.status, // check if used
|
||||||
user: state.session.session.user,
|
user: state.session.session.user,
|
||||||
userOwnsProject: userOwnsProject
|
userOwnsProject: userOwnsProject,
|
||||||
|
userPresent: userPresent
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -613,6 +659,9 @@ const mapDispatchToProps = dispatch => ({
|
||||||
reportProject: (id, formData, token) => {
|
reportProject: (id, formData, token) => {
|
||||||
dispatch(previewActions.reportProject(id, formData, token));
|
dispatch(previewActions.reportProject(id, formData, token));
|
||||||
},
|
},
|
||||||
|
resetProject: () => {
|
||||||
|
dispatch(previewActions.resetProject());
|
||||||
|
},
|
||||||
setOriginalInfo: info => {
|
setOriginalInfo: info => {
|
||||||
dispatch(previewActions.setOriginalInfo(info));
|
dispatch(previewActions.setOriginalInfo(info));
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue