From 23b7ebd7761bbe0400ff4893d204b4b0f2231619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Mon, 19 Oct 2015 15:29:57 +0200 Subject: [PATCH 01/11] add 404 routine to EditionContainer --- js/actions/edition_actions.js | 4 +- .../ascribe_detail/edition_container.js | 50 ++++++++++++------- js/stores/edition_store.js | 6 +++ js/utils/requests.js | 9 ++++ 4 files changed, 50 insertions(+), 19 deletions(-) diff --git a/js/actions/edition_actions.js b/js/actions/edition_actions.js index 4bdf093a..3f659524 100644 --- a/js/actions/edition_actions.js +++ b/js/actions/edition_actions.js @@ -7,7 +7,8 @@ import EditionFetcher from '../fetchers/edition_fetcher'; class EditionActions { constructor() { this.generateActions( - 'updateEdition' + 'updateEdition', + 'editionFailed' ); } @@ -18,6 +19,7 @@ class EditionActions { }) .catch((err) => { console.logGlobal(err); + this.actions.editionFailed(err.json); }); } } diff --git a/js/components/ascribe_detail/edition_container.js b/js/components/ascribe_detail/edition_container.js index fa24bcbf..030a7150 100644 --- a/js/components/ascribe_detail/edition_container.js +++ b/js/components/ascribe_detail/edition_container.js @@ -1,6 +1,7 @@ 'use strict'; import React from 'react'; +import { History } from 'react-router'; import EditionActions from '../../actions/edition_actions'; import EditionStore from '../../stores/edition_store'; @@ -15,26 +16,23 @@ import AppConstants from '../../constants/application_constants'; */ let EditionContainer = React.createClass({ propTypes: { - location: React.PropTypes.object + location: React.PropTypes.object, + params: React.PropTypes.object }, + mixins: [History], + getInitialState() { return EditionStore.getState(); }, - onChange(state) { - this.setState(state); - if (!state.edition.digital_work) { - return; - } - let isEncoding = state.edition.digital_work.isEncoding; - if (state.edition.digital_work.mime === 'video' && typeof isEncoding === 'number' && isEncoding !== 100 && !this.state.timerId) { - let timerId = window.setInterval(() => EditionActions.fetchOne(this.props.params.editionId), 10000); - this.setState({timerId: timerId}); - } - }, - componentDidMount() { + // Every time we're entering the edition detail page, + // just reset the edition that is saved in the edition store + // as it will otherwise display wrong/old data once the user loads + // the edition detail a second time + EditionActions.updateEdition({}); + EditionStore.listen(this.onChange); EditionActions.fetchOne(this.props.params.editionId); }, @@ -48,16 +46,32 @@ let EditionContainer = React.createClass({ } }, + componentDidUpdate() { + const { editionError } = this.state; + + if(editionError && editionError.status === 404) { + // Even though the /404 path doesn't really exist, + // we can still redirect there and the page will show up + this.history.pushState(null, '/404'); + } + }, + componentWillUnmount() { - // Every time we're leaving the edition detail page, - // just reset the edition that is saved in the edition store - // as it will otherwise display wrong/old data once the user loads - // the edition detail a second time - EditionActions.updateEdition({}); window.clearInterval(this.state.timerId); EditionStore.unlisten(this.onChange); }, + onChange(state) { + this.setState(state); + if (!state.edition.digital_work) { + return; + } + let isEncoding = state.edition.digital_work.isEncoding; + if (state.edition.digital_work.mime === 'video' && typeof isEncoding === 'number' && isEncoding !== 100 && !this.state.timerId) { + let timerId = window.setInterval(() => EditionActions.fetchOne(this.props.params.editionId), 10000); + this.setState({timerId: timerId}); + } + }, loadEdition() { EditionActions.fetchOne(this.props.params.editionId); diff --git a/js/stores/edition_store.js b/js/stores/edition_store.js index 14ee4fee..22e78d23 100644 --- a/js/stores/edition_store.js +++ b/js/stores/edition_store.js @@ -7,11 +7,17 @@ import EditionActions from '../actions/edition_actions'; class EditionStore { constructor() { this.edition = {}; + this.editionError = null; this.bindActions(EditionActions); } onUpdateEdition(edition) { this.edition = edition; + this.editionError = null; + } + + onEditionFailed(error) { + this.editionError = error; } } diff --git a/js/utils/requests.js b/js/utils/requests.js index 7e9c9a58..eeaa3513 100644 --- a/js/utils/requests.js +++ b/js/utils/requests.js @@ -40,6 +40,15 @@ class Requests { reject(error); } else if(body && body.detail) { reject(new Error(body.detail)); + } else if(!body.success) { + let error = new Error('Client Request Error'); + error.json = { + status: response.status, + statusText: response.status, + type: response.status, + url: response.url + }; + reject(error); } else { resolve(body); } From 757ee40e4eb874a300f9e442cb50b47f1bf6b515 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Mon, 19 Oct 2015 15:58:05 +0200 Subject: [PATCH 02/11] add 404 routine to PieceContainer --- js/actions/piece_actions.js | 4 ++- .../ascribe_detail/piece_container.js | 33 +++++++++++++------ js/stores/piece_store.js | 6 ++++ 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/js/actions/piece_actions.js b/js/actions/piece_actions.js index 7aed13fc..64dcb2e2 100644 --- a/js/actions/piece_actions.js +++ b/js/actions/piece_actions.js @@ -8,7 +8,8 @@ class PieceActions { constructor() { this.generateActions( 'updatePiece', - 'updateProperty' + 'updateProperty', + 'pieceFailed' ); } @@ -18,6 +19,7 @@ class PieceActions { this.actions.updatePiece(res.piece); }) .catch((err) => { + this.actions.pieceFailed(err.json); console.logGlobal(err); }); } diff --git a/js/components/ascribe_detail/piece_container.js b/js/components/ascribe_detail/piece_container.js index e2ebaa43..175008b4 100644 --- a/js/components/ascribe_detail/piece_container.js +++ b/js/components/ascribe_detail/piece_container.js @@ -44,7 +44,8 @@ import { getLangText } from '../../utils/lang_utils'; */ let PieceContainer = React.createClass({ propTypes: { - location: React.PropTypes.object + location: React.PropTypes.object, + params: React.PropTypes.object }, mixins: [History], @@ -61,19 +62,31 @@ let PieceContainer = React.createClass({ }, componentDidMount() { - UserStore.listen(this.onChange); - PieceListStore.listen(this.onChange); - UserActions.fetchCurrentUser(); - PieceStore.listen(this.onChange); - PieceActions.fetchOne(this.props.params.pieceId); - }, - - componentWillUnmount() { - // Every time we're leaving the piece detail page, + // Every time we're entering the piece detail page, // just reset the piece that is saved in the piece store // as it will otherwise display wrong/old data once the user loads // the piece detail a second time PieceActions.updatePiece({}); + + UserStore.listen(this.onChange); + PieceListStore.listen(this.onChange); + PieceStore.listen(this.onChange); + + UserActions.fetchCurrentUser(); + PieceActions.fetchOne(this.props.params.pieceId); + }, + + componentDidUpdate() { + const { pieceError } = this.state; + + if(pieceError && pieceError.status === 404) { + // Even though this path doesn't exist we can redirect + // to it as it catches all unknown paths + this.history.pushState(null, '/404'); + } + }, + + componentWillUnmount() { PieceStore.unlisten(this.onChange); UserStore.unlisten(this.onChange); PieceListStore.unlisten(this.onChange); diff --git a/js/stores/piece_store.js b/js/stores/piece_store.js index ccef50b1..3b04736b 100644 --- a/js/stores/piece_store.js +++ b/js/stores/piece_store.js @@ -7,11 +7,13 @@ import PieceActions from '../actions/piece_actions'; class PieceStore { constructor() { this.piece = {}; + this.pieceError = null; this.bindActions(PieceActions); } onUpdatePiece(piece) { this.piece = piece; + this.pieceError = null; } onUpdateProperty({key, value}) { @@ -21,6 +23,10 @@ class PieceStore { throw new Error('There is no piece defined in PieceStore or the piece object does not have the property you\'re looking for.'); } } + + onPieceFailed(err) { + this.pieceError = err; + } } export default alt.createStore(PieceStore, 'PieceStore'); From f82278070b2d19c9f154d0a701116cd35282941f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Mon, 19 Oct 2015 16:01:05 +0200 Subject: [PATCH 03/11] Log before dispatch in PieceActions --- js/actions/piece_actions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/actions/piece_actions.js b/js/actions/piece_actions.js index 64dcb2e2..9002e8c5 100644 --- a/js/actions/piece_actions.js +++ b/js/actions/piece_actions.js @@ -19,8 +19,8 @@ class PieceActions { this.actions.updatePiece(res.piece); }) .catch((err) => { - this.actions.pieceFailed(err.json); console.logGlobal(err); + this.actions.pieceFailed(err.json); }); } } From 74e07d5ce597567dfd341eb73db6cb4b383af062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Tue, 20 Oct 2015 11:33:04 +0200 Subject: [PATCH 04/11] Fix stupid mistake --- js/utils/requests.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/utils/requests.js b/js/utils/requests.js index eeaa3513..04bde25c 100644 --- a/js/utils/requests.js +++ b/js/utils/requests.js @@ -44,8 +44,8 @@ class Requests { let error = new Error('Client Request Error'); error.json = { status: response.status, - statusText: response.status, - type: response.status, + statusText: response.statusText, + type: response.type, url: response.url }; reject(error); From 68f41f755040411964a5b0eb98076775767ef138 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Mon, 30 Nov 2015 15:01:07 +0100 Subject: [PATCH 05/11] Implement boilerplate logic for ReactError --- js/app.js | 22 ++++++++++++++------ js/react_error.js | 42 ++++++++++++++++++++++++++++++++++++++ js/react_error_handlers.js | 5 +++++ 3 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 js/react_error.js create mode 100644 js/react_error_handlers.js diff --git a/js/app.js b/js/app.js index 520bedbd..9e8e3a91 100644 --- a/js/app.js +++ b/js/app.js @@ -5,6 +5,8 @@ require('babel/polyfill'); import React from 'react'; import { Router, Redirect } from 'react-router'; import history from './history'; +import { ReactError, ResourceNotFoundError } from './react_error.js'; +import { ResourceNotFoundErrorHandler } from './react_error_handlers'; /* eslint-disable */ import fetch from 'isomorphic-fetch'; @@ -91,12 +93,20 @@ class AppGateway { // us in that case. history.listen(EventActions.routeDidChange); - React.render(( - - {redirectRoute} - {getRoutes(type, subdomain)} - - ), document.getElementById('main')); + ReactError({ + render: React.render, + params: [( + + {redirectRoute} + {getRoutes(type, subdomain)} + + ), + document.getElementById('main')], + errors: [{ + error: ResourceNotFoundError, + handler: ResourceNotFoundErrorHandler + }] + }); // Send the applicationDidBoot event to the third-party stores EventActions.applicationDidBoot(settings); diff --git a/js/react_error.js b/js/react_error.js new file mode 100644 index 00000000..61fb4a70 --- /dev/null +++ b/js/react_error.js @@ -0,0 +1,42 @@ +'use strict'; + +function _transformErrorsListToHashMap(errors) { + return errors.reduce((errObj, { error, handler }) => { + const errorName = error.name; + if(!errObj[errorName]) { + errObj[errorName] = { + error: error, + handler: handler + }; + return errObj; + } else { + throw new Error('You\'re trying to override the error handler for ' + errorName + ' by specifying it twice.'); + } + }, {}); +} + +export function ReactError({ render, params, errors }) { + errors = _transformErrorsListToHashMap(errors); + try { + // use react's render function + parameters to render + // the application + render(...params); + } catch(err) { + const potErrorRoutine = errors[err.name]; + if(potErrorRoutine) { + potErrorRoutine.handler(err); + } else { + console.error(err.stack); + } + } +} + +// Followed: http://stackoverflow.com/a/32749533/1263876 for extending errors +export class ResourceNotFoundError extends Error { + constructor(message) { + super(message); + this.name = this.constructor.name; + this.message = message; + Error.captureStackTrace(this, this.constructor.name); + } +} \ No newline at end of file diff --git a/js/react_error_handlers.js b/js/react_error_handlers.js new file mode 100644 index 00000000..57366603 --- /dev/null +++ b/js/react_error_handlers.js @@ -0,0 +1,5 @@ +'use strict'; + +export function ResourceNotFoundErrorHandler() { + console.log(arguments); +} \ No newline at end of file From 0b2bc1c57b48e5817a4b1a4728112ec61d5ec771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Mon, 30 Nov 2015 18:23:03 +0100 Subject: [PATCH 06/11] Introduce ReactError mixin for handling errors in React components --- js/app.js | 22 +++------- .../ascribe_detail/edition_container.js | 17 +++----- .../ascribe_detail/piece_container.js | 9 ++-- js/components/error_not_found_page.js | 12 +++++- js/mixins/react_error.js | 12 ++++++ js/models/errors.js | 24 +++++++++++ js/react_error.js | 42 ------------------- js/react_error_handlers.js | 5 --- 8 files changed, 64 insertions(+), 79 deletions(-) create mode 100644 js/mixins/react_error.js create mode 100644 js/models/errors.js delete mode 100644 js/react_error.js delete mode 100644 js/react_error_handlers.js diff --git a/js/app.js b/js/app.js index 9e8e3a91..520bedbd 100644 --- a/js/app.js +++ b/js/app.js @@ -5,8 +5,6 @@ require('babel/polyfill'); import React from 'react'; import { Router, Redirect } from 'react-router'; import history from './history'; -import { ReactError, ResourceNotFoundError } from './react_error.js'; -import { ResourceNotFoundErrorHandler } from './react_error_handlers'; /* eslint-disable */ import fetch from 'isomorphic-fetch'; @@ -93,20 +91,12 @@ class AppGateway { // us in that case. history.listen(EventActions.routeDidChange); - ReactError({ - render: React.render, - params: [( - - {redirectRoute} - {getRoutes(type, subdomain)} - - ), - document.getElementById('main')], - errors: [{ - error: ResourceNotFoundError, - handler: ResourceNotFoundErrorHandler - }] - }); + React.render(( + + {redirectRoute} + {getRoutes(type, subdomain)} + + ), document.getElementById('main')); // Send the applicationDidBoot event to the third-party stores EventActions.applicationDidBoot(settings); diff --git a/js/components/ascribe_detail/edition_container.js b/js/components/ascribe_detail/edition_container.js index af7b7928..189f7e60 100644 --- a/js/components/ascribe_detail/edition_container.js +++ b/js/components/ascribe_detail/edition_container.js @@ -3,6 +3,9 @@ import React from 'react'; import { History } from 'react-router'; +import ReactError from '../../mixins/react_error'; +import { ResourceNotFoundError } from '../../models/errors'; + import EditionActions from '../../actions/edition_actions'; import EditionStore from '../../stores/edition_store'; @@ -10,6 +13,7 @@ import Edition from './edition'; import AscribeSpinner from '../ascribe_spinner'; +import { getLangText } from '../../utils/lang_utils'; import { setDocumentTitle } from '../../utils/dom_utils'; @@ -21,7 +25,7 @@ let EditionContainer = React.createClass({ params: React.PropTypes.object }, - mixins: [History], + mixins: [History, ReactError], getInitialState() { return EditionStore.getState(); @@ -33,14 +37,7 @@ let EditionContainer = React.createClass({ // as it will otherwise display wrong/old data once the user loads // the edition detail a second time EditionActions.updateEdition({}); - EditionStore.listen(this.onChange); - - // Every time we enter the edition detail page, just reset the edition - // store as it will otherwise display wrong/old data once the user loads - // the edition detail a second time - EditionActions.updateEdition({}); - this.loadEdition(); }, @@ -57,9 +54,7 @@ let EditionContainer = React.createClass({ const { editionError } = this.state; if(editionError && editionError.status === 404) { - // Even though the /404 path doesn't really exist, - // we can still redirect there and the page will show up - this.history.pushState(null, '/404'); + this.throws(new ResourceNotFoundError(getLangText('Ups, the edition you\'re looking for doesn\'t exist.'))); } }, diff --git a/js/components/ascribe_detail/piece_container.js b/js/components/ascribe_detail/piece_container.js index 187c53c1..5198e6ac 100644 --- a/js/components/ascribe_detail/piece_container.js +++ b/js/components/ascribe_detail/piece_container.js @@ -4,6 +4,9 @@ import React from 'react'; import { History } from 'react-router'; import Moment from 'moment'; +import ReactError from '../../mixins/react_error'; +import { ResourceNotFoundError } from '../../models/errors'; + import PieceActions from '../../actions/piece_actions'; import PieceStore from '../../stores/piece_store'; @@ -53,7 +56,7 @@ let PieceContainer = React.createClass({ params: React.PropTypes.object }, - mixins: [History], + mixins: [History, ReactError], getInitialState() { return mergeOptions( @@ -91,9 +94,7 @@ let PieceContainer = React.createClass({ const { pieceError } = this.state; if(pieceError && pieceError.status === 404) { - // Even though this path doesn't exist we can redirect - // to it as it catches all unknown paths - this.history.pushState(null, '/404'); + this.throws(new ResourceNotFoundError(getLangText('Ups, the piece you\'re looking for doesn\'t exist.'))); } }, diff --git a/js/components/error_not_found_page.js b/js/components/error_not_found_page.js index 61f83196..670f8629 100644 --- a/js/components/error_not_found_page.js +++ b/js/components/error_not_found_page.js @@ -6,6 +6,16 @@ import { getLangText } from '../utils/lang_utils'; let ErrorNotFoundPage = React.createClass({ + propTypes: { + message: React.PropTypes.string + }, + + getDefaultProps() { + return { + message: getLangText('Ups, the page you are looking for does not exist.') + }; + }, + render() { return (
@@ -13,7 +23,7 @@ let ErrorNotFoundPage = React.createClass({

404

- {getLangText('Ups, the page you are looking for does not exist.')} + {this.props.message}

diff --git a/js/mixins/react_error.js b/js/mixins/react_error.js new file mode 100644 index 00000000..0b7669de --- /dev/null +++ b/js/mixins/react_error.js @@ -0,0 +1,12 @@ +'use strict'; + +import invariant from 'invariant'; + +const ReactError = { + throws(err) { + invariant(err.handler, 'You need to specify a `handler` for this error'); + err.handler(this, err); + } +}; + +export default ReactError; diff --git a/js/models/errors.js b/js/models/errors.js new file mode 100644 index 00000000..68d97c86 --- /dev/null +++ b/js/models/errors.js @@ -0,0 +1,24 @@ +'use strict'; + +import React from 'react'; + +import ErrorNotFoundPage from '../components/error_not_found_page'; + + +export class ResourceNotFoundError extends Error { + constructor(message) { + super(message); + this.name = this.constructor.name; + this.message = message; + Error.captureStackTrace(this, this.constructor.name); + } + + handler(component, err) { + if(!component.state._monkeyPatched) { + component.render = () => ; + component.setState({ + _monkeyPatched: true + }); + } + } +} diff --git a/js/react_error.js b/js/react_error.js deleted file mode 100644 index 61fb4a70..00000000 --- a/js/react_error.js +++ /dev/null @@ -1,42 +0,0 @@ -'use strict'; - -function _transformErrorsListToHashMap(errors) { - return errors.reduce((errObj, { error, handler }) => { - const errorName = error.name; - if(!errObj[errorName]) { - errObj[errorName] = { - error: error, - handler: handler - }; - return errObj; - } else { - throw new Error('You\'re trying to override the error handler for ' + errorName + ' by specifying it twice.'); - } - }, {}); -} - -export function ReactError({ render, params, errors }) { - errors = _transformErrorsListToHashMap(errors); - try { - // use react's render function + parameters to render - // the application - render(...params); - } catch(err) { - const potErrorRoutine = errors[err.name]; - if(potErrorRoutine) { - potErrorRoutine.handler(err); - } else { - console.error(err.stack); - } - } -} - -// Followed: http://stackoverflow.com/a/32749533/1263876 for extending errors -export class ResourceNotFoundError extends Error { - constructor(message) { - super(message); - this.name = this.constructor.name; - this.message = message; - Error.captureStackTrace(this, this.constructor.name); - } -} \ No newline at end of file diff --git a/js/react_error_handlers.js b/js/react_error_handlers.js deleted file mode 100644 index 57366603..00000000 --- a/js/react_error_handlers.js +++ /dev/null @@ -1,5 +0,0 @@ -'use strict'; - -export function ResourceNotFoundErrorHandler() { - console.log(arguments); -} \ No newline at end of file From 4fe10bb8871c5194408dc84174301fc2466a0be4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Mon, 30 Nov 2015 18:26:27 +0100 Subject: [PATCH 07/11] Cosmetic changes for EditionContainer & PieceContainer --- js/components/ascribe_detail/edition_container.js | 3 ++- js/components/ascribe_detail/piece_container.js | 10 +--------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/js/components/ascribe_detail/edition_container.js b/js/components/ascribe_detail/edition_container.js index 189f7e60..eac8fcb3 100644 --- a/js/components/ascribe_detail/edition_container.js +++ b/js/components/ascribe_detail/edition_container.js @@ -32,12 +32,13 @@ let EditionContainer = React.createClass({ }, componentDidMount() { + EditionStore.listen(this.onChange); + // Every time we're entering the edition detail page, // just reset the edition that is saved in the edition store // as it will otherwise display wrong/old data once the user loads // the edition detail a second time EditionActions.updateEdition({}); - EditionStore.listen(this.onChange); this.loadEdition(); }, diff --git a/js/components/ascribe_detail/piece_container.js b/js/components/ascribe_detail/piece_container.js index 5198e6ac..86c7fc74 100644 --- a/js/components/ascribe_detail/piece_container.js +++ b/js/components/ascribe_detail/piece_container.js @@ -70,24 +70,16 @@ let PieceContainer = React.createClass({ }, componentDidMount() { - // Every time we're entering the piece detail page, - // just reset the piece that is saved in the piece store - // as it will otherwise display wrong/old data once the user loads - // the piece detail a second time - PieceActions.updatePiece({}); - UserStore.listen(this.onChange); PieceListStore.listen(this.onChange); PieceStore.listen(this.onChange); + // Every time we enter the piece detail page, just reset the piece // store as it will otherwise display wrong/old data once the user loads // the piece detail a second time PieceActions.updatePiece({}); - this.loadPiece(); - UserActions.fetchCurrentUser(); - PieceActions.fetchOne(this.props.params.pieceId); }, componentDidUpdate() { From 3374862edfc6eea8c5a38db72e2e515ef1f9ac83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Mon, 7 Dec 2015 10:57:49 +0100 Subject: [PATCH 08/11] Make monkeyPatched key specific to component's displayName --- js/components/ascribe_detail/edition_container.js | 2 +- js/components/ascribe_detail/piece_container.js | 2 +- js/models/errors.js | 8 ++++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/js/components/ascribe_detail/edition_container.js b/js/components/ascribe_detail/edition_container.js index eac8fcb3..c49b87ba 100644 --- a/js/components/ascribe_detail/edition_container.js +++ b/js/components/ascribe_detail/edition_container.js @@ -55,7 +55,7 @@ let EditionContainer = React.createClass({ const { editionError } = this.state; if(editionError && editionError.status === 404) { - this.throws(new ResourceNotFoundError(getLangText('Ups, the edition you\'re looking for doesn\'t exist.'))); + this.throws(new ResourceNotFoundError(getLangText('Oops, the edition you\'re looking for doesn\'t exist.'))); } }, diff --git a/js/components/ascribe_detail/piece_container.js b/js/components/ascribe_detail/piece_container.js index 86c7fc74..0b4cfbb1 100644 --- a/js/components/ascribe_detail/piece_container.js +++ b/js/components/ascribe_detail/piece_container.js @@ -86,7 +86,7 @@ let PieceContainer = React.createClass({ const { pieceError } = this.state; if(pieceError && pieceError.status === 404) { - this.throws(new ResourceNotFoundError(getLangText('Ups, the piece you\'re looking for doesn\'t exist.'))); + this.throws(new ResourceNotFoundError(getLangText('Oops, the piece you\'re looking for doesn\'t exist.'))); } }, diff --git a/js/models/errors.js b/js/models/errors.js index 68d97c86..d7843d30 100644 --- a/js/models/errors.js +++ b/js/models/errors.js @@ -14,10 +14,14 @@ export class ResourceNotFoundError extends Error { } handler(component, err) { - if(!component.state._monkeyPatched) { + const { displayName } = component.constructor; + const monkeyPatchedKey = typeof displayName === 'string' ? `_${displayName}MonkeyPatched` + : '_monkeyPatched'; + + if(!component.state[monkeyPatchedKey]) { component.render = () => ; component.setState({ - _monkeyPatched: true + [monkeyPatchedKey]: true }); } } From 4f39103baf44d9ebbe280afd26dc83293df23aaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Mon, 7 Dec 2015 10:59:07 +0100 Subject: [PATCH 09/11] Use Oops --- js/components/error_not_found_page.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/components/error_not_found_page.js b/js/components/error_not_found_page.js index 670f8629..64776f2f 100644 --- a/js/components/error_not_found_page.js +++ b/js/components/error_not_found_page.js @@ -12,7 +12,7 @@ let ErrorNotFoundPage = React.createClass({ getDefaultProps() { return { - message: getLangText('Ups, the page you are looking for does not exist.') + message: getLangText('Oops, the page you are looking for does not exist.') }; }, From 0a67766abaf9e71f9278695976253e560504c3b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Mon, 7 Dec 2015 11:30:45 +0100 Subject: [PATCH 10/11] Include PR Feedback: Check if Error.captureStackTrace is available --- js/mixins/react_error.js | 3 ++- js/models/errors.js | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/js/mixins/react_error.js b/js/mixins/react_error.js index 0b7669de..1169a069 100644 --- a/js/mixins/react_error.js +++ b/js/mixins/react_error.js @@ -4,7 +4,8 @@ import invariant from 'invariant'; const ReactError = { throws(err) { - invariant(err.handler, 'You need to specify a `handler` for this error'); + invariant(err.handler, 'Error thrown to ReactError did not have a `handler` function'); + console.logGlobal('Error thrown to ReactError did not have a `handler` function'); err.handler(this, err); } }; diff --git a/js/models/errors.js b/js/models/errors.js index d7843d30..e748d953 100644 --- a/js/models/errors.js +++ b/js/models/errors.js @@ -10,7 +10,12 @@ export class ResourceNotFoundError extends Error { super(message); this.name = this.constructor.name; this.message = message; - Error.captureStackTrace(this, this.constructor.name); + + // `captureStackTrace` might not be available in IE: + // - http://stackoverflow.com/a/8460753/1263876 + if(Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor.name); + } } handler(component, err) { From 3ce63c33bf9999a989c757e551eed0155c13529b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Daubensch=C3=BCtz?= Date: Mon, 7 Dec 2015 11:48:28 +0100 Subject: [PATCH 11/11] Finalize ReactError component --- js/components/ascribe_detail/edition_container.js | 2 +- js/components/ascribe_detail/piece_container.js | 2 +- js/components/error_not_found_page.js | 2 +- js/mixins/react_error.js | 9 ++++++--- js/models/errors.js | 4 +--- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/js/components/ascribe_detail/edition_container.js b/js/components/ascribe_detail/edition_container.js index c49b87ba..fdc18c39 100644 --- a/js/components/ascribe_detail/edition_container.js +++ b/js/components/ascribe_detail/edition_container.js @@ -55,7 +55,7 @@ let EditionContainer = React.createClass({ const { editionError } = this.state; if(editionError && editionError.status === 404) { - this.throws(new ResourceNotFoundError(getLangText('Oops, the edition you\'re looking for doesn\'t exist.'))); + this.throws(new ResourceNotFoundError(getLangText("Oops, the edition you're looking for doesn't exist."))); } }, diff --git a/js/components/ascribe_detail/piece_container.js b/js/components/ascribe_detail/piece_container.js index 0b4cfbb1..44b9357c 100644 --- a/js/components/ascribe_detail/piece_container.js +++ b/js/components/ascribe_detail/piece_container.js @@ -86,7 +86,7 @@ let PieceContainer = React.createClass({ const { pieceError } = this.state; if(pieceError && pieceError.status === 404) { - this.throws(new ResourceNotFoundError(getLangText('Oops, the piece you\'re looking for doesn\'t exist.'))); + this.throws(new ResourceNotFoundError(getLangText("Oops, the piece you're looking for doesn't exist."))); } }, diff --git a/js/components/error_not_found_page.js b/js/components/error_not_found_page.js index 64776f2f..0e111ce7 100644 --- a/js/components/error_not_found_page.js +++ b/js/components/error_not_found_page.js @@ -12,7 +12,7 @@ let ErrorNotFoundPage = React.createClass({ getDefaultProps() { return { - message: getLangText('Oops, the page you are looking for does not exist.') + message: getLangText("Oops, the page you are looking for doesn't exist.") }; }, diff --git a/js/mixins/react_error.js b/js/mixins/react_error.js index 1169a069..14f33a61 100644 --- a/js/mixins/react_error.js +++ b/js/mixins/react_error.js @@ -4,9 +4,12 @@ import invariant from 'invariant'; const ReactError = { throws(err) { - invariant(err.handler, 'Error thrown to ReactError did not have a `handler` function'); - console.logGlobal('Error thrown to ReactError did not have a `handler` function'); - err.handler(this, err); + if(!err.handler) { + invariant(err.handler, 'Error thrown to ReactError did not have a `handler` function'); + console.logGlobal('Error thrown to ReactError did not have a `handler` function'); + } else { + err.handler(this, err); + } } }; diff --git a/js/models/errors.js b/js/models/errors.js index e748d953..4573afe4 100644 --- a/js/models/errors.js +++ b/js/models/errors.js @@ -19,9 +19,7 @@ export class ResourceNotFoundError extends Error { } handler(component, err) { - const { displayName } = component.constructor; - const monkeyPatchedKey = typeof displayName === 'string' ? `_${displayName}MonkeyPatched` - : '_monkeyPatched'; + const monkeyPatchedKey = `_${this.name}MonkeyPatched`; if(!component.state[monkeyPatchedKey]) { component.render = () => ;