Skip to content

Commit 4fd0019

Browse files
authored
Merge pull request #3436 from benjiwheeler/project-state-error
use project state error for project state related errors
2 parents 9b51399 + 885d0b5 commit 4fd0019

File tree

6 files changed

+41
-33
lines changed

6 files changed

+41
-33
lines changed

src/containers/gui.jsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {defineMessages, injectIntl, intlShape} from 'react-intl';
99
import ErrorBoundaryHOC from '../lib/error-boundary-hoc.jsx';
1010
import {openExtensionLibrary} from '../reducers/modals';
1111
import {
12+
getIsError,
1213
getIsShowingProject
1314
} from '../reducers/project-state';
1415
import {setProjectTitle} from '../reducers/project-title';
@@ -63,16 +64,16 @@ class GUI extends React.Component {
6364
}
6465
}
6566
render () {
66-
if (this.props.loadingError) {
67+
if (this.props.isError) {
6768
throw new Error(
68-
`Failed to load project from server [id=${window.location.hash}]: ${this.props.errorMessage}`);
69+
`Error in Scratch GUI [location=${window.location}]: ${this.props.error}`);
6970
}
7071
const {
7172
/* eslint-disable no-unused-vars */
7273
assetHost,
73-
errorMessage,
74+
error,
7475
hideIntro,
75-
loadingError,
76+
isError,
7677
isShowingProject,
7778
onUpdateProjectId,
7879
onUpdateReduxProjectTitle,
@@ -100,14 +101,14 @@ class GUI extends React.Component {
100101
GUI.propTypes = {
101102
assetHost: PropTypes.string,
102103
children: PropTypes.node,
103-
errorMessage: PropTypes.string,
104+
error: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
104105
fetchingProject: PropTypes.bool,
105106
hideIntro: PropTypes.bool,
106107
importInfoVisible: PropTypes.bool,
107108
intl: intlShape,
109+
isError: PropTypes.bool,
108110
isLoading: PropTypes.bool,
109111
isShowingProject: PropTypes.bool,
110-
loadingError: PropTypes.bool,
111112
loadingStateVisible: PropTypes.bool,
112113
onSeeCommunity: PropTypes.func,
113114
onUpdateProjectId: PropTypes.func,
@@ -135,7 +136,9 @@ const mapStateToProps = (state, ownProps) => {
135136
connectionModalVisible: state.scratchGui.modals.connectionModal,
136137
costumeLibraryVisible: state.scratchGui.modals.costumeLibrary,
137138
costumesTabVisible: state.scratchGui.editorTab.activeTabIndex === COSTUMES_TAB_INDEX,
139+
error: state.scratchGui.projectState.error,
138140
importInfoVisible: state.scratchGui.modals.importInfo,
141+
isError: getIsError(loadingState),
139142
isPlayerOnly: state.scratchGui.mode.isPlayerOnly,
140143
isRtl: state.locales.isRtl,
141144
isShowingProject: getIsShowingProject(loadingState),

src/lib/project-fetcher-hoc.jsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
LoadingStates,
99
defaultProjectId,
1010
getIsFetchingWithId,
11+
onError,
1112
onFetchedProjectData,
1213
setProjectId
1314
} from '../reducers/project-state';
@@ -74,18 +75,20 @@ const ProjectFetcherHOC = function (WrappedComponent) {
7475
}
7576
})
7677
.catch(err => {
78+
this.props.onError(err);
7779
log.error(err);
7880
});
7981
}
8082
render () {
8183
const {
8284
/* eslint-disable no-unused-vars */
8385
assetHost,
84-
onFetchedProjectData: onFetchedProjectDataProp,
8586
intl,
87+
loadingState,
88+
onError: onErrorProp,
89+
onFetchedProjectData: onFetchedProjectDataProp,
8690
projectHost,
8791
projectId,
88-
loadingState,
8992
reduxProjectId,
9093
setProjectId: setProjectIdProp,
9194
/* eslint-enable no-unused-vars */
@@ -106,6 +109,7 @@ const ProjectFetcherHOC = function (WrappedComponent) {
106109
intl: intlShape.isRequired,
107110
isFetchingWithId: PropTypes.bool,
108111
loadingState: PropTypes.oneOf(LoadingStates),
112+
onError: PropTypes.func,
109113
onFetchedProjectData: PropTypes.func,
110114
projectHost: PropTypes.string,
111115
projectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
@@ -123,6 +127,7 @@ const ProjectFetcherHOC = function (WrappedComponent) {
123127
reduxProjectId: state.scratchGui.projectState.projectId
124128
});
125129
const mapDispatchToProps = dispatch => ({
130+
onError: error => dispatch(onError(error)),
126131
onFetchedProjectData: (projectData, loadingState) =>
127132
dispatch(onFetchedProjectData(projectData, loadingState)),
128133
setProjectId: projectId => dispatch(setProjectId(projectId))

src/lib/project-saver-hoc.jsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,14 @@ const ProjectSaverHOC = function (WrappedComponent) {
9090
const {
9191
/* eslint-disable no-unused-vars */
9292
createProject: createProjectProp,
93-
onCreated: onCreatedProp,
94-
onUpdated: onUpdatedProp,
95-
onError: onErrorProp,
9693
isCreating: isCreatingProp,
9794
isShowingWithId: isShowingWithIdProp,
9895
isShowingWithoutId: isShowingWithoutIdProp,
9996
isUpdating: isUpdatingProp,
10097
loadingState,
98+
onCreated: onCreatedProp,
99+
onError: onErrorProp,
100+
onUpdated: onUpdatedProp,
101101
reduxProjectId,
102102
saveProject: saveProjectProp,
103103
/* eslint-enable no-unused-vars */
@@ -141,7 +141,7 @@ const ProjectSaverHOC = function (WrappedComponent) {
141141
createProject: () => dispatch(createProject()),
142142
onCreated: projectId => dispatch(onCreated(projectId)),
143143
onUpdated: (projectId, loadingState) => dispatch(onUpdated(projectId, loadingState)),
144-
onError: errStr => dispatch(onError(errStr)),
144+
onError: error => dispatch(onError(error)),
145145
saveProject: () => dispatch(saveProject())
146146
});
147147
// Allow incoming props to override redux-provided props. Used to mock in tests.

src/lib/vm-manager-hoc.jsx

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

99
import {
1010
LoadingStates,
11+
onError,
1112
onLoadedProject,
1213
getIsLoadingWithId
1314
} from '../reducers/project-state';
@@ -24,10 +25,6 @@ const vmManagerHOC = function (WrappedComponent) {
2425
bindAll(this, [
2526
'loadProject'
2627
]);
27-
this.state = {
28-
loadingError: false,
29-
errorMessage: ''
30-
};
3128
}
3229
componentDidMount () {
3330
if (this.props.vm.initialized) return;
@@ -51,29 +48,26 @@ const vmManagerHOC = function (WrappedComponent) {
5148
this.props.onLoadedProject(this.props.loadingState, this.props.canSave);
5249
})
5350
.catch(e => {
54-
// Need to catch this error and update component state so that
55-
// error page gets rendered if project failed to load
56-
this.setState({loadingError: true, errorMessage: e});
51+
this.props.onError(e);
5752
});
5853
}
5954
render () {
6055
const {
6156
/* eslint-disable no-unused-vars */
6257
fontsLoaded,
58+
loadingState,
59+
onError: onErrorProp,
6360
onLoadedProject: onLoadedProjectProp,
6461
projectData,
6562
projectId,
66-
loadingState,
6763
/* eslint-enable no-unused-vars */
6864
isLoadingWithId: isLoadingWithIdProp,
6965
vm,
7066
...componentProps
7167
} = this.props;
7268
return (
7369
<WrappedComponent
74-
errorMessage={this.state.errorMessage}
7570
isLoading={isLoadingWithIdProp}
76-
loadingError={this.state.loadingError}
7771
vm={vm}
7872
{...componentProps}
7973
/>
@@ -86,6 +80,7 @@ const vmManagerHOC = function (WrappedComponent) {
8680
fontsLoaded: PropTypes.bool,
8781
isLoadingWithId: PropTypes.bool,
8882
loadingState: PropTypes.oneOf(LoadingStates),
83+
onError: PropTypes.func,
8984
onLoadedProject: PropTypes.func,
9085
projectData: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
9186
projectId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
@@ -103,6 +98,7 @@ const vmManagerHOC = function (WrappedComponent) {
10398
};
10499

105100
const mapDispatchToProps = dispatch => ({
101+
onError: error => dispatch(onError(error)),
106102
onLoadedProject: (loadingState, canSave) =>
107103
dispatch(onLoadedProject(loadingState, canSave))
108104
});

src/reducers/project-state.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,12 @@ const getIsShowingWithId = loadingState => (
6565
const getIsShowingWithoutId = loadingState => (
6666
loadingState === LoadingState.SHOWING_WITHOUT_ID
6767
);
68+
const getIsError = loadingState => (
69+
loadingState === LoadingState.ERROR
70+
);
6871

6972
const initialState = {
70-
errStr: null,
73+
error: null,
7174
projectData: null,
7275
projectId: null,
7376
loadingState: LoadingState.NOT_LOADED
@@ -221,7 +224,7 @@ const reducer = function (state, action) {
221224
].includes(state.loadingState)) {
222225
return Object.assign({}, state, {
223226
loadingState: LoadingState.ERROR,
224-
errStr: action.errStr
227+
error: action.error
225228
});
226229
}
227230
return state;
@@ -295,9 +298,9 @@ const onUpdated = loadingState => {
295298
}
296299
};
297300

298-
const onError = errStr => ({
301+
const onError = error => ({
299302
type: GO_TO_ERROR_STATE,
300-
errStr: errStr
303+
error: error
301304
});
302305

303306
const setProjectId = id => ({
@@ -326,6 +329,7 @@ export {
326329
createProject,
327330
defaultProjectId,
328331
getIsCreating,
332+
getIsError,
329333
getIsFetchingWithoutId,
330334
getIsFetchingWithId,
331335
getIsLoadingWithId,

test/unit/reducers/project-state-reducer.test.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ test('initialState', () => {
1717
let defaultState;
1818
/* projectStateReducer(state, action) */
1919
expect(projectStateReducer(defaultState, {type: 'anything'})).toBeDefined();
20-
expect(projectStateReducer(defaultState, {type: 'anything'}).errStr).toBe(null);
20+
expect(projectStateReducer(defaultState, {type: 'anything'}).error).toBe(null);
2121
expect(projectStateReducer(defaultState, {type: 'anything'}).projectData).toBe(null);
2222
expect(projectStateReducer(defaultState, {type: 'anything'}).projectId).toBe(null);
2323
expect(projectStateReducer(defaultState, {type: 'anything'}).loadingState).toBe(LoadingState.NOT_LOADED);
@@ -229,23 +229,23 @@ test('onError from various states should show error', () => {
229229
];
230230
for (const startState of startStates) {
231231
const initialState = {
232-
errStr: null,
232+
error: null,
233233
loadingState: startState
234234
};
235-
const action = onError('Error string');
235+
const action = onError({message: 'Error string'});
236236
const resultState = projectStateReducer(initialState, action);
237237
expect(resultState.loadingState).toBe(LoadingState.ERROR);
238-
expect(resultState.errStr).toBe('Error string');
238+
expect(resultState.error).toEqual({message: 'Error string'});
239239
}
240240
});
241241

242242
test('onError from showing project should show error', () => {
243243
const initialState = {
244-
errStr: null,
244+
error: null,
245245
loadingState: LoadingState.FETCHING_WITH_ID
246246
};
247-
const action = onError('Error string');
247+
const action = onError({message: 'Error string'});
248248
const resultState = projectStateReducer(initialState, action);
249249
expect(resultState.loadingState).toBe(LoadingState.ERROR);
250-
expect(resultState.errStr).toBe('Error string');
250+
expect(resultState.error).toEqual({message: 'Error string'});
251251
});

0 commit comments

Comments
 (0)