Skip to content

Commit 675d19b

Browse files
committed
Merge branch 'develop' of https://github.com/LLK/scratch-gui into develop
2 parents 7dab716 + 54c8588 commit 675d19b

23 files changed

+5478
-4293
lines changed

package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,13 @@
107107
"redux-throttle": "0.1.1",
108108
"rimraf": "^2.6.1",
109109
"scratch-audio": "0.1.0-prerelease.20181023202904",
110-
"scratch-blocks": "0.1.0-prerelease.1544731479",
110+
"scratch-blocks": "0.1.0-prerelease.1545162154",
111111
"scratch-l10n": "3.1.20181213173343",
112-
"scratch-paint": "0.2.0-prerelease.20181214155149",
113-
"scratch-render": "0.1.0-prerelease.20181213205951",
114-
"scratch-storage": "1.2.0",
115-
"scratch-svg-renderer": "0.2.0-prerelease.20181213192400",
116-
"scratch-vm": "0.2.0-prerelease.20181213211546",
112+
"scratch-paint": "0.2.0-prerelease.20181218161344",
113+
"scratch-render": "0.1.0-prerelease.20181218160410",
114+
"scratch-storage": "1.2.1",
115+
"scratch-svg-renderer": "0.2.0-prerelease.20181218153528",
116+
"scratch-vm": "0.2.0-prerelease.20181219200003",
117117
"selenium-webdriver": "3.6.0",
118118
"startaudiocontext": "1.2.1",
119119
"style-loader": "^0.23.0",

