Skip to content

Commit 4ab73a7

Browse files
authored
Merge pull request #3461 from benjiwheeler/remix
can remix, can save as copy; project state names are more consistent
2 parents 44daf84 + 5f19b7e commit 4ab73a7

File tree

8 files changed

+309
-168
lines changed

8 files changed

+309
-168
lines changed

src/components/gui/gui.jsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ const GUIComponent = props => {
6666
canCreateNew,
6767
canRemix,
6868
canSave,
69-
canSaveAsCopy,
69+
canCreateCopy,
7070
canShare,
7171
children,
7272
connectionModalVisible,
@@ -178,10 +178,10 @@ const GUIComponent = props => {
178178
) : null}
179179
<MenuBar
180180
accountNavOpen={accountNavOpen}
181+
canCreateCopy={canCreateCopy}
181182
canCreateNew={canCreateNew}
182183
canRemix={canRemix}
183184
canSave={canSave}
184-
canSaveAsCopy={canSaveAsCopy}
185185
canShare={canShare}
186186
className={styles.menuBarPosition}
187187
enableCommunity={enableCommunity}
@@ -328,10 +328,10 @@ GUIComponent.propTypes = {
328328
}),
329329
basePath: PropTypes.string,
330330
blocksTabVisible: PropTypes.bool,
331+
canCreateCopy: PropTypes.bool,
331332
canCreateNew: PropTypes.bool,
332333
canRemix: PropTypes.bool,
333334
canSave: PropTypes.bool,
334-
canSaveAsCopy: PropTypes.bool,
335335
canShare: PropTypes.bool,
336336
cardsVisible: PropTypes.bool,
337337
children: PropTypes.node,
@@ -373,10 +373,10 @@ GUIComponent.defaultProps = {
373373
visible: false
374374
},
375375
basePath: './',
376-
canCreateNew: true,
376+
canCreateNew: false,
377377
canRemix: false,
378378
canSave: false,
379-
canSaveAsCopy: false,
379+
canCreateCopy: false,
380380
canShare: false,
381381
onUpdateProjectTitle: () => {},
382382
showComingSoon: false,

