Skip to content

Commit

Permalink
feat(Service): Add error screen for services that failed to load
Browse files Browse the repository at this point in the history
  • Loading branch information
adlk committed Dec 8, 2018
1 parent 8ec217f commit a5e7171
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { observer } from 'mobx-react';
import { defineMessages, intlShape } from 'react-intl';
import injectSheet from 'react-jss';

import Button from '../../../ui/Button';

import styles from './styles';

const messages = defineMessages({
headline: {
id: 'service.errorHandler.headline',
defaultMessage: '!!!Oh no!',
},
text: {
id: 'service.errorHandler.text',
defaultMessage: '!!!{name} has failed to load.',
},
action: {
id: 'service.errorHandler.action',
defaultMessage: '!!!Reload {name}',
},
editAction: {
id: 'service.errorHandler.editAction',
defaultMessage: '!!!Edit {name}',
},
errorMessage: {
id: 'service.errorHandler.message',
defaultMessage: '!!!Error: {error}',
},
});

export default @injectSheet(styles) @observer class WebviewCrashHandler extends Component {
static propTypes = {
name: PropTypes.string.isRequired,
reload: PropTypes.func.isRequired,
edit: PropTypes.func.isRequired,
errorMessage: PropTypes.string.isRequired,
classes: PropTypes.object.isRequired,
};

static contextTypes = {
intl: intlShape,
};

render() {
const {
name,
reload,
edit,
errorMessage,
classes,
} = this.props;
const { intl } = this.context;

return (
<div className={classes.component}>
<h1>{intl.formatMessage(messages.headline)}</h1>
<p>{intl.formatMessage(messages.text, { name })}</p>
<p>{intl.formatMessage(messages.errorMessage, {
error: errorMessage,
})}</p>
<div className={classes.buttonContainer}>
<Button
label={intl.formatMessage(messages.editAction, { name })}
buttonType="inverted"
onClick={() => edit()}
/>
<Button
label={intl.formatMessage(messages.action, { name })}
buttonType="inverted"
onClick={() => reload()}
/>
</div>
</div>
);
}
}
25 changes: 25 additions & 0 deletions src/components/services/content/ErrorHandlers/styles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export default {
component: {
left: 0,
position: 'absolute',
top: 0,
width: '100%',
zIndex: 0,
alignItems: 'center',
// background: $theme-gray-lighter;
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
textAlign: 'center',
},
buttonContainer: {
display: 'flex',
flexDirection: 'row',
height: 'auto',
margin: [40, 0, 20],

'& button': {
margin: [0, 10, 0, 10],
},
},
};
18 changes: 18 additions & 0 deletions src/components/services/content/ServiceWebview.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ import classnames from 'classnames';

import ServiceModel from '../../../models/Service';
import StatusBarTargetUrl from '../../ui/StatusBarTargetUrl';
import WebviewLoader from '../../ui/WebviewLoader';
import WebviewCrashHandler from './WebviewCrashHandler';
import WebviewErrorHandler from './ErrorHandlers/WebviewErrorHandler';
import ServiceDisabled from './ServiceDisabled';

export default @observer class ServiceWebview extends Component {
static propTypes = {
service: PropTypes.instanceOf(ServiceModel).isRequired,
setWebviewReference: PropTypes.func.isRequired,
reload: PropTypes.func.isRequired,
edit: PropTypes.func.isRequired,
isAppMuted: PropTypes.bool.isRequired,
enable: PropTypes.func.isRequired,
};
Expand Down Expand Up @@ -58,6 +61,7 @@ export default @observer class ServiceWebview extends Component {
service,
setWebviewReference,
reload,
edit,
isAppMuted,
enable,
} = this.props;
Expand Down Expand Up @@ -85,6 +89,20 @@ export default @observer class ServiceWebview extends Component {
reload={reload}
/>
)}
{service.isLoading && (
<WebviewLoader
loaded={!service.isLoading}
name={service.name}
/>
)}
{service.isError && (
<WebviewErrorHandler
name={service.recipe.name}
errorMessage={service.errorMessage}
reload={reload}
edit={edit}
/>
)}
{!service.isEnabled ? (
<ServiceDisabled
name={service.recipe.name}
Expand Down
3 changes: 3 additions & 0 deletions src/components/services/content/Services.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export default @observer class Services extends Component {
handleIPCMessage: PropTypes.func.isRequired,
openWindow: PropTypes.func.isRequired,
reload: PropTypes.func.isRequired,
openSettings: PropTypes.func.isRequired,
isAppMuted: PropTypes.bool.isRequired,
update: PropTypes.func.isRequired,
};
Expand All @@ -45,6 +46,7 @@ export default @observer class Services extends Component {
setWebviewReference,
openWindow,
reload,
openSettings,
isAppMuted,
update,
} = this.props;
Expand Down Expand Up @@ -79,6 +81,7 @@ export default @observer class Services extends Component {
setWebviewReference={setWebviewReference}
openWindow={openWindow}
reload={() => reload({ serviceId: service.id })}
edit={() => openSettings({ path: `services/edit/${service.id}` })}
isAppMuted={isAppMuted}
enable={() => update({
serviceId: service.id,
Expand Down
1 change: 1 addition & 0 deletions src/containers/layout/AppLayoutContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export default @inject('stores', 'actions') @observer class AppLayoutContainer e
setWebviewReference={setWebviewReference}
openWindow={openWindow}
reload={reload}
openSettings={openSettings}
isAppMuted={settings.all.app.isAppMuted}
update={updateService}
/>
Expand Down
5 changes: 5 additions & 0 deletions src/i18n/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,11 @@
"service.crashHandler.text": "{name} has caused an error.",
"service.crashHandler.action": "Reload {name}",
"service.crashHandler.autoReload": "Trying to automatically restore {name} in {seconds} seconds",
"service.errorHandler.headline": "Oh no!",
"service.errorHandler.text": "{name} has failed to load.",
"service.errorHandler.message": "Error: {error}",
"service.errorHandler.action": "Reload {name}",
"service.errorHandler.editAction": "Edit {name}",
"service.disabledHandler.headline": "{name} is disabled",
"service.disabledHandler.action": "Enable {name}",
"menu.edit": "Edit",
Expand Down
23 changes: 23 additions & 0 deletions src/models/Service.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { computed, observable, autorun } from 'mobx';
import path from 'path';
import normalizeUrl from 'normalize-url';

const debug = require('debug')('Franz:Service');

export default class Service {
id = '';
recipe = '';
Expand Down Expand Up @@ -31,6 +33,11 @@ export default class Service {
@observable isDarkModeEnabled = false;
@observable spellcheckerLanguage = null;
// @observable isFirstNavigation = true;
@observable isLoading = true;
@observable isError = false;
@observable errorMessage = '';
constructor(data, recipe) {
if (!data) {
console.error('Service config not valid');
Expand Down Expand Up @@ -150,9 +157,25 @@ export default class Service {
this.webview.addEventListener('did-start-loading', () => {
this.hasCrashed = false;
this.isLoading = true;
this.isError = false;
});
this.webview.addEventListener('did-stop-loading', () => {
this.isLoading = false;
});
this.webview.addEventListener('did-fail-load', (event) => {
debug('Service failed to load', this.name, event);
if (event.isMainFrame) {
this.isError = true;
this.errorMessage = event.errorDescription;
this.isLoading = false;
}
});
this.webview.addEventListener('crashed', () => {
debug('Service crashed', this.name);
this.hasCrashed = true;
});
}
Expand Down

0 comments on commit a5e7171

Please sign in to comment.