src/components/crash-message/crash-message.jsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,18 @@ const CrashMessage = props => (
2929
id="gui.smalruby3.crashMessage.description"
3030
/>
3131
</p>
32+
{props.eventId && (
33+
<p>
34+
<FormattedMessage
35+
defaultMessage="Your error was logged with id {errorId}"
36+
description="Message to inform the user that page has crashed."
37+
id="gui.crashMessage.errorNumber"
38+
values={{
39+
errorId: props.eventId
40+
}}
41+
/>
42+
</p>
43+
)}
3244
<button
3345
className={styles.reloadButton}
3446
onClick={props.onReload}
@@ -44,6 +56,7 @@ const CrashMessage = props => (
4456
);
4557

4658
CrashMessage.propTypes = {
59+
eventId: PropTypes.string,
4760
onReload: PropTypes.func.isRequired
4861
};
4962

src/containers/costume-library.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class CostumeLibrary extends React.PureComponent {
3737
bitmapResolution: item.info.length > 2 ? item.info[2] : 1,
3838
skinId: null
3939
};
40-
this.props.vm.addCostume(item.md5, vmCostume);
40+
this.props.vm.addCostumeFromLibrary(item.md5, vmCostume);
4141
analytics.event({
4242
category: 'library',
4343
action: 'Select Costume',

src/containers/costume-tab.jsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,12 @@ class CostumeTab extends React.Component {
150150
handleDuplicateCostume (costumeIndex) {
151151
this.props.vm.duplicateCostume(costumeIndex);
152152
}
153-
handleNewCostume (costume) {
154-
this.props.vm.addCostume(costume.md5, costume);
153+
handleNewCostume (costume, fromCostumeLibrary) {
154+
if (fromCostumeLibrary) {
155+
this.props.vm.addCostumeFromLibrary(costume.md5, costume);
156+
} else {
157+
this.props.vm.addCostume(costume.md5, costume);
158+
}
155159
}
156160
handleNewBlankCostume () {
157161
const name = this.props.vm.editingTarget.isStage ?
@@ -173,7 +177,7 @@ class CostumeTab extends React.Component {
173177
bitmapResolution: item.info.length > 2 ? item.info[2] : 1,
174178
skinId: null
175179
};
176-
this.handleNewCostume(vmCostume);
180+
this.handleNewCostume(vmCostume, true /* fromCostumeLibrary */);
177181
}
178182
handleSurpriseBackdrop () {
179183
const item = backdropLibraryContent[Math.floor(Math.random() * backdropLibraryContent.length)];

src/containers/error-boundary.jsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ class ErrorBoundary extends React.Component {
1212
constructor (props) {
1313
super(props);
1414
this.state = {
15-
hasError: false
15+
hasError: false,
16+
errorId: null
1617
};
1718
}
1819

@@ -24,7 +25,10 @@ class ErrorBoundary extends React.Component {
2425
};
2526

2627
// Display fallback UI
27-
this.setState({hasError: true});
28+
this.setState({
29+
hasError: true,
30+
errorId: window.Raven ? window.Raven.lastEventId() : null
31+
});
2832

2933
// Log errors to analytics, separating supported browsers from unsupported.
3034
if (supportedBrowser()) {
@@ -33,6 +37,9 @@ class ErrorBoundary extends React.Component {
3337
action: this.props.action,
3438
label: error.message
3539
});
40+
if (window.Raven) {
41+
window.Raven.captureException(error, {extra: info});
42+
}
3643
} else {
3744
analytics.event({
3845
category: 'Unsupported Browser Error',
@@ -56,7 +63,12 @@ class ErrorBoundary extends React.Component {
5663
render () {
5764
if (this.state.hasError) {
5865
if (supportedBrowser()) {
59-
return <CrashMessageComponent onReload={this.handleReload} />;
66+
return (
67+
<CrashMessageComponent
68+
eventId={this.state.errorId}
69+
onReload={this.handleReload}
70+
/>
71+
);
6072
}
6173
return (<BrowserModalComponent
6274
isRtl={this.props.isRtl}

src/containers/gui.jsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import vmManagerHOC from '../lib/vm-manager-hoc.jsx';
3737
import cloudManagerHOC from '../lib/cloud-manager-hoc.jsx';
3838

3939
import GUIComponent from '../components/gui/gui.jsx';
40+
import {setIsScratchDesktop} from '../lib/isScratchDesktop.js';
4041

4142
const messages = defineMessages({
4243
defaultProjectTitle: {
@@ -48,6 +49,7 @@ const messages = defineMessages({
4849

4950
class GUI extends React.Component {
5051
componentDidMount () {
52+
setIsScratchDesktop(this.props.isScratchDesktop);
5153
this.setReduxTitle(this.props.projectTitle);
5254
this.props.onStorageInit(storage);
5355
}
@@ -79,6 +81,7 @@ class GUI extends React.Component {
7981
cloudHost,
8082
error,
8183
isError,
84+
isScratchDesktop,
8285
isShowingProject,
8386
onStorageInit,
8487
onUpdateProjectId,
@@ -114,6 +117,7 @@ GUI.propTypes = {
114117
intl: intlShape,
115118
isError: PropTypes.bool,
116119
isLoading: PropTypes.bool,
120+
isScratchDesktop: PropTypes.bool,
117121
isShowingProject: PropTypes.bool,
118122
loadingStateVisible: PropTypes.bool,
119123
onSeeCommunity: PropTypes.func,
@@ -128,6 +132,7 @@ GUI.propTypes = {
128132
};
129133

130134
GUI.defaultProps = {
135+
isScratchDesktop: false,
131136
onStorageInit: storageInstance => storageInstance.addOfficialScratchWebStores(),
132137
onUpdateProjectId: () => {}
133138
};

src/containers/sb-file-uploader.jsx

Lines changed: 77 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@ import {defineMessages, injectIntl, intlShape} from 'react-intl';
66

77
import analytics from '../lib/analytics';
88
import log from '../lib/log';
9-
import {LoadingStates, onLoadedProject, onProjectUploadStarted} from '../reducers/project-state';
9+
import {
10+
LoadingStates,
11+
getIsLoadingUpload,
12+
onLoadedProject,
13+
requestProjectUpload
14+
} from '../reducers/project-state';
1015

1116
import {
1217
openLoadingProject,
@@ -48,9 +53,31 @@ class SBFileUploader extends React.Component {
4853
'renderFileInput',
4954
'setFileInput',
5055
'handleChange',
51-
'handleClick'
56+
'handleClick',
57+
'onload',
58+
'resetFileInput'
5259
]);
5360
}
61+
componentWillMount () {
62+
this.reader = new FileReader();
63+
this.reader.onload = this.onload;
64+
this.resetFileInput();
65+
}
66+
componentDidUpdate (prevProps) {
67+
if (this.props.isLoadingUpload && !prevProps.isLoadingUpload && this.fileToUpload && this.reader) {
68+
this.reader.readAsArrayBuffer(this.fileToUpload);
69+
}
70+
}
71+
componentWillUnmount () {
72+
this.reader = null;
73+
this.resetFileInput();
74+
}
75+
resetFileInput () {
76+
this.fileToUpload = null;
77+
if (this.fileInput) {
78+
this.fileInput.value = null;
79+
}
80+
}
5481
getProjectTitleFromFilename (fileInputFilename) {
5582
if (!fileInputFilename) return '';
5683
// only parse title from files like "filename.sb2" or "filename.sb3"
@@ -60,35 +87,43 @@ class SBFileUploader extends React.Component {
6087
}
6188
// called when user has finished selecting a file to upload
6289
handleChange (e) {
63-
// Remove the hash if any (without triggering a hash change event or a reload)
64-
history.replaceState({}, document.title, '.');
65-
const reader = new FileReader();
6690
const thisFileInput = e.target;
67-
reader.onload = () => this.props.vm.loadProject(reader.result)
68-
.then(() => {
69-
analytics.event({
70-
category: 'project',
71-
action: 'Import Project File',
72-
nonInteraction: true
73-
});
74-
this.props.onLoadingFinished(this.props.loadingState);
75-
// Reset the file input after project is loaded
76-
// This is necessary in case the user wants to reload a project
77-
thisFileInput.value = null;
78-
})
79-
.catch(error => {
80-
log.warn(error);
81-
alert(this.props.intl.formatMessage(messages.loadError)); // eslint-disable-line no-alert
82-
this.props.onLoadingFinished(this.props.loadingState);
83-
// Reset the file input after project is loaded
84-
// This is necessary in case the user wants to reload a project
85-
thisFileInput.value = null;
86-
});
8791
if (thisFileInput.files) { // Don't attempt to load if no file was selected
92+
this.fileToUpload = thisFileInput.files[0];
93+
this.props.requestProjectUpload(this.props.loadingState);
94+
}
95+
}
96+
// called when file upload raw data is available in the reader
97+
onload () {
98+
if (this.reader) {
8899
this.props.onLoadingStarted();
89-
reader.readAsArrayBuffer(thisFileInput.files[0]);
90-
const uploadedProjectTitle = this.getProjectTitleFromFilename(thisFileInput.files[0].name);
91-
this.props.onUpdateProjectTitle(uploadedProjectTitle);
100+
const filename = this.fileToUpload && this.fileToUpload.name;
101+
this.props.vm.loadProject(this.reader.result)
102+
.then(() => {
103+
analytics.event({
104+
category: 'project',
105+
action: 'Import Project File',
106+
nonInteraction: true
107+
});
108+
// Remove the hash if any (without triggering a hash change event or a reload)
109+
history.replaceState({}, document.title, '.');
110+
this.props.onLoadingFinished(this.props.loadingState, true);
111+
// Reset the file input after project is loaded
112+
// This is necessary in case the user wants to reload a project
113+
if (filename) {
114+
const uploadedProjectTitle = this.getProjectTitleFromFilename(filename);
115+
this.props.onUpdateProjectTitle(uploadedProjectTitle);
116+
}
117+
this.resetFileInput();
118+
})
119+
.catch(error => {
120+
log.warn(error);
121+
alert(this.props.intl.formatMessage(messages.loadError)); // eslint-disable-line no-alert
122+
this.props.onLoadingFinished(this.props.loadingState, false);
123+
// Reset the file input after project is loaded
124+
// This is necessary in case the user wants to reload a project
125+
this.resetFileInput();
126+
});
92127
}
93128
}
94129
handleClick () {
@@ -119,32 +154,36 @@ SBFileUploader.propTypes = {
119154
children: PropTypes.func,
120155
className: PropTypes.string,
121156
intl: intlShape.isRequired,
157+
isLoadingUpload: PropTypes.bool,
122158
loadingState: PropTypes.oneOf(LoadingStates),
123159
onLoadingFinished: PropTypes.func,
124160
onLoadingStarted: PropTypes.func,
125161
onUpdateProjectTitle: PropTypes.func,
162+
requestProjectUpload: PropTypes.func,
126163
vm: PropTypes.shape({
127164
loadProject: PropTypes.func
128165
})
129166
};
130167
SBFileUploader.defaultProps = {
131168
className: ''
132169
};
133-
const mapStateToProps = state => ({
134-
loadingState: state.scratchGui.projectState.loadingState,
135-
vm: state.scratchGui.vm
136-
});
170+
const mapStateToProps = state => {
171+
const loadingState = state.scratchGui.projectState.loadingState;
172+
return {
173+
isLoadingUpload: getIsLoadingUpload(loadingState),
174+
loadingState: loadingState,
175+
vm: state.scratchGui.vm
176+
};
177+
};
137178

138179
const mapDispatchToProps = (dispatch, ownProps) => ({
139-
onLoadingFinished: loadingState => {
140-
dispatch(onLoadedProject(loadingState, ownProps.canSave));
180+
onLoadingFinished: (loadingState, success) => {
181+
dispatch(onLoadedProject(loadingState, ownProps.canSave, success));
141182
dispatch(closeLoadingProject());
142183
dispatch(closeFileMenu());
143184
},
144-
onLoadingStarted: () => {
145-
dispatch(openLoadingProject());
146-
dispatch(onProjectUploadStarted());
147-
}
185+
requestProjectUpload: loadingState => dispatch(requestProjectUpload(loadingState)),
186+
onLoadingStarted: () => dispatch(openLoadingProject())
148187
});
149188

150189
// Allow incoming props to override redux-provided props. Used to mock in tests.

src/containers/stage.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ class Stage extends React.Component {
236236
this.updateRect();
237237
const {x, y} = getEventXY(e);
238238
const mousePosition = [x - this.rect.left, y - this.rect.top];
239-
if (e.button === 0 || e instanceof TouchEvent) {
239+
if (e.button === 0 || (window.TouchEvent && e instanceof TouchEvent)) {
240240
this.setState({
241241
mouseDown: true,
242242
mouseDownPosition: mousePosition,

0 commit comments

Comments
 (0)