src/components/menu-bar/menu-bar.jsx

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ import {
2626
getIsUpdating,
2727
getIsShowingProject,
2828
requestNewProject,
29-
saveProject
29+
remixProject,
30+
updateProject,
31+
saveProjectAsCopy
3032
} from '../../reducers/project-state';
3133
import {
3234
openAccountMenu,
@@ -130,7 +132,9 @@ class MenuBar extends React.Component {
130132
super(props);
131133
bindAll(this, [
132134
'handleClickNew',
135+
'handleClickRemix',
133136
'handleClickSave',
137+
'handleClickSaveAsCopy',
134138
'handleCloseFileMenuAndThen',
135139
'handleLanguageMouseUp',
136140
'handleRestoreOption',
@@ -145,16 +149,23 @@ class MenuBar extends React.Component {
145149
}
146150
}
147151
handleClickNew () {
148-
// if canSave===true, it's safe to replace current project, since we will auto-save first
149-
const readyToReplaceProject =
150-
this.props.canSave || confirm('Replace contents of the current project?'); // eslint-disable-line no-alert
152+
// if canCreateNew===true, it's safe to replace current project, since we will auto-save first.
153+
// else confirm first.
154+
const readyToReplaceProject = this.props.canCreateNew ||
155+
confirm('Replace contents of the current project?'); // eslint-disable-line no-alert
151156
if (readyToReplaceProject) {
152-
this.props.onClickNew(this.props.canSave);
157+
this.props.onClickNew(this.props.canCreateNew);
153158
}
154159
}
160+
handleClickRemix () {
161+
this.props.onClickRemix();
162+
}
155163
handleClickSave () {
156164
this.props.onClickSave();
157165
}
166+
handleClickSaveAsCopy () {
167+
this.props.onClickSaveAsCopy();
168+
}
158169
handleRestoreOption (restoreFun) {
159170
return () => {
160171
restoreFun();
@@ -209,13 +220,20 @@ class MenuBar extends React.Component {
209220
id="gui.menuBar.saveNow"
210221
/>
211222
);
212-
const saveAsCopyMessage = (
223+
const createCopyMessage = (
213224
<FormattedMessage
214225
defaultMessage="Save as a copy"
215226
description="Menu bar item for saving as a copy"
216227
id="gui.menuBar.saveAsCopy"
217228
/>
218229
);
230+
const remixMessage = (
231+
<FormattedMessage
232+
defaultMessage="Remix"
233+
description="Menu bar item for remixing"
234+
id="gui.menuBar.remix"
235+
/>
236+
);
219237
const newProjectMessage = (
220238
<FormattedMessage
221239
defaultMessage="New"
@@ -291,21 +309,12 @@ class MenuBar extends React.Component {
291309
place={this.props.isRtl ? 'left' : 'right'}
292310
onRequestClose={this.props.onRequestCloseFile}
293311
>
294-
{this.props.canCreateNew ? (
295-
<MenuItem
296-
isRtl={this.props.isRtl}
297-
onClick={this.handleClickNew}
298-
>
299-
{newProjectMessage}
300-
</MenuItem>
301-
) : (this.props.showComingSoon ? (
302-
<MenuItemTooltip
303-
id="new"
304-
isRtl={this.props.isRtl}
305-
>
306-
<MenuItem>{newProjectMessage}</MenuItem>
307-
</MenuItemTooltip>
308-
) : [])}
312+
<MenuItem
313+
isRtl={this.props.isRtl}
314+
onClick={this.handleClickNew}
315+
>
316+
{newProjectMessage}
317+
</MenuItem>
309318
<MenuSection>
310319
{this.props.canSave ? (
311320
<MenuItem onClick={this.handleClickSave}>
@@ -319,18 +328,23 @@ class MenuBar extends React.Component {
319328
<MenuItem>{saveNowMessage}</MenuItem>
320329
</MenuItemTooltip>
321330
) : [])}
322-
{this.props.canSaveAsCopy ? (
331+
{this.props.canCreateCopy ? (
323332
<MenuItem onClick={this.handleClickSaveAsCopy}>
324-
{saveAsCopyMessage}
333+
{createCopyMessage}
325334
</MenuItem>
326335
) : (this.props.showComingSoon ? (
327336
<MenuItemTooltip
328337
id="copy"
329338
isRtl={this.props.isRtl}
330339
>
331-
<MenuItem>{saveAsCopyMessage}</MenuItem>
340+
<MenuItem>{createCopyMessage}</MenuItem>
332341
</MenuItemTooltip>
333342
) : [])}
343+
{this.props.canRemix ? (
344+
<MenuItem onClick={this.handleClickRemix}>
345+
{remixMessage}
346+
</MenuItem>
347+
) : []}
334348
</MenuSection>
335349
<MenuSection>
336350
<SBFileUploader onUpdateProjectTitle={this.props.onUpdateProjectTitle}>
@@ -627,10 +641,10 @@ class MenuBar extends React.Component {
627641

628642
MenuBar.propTypes = {
629643
accountMenuOpen: PropTypes.bool,
644+
canCreateCopy: PropTypes.bool,
630645
canCreateNew: PropTypes.bool,
631646
canRemix: PropTypes.bool,
632647
canSave: PropTypes.bool,
633-
canSaveAsCopy: PropTypes.bool,
634648
canShare: PropTypes.bool,
635649
className: PropTypes.string,
636650
editMenuOpen: PropTypes.bool,
@@ -648,7 +662,9 @@ MenuBar.propTypes = {
648662
onClickLanguage: PropTypes.func,
649663
onClickLogin: PropTypes.func,
650664
onClickNew: PropTypes.func,
665+
onClickRemix: PropTypes.func,
651666
onClickSave: PropTypes.func,
667+
onClickSaveAsCopy: PropTypes.func,
652668
onLogOut: PropTypes.func,
653669
onOpenRegistration: PropTypes.func,
654670
onOpenTipLibrary: PropTypes.func,
@@ -700,8 +716,10 @@ const mapDispatchToProps = dispatch => ({
700716
onRequestCloseLanguage: () => dispatch(closeLanguageMenu()),
701717
onClickLogin: () => dispatch(openLoginMenu()),
702718
onRequestCloseLogin: () => dispatch(closeLoginMenu()),
703-
onClickNew: canSave => dispatch(requestNewProject(canSave)),
704-
onClickSave: () => dispatch(saveProject()),
719+
onClickNew: canCreateNew => dispatch(requestNewProject(canCreateNew)),
720+
onClickRemix: () => dispatch(remixProject()),
721+
onClickSave: () => dispatch(updateProject()),
722+
onClickSaveAsCopy: () => dispatch(saveProjectAsCopy()),
705723
onSeeCommunity: () => dispatch(setPlayer(true))
706724
});
707725

src/lib/project-fetcher-hoc.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import {
88
LoadingStates,
99
defaultProjectId,
1010
getIsFetchingWithId,
11-
onError,
1211
onFetchedProjectData,
12+
projectError,
1313
setProjectId
1414
} from '../reducers/project-state';
1515

@@ -127,7 +127,7 @@ const ProjectFetcherHOC = function (WrappedComponent) {
127127
reduxProjectId: state.scratchGui.projectState.projectId
128128
});
129129
const mapDispatchToProps = dispatch => ({
130-
onError: error => dispatch(onError(error)),
130+
onError: error => dispatch(projectError(error)),
131131
onFetchedProjectData: (projectData, loadingState) =>
132132
dispatch(onFetchedProjectData(projectData, loadingState)),
133133
setProjectId: projectId => dispatch(setProjectId(projectId))

src/lib/project-saver-hoc.jsx

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ import storage from '../lib/storage';
77
import {
88
LoadingStates,
99
createProject,
10+
doneCreatingProject,
11+
doneUpdatingProject,
1012
getIsCreating,
1113
getIsShowingProject,
1214
getIsShowingWithoutId,
1315
getIsUpdating,
14-
onCreated,
15-
onUpdated,
16-
onError,
17-
saveProject
16+
projectError,
17+
updateProject
1818
} from '../reducers/project-state';
1919

2020
/**
@@ -33,34 +33,39 @@ const ProjectSaverHOC = function (WrappedComponent) {
3333
this.storeProject(this.props.reduxProjectId)
3434
.then(() => {
3535
// there is nothing we expect to find in response that we need to check here
36-
this.props.onUpdated(this.props.loadingState);
36+
this.props.onUpdatedProject(this.props.loadingState);
3737
})
3838
.catch(err => {
3939
// NOTE: should throw up a notice for user
40-
this.props.onError(`Saving the project failed with error: ${err}`);
40+
this.props.onProjectError(`Saving the project failed with error: ${err}`);
4141
});
4242
}
43+
// TODO: distinguish between creating new, remixing, and saving as a copy
4344
if (this.props.isCreating && !prevProps.isCreating) {
4445
this.storeProject()
4546
.then(response => {
46-
this.props.onCreated(response.id.toString());
47+
this.props.onCreatedProject(response.id.toString(), this.props.loadingState);
4748
})
4849
.catch(err => {
4950
// NOTE: should throw up a notice for user
50-
this.props.onError(`Creating a new project failed with error: ${err}`);
51+
this.props.onProjectError(`Creating a new project failed with error: ${err}`);
5152
});
5253
}
53-
// if this is the first time we're able to create this project on the server, create it!
54-
const showingCreateable = this.props.canSave && this.props.isShowingWithoutId;
55-
const prevShowingCreateable = prevProps.canSave && prevProps.isShowingWithoutId;
54+
55+
// check if the project state, and user capabilities, have changed so as to indicate
56+
// that we should create or update project
57+
//
58+
// if we're newly able to create this project on the server, create it!
59+
const showingCreateable = this.props.canCreateNew && this.props.isShowingWithoutId;
60+
const prevShowingCreateable = prevProps.canCreateNew && prevProps.isShowingWithoutId;
5661
if (showingCreateable && !prevShowingCreateable) {
57-
this.props.createProject();
62+
this.props.onCreateProject();
5863
} else {
59-
// if we're newly *able* to save this project, save it!
64+
// if we're newly able to save this project, save it!
6065
const showingSaveable = this.props.canSave && this.props.isShowingWithId;
6166
const becameAbleToSave = this.props.canSave && !prevProps.canSave;
6267
if (showingSaveable && becameAbleToSave) {
63-
this.props.saveProject();
68+
this.props.onUpdateProject();
6469
}
6570
}
6671
}
@@ -89,17 +94,17 @@ const ProjectSaverHOC = function (WrappedComponent) {
8994
render () {
9095
const {
9196
/* eslint-disable no-unused-vars */
92-
createProject: createProjectProp,
9397
isCreating: isCreatingProp,
9498
isShowingWithId: isShowingWithIdProp,
9599
isShowingWithoutId: isShowingWithoutIdProp,
96100
isUpdating: isUpdatingProp,
97101
loadingState,
98-
onCreated: onCreatedProp,
99-
onError: onErrorProp,
100-
onUpdated: onUpdatedProp,
102+
onCreatedProject: onCreatedProjectProp,
103+
onCreateProject: onCreateProjectProp,
104+
onProjectError: onProjectErrorProp,
105+
onUpdatedProject: onUpdatedProjectProp,
106+
onUpdateProject: onUpdateProjectProp,
101107
reduxProjectId,
102-
saveProject: saveProjectProp,
103108
/* eslint-enable no-unused-vars */
104109
...componentProps
105110
} = this.props;
@@ -111,18 +116,19 @@ const ProjectSaverHOC = function (WrappedComponent) {
111116
}
112117
}
113118
ProjectSaverComponent.propTypes = {
119+
canCreateNew: PropTypes.bool,
114120
canSave: PropTypes.bool,
115-
createProject: PropTypes.func,
116121
isCreating: PropTypes.bool,
117122
isShowingWithId: PropTypes.bool,
118123
isShowingWithoutId: PropTypes.bool,
119124
isUpdating: PropTypes.bool,
120125
loadingState: PropTypes.oneOf(LoadingStates),
121-
onCreated: PropTypes.func,
122-
onError: PropTypes.func,
123-
onUpdated: PropTypes.func,
126+
onCreateProject: PropTypes.func,
127+
onCreatedProject: PropTypes.func,
128+
onProjectError: PropTypes.func,
129+
onUpdateProject: PropTypes.func,
130+
onUpdatedProject: PropTypes.func,
124131
reduxProjectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
125-
saveProject: PropTypes.func,
126132
vm: PropTypes.instanceOf(VM).isRequired
127133
};
128134
const mapStateToProps = state => {
@@ -138,11 +144,11 @@ const ProjectSaverHOC = function (WrappedComponent) {
138144
};
139145
};
140146
const mapDispatchToProps = dispatch => ({
141-
createProject: () => dispatch(createProject()),
142-
onCreated: projectId => dispatch(onCreated(projectId)),
143-
onUpdated: (projectId, loadingState) => dispatch(onUpdated(projectId, loadingState)),
144-
onError: error => dispatch(onError(error)),
145-
saveProject: () => dispatch(saveProject())
147+
onCreatedProject: (projectId, loadingState) => dispatch(doneCreatingProject(projectId, loadingState)),
148+
onCreateProject: () => dispatch(createProject()),
149+
onProjectError: error => dispatch(projectError(error)),
150+
onUpdateProject: () => dispatch(updateProject()),
151+
onUpdatedProject: (projectId, loadingState) => dispatch(doneUpdatingProject(projectId, loadingState))
146152
});
147153
// Allow incoming props to override redux-provided props. Used to mock in tests.
148154
const mergeProps = (stateProps, dispatchProps, ownProps) => Object.assign(

src/lib/vm-manager-hoc.jsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import AudioEngine from 'scratch-audio';
88

99
import {
1010
LoadingStates,
11-
onError,
11+
getIsLoadingWithId,
1212
onLoadedProject,
13-
getIsLoadingWithId
13+
projectError
1414
} from '../reducers/project-state';
1515

1616
/*
@@ -98,7 +98,7 @@ const vmManagerHOC = function (WrappedComponent) {
9898
};
9999

100100
const mapDispatchToProps = dispatch => ({
101-
onError: error => dispatch(onError(error)),
101+
onError: error => dispatch(projectError(error)),
102102
onLoadedProject: (loadingState, canSave) =>
103103
dispatch(onLoadedProject(loadingState, canSave))
104104
});

0 commit comments

Comments
 (0)