From a5e7171402eb27a4527a238d186c88ac03f8ffd7 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Sat, 8 Dec 2018 17:08:58 +0100 Subject: feat(Service): Add error screen for services that failed to load --- .../content/ErrorHandlers/WebviewErrorHandler.js | 79 ++++++++++++++++++++++ .../services/content/ErrorHandlers/styles.js | 25 +++++++ src/components/services/content/ServiceWebview.js | 18 +++++ src/components/services/content/Services.js | 3 + src/containers/layout/AppLayoutContainer.js | 1 + src/i18n/locales/en-US.json | 5 ++ src/models/Service.js | 23 +++++++ 7 files changed, 154 insertions(+) create mode 100644 src/components/services/content/ErrorHandlers/WebviewErrorHandler.js create mode 100644 src/components/services/content/ErrorHandlers/styles.js (limited to 'src') diff --git a/src/components/services/content/ErrorHandlers/WebviewErrorHandler.js b/src/components/services/content/ErrorHandlers/WebviewErrorHandler.js new file mode 100644 index 000000000..05a64209b --- /dev/null +++ b/src/components/services/content/ErrorHandlers/WebviewErrorHandler.js @@ -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 ( +
+

{intl.formatMessage(messages.headline)}

+

{intl.formatMessage(messages.text, { name })}

+

{intl.formatMessage(messages.errorMessage, { + error: errorMessage, + })}

+
+
+
+ ); + } +} diff --git a/src/components/services/content/ErrorHandlers/styles.js b/src/components/services/content/ErrorHandlers/styles.js new file mode 100644 index 000000000..f11386798 --- /dev/null +++ b/src/components/services/content/ErrorHandlers/styles.js @@ -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], + }, + }, +}; diff --git a/src/components/services/content/ServiceWebview.js b/src/components/services/content/ServiceWebview.js index 6e56de92f..b3def3fa5 100644 --- a/src/components/services/content/ServiceWebview.js +++ b/src/components/services/content/ServiceWebview.js @@ -7,7 +7,9 @@ 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 { @@ -15,6 +17,7 @@ export default @observer class ServiceWebview extends Component { 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, }; @@ -58,6 +61,7 @@ export default @observer class ServiceWebview extends Component { service, setWebviewReference, reload, + edit, isAppMuted, enable, } = this.props; @@ -85,6 +89,20 @@ export default @observer class ServiceWebview extends Component { reload={reload} /> )} + {service.isLoading && ( + + )} + {service.isError && ( + + )} {!service.isEnabled ? ( reload({ serviceId: service.id })} + edit={() => openSettings({ path: `services/edit/${service.id}` })} isAppMuted={isAppMuted} enable={() => update({ serviceId: service.id, diff --git a/src/containers/layout/AppLayoutContainer.js b/src/containers/layout/AppLayoutContainer.js index e1423bdaa..455df10d7 100644 --- a/src/containers/layout/AppLayoutContainer.js +++ b/src/containers/layout/AppLayoutContainer.js @@ -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} /> diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index 7ccf94a87..17af2b128 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -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", diff --git a/src/models/Service.js b/src/models/Service.js index deeb544d1..7ba9aa396 100644 --- a/src/models/Service.js +++ b/src/models/Service.js @@ -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 = ''; @@ -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'); @@ -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; }); } -- cgit v1.2.3-70-g09d2