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) {
|
||||
case 'RESET_TO_INTIAL_STATE':
|
||||
return module.exports.getInitialState();
|
||||
case 'SET_PROJECT_INFO':
|
||||
return Object.assign({}, state, {
|
||||
projectInfo: action.info
|
||||
|
@ -164,6 +166,10 @@ module.exports.setError = error => ({
|
|||
error: error
|
||||
});
|
||||
|
||||
module.exports.resetProject = () => ({
|
||||
type: 'RESET_TO_INTIAL_STATE'
|
||||
});
|
||||
|
||||
module.exports.setProjectInfo = info => ({
|
||||
type: 'SET_PROJECT_INFO',
|
||||
info: info
|
||||
|
|
|
@ -33,6 +33,7 @@ class Preview extends React.Component {
|
|||
super(props);
|
||||
bindAll(this, [
|
||||
'addEventListeners',
|
||||
'fetchCommunityData',
|
||||
'handleAddComment',
|
||||
'handleDeleteComment',
|
||||
'handleToggleStudio',
|
||||
|
@ -49,6 +50,7 @@ class Preview extends React.Component {
|
|||
'handleAddToStudioClose',
|
||||
'handleSeeInside',
|
||||
'handleShare',
|
||||
'handleUpdateProjectId',
|
||||
'handleUpdateProjectTitle',
|
||||
'handleUpdate',
|
||||
'handleToggleComments',
|
||||
|
@ -66,7 +68,7 @@ class Preview extends React.Component {
|
|||
extensions: [],
|
||||
favoriteCount: 0,
|
||||
loveCount: 0,
|
||||
projectId: parts[1] === 'editor' ? 0 : parts[1],
|
||||
projectId: parts[1] === 'editor' ? '0' : parts[1],
|
||||
addToStudioOpen: 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 */
|
||||
this.setScreenFromOrientation();
|
||||
}
|
||||
componentDidUpdate (prevProps) {
|
||||
if (this.props.sessionStatus !== prevProps.sessionStatus &&
|
||||
this.props.sessionStatus === sessionActions.Status.FETCHED &&
|
||||
this.state.projectId) {
|
||||
if (this.props.user) {
|
||||
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);
|
||||
}
|
||||
componentDidUpdate (prevProps, prevState) {
|
||||
if (this.state.projectId > 0 &&
|
||||
((this.props.sessionStatus !== prevProps.sessionStatus &&
|
||||
this.props.sessionStatus === sessionActions.Status.FETCHED) ||
|
||||
(this.state.projectId !== prevState.projectId))) {
|
||||
this.fetchCommunityData();
|
||||
}
|
||||
if (this.state.projectId === '0' && this.state.projectId !== prevState.projectId) {
|
||||
this.props.resetProject();
|
||||
}
|
||||
if (this.props.projectInfo.id !== prevProps.projectInfo.id) {
|
||||
this.getExtensions(this.state.projectId);
|
||||
this.initCounts(this.props.projectInfo.stats.favorites, this.props.projectInfo.stats.loves);
|
||||
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);
|
||||
if (typeof this.props.projectInfo.id === 'undefined') {
|
||||
this.initCounts(0, 0);
|
||||
} else {
|
||||
this.initCounts(this.props.projectInfo.stats.favorites, this.props.projectInfo.stats.loves);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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('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 () {
|
||||
/*
|
||||
* If the user is on a mobile device, switching to
|
||||
|
@ -141,36 +154,42 @@ class Preview extends React.Component {
|
|||
}
|
||||
}
|
||||
getExtensions (projectId) {
|
||||
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);
|
||||
}
|
||||
parser(projectAsset.data, false, (err, projectData) => {
|
||||
if (err) {
|
||||
log.error(`Unhandled project parsing error: ${err}`);
|
||||
return;
|
||||
if (projectId > 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);
|
||||
}
|
||||
const extensionSet = new Set();
|
||||
if (projectData[0].extensions) {
|
||||
projectData[0].extensions.forEach(extension => {
|
||||
extensionSet.add(EXTENSION_INFO[extension]);
|
||||
parser(projectAsset.data, false, (err, projectData) => {
|
||||
if (err) {
|
||||
log.error(`Unhandled project parsing error: ${err}`);
|
||||
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 () {
|
||||
this.props.updateProject(
|
||||
|
@ -313,6 +332,25 @@ class Preview extends React.Component {
|
|||
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) {
|
||||
this.setState({
|
||||
favoriteCount: favorites,
|
||||
|
@ -389,7 +427,6 @@ class Preview extends React.Component {
|
|||
</Page> :
|
||||
<React.Fragment>
|
||||
<IntlGUI
|
||||
enableCommunity
|
||||
hideIntro
|
||||
assetHost={this.props.assetHost}
|
||||
backpackOptions={this.props.backpackOptions}
|
||||
|
@ -400,6 +437,7 @@ class Preview extends React.Component {
|
|||
canSaveAsCopy={this.props.canSaveAsCopy}
|
||||
canShare={this.props.canShare}
|
||||
className="gui"
|
||||
enableCommunity={this.props.enableCommunity}
|
||||
projectHost={this.props.projectHost}
|
||||
projectId={this.state.projectId}
|
||||
projectTitle={this.props.projectInfo.title}
|
||||
|
@ -408,6 +446,7 @@ class Preview extends React.Component {
|
|||
onOpenRegistration={this.props.handleOpenRegistration}
|
||||
onShare={this.handleShare}
|
||||
onToggleLoginOpen={this.props.handleToggleLoginOpen}
|
||||
onUpdateProjectId={this.handleUpdateProjectId}
|
||||
onUpdateProjectTitle={this.handleUpdateProjectTitle}
|
||||
/>
|
||||
<Registration />
|
||||
|
@ -432,6 +471,7 @@ Preview.propTypes = {
|
|||
canSaveAsCopy: PropTypes.bool,
|
||||
canShare: PropTypes.bool,
|
||||
comments: PropTypes.arrayOf(PropTypes.object),
|
||||
enableCommunity: PropTypes.bool,
|
||||
faved: PropTypes.bool,
|
||||
fullScreen: PropTypes.bool,
|
||||
getCuratedStudios: PropTypes.func.isRequired,
|
||||
|
@ -465,6 +505,7 @@ Preview.propTypes = {
|
|||
remixes: PropTypes.arrayOf(PropTypes.object),
|
||||
replies: PropTypes.objectOf(PropTypes.array),
|
||||
reportProject: PropTypes.func,
|
||||
resetProject: PropTypes.func,
|
||||
sessionStatus: PropTypes.string,
|
||||
setFavedStatus: PropTypes.func.isRequired,
|
||||
setFullScreen: PropTypes.func.isRequired,
|
||||
|
@ -482,7 +523,8 @@ Preview.propTypes = {
|
|||
email: PropTypes.string,
|
||||
classroomId: PropTypes.string
|
||||
}),
|
||||
userOwnsProject: PropTypes.bool
|
||||
userOwnsProject: PropTypes.bool,
|
||||
userPresent: PropTypes.bool
|
||||
};
|
||||
|
||||
Preview.defaultProps = {
|
||||
|
@ -493,12 +535,14 @@ Preview.defaultProps = {
|
|||
},
|
||||
projectHost: process.env.PROJECT_HOST,
|
||||
sessionStatus: sessionActions.Status.NOT_FETCHED,
|
||||
user: {}
|
||||
user: {},
|
||||
userPresent: false
|
||||
};
|
||||
|
||||
const mapStateToProps = state => {
|
||||
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;
|
||||
const isLoggedIn = state.session.status === sessionActions.Status.FETCHED &&
|
||||
userPresent;
|
||||
|
@ -510,13 +554,14 @@ const mapStateToProps = state => {
|
|||
|
||||
return {
|
||||
canAddToStudio: isLoggedIn && userOwnsProject,
|
||||
canCreateNew: false,
|
||||
canCreateNew: true,
|
||||
canRemix: false,
|
||||
canReport: isLoggedIn && !userOwnsProject,
|
||||
canSave: userOwnsProject,
|
||||
canSave: isLoggedIn && (userOwnsProject || !state.preview.projectInfo.id),
|
||||
canSaveAsCopy: false,
|
||||
canShare: userOwnsProject && state.permissions.social,
|
||||
comments: state.preview.comments,
|
||||
enableCommunity: state.preview.projectInfo && state.preview.projectInfo.id > 0,
|
||||
faved: state.preview.faved,
|
||||
fullScreen: state.scratchGui.mode.isFullScreen,
|
||||
// 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,
|
||||
sessionStatus: state.session.status, // check if used
|
||||
user: state.session.session.user,
|
||||
userOwnsProject: userOwnsProject
|
||||
userOwnsProject: userOwnsProject,
|
||||
userPresent: userPresent
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -613,6 +659,9 @@ const mapDispatchToProps = dispatch => ({
|
|||
reportProject: (id, formData, token) => {
|
||||
dispatch(previewActions.reportProject(id, formData, token));
|
||||
},
|
||||
resetProject: () => {
|
||||
dispatch(previewActions.resetProject());
|
||||
},
|
||||
setOriginalInfo: info => {
|
||||
dispatch(previewActions.setOriginalInfo(info));
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue