From 259d40c6aa150453671008a483754cb16d1da999 Mon Sep 17 00:00:00 2001 From: Danny Qiu Date: Mon, 30 Oct 2017 13:02:06 -0400 Subject: Add dialog to reload on service crash --- src/models/Service.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/models/Service.js b/src/models/Service.js index 7a0310ebc..08dbc0b5f 100644 --- a/src/models/Service.js +++ b/src/models/Service.js @@ -112,6 +112,16 @@ export default class Service { frameName, options, })); + + this.webview.addEventListener('crashed', (e) => { + console.log(e); + let reload = confirm('Service crashed. Reload?'); + if (reload) { + store.actions.service.reload({ + serviceId: this.id + }); + } + }); } initializeWebViewListener() { -- cgit v1.2.3-70-g09d2 From 11fd7e7b06233cdba4adaf75f3cea19c7dbbdb8c Mon Sep 17 00:00:00 2001 From: Danny Qiu Date: Mon, 30 Oct 2017 17:39:48 -0400 Subject: Fix lint errors --- src/models/Service.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/models/Service.js b/src/models/Service.js index 08dbc0b5f..89d6748b5 100644 --- a/src/models/Service.js +++ b/src/models/Service.js @@ -115,10 +115,10 @@ export default class Service { this.webview.addEventListener('crashed', (e) => { console.log(e); - let reload = confirm('Service crashed. Reload?'); + const reload = window.confirm('Service crashed. Reload?'); // eslint-disable-line no-alert if (reload) { store.actions.service.reload({ - serviceId: this.id + serviceId: this.id, }); } }); -- cgit v1.2.3-70-g09d2 From fa48f676c91f289a00ef12d0a6476eb71cd02d88 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Tue, 7 Nov 2017 14:26:19 +0100 Subject: Remove cumbersome style nesting --- src/styles/button.scss | 108 ++++++++++++++++++++++++------------------------- 1 file changed, 53 insertions(+), 55 deletions(-) diff --git a/src/styles/button.scss b/src/styles/button.scss index c2dd91293..75d2cb1d4 100644 --- a/src/styles/button.scss +++ b/src/styles/button.scss @@ -1,74 +1,72 @@ @import './config.scss'; -.franz-form { - .franz-form__button { - position: relative; - background: $theme-brand-primary; - display: block; - padding: 10px 20px; - color: #FFF; - border-radius: 3px; - transition: background 0.5s; - text-align: center; +.franz-form__button { + position: relative; + background: $theme-brand-primary; + display: block; + padding: 10px 20px; + color: #FFF; + border-radius: 3px; + transition: background 0.5s; + text-align: center; - &:hover { - background: darken($theme-brand-primary, 5%); - } + &:hover { + background: darken($theme-brand-primary, 5%); + } - &:active { - transition: none; - background: lighten($theme-brand-primary, 5%); - } + &:active { + transition: none; + background: lighten($theme-brand-primary, 5%); + } - &:disabled { - opacity: 0.2; - } + &:disabled { + opacity: 0.2; + } - &.franz-form__button--secondary { - background: $theme-gray-lighter; - color: $theme-gray; + &.franz-form__button--secondary { + background: $theme-gray-lighter; + color: $theme-gray; - &:hover { - background: darken($theme-gray-lighter, 5%); - } + &:hover { + background: darken($theme-gray-lighter, 5%); + } - &:active { - background: lighten($theme-gray-lighter, 5%); - } + &:active { + background: lighten($theme-gray-lighter, 5%); } + } - &.franz-form__button--danger { - background: $theme-brand-danger; + &.franz-form__button--danger { + background: $theme-brand-danger; - &:hover { - background: darken($theme-brand-danger, 5%); - } + &:hover { + background: darken($theme-brand-danger, 5%); + } - &:active { - background: lighten($theme-brand-danger, 5%); - } + &:active { + background: lighten($theme-brand-danger, 5%); } + } - &.franz-form__button--inverted { - background: none; - padding: 10px 20px; - border: 2px solid $theme-brand-primary; - color: $theme-brand-primary; - transition: background 0.5s, color 0.5s; + &.franz-form__button--inverted { + background: none; + padding: 10px 20px; + border: 2px solid $theme-brand-primary; + color: $theme-brand-primary; + transition: background 0.5s, color 0.5s; - &:hover { - background: darken($theme-brand-primary, 5%); - color: #FFF; - } + &:hover { + background: darken($theme-brand-primary, 5%); + color: #FFF; } + } - .loader { - position: relative; - width: 20px; - height: 12px; - z-index: 9999; - display: inline-block; - margin-right: 5px; - } + .loader { + position: relative; + width: 20px; + height: 12px; + z-index: 9999; + display: inline-block; + margin-right: 5px; } } -- cgit v1.2.3-70-g09d2 From dd307e3deb14f1738029ea38f7c1de7893455283 Mon Sep 17 00:00:00 2001 From: Stefan Malzner Date: Tue, 7 Nov 2017 15:29:31 +0100 Subject: feature(Service): Add webview crash handler to display a user friendly message --- .vscode/launch.json | 13 ++++ .vscode/tasks.json | 18 +++++ src/components/services/content/ServiceWebview.js | 10 +++ src/components/services/content/Services.js | 3 + .../services/content/WebviewCrashHandler.js | 81 ++++++++++++++++++++++ src/containers/layout/AppLayoutContainer.js | 2 +- src/i18n/locales/en-US.json | 6 +- src/models/Service.js | 15 ++-- src/styles/services.scss | 19 +++-- src/styles/type.scss | 5 ++ 10 files changed, 156 insertions(+), 16 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json create mode 100644 src/components/services/content/WebviewCrashHandler.js diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..7d14571f5 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Electron Main", + "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron", + "program": "${workspaceFolder}/build/index.js", + "protocol": "inspector" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000..a9fb57b06 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,18 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "dev", + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "type": "npm", + "script": "lint", + "group": "test" + } + ] +} \ No newline at end of file diff --git a/src/components/services/content/ServiceWebview.js b/src/components/services/content/ServiceWebview.js index 3ee3155be..cd59e0a8a 100644 --- a/src/components/services/content/ServiceWebview.js +++ b/src/components/services/content/ServiceWebview.js @@ -7,12 +7,14 @@ import classnames from 'classnames'; import ServiceModel from '../../../models/Service'; import StatusBarTargetUrl from '../../ui/StatusBarTargetUrl'; +import WebviewCrashHandler from './WebviewCrashHandler'; @observer export default class ServiceWebview extends Component { static propTypes = { service: PropTypes.instanceOf(ServiceModel).isRequired, setWebviewReference: PropTypes.func.isRequired, + reload: PropTypes.func.isRequired, }; static defaultProps = { @@ -53,6 +55,7 @@ export default class ServiceWebview extends Component { const { service, setWebviewReference, + reload, } = this.props; const webviewClasses = classnames({ @@ -70,6 +73,13 @@ export default class ServiceWebview extends Component { return (
+ {service.hasCrashed && ( + + )} { this.webview = element; }} diff --git a/src/components/services/content/Services.js b/src/components/services/content/Services.js index 03c68b06f..bad525d22 100644 --- a/src/components/services/content/Services.js +++ b/src/components/services/content/Services.js @@ -25,6 +25,7 @@ export default class Services extends Component { setWebviewReference: PropTypes.func.isRequired, handleIPCMessage: PropTypes.func.isRequired, openWindow: PropTypes.func.isRequired, + reload: PropTypes.func.isRequired, }; static defaultProps = { @@ -42,6 +43,7 @@ export default class Services extends Component { handleIPCMessage, setWebviewReference, openWindow, + reload, } = this.props; const { intl } = this.context; @@ -73,6 +75,7 @@ export default class Services extends Component { handleIPCMessage={handleIPCMessage} setWebviewReference={setWebviewReference} openWindow={openWindow} + reload={() => reload({ serviceId: service.id })} /> ))}
diff --git a/src/components/services/content/WebviewCrashHandler.js b/src/components/services/content/WebviewCrashHandler.js new file mode 100644 index 000000000..24903f3c5 --- /dev/null +++ b/src/components/services/content/WebviewCrashHandler.js @@ -0,0 +1,81 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { observer } from 'mobx-react'; +import { defineMessages, intlShape } from 'react-intl'; + +import Button from '../../ui/Button'; + +const messages = defineMessages({ + headline: { + id: 'service.crashHandler.headline', + defaultMessage: '!!!Oh no!', + }, + text: { + id: 'service.crashHandler.text', + defaultMessage: '!!!{name} has caused an error.', + }, + action: { + id: 'service.crashHandler.action', + defaultMessage: '!!!Reload {name}', + }, + autoReload: { + id: 'service.crashHandler.autoReload', + defaultMessage: '!!!Trying to automatically restore {name} in {seconds} seconds', + }, +}); + +@observer +export default class ServiceWebview extends Component { + static propTypes = { + name: PropTypes.string.isRequired, + reload: PropTypes.func.isRequired, + }; + + static contextTypes = { + intl: intlShape, + }; + + state = { + countdown: 10000, + } + + componentDidMount() { + const { reload } = this.props; + + this.countdownInterval = setInterval(() => { + this.setState({ + countdown: this.state.countdown - this.countdownIntervalTimeout, + }); + + if (this.state.countdown <= 0) { + reload(); + clearInterval(this.countdownInterval); + } + }, this.countdownIntervalTimeout); + } + + countdownInterval = null; + countdownIntervalTimeout = 1000; + + render() { + const { name, reload } = this.props; + const { intl } = this.context; + + return ( +
+

{intl.formatMessage(messages.headline)}

+

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

+
+ ); + } +} diff --git a/src/containers/layout/AppLayoutContainer.js b/src/containers/layout/AppLayoutContainer.js index aa7f7952a..68ad1039e 100644 --- a/src/containers/layout/AppLayoutContainer.js +++ b/src/containers/layout/AppLayoutContainer.js @@ -92,11 +92,11 @@ export default class AppLayoutContainer extends Component { const servicesContainer = ( ); diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index b1d260f0a..0493b547f 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json @@ -165,5 +165,9 @@ "tabs.item.disableNotifications": "Disable notifications", "tabs.item.enableNotification": "Enable notifications", "tabs.item.disableService": "Disable service", - "tabs.item.deleteService": "Delete service" + "tabs.item.deleteService": "Delete service", + "service.crashHandler.headline": "Oh no!", + "service.crashHandler.text": "{name} has caused an error.", + "service.crashHandler.action": "Reload {name}", + "service.crashHandler.autoReload": "Trying to automatically restore {name} in {seconds} seconds" } diff --git a/src/models/Service.js b/src/models/Service.js index c7276821a..dc53807f7 100644 --- a/src/models/Service.js +++ b/src/models/Service.js @@ -23,6 +23,7 @@ export default class Service { @observable isNotificationEnabled = true; @observable isIndirectMessageBadgeEnabled = true; @observable customIconUrl = ''; + @observable hasCrashed = false; constructor(data, recipe) { if (!data) { @@ -118,14 +119,12 @@ export default class Service { options, })); - this.webview.addEventListener('crashed', (e) => { - console.log(e); - const reload = window.confirm('Service crashed. Reload?'); // eslint-disable-line no-alert - if (reload) { - store.actions.service.reload({ - serviceId: this.id, - }); - } + this.webview.addEventListener('did-start-loading', () => { + this.hasCrashed = false; + }); + + this.webview.addEventListener('crashed', () => { + this.hasCrashed = true; }); } diff --git a/src/styles/services.scss b/src/styles/services.scss index 3347ea9d7..95738f123 100644 --- a/src/styles/services.scss +++ b/src/styles/services.scss @@ -8,7 +8,8 @@ background: #FFF; order: 5; - .services__webview { + .services__webview, + .services__crash-handler { position: absolute; width: 100%; top: 0; @@ -38,7 +39,8 @@ } } - .services__no-service { + .services__no-service, + .services__crash-handler { display: flex; flex-direction: column; justify-content: center; @@ -51,10 +53,15 @@ color: $theme-gray-dark; } - a.button { - margin-top: 40px; - // color: #FFF; - // border-color: #FFF; + a.button, + button { + margin: 40px 0 20px; } } + + .services__crash-handler { + position: absolut; + z-index: 110; + } + } diff --git a/src/styles/type.scss b/src/styles/type.scss index 935a36f4b..cacbec482 100644 --- a/src/styles/type.scss +++ b/src/styles/type.scss @@ -71,3 +71,8 @@ a { .label { @include formLabel(); } + +.footnote { + font-size: 12px; + color: $theme-gray-light; +} \ No newline at end of file -- cgit v1.2.3-70-g09d2